diff --git a/Source/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/BuilderModes/VisualModes/BaseVisualMode.cs index a92d3bb0..d52e0e74 100644 --- a/Source/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/BuilderModes/VisualModes/BaseVisualMode.cs @@ -97,13 +97,15 @@ namespace CodeImp.DoomBuilder.BuilderModes // This creates a visual sector protected override VisualSector CreateVisualSector(Sector s) { - return new BaseVisualSector(s); + BaseVisualSector vs = new BaseVisualSector(s); + return vs; } // This creates a visual thing protected override VisualThing CreateVisualThing(Thing t) { - return new BaseVisualThing(t); + BaseVisualThing vt = new BaseVisualThing(t); + if(vt.Setup()) return vt; else return null; } // This locks the target so that it isn't changed until unlocked @@ -167,7 +169,7 @@ namespace CodeImp.DoomBuilder.BuilderModes // Apply new target target = newtarget; } - + // This changes the target's height private void ChangeTargetHeight(int amount) { diff --git a/Source/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/BuilderModes/VisualModes/BaseVisualThing.cs index aceb2205..517f0aea 100644 --- a/Source/BuilderModes/VisualModes/BaseVisualThing.cs +++ b/Source/BuilderModes/VisualModes/BaseVisualThing.cs @@ -31,6 +31,8 @@ using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; #endregion @@ -39,29 +41,139 @@ namespace CodeImp.DoomBuilder.BuilderModes internal class BaseVisualThing : VisualThing { #region ================== Constants - + #endregion - + #region ================== Variables - + + private ThingTypeInfo info; + private bool isloaded; + private ImageData sprite; + #endregion - + #region ================== Properties - + #endregion - + #region ================== Constructor / Disposer - + // Constructor public BaseVisualThing(Thing t) : base(t) { + // Find thing information + info = General.Map.Config.GetThingInfo(Thing.Type); + // Find sprite texture + if(info.Sprite.Length > 0) + { + sprite = General.Map.Data.GetSpriteImage(info.Sprite); + if(sprite != null) sprite.AddReference(); + } + + // We have no destructor + GC.SuppressFinalize(this); } - + + // This builds the thing geometry. Returns false when nothing was created. + public virtual bool Setup() + { + if(sprite != null) + { + // Color to modulate sprite with (god knows what we could use this for) + PixelColor pc = new PixelColor(255, 255, 255, 255); + + // Find the sector in which the thing resides + Thing.DetermineSector(); + + // Check if the texture is loaded + isloaded = sprite.IsImageLoaded; + if(isloaded) + { + base.Texture = sprite; + + // Determine sprite size + float radius = sprite.ScaledWidth * 0.5f; + float height = sprite.ScaledHeight; + + // Determine texture coordinates + Vector2D t1 = new Vector2D(1.0f / sprite.ScaledWidth, 1.0f / sprite.ScaledHeight); + Vector2D t2 = new Vector2D(1.0f - t1.x, 1.0f - t1.y); + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, pc.ToInt(), t1.x, t2.y); + verts[1] = new WorldVertex(-radius, 0.0f, height, pc.ToInt(), t1.x, t1.y); + verts[2] = new WorldVertex(+radius, 0.0f, height, pc.ToInt(), t2.x, t1.y); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, pc.ToInt(), t2.x, t2.y); + SetVertices(verts); + } + else + { + base.Texture = General.Map.Data.Hourglass3D; + + // Determine sprite size + float radius = info.Width * 0.5f; + float height = info.Height; + + // Determine texture coordinates + Vector2D t1 = new Vector2D(1.0f / base.Texture.ScaledWidth, 1.0f / base.Texture.ScaledHeight); + Vector2D t2 = new Vector2D(1.0f - t1.x, 1.0f - t1.y); + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, pc.ToInt(), t1.x, t2.y); + verts[1] = new WorldVertex(-radius, 0.0f, height, pc.ToInt(), t1.x, t1.y); + verts[2] = new WorldVertex(+radius, 0.0f, height, pc.ToInt(), t2.x, t1.y); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, pc.ToInt(), t2.x, t2.y); + SetVertices(verts); + } + } + + // Setup position + Vector3D pos = Thing.Position; + if(Thing.Sector != null) pos.z += Thing.Sector.FloorHeight; + SetPosition(pos); + + // Done + return true; + } + + // Disposing + public override void Dispose() + { + if(!IsDisposed) + { + if(sprite != null) sprite.RemoveReference(); + } + + base.Dispose(); + } + #endregion - + #region ================== Methods - + + // This updates the thing when needed + public override void Update() + { + if(!isloaded) + { + // Rebuild sprite geometry when sprite is loaded + if(sprite.IsImageLoaded) + { + Setup(); + } + } + + // Let the base update + base.Update(); + } + #endregion } } diff --git a/Source/Data/SpriteImage.cs b/Source/Data/SpriteImage.cs index 423c7eca..94448522 100644 --- a/Source/Data/SpriteImage.cs +++ b/Source/Data/SpriteImage.cs @@ -72,7 +72,7 @@ namespace CodeImp.DoomBuilder.Data lumpdata.Read(membytes, 0, (int)lumpdata.Length); mem = new MemoryStream(membytes); mem.Seek(0, SeekOrigin.Begin); - + // Get a reader for the data reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); if(reader is UnknownImageReader) @@ -80,19 +80,21 @@ namespace CodeImp.DoomBuilder.Data // Data is in an unknown format! General.WriteLogLine("WARNING: Sprite lump '" + Name + "' data format could not be read!"); } - + // Read data as bitmap mem.Seek(0, SeekOrigin.Begin); if(bitmap != null) bitmap.Dispose(); bitmap = reader.ReadAsBitmap(mem); if(bitmap == null) return; - + // Done mem.Dispose(); // Get width and height from image width = bitmap.Size.Width; height = bitmap.Size.Height; + scaledwidth = (float)bitmap.Size.Width; + scaledheight = (float)bitmap.Size.Height; } else { diff --git a/Source/Rendering/Renderer3D.cs b/Source/Rendering/Renderer3D.cs index 9ebaf9f2..e8c98176 100644 --- a/Source/Rendering/Renderer3D.cs +++ b/Source/Rendering/Renderer3D.cs @@ -54,8 +54,9 @@ namespace CodeImp.DoomBuilder.Rendering private Matrix projection; private Matrix view3d; private Matrix billboard; - private Matrix viewproj; + private Matrix worldviewproj; private Matrix view2d; + private Matrix world; // Window size private Size windowsize; @@ -213,12 +214,9 @@ namespace CodeImp.DoomBuilder.Rendering // Make the view matrix view3d = Matrix.LookAtRH(D3DDevice.V3(pos), D3DDevice.V3(lookat), new Vector3(0f, 0f, 1f)); - + // Make the billboard matrix - Vector3D lookat2d = new Vector3D(lookat.x, lookat.y, 0.0f); - Vector3D campos2d = new Vector3D(pos.x, pos.y, 0.0f); - Vector3D delta2d = lookat2d - campos2d; - billboard = Matrix.Billboard(D3DDevice.V3(lookat2d), D3DDevice.V3(campos2d), new Vector3(0f, 0f, 1f), D3DDevice.V3(delta2d.GetNormal())); + billboard = Matrix.RotationZ(anglexy + Angle2D.PI); } // This creates 2D view matrix @@ -233,8 +231,9 @@ namespace CodeImp.DoomBuilder.Rendering // This applies the matrices private void ApplyMatrices3D() { - viewproj = view3d * projection; - graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + worldviewproj = world * view3d * projection; + graphics.Shaders.World3D.WorldViewProj = worldviewproj; + graphics.Device.SetTransform(TransformState.World, world); graphics.Device.SetTransform(TransformState.Projection, projection); graphics.Device.SetTransform(TransformState.View, view3d); } @@ -242,7 +241,7 @@ namespace CodeImp.DoomBuilder.Rendering // This sets the appropriate view matrix public void ApplyMatrices2D() { - graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + graphics.Device.SetTransform(TransformState.World, world); graphics.Device.SetTransform(TransformState.Projection, Matrix.Identity); graphics.Device.SetTransform(TransformState.View, view2d); } @@ -274,6 +273,7 @@ namespace CodeImp.DoomBuilder.Rendering graphics.Device.SetRenderState(RenderState.RangeFogEnable, false); // Matrices + world = Matrix.Identity; ApplyMatrices3D(); // Create crosshair vertices @@ -314,26 +314,25 @@ namespace CodeImp.DoomBuilder.Rendering graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); graphics.Device.SetRenderState(RenderState.TextureFactor, -1); graphics.Shaders.World3D.Begin(); - graphics.Shaders.World3D.WorldViewProj = viewproj; - // Matrices - ApplyMatrices3D(); - // SOLID PASS - graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + world = Matrix.Identity; + ApplyMatrices3D(); graphics.Shaders.World3D.BeginPass(0); RenderSinglePass((int)RenderPass.Solid); graphics.Shaders.World3D.EndPass(); // MASK PASS - graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + world = Matrix.Identity; + ApplyMatrices3D(); graphics.Device.SetRenderState(RenderState.AlphaTestEnable, true); graphics.Shaders.World3D.BeginPass(0); RenderSinglePass((int)RenderPass.Mask); graphics.Shaders.World3D.EndPass(); // ALPHA PASS - graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + world = Matrix.Identity; + ApplyMatrices3D(); graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); graphics.Device.SetRenderState(RenderState.ZWriteEnable, false); @@ -344,7 +343,8 @@ namespace CodeImp.DoomBuilder.Rendering graphics.Shaders.World3D.EndPass(); // ADDITIVE PASS - graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + world = Matrix.Identity; + ApplyMatrices3D(); graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One); graphics.Shaders.World3D.BeginPass(0); RenderSinglePass((int)RenderPass.Additive); @@ -424,41 +424,41 @@ namespace CodeImp.DoomBuilder.Rendering foreach(KeyValuePair> group in thingspass) { ImageData curtexture; - + // What texture to use? if((group.Key != null) && group.Key.IsImageLoaded && !group.Key.IsDisposed) curtexture = group.Key; else curtexture = General.Map.Data.Hourglass3D; - + // Create Direct3D texture if still needed if((curtexture.Texture == null) || curtexture.Texture.Disposed) curtexture.CreateTexture(); - + // Apply texture graphics.Device.SetTexture(0, curtexture.Texture); graphics.Shaders.World3D.Texture1 = curtexture.Texture; graphics.Shaders.World3D.ApplySettings(); - + // Render all things with this texture foreach(VisualThing t in group.Value) { // Update buffer if needed if(t.NeedsUpdateGeo) t.Update(); - + // Only do this sector when a vertexbuffer is created if(t.GeometryBuffer != null) { // Create the matrix for positioning / rotation - Matrix transform = t.Orientation; - if(t.Billboard) transform = Matrix.Multiply(transform, billboard); - transform = Matrix.Multiply(transform, t.Position); - graphics.Device.SetTransform(TransformState.World, transform); + world = t.Orientation; + if(t.Billboard) world = Matrix.Multiply(world, billboard); + world = Matrix.Multiply(world, t.Position); + ApplyMatrices3D(); graphics.Shaders.World3D.ApplySettings(); // Apply buffer graphics.Device.SetStreamSource(0, t.GeometryBuffer, 0, WorldVertex.Stride); - + // Render! graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, t.Triangles); } diff --git a/Source/VisualModes/VisualMode.cs b/Source/VisualModes/VisualMode.cs index 994d4990..1d61576a 100644 --- a/Source/VisualModes/VisualMode.cs +++ b/Source/VisualModes/VisualMode.cs @@ -174,7 +174,8 @@ namespace CodeImp.DoomBuilder.VisualModes public override void OnDisengage() { base.OnDisengage(); - + DisposeVisuals(); + // Do we have a 3D Mode thing? if(modething != null) { @@ -203,9 +204,8 @@ namespace CodeImp.DoomBuilder.VisualModes public override void OnUndoEnd() { base.OnUndoEnd(); - - allsectors.Clear(); - allthings.Clear(); + + DisposeVisuals(); visiblesectors.Clear(); visibleblocks.Clear(); visiblegeometry.Clear(); @@ -236,9 +236,8 @@ namespace CodeImp.DoomBuilder.VisualModes public override void OnRedoEnd() { base.OnRedoEnd(); - - allsectors.Clear(); - allthings.Clear(); + + DisposeVisuals(); visiblesectors.Clear(); visibleblocks.Clear(); visiblegeometry.Clear(); @@ -398,10 +397,13 @@ namespace CodeImp.DoomBuilder.VisualModes { // Create new visual thing vt = CreateVisualThing(t); - allthings.Add(t, vt); + if(vt != null) allthings.Add(t, vt); } - visiblethings.Add(vt); + if(vt != null) + { + visiblethings.Add(vt); + } } } @@ -461,18 +463,21 @@ namespace CodeImp.DoomBuilder.VisualModes { // Make new visualsector vs = CreateVisualSector(sd.Sector); - allsectors.Add(sd.Sector, vs); + if(vs != null) allsectors.Add(sd.Sector, vs); } - // Add to visible sectors if not added yet - if(!visiblesectors.ContainsKey(sd.Sector)) + if(vs != null) { - visiblesectors.Add(sd.Sector, vs); - visiblegeometry.AddRange(vs.FixedGeometry); + // Add to visible sectors if not added yet + if(!visiblesectors.ContainsKey(sd.Sector)) + { + visiblesectors.Add(sd.Sector, vs); + visiblegeometry.AddRange(vs.FixedGeometry); + } + + // Add sidedef geometry + visiblegeometry.AddRange(vs.GetSidedefGeometry(sd)); } - - // Add sidedef geometry - visiblegeometry.AddRange(vs.GetSidedefGeometry(sd)); } // This returns the camera sector from linedef @@ -647,7 +652,20 @@ namespace CodeImp.DoomBuilder.VisualModes #endregion #region ================== Processing - + + // This disposes all visual sectors and things + private void DisposeVisuals() + { + foreach(KeyValuePair vs in allsectors) + vs.Value.Dispose(); + + foreach(KeyValuePair vt in allthings) + vt.Value.Dispose(); + + allsectors.Clear(); + allthings.Clear(); + } + /// /// Implement this to create an instance of your VisualSector implementation. /// @@ -744,6 +762,12 @@ namespace CodeImp.DoomBuilder.VisualModes // Render all visible sectors foreach(VisualGeometry g in visiblegeometry) renderer.AddSectorGeometry(g); + + // Render all visible things + foreach(VisualThing t in visiblethings) + renderer.AddThingGeometry(t); + + General.WriteLogLine("Things: " + visiblethings.Count); } #endregion diff --git a/Source/VisualModes/VisualThing.cs b/Source/VisualModes/VisualThing.cs index 80e4721a..550cbbdb 100644 --- a/Source/VisualModes/VisualThing.cs +++ b/Source/VisualModes/VisualThing.cs @@ -118,6 +118,7 @@ namespace CodeImp.DoomBuilder.VisualModes this.renderpass = (int)RenderPass.Mask; this.billboard = true; this.orientation = Matrix.Identity; + this.position = Matrix.Identity; // Register as resource General.Map.Graphics.RegisterResource(this); @@ -188,9 +189,9 @@ namespace CodeImp.DoomBuilder.VisualModes triangles = vertices.Length / 3; updategeo = true; } - - // This updates the visual sector - public void Update() + + // This updates the visual thing + public virtual void Update() { // Trash geometry buffer if(geobuffer != null) geobuffer.Dispose();