mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-18 14:31:50 +00:00
changing 2D renderer from D3D to software rendering
This commit is contained in:
parent
2d10662dcb
commit
a6f39c05ad
15 changed files with 237 additions and 624 deletions
|
@ -14,10 +14,10 @@ shortcuts
|
|||
|
||||
mainwindow
|
||||
{
|
||||
positionx = 378;
|
||||
positionx = 245;
|
||||
sizewidth = 739;
|
||||
sizeheight = 572;
|
||||
windowstate = 2;
|
||||
sizewidth = 739;
|
||||
positiony = 175;
|
||||
positiony = 273;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
@ -112,9 +112,9 @@
|
|||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Rendering\Graphics.cs" />
|
||||
<Compile Include="Rendering\D3DGraphics.cs" />
|
||||
<Compile Include="Rendering\IResource.cs" />
|
||||
<Compile Include="Rendering\ManagedVertexBuffer.cs" />
|
||||
<Compile Include="Rendering\PixelColor.cs" />
|
||||
<Compile Include="Rendering\PTVertex.cs" />
|
||||
<Compile Include="Rendering\ReloadResourceDelegate.cs" />
|
||||
<Compile Include="Rendering\Renderer2D.cs" />
|
||||
|
@ -170,7 +170,7 @@
|
|||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>aximp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<Isolated>True</Isolated>
|
||||
</COMReference>
|
||||
<COMReference Include="CodeSense">
|
||||
<Guid>{665BF2B8-F41F-4EF4-A8D0-303FBFFC475E}</Guid>
|
||||
|
@ -178,7 +178,7 @@
|
|||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<Isolated>True</Isolated>
|
||||
</COMReference>
|
||||
<COMReference Include="stdole">
|
||||
<Guid>{00020430-0000-0000-C000-000000000046}</Guid>
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
#region ================== Variables
|
||||
|
||||
// Graphics
|
||||
protected Graphics graphics;
|
||||
protected D3DGraphics graphics;
|
||||
|
||||
// Disposing
|
||||
protected bool isdisposed = false;
|
||||
|
|
|
@ -42,6 +42,9 @@ namespace CodeImp.DoomBuilder
|
|||
[DllImport("user32.dll")]
|
||||
public static extern int LockWindowUpdate(IntPtr hwnd);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint="RtlZeroMemory", SetLastError=false)]
|
||||
public static extern void ZeroMemory(IntPtr dest, int size);
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constants
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace CodeImp.DoomBuilder
|
|||
private MapOptions options;
|
||||
private Configuration config;
|
||||
private EditMode mode;
|
||||
private Graphics graphics;
|
||||
private D3DGraphics graphics;
|
||||
private WAD tempwad;
|
||||
|
||||
// Disposing
|
||||
|
@ -72,7 +72,7 @@ namespace CodeImp.DoomBuilder
|
|||
public EditMode Mode { get { return mode; } }
|
||||
public bool IsChanged { get { return changed; } set { changed = value; } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
public Graphics Graphics { get { return graphics; } }
|
||||
public D3DGraphics Graphics { get { return graphics; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -122,7 +122,7 @@ namespace CodeImp.DoomBuilder
|
|||
this.options = options;
|
||||
|
||||
// Initiate graphics
|
||||
graphics = new Graphics(General.MainWindow.Display);
|
||||
graphics = new D3DGraphics(General.MainWindow.Display);
|
||||
if(!graphics.Initialize()) return false;
|
||||
|
||||
// Load game configuration
|
||||
|
@ -130,7 +130,6 @@ namespace CodeImp.DoomBuilder
|
|||
|
||||
// Create map data
|
||||
data = new MapSet();
|
||||
data.EnableRendering();
|
||||
|
||||
// Create temp wadfile
|
||||
tempwad = new WAD(General.MakeTempFilename());
|
||||
|
@ -155,7 +154,7 @@ namespace CodeImp.DoomBuilder
|
|||
this.options = options;
|
||||
|
||||
// Initiate graphics
|
||||
graphics = new Graphics(General.MainWindow.Display);
|
||||
graphics = new D3DGraphics(General.MainWindow.Display);
|
||||
if(!graphics.Initialize()) return false;
|
||||
|
||||
// Load game configuration
|
||||
|
@ -163,7 +162,6 @@ namespace CodeImp.DoomBuilder
|
|||
|
||||
// Create map data
|
||||
data = new MapSet();
|
||||
data.EnableRendering();
|
||||
|
||||
// Create temp wadfile
|
||||
tempwad = new WAD(General.MakeTempFilename());
|
||||
|
|
|
@ -260,6 +260,14 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
return x + ", " + y;
|
||||
}
|
||||
|
||||
// Transform
|
||||
public void Transform(Vector2D offset, Vector2D scale)
|
||||
{
|
||||
// Transform
|
||||
x = (x + offset.x) * scale.x;
|
||||
y = (y + offset.y) * scale.y;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace CodeImp.DoomBuilder.IO
|
|||
y = reader.ReadInt16();
|
||||
|
||||
// Create new item
|
||||
v = map.CreateVertex(new Vector2D(x, y));
|
||||
v = map.CreateVertex(x, y);
|
||||
|
||||
// Add it to the lookup table
|
||||
link.Add(i, v);
|
||||
|
|
|
@ -69,9 +69,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
private int action;
|
||||
private int tag;
|
||||
private byte[] args;
|
||||
|
||||
// Rendering
|
||||
private int bufferindex;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed = false;
|
||||
|
@ -85,7 +82,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
public Vertex End { get { return end; } }
|
||||
public Sidedef Front { get { return front; } }
|
||||
public Sidedef Back { get { return back; } }
|
||||
public int BufferIndex { get { return bufferindex; } set { bufferindex = value; } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
|
||||
#endregion
|
||||
|
@ -122,9 +118,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Remove from main list
|
||||
mainlistitem.List.Remove(mainlistitem);
|
||||
|
||||
// Remove from rendering buffer
|
||||
if(map.IsRenderEnabled) map.LinedefsBuffer.FreeItem(bufferindex);
|
||||
|
||||
// Detach from vertices
|
||||
start.DetachLinedef(startvertexlistitem);
|
||||
end.DetachLinedef(endvertexlistitem);
|
||||
|
@ -213,9 +206,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
// Updated
|
||||
updateneeded = false;
|
||||
|
||||
// If rendering is enabled, then update to buffer as well
|
||||
if(map.IsRenderEnabled && map.IsUpdating) UpdateToBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,72 +226,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
l.updateneeded = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Rendering
|
||||
|
||||
// This writes the vertex to buffer
|
||||
public void UpdateToBuffer()
|
||||
{
|
||||
PTVertex[] lineverts = new PTVertex[4];
|
||||
Vector2D delta;
|
||||
Vector2D normal;
|
||||
int color;
|
||||
float normallength;
|
||||
|
||||
// Not up to date? Then do that first (Update will call this method again)
|
||||
if(updateneeded) { Update(); return; }
|
||||
|
||||
// Delta vector
|
||||
delta = end.Position - start.Position;
|
||||
|
||||
// Recalculate values
|
||||
normal = new Vector2D(delta.x / length, delta.y / length);
|
||||
|
||||
// Single sided?
|
||||
if((front == null) || (back == null))
|
||||
{
|
||||
// Line has an action?
|
||||
if(action != 0)
|
||||
color = Graphics.RGB(140, 255, 140);
|
||||
else
|
||||
color = Graphics.RGB(255, 255, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Line has an action?
|
||||
if(action != 0)
|
||||
color = Graphics.RGB(50, 140, 50);
|
||||
else
|
||||
color = Graphics.RGB(140, 140, 140);
|
||||
}
|
||||
|
||||
// Calculate normal length
|
||||
normallength = NORMAL_LENGTH / General.Map.Graphics.Renderer2D.Scale;
|
||||
|
||||
// Create line normal
|
||||
lineverts[0].x = start.Position.x + delta.x * 0.5f;
|
||||
lineverts[0].y = start.Position.y + delta.y * 0.5f;
|
||||
lineverts[1].x = lineverts[0].x + normal.y * normallength;
|
||||
lineverts[1].y = lineverts[0].y - normal.x * normallength;
|
||||
lineverts[0].c = color;
|
||||
lineverts[1].c = color;
|
||||
|
||||
// Create line vertices
|
||||
lineverts[2].x = start.Position.x;
|
||||
lineverts[2].y = start.Position.y;
|
||||
lineverts[3].x = end.Position.x;
|
||||
lineverts[3].y = end.Position.y;
|
||||
lineverts[2].c = color;
|
||||
lineverts[3].c = color;
|
||||
|
||||
// Seek to start of item
|
||||
map.LinedefsBuffer.SeekToItem(bufferindex);
|
||||
|
||||
// Write vertices to buffer
|
||||
foreach(PTVertex v in lineverts) map.LinedefsBuffer.WriteItem(v);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
|
|
@ -32,14 +32,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
{
|
||||
internal class MapSet : IDisposable
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
// Minimum size for primitives in buffers
|
||||
private const int MIN_PRIMITIVE_COUNT = 500;
|
||||
private const int VERTS_PER_LINEDEF = 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
||||
// Map structures
|
||||
|
@ -48,12 +40,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
private LinkedList<Sidedef> sidedefs;
|
||||
private LinkedList<Sector> sectors;
|
||||
private LinkedList<Thing> things;
|
||||
|
||||
// Rendering
|
||||
private bool renderenabled = false;
|
||||
private int updating = 0;
|
||||
private ManagedVertexBuffer verts;
|
||||
private ManagedVertexBuffer lines;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed = false;
|
||||
|
@ -68,10 +54,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
public ICollection<Sector> Sectors { get { return sectors; } }
|
||||
public ICollection<Thing> Things { get { return things; } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
public bool IsRenderEnabled { get { return renderenabled; } }
|
||||
public bool IsUpdating { get { return updating > 0; } }
|
||||
public ManagedVertexBuffer VerticesBuffer { get { return verts; } }
|
||||
public ManagedVertexBuffer LinedefsBuffer { get { return lines; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -102,10 +84,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Already set isdisposed so that changes can be prohibited
|
||||
isdisposed = true;
|
||||
|
||||
// No more rendering
|
||||
DisableRendering();
|
||||
updating = 0;
|
||||
|
||||
// Dispose all things
|
||||
list = new ArrayList(things);
|
||||
foreach(Thing t in list) t.Dispose();
|
||||
|
@ -159,7 +137,7 @@ namespace CodeImp.DoomBuilder.Map
|
|||
foreach(Vertex v in vertices)
|
||||
{
|
||||
// Make new vertex
|
||||
Vertex nv = newset.CreateVertex(v.Position);
|
||||
Vertex nv = newset.CreateVertex(v.X, v.Y);
|
||||
vertexlink.Add(v, nv);
|
||||
}
|
||||
|
||||
|
@ -213,7 +191,7 @@ namespace CodeImp.DoomBuilder.Map
|
|||
}
|
||||
|
||||
// This creates a new vertex
|
||||
public Vertex CreateVertex(Vector2D pos)
|
||||
public Vertex CreateVertex(int x, int y)
|
||||
{
|
||||
LinkedListNode<Vertex> listitem;
|
||||
Vertex v;
|
||||
|
@ -222,15 +200,12 @@ namespace CodeImp.DoomBuilder.Map
|
|||
listitem = new LinkedListNode<Vertex>(null);
|
||||
|
||||
// Make the vertex
|
||||
v = new Vertex(this, listitem, pos);
|
||||
v = new Vertex(this, listitem, x, y);
|
||||
listitem.Value = v;
|
||||
|
||||
// Add vertex to the list
|
||||
vertices.AddLast(listitem);
|
||||
|
||||
// Add vertex to rendering bufer
|
||||
if(renderenabled) v.BufferIndex = verts.AddItem();
|
||||
|
||||
// Return result
|
||||
return v;
|
||||
}
|
||||
|
@ -251,9 +226,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Add linedef to the list
|
||||
linedefs.AddLast(listitem);
|
||||
|
||||
// Add linedef to rendering bufer
|
||||
if(renderenabled) l.BufferIndex = lines.AddItem();
|
||||
|
||||
// Return result
|
||||
return l;
|
||||
}
|
||||
|
@ -317,118 +289,16 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Return result
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Resources
|
||||
|
||||
// This reloads vertices into rendering buffer
|
||||
private void ReloadVertices()
|
||||
{
|
||||
// Update all vertices to buffer
|
||||
foreach(Vertex v in vertices) v.UpdateToBuffer();
|
||||
}
|
||||
|
||||
// This reloads linedefs into rendering buffer
|
||||
private void ReloadLinedefs()
|
||||
{
|
||||
// Update all linedefs to buffer
|
||||
foreach(Linedef l in linedefs) l.UpdateToBuffer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Rendering
|
||||
|
||||
// This enables rendering of map structures
|
||||
public void EnableRendering()
|
||||
{
|
||||
// Not already enabled?
|
||||
if(!renderenabled)
|
||||
{
|
||||
// Enable rendering
|
||||
renderenabled = true;
|
||||
|
||||
// Create buffers
|
||||
verts = new ManagedVertexBuffer(PTVertex.Stride * Vertex.BUFFERVERTICES, vertices.Count);
|
||||
lines = new ManagedVertexBuffer(PTVertex.Stride * Linedef.BUFFERVERTICES, linedefs.Count);
|
||||
#region ================== Updating
|
||||
|
||||
// Go for all vertices to add to the buffer
|
||||
foreach(Vertex v in vertices) v.BufferIndex = verts.AddItem();
|
||||
|
||||
// Go for all linedefs to add to the buffer
|
||||
foreach(Linedef l in linedefs) l.BufferIndex = lines.AddItem();
|
||||
|
||||
// Attach events
|
||||
verts.ReloadResources += new ReloadResourceDelegate(ReloadVertices);
|
||||
lines.ReloadResources += new ReloadResourceDelegate(ReloadLinedefs);
|
||||
}
|
||||
}
|
||||
|
||||
// This disables rendering of map structures
|
||||
public void DisableRendering()
|
||||
{
|
||||
// Disable rendering
|
||||
renderenabled = false;
|
||||
|
||||
// Stop any updating
|
||||
while(updating > 0) EndUpdate();
|
||||
|
||||
// Trash buffers
|
||||
if(verts != null) verts.Dispose();
|
||||
if(lines != null) lines.Dispose();
|
||||
verts = null;
|
||||
lines = null;
|
||||
}
|
||||
|
||||
// This locks the buffers for updates
|
||||
public void BeginUpdate()
|
||||
{
|
||||
// Not already updating
|
||||
if(updating == 0)
|
||||
{
|
||||
// Lock buffers for updating
|
||||
verts.BeginUpdate();
|
||||
lines.BeginUpdate();
|
||||
}
|
||||
|
||||
// Now updating
|
||||
updating++;
|
||||
}
|
||||
|
||||
// This unlocks the buffers
|
||||
public void EndUpdate()
|
||||
{
|
||||
// Updating?
|
||||
if(updating > 0)
|
||||
{
|
||||
// No longer updating
|
||||
updating--;
|
||||
|
||||
// Done updating?
|
||||
if(updating == 0)
|
||||
{
|
||||
// Unlock buffers
|
||||
verts.EndUpdate();
|
||||
lines.EndUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This updates all structures if needed
|
||||
public void Update()
|
||||
{
|
||||
// Updating begins now
|
||||
BeginUpdate();
|
||||
|
||||
// Update all vertices
|
||||
foreach(Vertex v in vertices) v.Update();
|
||||
|
||||
// Update all linedefs
|
||||
foreach(Linedef l in linedefs) l.Update();
|
||||
|
||||
// Updating has finished
|
||||
EndUpdate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -48,14 +48,11 @@ namespace CodeImp.DoomBuilder.Map
|
|||
private LinkedListNode<Vertex> mainlistitem;
|
||||
|
||||
// Position
|
||||
private int x, y;
|
||||
private Vector2D pos;
|
||||
|
||||
// References
|
||||
private LinkedList<Linedef> linedefs;
|
||||
|
||||
// Rendering
|
||||
private bool updateneeded;
|
||||
private int bufferindex;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed = false;
|
||||
|
@ -67,7 +64,8 @@ namespace CodeImp.DoomBuilder.Map
|
|||
public MapSet Map { get { return map; } }
|
||||
public ICollection<Linedef> Linedefs { get { return linedefs; } }
|
||||
public Vector2D Position { get { return pos; } }
|
||||
public int BufferIndex { get { return bufferindex; } set { bufferindex = value; } }
|
||||
public int X { get { return x; } }
|
||||
public int Y { get { return y; } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
|
||||
#endregion
|
||||
|
@ -75,14 +73,15 @@ namespace CodeImp.DoomBuilder.Map
|
|||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
public Vertex(MapSet map, LinkedListNode<Vertex> listitem, Vector2D pos)
|
||||
public Vertex(MapSet map, LinkedListNode<Vertex> listitem, int x, int y)
|
||||
{
|
||||
// Initialize
|
||||
this.map = map;
|
||||
this.linedefs = new LinkedList<Linedef>();
|
||||
this.mainlistitem = listitem;
|
||||
this.pos = pos;
|
||||
this.updateneeded = true;
|
||||
this.pos = new Vector2D(x, y);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -100,9 +99,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Remove from main list
|
||||
mainlistitem.List.Remove(mainlistitem);
|
||||
|
||||
// Remove from rendering buffer
|
||||
if(map.IsRenderEnabled) map.VerticesBuffer.FreeItem(bufferindex);
|
||||
|
||||
// Dispose the lines that are attached to this vertex
|
||||
// because a linedef cannot exist without 2 vertices.
|
||||
foreach(Linedef l in linedefs) l.Dispose();
|
||||
|
@ -138,55 +134,9 @@ namespace CodeImp.DoomBuilder.Map
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This rounds the coordinates to integrals
|
||||
public void Round()
|
||||
{
|
||||
// Round to integrals
|
||||
pos.x = (float)Math.Round(pos.x);
|
||||
pos.y = (float)Math.Round(pos.y);
|
||||
updateneeded = true;
|
||||
}
|
||||
|
||||
// This updates the vertex when changes have been made
|
||||
public void Update()
|
||||
{
|
||||
// Update if needed
|
||||
if(updateneeded)
|
||||
{
|
||||
// Updated
|
||||
updateneeded = false;
|
||||
|
||||
// If rendering is enabled, then update to buffer as well
|
||||
if(map.IsRenderEnabled && map.IsUpdating) UpdateToBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Rendering
|
||||
|
||||
// This writes the vertex to buffer
|
||||
public void UpdateToBuffer()
|
||||
{
|
||||
PTVertex v = new PTVertex();
|
||||
|
||||
// Not up to date? Then do that first (Update will call this method again)
|
||||
if(updateneeded) { Update(); return; }
|
||||
|
||||
// Seek to start of item
|
||||
map.VerticesBuffer.SeekToItem(bufferindex);
|
||||
|
||||
// Write vertices to buffer
|
||||
v.x = pos.x;
|
||||
v.y = pos.y;
|
||||
v.z = 0f;
|
||||
v.c = Color.SlateBlue.ToArgb();
|
||||
map.VerticesBuffer.WriteItem(v);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
// This returns the distance from given coordinates
|
||||
|
@ -211,11 +161,12 @@ namespace CodeImp.DoomBuilder.Map
|
|||
#region ================== Changes
|
||||
|
||||
// This moves the vertex
|
||||
public void Move(Vector2D newpos)
|
||||
public void Move(int newx, int newy)
|
||||
{
|
||||
// Change position
|
||||
pos = newpos;
|
||||
updateneeded = true;
|
||||
x = newx;
|
||||
y = newy;
|
||||
pos = new Vector2D(newx, newy);
|
||||
|
||||
// Let all lines know they need an update
|
||||
foreach(Linedef l in linedefs) l.NeedUpdate();
|
||||
|
|
|
@ -35,7 +35,7 @@ using SlimDX;
|
|||
|
||||
namespace CodeImp.DoomBuilder.Rendering
|
||||
{
|
||||
internal class Graphics : IDisposable
|
||||
internal class D3DGraphics : IDisposable
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
|
@ -50,7 +50,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
private int adapter;
|
||||
|
||||
// Main objects
|
||||
private Control rendertarget;
|
||||
private Panel rendertarget;
|
||||
private Capabilities devicecaps;
|
||||
private Device device;
|
||||
private Renderer2D renderer2d;
|
||||
|
@ -68,7 +68,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
public bool IsDisposed { get { return isdisposed; } }
|
||||
public Renderer2D Renderer2D { get { return renderer2d; } }
|
||||
public Renderer3D Renderer3D { get { return renderer3d; } }
|
||||
public Control RenderTarget { get { return rendertarget; } }
|
||||
public Panel RenderTarget { get { return rendertarget; } }
|
||||
public Viewport Viewport { get { return viewport; } }
|
||||
|
||||
#endregion
|
||||
|
@ -76,7 +76,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
public Graphics(Control rendertarget)
|
||||
public D3DGraphics(Panel rendertarget)
|
||||
{
|
||||
// Set render target
|
||||
this.rendertarget = rendertarget;
|
|
@ -1,266 +0,0 @@
|
|||
|
||||
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
||||
* This program is released under GNU General Public License
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Drawing;
|
||||
using SlimDX.Direct3D9;
|
||||
using System.ComponentModel;
|
||||
using CodeImp.DoomBuilder.Geometry;
|
||||
using SlimDX;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Rendering
|
||||
{
|
||||
internal class ManagedVertexBuffer : IDisposable, IResource
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
// Minimum items
|
||||
private const int MIN_ITEMS = 500;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Events / Delegates
|
||||
|
||||
public event ReloadResourceDelegate ReloadResources;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
||||
// Buffer info
|
||||
private int bytesperitem;
|
||||
private int maxitems;
|
||||
private LinkedList<int> freeitems;
|
||||
|
||||
// The vertexbuffer
|
||||
private VertexBuffer buffer;
|
||||
|
||||
// The stream for updating
|
||||
private GraphicsStream stream;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public VertexBuffer VertexBuffer { get { return buffer; } }
|
||||
public int ItemCapacity { get { return maxitems; } }
|
||||
public int ItemCount { get { return maxitems - freeitems.Count; } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
public ManagedVertexBuffer(int bytesperitem, int initialsize)
|
||||
{
|
||||
// Initialize
|
||||
this.bytesperitem = bytesperitem;
|
||||
this.maxitems = initialsize;
|
||||
if(this.maxitems < MIN_ITEMS) this.maxitems = MIN_ITEMS;
|
||||
this.freeitems = new LinkedList<int>();
|
||||
|
||||
// Add free items to list
|
||||
for(int i = 0; i < maxitems; i++) freeitems.AddLast(i);
|
||||
|
||||
// Create the buffer
|
||||
CreateBuffer();
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Diposer
|
||||
public void Dispose()
|
||||
{
|
||||
// Not already disposed?
|
||||
if(!isdisposed)
|
||||
{
|
||||
// Clean up
|
||||
UnloadResource();
|
||||
freeitems = null;
|
||||
|
||||
// Done
|
||||
isdisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Buffer
|
||||
|
||||
// This unloads the vertex buffer
|
||||
public void UnloadResource()
|
||||
{
|
||||
// Clean up
|
||||
if(stream != null) EndUpdate();
|
||||
if(buffer != null) buffer.Dispose();
|
||||
|
||||
// Done
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
// This reloads the vertex buffer
|
||||
public void ReloadResource()
|
||||
{
|
||||
// Create the buffer
|
||||
CreateBuffer();
|
||||
|
||||
// Signal that the buffer needs to be rebuild
|
||||
if(ReloadResources != null) ReloadResources();
|
||||
}
|
||||
|
||||
// This creates the buffer according to settings
|
||||
private void CreateBuffer()
|
||||
{
|
||||
// Create the buffer
|
||||
buffer = new VertexBuffer(General.Map.Graphics.Device, maxitems * bytesperitem,
|
||||
Usage.WriteOnly, PTVertex.Format, Pool.Managed);
|
||||
}
|
||||
|
||||
// This starts an update session
|
||||
public void BeginUpdate()
|
||||
{
|
||||
// Lock the buffer and get the stream
|
||||
stream = buffer.Lock(0, LockFlags.None);
|
||||
}
|
||||
|
||||
// This stops an update session
|
||||
public void EndUpdate()
|
||||
{
|
||||
// Unlock the buffer
|
||||
buffer.Unlock();
|
||||
stream.Dispose();
|
||||
stream = null;
|
||||
}
|
||||
|
||||
// This doubles the buffer size
|
||||
private void DoubleBuffer()
|
||||
{
|
||||
VertexBuffer newbuf;
|
||||
GraphicsStream newstream;
|
||||
byte[] copybuf;
|
||||
int newmaxitems;
|
||||
bool updating;
|
||||
|
||||
// Not in an updating session yet?
|
||||
if(stream == null)
|
||||
{
|
||||
// Start updating
|
||||
BeginUpdate();
|
||||
|
||||
// Remember to stop updating when done
|
||||
updating = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remember we want to keep the updating session open
|
||||
updating = true;
|
||||
}
|
||||
|
||||
// Increase size
|
||||
newmaxitems = maxitems * 2;
|
||||
|
||||
// Add free items to list
|
||||
for(int i = maxitems; i < newmaxitems; i++) freeitems.AddLast(i);
|
||||
|
||||
// Create a new buffer
|
||||
newbuf = new VertexBuffer(General.Map.Graphics.Device, newmaxitems * bytesperitem,
|
||||
Usage.WriteOnly, PTVertex.Format, Pool.Managed);
|
||||
|
||||
// Copy old data to new buffer
|
||||
newstream = newbuf.Lock(0, LockFlags.None);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
copybuf = new byte[maxitems * bytesperitem];
|
||||
stream.Read(copybuf, 0, copybuf.Length);
|
||||
newstream.Write(copybuf, 0, copybuf.Length);
|
||||
|
||||
// Dispose old buffer
|
||||
buffer.Unlock();
|
||||
stream.Dispose();
|
||||
buffer.Dispose();
|
||||
|
||||
// Switch to new buffer
|
||||
maxitems = newmaxitems;
|
||||
buffer = newbuf;
|
||||
stream = newstream;
|
||||
|
||||
// Stop updating session?
|
||||
if(!updating)
|
||||
{
|
||||
// Stop updating now
|
||||
EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Items
|
||||
|
||||
// This adds an item to the buffer and returns its unique index
|
||||
public int AddItem()
|
||||
{
|
||||
int itemindex;
|
||||
|
||||
// Double the buffer for more items when none are available
|
||||
if(freeitems.Count == 0) DoubleBuffer();
|
||||
|
||||
// Fetch the first free index from the list
|
||||
itemindex = freeitems.First.Value;
|
||||
freeitems.RemoveFirst();
|
||||
|
||||
// Return result
|
||||
return itemindex;
|
||||
}
|
||||
|
||||
// This frees an item
|
||||
public void FreeItem(int index)
|
||||
{
|
||||
// Add item back into the list
|
||||
freeitems.AddLast(index);
|
||||
}
|
||||
|
||||
// This seeks the stream to the position for a specific item
|
||||
public void SeekToItem(int index)
|
||||
{
|
||||
// Seek to item start position
|
||||
stream.Seek(index * bytesperitem, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// This allows writing to the stream
|
||||
public void WriteItem<T>(T item) where T : struct
|
||||
{
|
||||
// Write the item
|
||||
stream.Write(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
56
Source/Rendering/PixelColor.cs
Normal file
56
Source/Rendering/PixelColor.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
|
||||
namespace CodeImp.DoomBuilder.Rendering
|
||||
{
|
||||
public struct PixelColor
|
||||
{
|
||||
#region ================== Variables
|
||||
|
||||
// Members
|
||||
public byte b;
|
||||
public byte g;
|
||||
public byte r;
|
||||
public byte a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructors
|
||||
|
||||
// Constructor
|
||||
public PixelColor(byte a, byte r, byte g, byte b)
|
||||
{
|
||||
// Initialize
|
||||
this.a = a;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Static Methods
|
||||
|
||||
// Construct from color
|
||||
public static PixelColor FromColor(Color c)
|
||||
{
|
||||
return new PixelColor(c.A, c.R, c.G, c.B);
|
||||
}
|
||||
|
||||
// Construct from int
|
||||
public static PixelColor FromInt(int c)
|
||||
{
|
||||
return FromColor(Color.FromArgb(c));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -31,12 +31,13 @@ using SlimDX.Direct3D;
|
|||
using SlimDX.Direct3D9;
|
||||
using SlimDX;
|
||||
using CodeImp.DoomBuilder.Geometry;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Rendering
|
||||
{
|
||||
internal class Renderer2D : IDisposable
|
||||
internal unsafe class Renderer2D : IDisposable
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
|
@ -45,16 +46,20 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
#region ================== Variables
|
||||
|
||||
// Owner
|
||||
private Graphics graphics;
|
||||
private D3DGraphics graphics;
|
||||
|
||||
// Rendering memory
|
||||
private Bitmap image;
|
||||
private BitmapData pixeldata;
|
||||
private PixelColor* pixels;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
// View settings (world coordinates)
|
||||
private float scale;
|
||||
private float offsetx;
|
||||
private float offsety;
|
||||
|
||||
// Matrices
|
||||
private Matrix matproj, matview, matworld;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed = false;
|
||||
|
||||
|
@ -70,13 +75,16 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
||||
|
||||
// Constructor
|
||||
public Renderer2D(Graphics graphics)
|
||||
public Renderer2D(D3DGraphics graphics)
|
||||
{
|
||||
// Initialize
|
||||
this.graphics = graphics;
|
||||
|
||||
// Create image memory
|
||||
CreateMemory();
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
@ -88,6 +96,9 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
if(!isdisposed)
|
||||
{
|
||||
// Clean up
|
||||
graphics.RenderTarget.BackgroundImage = null;
|
||||
if(image != null) image.Dispose();
|
||||
pixels = null;
|
||||
|
||||
// Done
|
||||
isdisposed = true;
|
||||
|
@ -98,51 +109,52 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
|
||||
#region ================== Control
|
||||
|
||||
// This rebuilds matrices according to view settings
|
||||
private void SetupMatrices()
|
||||
// This is called resets when the device is reset
|
||||
// (when resized or display adapter was changed)
|
||||
public void Reset()
|
||||
{
|
||||
float width, height, left, top, right, bottom;
|
||||
|
||||
// Build projection matrix
|
||||
width = (float)graphics.RenderTarget.ClientSize.Width / scale;
|
||||
height = (float)graphics.RenderTarget.ClientSize.Height / scale;
|
||||
left = offsetx - width * 0.5f;
|
||||
top = offsety - height * 0.5f;
|
||||
right = offsetx + width * 0.5f;
|
||||
bottom = offsety + height * 0.5f;
|
||||
matproj = Matrix.OrthoOffCenterLH(left, right, top, bottom, -1f, 1f);
|
||||
|
||||
// World and view are fixed
|
||||
matview = Matrix.Identity;
|
||||
matworld = Matrix.Identity;
|
||||
// Re-create image memory
|
||||
CreateMemory();
|
||||
}
|
||||
|
||||
// This begins a drawing session
|
||||
public bool StartRendering()
|
||||
{
|
||||
// Can we render?
|
||||
if(graphics.StartRendering())
|
||||
{
|
||||
// Apply matrices
|
||||
graphics.Device.SetTransform(TransformState.Projection, matproj);
|
||||
graphics.Device.SetTransform(TransformState.View, matview);
|
||||
graphics.Device.SetTransform(TransformState.World, matworld);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot render
|
||||
return false;
|
||||
}
|
||||
// Allocates new image memory to render on
|
||||
public void CreateMemory()
|
||||
{
|
||||
// Get new width and height
|
||||
width = graphics.RenderTarget.ClientSize.Width;
|
||||
height = graphics.RenderTarget.ClientSize.Height;
|
||||
|
||||
// Trash old image
|
||||
graphics.RenderTarget.BackgroundImage = null;
|
||||
if(image != null) image.Dispose();
|
||||
|
||||
// Allocate memory
|
||||
image = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
||||
graphics.RenderTarget.BackgroundImage = image;
|
||||
}
|
||||
|
||||
// This begins a drawing session
|
||||
public unsafe bool StartRendering()
|
||||
{
|
||||
// Lock memory
|
||||
pixeldata = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
pixels = (PixelColor*)pixeldata.Scan0.ToPointer();
|
||||
|
||||
// Erase image
|
||||
General.ZeroMemory(pixeldata.Scan0, width * height * 4);
|
||||
|
||||
// Ready for rendering
|
||||
return true;
|
||||
}
|
||||
|
||||
// This ends a drawing session
|
||||
public void FinishRendering()
|
||||
{
|
||||
// Finish and present our drawing
|
||||
graphics.FinishRendering();
|
||||
// Unlock memory
|
||||
image.UnlockBits(pixeldata);
|
||||
|
||||
// Refresh
|
||||
graphics.RenderTarget.Invalidate();
|
||||
}
|
||||
|
||||
// This changes view position
|
||||
|
@ -151,9 +163,6 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
// Change position in world coordinates
|
||||
offsetx = x;
|
||||
offsety = y;
|
||||
|
||||
// Setup new matrices
|
||||
SetupMatrices();
|
||||
}
|
||||
|
||||
// This changes zoom
|
||||
|
@ -161,9 +170,6 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
{
|
||||
// Change zoom scale
|
||||
this.scale = scale;
|
||||
|
||||
// Setup new matrices
|
||||
SetupMatrices();
|
||||
|
||||
// Recalculate linedefs (normal lengths must be adjusted)
|
||||
foreach(Linedef l in General.Map.Data.Linedefs) l.NeedUpdate();
|
||||
|
@ -174,39 +180,102 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
{
|
||||
Vector3 mp, res;
|
||||
|
||||
// FIXME!
|
||||
|
||||
// Get mouse position in Vector3
|
||||
mp = new Vector3(mousepos.x, mousepos.y, 1f);
|
||||
//mp = new Vector3(mousepos.x, mousepos.y, 1f);
|
||||
|
||||
// Unproject
|
||||
res = mp.Unproject(graphics.Viewport, matproj, matview, matworld);
|
||||
//res = mp.Unproject(graphics.Viewport, matproj, matview, matworld);
|
||||
|
||||
// Return result
|
||||
return new Vector2D(res.X, res.Y);
|
||||
//return new Vector2D(res.X, res.Y);
|
||||
return new Vector2D();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Pixel Rendering
|
||||
|
||||
// This draws a pixel normally
|
||||
private void DrawPixelSolid(int x, int y, PixelColor c)
|
||||
{
|
||||
// Draw pixel when within range
|
||||
if((x >= 0) && (x < width) && (y >= 0) && (y < height))
|
||||
pixels[y * width + x] = c;
|
||||
}
|
||||
|
||||
// This draws a pixel alpha blended
|
||||
private void DrawPixelAlpha(int x, int y, PixelColor c)
|
||||
{
|
||||
// Draw only when within range
|
||||
if((x >= 0) && (x < width) && (y >= 0) && (y < height))
|
||||
{
|
||||
// Get the target pixel
|
||||
PixelColor* p = pixels + (y * width + x);
|
||||
|
||||
// Not drawn on target yet?
|
||||
if(*(int*)p == 0)
|
||||
{
|
||||
// Simply apply color to pixel
|
||||
*p = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blend with pixel
|
||||
if((int)p->a + (int)c.a > 255) p->a = 255; else p->a += c.a;
|
||||
p->r = (byte)((float)p->r * (1f - c.a) + (float)c.r * c.a);
|
||||
p->g = (byte)((float)p->g * (1f - c.a) + (float)c.g * c.a);
|
||||
p->b = (byte)((float)p->b * (1f - c.a) + (float)c.b * c.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// This draws a line normally
|
||||
private void DrawLineSolid(int x0, int y0, int x1, int y1, PixelColor c)
|
||||
{
|
||||
// TODO: See http://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm
|
||||
}
|
||||
|
||||
#region ================== Map Rendering
|
||||
|
||||
// This renders a set of Linedefs
|
||||
public void RenderLinedefs(MapSet map, ICollection<Linedef> linedefs)
|
||||
public unsafe void RenderLinedefs(MapSet map, ICollection<Linedef> linedefs)
|
||||
{
|
||||
// Any linedefs?
|
||||
if(linedefs.Count > 0)
|
||||
{
|
||||
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
||||
graphics.Device.SetStreamSource(0, map.LinedefsBuffer.VertexBuffer, 0, PTVertex.Stride);
|
||||
|
||||
foreach(Linedef l in linedefs)
|
||||
{
|
||||
graphics.Device.DrawPrimitives(PrimitiveType.LineList, l.BufferIndex * Linedef.BUFFERVERTICES, Linedef.RENDERPRIMITIVES);
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
// This renders a set of Linedefs
|
||||
public void RenderVertices(MapSet map, ICollection<Vertex> vertices)
|
||||
{
|
||||
Vector2D nv;
|
||||
Vector2D voffset = new Vector2D(-offsetx + (width * 0.5f) / scale, -offsety - (height * 0.5f) / scale);
|
||||
Vector2D vscale = new Vector2D(scale, -scale);
|
||||
PixelColor c = PixelColor.FromInt(-1);
|
||||
int x, y;
|
||||
|
||||
// Go for all vertices
|
||||
foreach(Vertex v in vertices)
|
||||
{
|
||||
// Transform vertex coordinates
|
||||
nv = v.Position;
|
||||
nv.Transform(voffset, vscale);
|
||||
x = (int)nv.x;
|
||||
y = (int)nv.y;
|
||||
|
||||
// Draw pixel here
|
||||
DrawPixelSolid(x, y, c);
|
||||
DrawPixelSolid(x + 1, y, c);
|
||||
DrawPixelSolid(x, y + 1, c);
|
||||
DrawPixelSolid(x - 1, y, c);
|
||||
DrawPixelSolid(x, y - 1, c);
|
||||
DrawPixelSolid(x + 1, y - 1, c);
|
||||
DrawPixelSolid(x + 1, y + 1, c);
|
||||
DrawPixelSolid(x - 1, y - 1, c);
|
||||
DrawPixelSolid(x - 1, y + 1, c);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
#region ================== Variables
|
||||
|
||||
// Owner
|
||||
private Graphics graphics;
|
||||
private D3DGraphics graphics;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed = false;
|
||||
|
@ -58,7 +58,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
public Renderer3D(Graphics graphics)
|
||||
public Renderer3D(D3DGraphics graphics)
|
||||
{
|
||||
// Initialize
|
||||
this.graphics = graphics;
|
||||
|
|
Loading…
Reference in a new issue