#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.Generic; using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.GZBuilder.Data; //mxd using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Rendering; using Plane = CodeImp.DoomBuilder.Geometry.Plane; using CodeImp.DoomBuilder.GZBuilder; using CodeImp.DoomBuilder.GZBuilder.MD3; #endregion namespace CodeImp.DoomBuilder.VisualModes { public abstract class VisualThing : IVisualPickable, IRenderResource, IDisposable { #region ================== Constants protected const int FIXED_RADIUS = 8; //mxd. Used to render things with zero width and radius private const float DYNLIGHT_INTENSITY_SCALER = 255.0f; private const float SUBLIGHT_INTENSITY_SCALER = 255.0f * 1.5f; // Scaler for subtractive dynamic lights #endregion #region ================== Variables // Thing private readonly Thing thing; //mxd. Info protected ThingTypeInfo info; // Textures protected ImageData[] textures; // Geometry private WorldVertex[][] vertices; private VertexBuffer[] geobuffers; private VertexBuffer cagebuffer; //mxd private int cagelength; //mxd private bool updategeo; private bool updatecage; //mxd private int[] triangles; private int spriteframe; //mxd // Rendering private RenderPass renderpass; private Matrix position; private int cameradistance; private Color4 cagecolor; protected bool sizeless; //mxd. Used to render visual things with 0 width and height protected float fogfactor; //mxd // Selected? protected bool selected; // Disposing private bool isdisposed; //mxd protected float thingheight; //mxd. light properties private GZGeneral.LightData lightType; private Color4 lightColor; private float lightRadius; //current radius. used in light animation private float lightSpotRadius1; private float lightSpotRadius2; private float lightPrimaryRadius; private float lightSecondaryRadius; private Vector3f position_v3; private float lightDelta; //used in light animation private Vector3D[] boundingBox; //gldefs light private Vector3f lightOffset; private int lightInterval; private bool isGldefsLight; // [ZZ] protected PixelColor stencilColor; #endregion #region ================== Properties internal VertexBuffer GeometryBuffer { get { return geobuffers[spriteframe]; } } internal VertexBuffer CageBuffer { get { return cagebuffer; } } //mxd internal int CageLength { get { return cagelength; } } //mxd internal bool NeedsUpdateGeo { get { return updategeo; } } internal int Triangles { get { return triangles[spriteframe]; } } internal Matrix Position { get { return position; } } internal Color4 CageColor { get { return cagecolor; } } public ThingTypeInfo Info { get { return info; } } //mxd //mxd internal int VertexColor { get { return vertices.Length > 0 && vertices[0].Length > 0 ? vertices[0][0].c : 0; } } public int CameraDistance { get { return cameradistance; } } public float FogFactor { get { return fogfactor; } } public Vector3f Center { get { if (isGldefsLight) return position_v3 + lightOffset; else if (Thing.DynamicLightType != null) return position_v3; // fixes GZDoomBuilder-Bugfix#137 return new Vector3f(position_v3.X, position_v3.Y, position_v3.Z + thingheight / 2f); } } public Vector3D CenterV3D { get { return RenderDevice.V3D(Center); } } public float LocalCenterZ { get { return thingheight / 2f; } } //mxd public Vector3f PositionV3 { get { return position_v3; } } public Vector3D[] BoundingBox { get { return boundingBox; } } //mxd. light properties public GZGeneral.LightData LightType { get { return lightType; } } public float LightRadius { get { return lightRadius; } } public float LightSpotRadius1 { get { return lightSpotRadius1; } } public float LightSpotRadius2 { get { return lightSpotRadius2; } } public Color4 LightColor { get { return lightColor; } } // [ZZ] public PixelColor StencilColor { get { return stencilColor; } } // [ZZ] this is used for spotlights public Vector3f VectorLookAt { get { // this esoteric value (1.5708) is 90 degrees but in radians return new Vector3f((float)(Math.Cos(Thing.Angle+1.5708) * Math.Cos(Angle2D.DegToRad(Thing.Pitch))), (float)(Math.Sin(Thing.Angle+1.5708) * Math.Cos(Angle2D.DegToRad(Thing.Pitch))), (float)Math.Sin(Angle2D.DegToRad(Thing.Pitch))); } } /// /// Returns the Thing that this VisualThing is created for. /// public Thing Thing { get { return thing; } } /// /// Render pass in which this geometry must be rendered. Default is Mask. /// public RenderPass RenderPass { get { return renderpass; } set { renderpass = value; } } /// /// Image to use as texture on the geometry. /// public ImageData Texture { get { return textures[spriteframe]; } } /// /// Disposed or not? /// public bool IsDisposed { get { return isdisposed; } } /// /// Selected or not? This is only used by the core to determine what color to draw it with. /// public bool Selected { get { return selected; } set { selected = value; } } #endregion #region ================== Constructor / Destructor // Constructor protected VisualThing(Thing t) { // Initialize this.thing = t; this.renderpass = RenderPass.Mask; this.position = Matrix.Identity; //mxd lightType = null; lightPrimaryRadius = -1; lightSecondaryRadius = -1; lightInterval = -1; lightColor = new Color4(); boundingBox = new Vector3D[9]; // Register as resource General.Map.Graphics.RegisterResource(this); } // Disposer public virtual void Dispose() { // Not already disposed? if(!isdisposed) { // Clean up if(geobuffers != null) //mxd { foreach(VertexBuffer buffer in geobuffers) buffer.Dispose(); geobuffers = null; } if(cagebuffer != null) cagebuffer.Dispose(); //mxd cagebuffer = null; //mxd // Unregister resource General.Map.Graphics.UnregisterResource(this); // Done isdisposed = true; } } #endregion #region ================== Methods //mxd internal void CalculateCameraDistance(Vector3D campos) { cameradistance = (int)((CenterV3D - campos).GetLengthSq()); } // This is called before a device is reset (when resized or display adapter was changed) public void UnloadResource() { // Trash geometry buffers if(geobuffers != null) //mxd { foreach(VertexBuffer buffer in geobuffers) buffer.Dispose(); geobuffers = null; } if(cagebuffer != null) cagebuffer.Dispose(); //mxd cagebuffer = null; //mxd updategeo = true; updatecage = true; //mxd } // This is called resets when the device is reset // (when resized or display adapter was changed) public void ReloadResource() { // Make new geometry //Update(); } /// /// Sets the color of the cage around the thing geometry and rebuilds the thing cage. /// protected void SetCageColor(PixelColor color) { cagecolor = color.ToColorValue(); updatecage = true; } /// /// This sets the position to use for the thing geometry. /// public void SetPosition(Vector3D pos) { position_v3 = RenderDevice.V3(pos); //mxd position = Matrix.Translation(position_v3); updategeo = true; updatecage = true; //mxd //mxd. update bounding box? if (lightType != null && lightRadius > thing.Size) { UpdateBoundingBox(lightRadius, lightRadius * 2); } } // This sets the vertices for the thing sprite protected void SetVertices(WorldVertex[][] verts, Vector2D[] offsets/*, Plane floor, Plane ceiling*/) { // Copy vertices vertices = new WorldVertex[verts.Length][]; triangles = new int[verts.Length]; //mxd for(int i = 0; i < verts.Length; i++) { vertices[i] = new WorldVertex[verts[i].Length]; verts[i].CopyTo(vertices[i], 0); triangles[i] = vertices[i].Length / 3; } updategeo = true; //mxd. Do some special GZDoom rendering shenanigans... for(int c = 0; c < vertices.Length; c++) { if(triangles[c] < 2) continue; Matrix transform, rotation; float centerx, centerz; // ROLLCENTER flag support if(info.RollSprite && info.RollCenter && thing.Roll != 0) { // Rotate around sprite center centerx = offsets[c].x; centerz = vertices[c][1].z * 0.5f - offsets[c].y; } else { // Sprite center is already where it needs to be centerx = 0f; centerz = 0f; } switch(thing.RenderMode) { // Don't do anything case ThingRenderMode.MODEL: break; case ThingRenderMode.VOXEL: break; // Actor becomes a flat sprite which can be tilted with the use of the Pitch actor property. case ThingRenderMode.FLATSPRITE: transform = Matrix.Scaling(thing.ScaleX, thing.ScaleX, thing.ScaleY); // Apply roll? if(thing.Roll != 0) { if(info.RollCenter) { rotation = Matrix.RotationY(-thing.RollRad); transform *= Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); } else { // Sprite center is already where it needs to be transform *= Matrix.RotationY(-thing.RollRad); } } // Apply pitch transform *= Matrix.RotationX(thing.PitchRad + Angle2D.PIHALF); // Apply angle transform *= Matrix.RotationZ(thing.Angle); // Apply transform float zoffset = ((thing.Pitch == 0f && thing.Position.z == 0f) ? 0.1f : 0f); // Slight offset to avoid z-fighting... for(int i = 0; i < vertices[c].Length; i++) { Vector4f transformed = Vector3f.Transform(new Vector3f(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z + zoffset; } break; // Similar to FLATSPRITE but is not affected by pitch. case ThingRenderMode.WALLSPRITE: transform = Matrix.Scaling(thing.ScaleX, thing.ScaleX, thing.ScaleY); // Apply roll? if(thing.Roll != 0) { rotation = Matrix.RotationY(-thing.RollRad) * Matrix.RotationZ(thing.Angle); if(info.RollCenter) transform *= Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); else transform *= rotation; // Sprite center is already where it needs to be } else { transform *= Matrix.RotationZ(thing.Angle); } // Apply transform for(int i = 0; i < vertices[c].Length; i++) { Vector4f transformed = Vector3f.Transform(new Vector3f(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z; } break; #region Some old GLOOME FLOOR_SPRITE/CEILING_SPRITE support code /*case Thing.SpriteRenderMode.FLOOR_SPRITE: Matrix floorrotation = Matrix.RotationZ(info.RollSprite ? Thing.RollRad : 0f) * Matrix.RotationY(Thing.Angle) * Matrix.RotationX(Angle2D.PIHALF); m = Matrix.Translation(0f, 0f, -localcenterz) * floorrotation * Matrix.Translation(0f, 0f, localcenterz); for(int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), m); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z; } // TODO: this won't work on things with AbsoluteZ flag // TODO: +ROLLSPRITE implies +STICKTOPLANE? if(info.StickToPlane || info.RollSprite) { // Calculate vertical offset float floorz = floor.GetZ(Thing.Position); float ceilz = ceiling.GetZ(Thing.Position); if(!float.IsNaN(floorz) && !float.IsNaN(ceilz)) { float voffset; if(info.Hangs) { float thingz = ceilz - Thing.Position.z + Thing.Height; voffset = 0.01f - floorz - General.Clamp(thingz, 0, ceilz - floorz); } else { voffset = 0.01f - floorz - General.Clamp(Thing.Position.z, 0, ceilz - floorz); } // Apply it for(int i = 0; i < vertices[c].Length; i++) vertices[c][i].z = floor.GetZ(vertices[c][i].x + Thing.Position.x, vertices[c][i].y + Thing.Position.y) + voffset; } } break; case Thing.SpriteRenderMode.CEILING_SPRITE: Matrix ceilrotation = Matrix.RotationZ(info.RollSprite ? Thing.RollRad : 0f) * Matrix.RotationY(Thing.Angle) * Matrix.RotationX(Angle2D.PIHALF); m = Matrix.Translation(0f, 0f, -localcenterz) * ceilrotation * Matrix.Translation(0f, 0f, localcenterz); for(int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), m); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z; } // TODO: this won't work on things with AbsoluteZ flag // TODO: +ROLLSPRITE implies +STICKTOPLANE? if(info.StickToPlane || info.RollSprite) { // Calculate vertical offset float floorz = floor.GetZ(Thing.Position); float ceilz = ceiling.GetZ(Thing.Position); if(!float.IsNaN(floorz) && !float.IsNaN(ceilz)) { float voffset; if(info.Hangs) { float thingz = ceilz - Math.Max(0, Thing.Position.z) - Thing.Height; voffset = -0.01f - General.Clamp(thingz, 0, ceilz - floorz); } else { voffset = -0.01f - floorz - General.Clamp(Thing.Position.z, 0, ceilz - floorz); } // Apply it for(int i = 0; i < vertices[c].Length; i++) vertices[c][i].z = ceiling.GetZ(vertices[c][i].x + Thing.Position.x, vertices[c][i].y + Thing.Position.y) + voffset; } } break;*/ #endregion case ThingRenderMode.NORMAL: transform = Matrix.Scaling(thing.ScaleX, thing.ScaleX, thing.ScaleY); // Apply roll? if(info.RollSprite && thing.Roll != 0) { rotation = Matrix.RotationY(-thing.RollRad); if(info.RollCenter) transform *= Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); else transform *= rotation; // Sprite center is already where it needs to be } // Apply transform for(int i = 0; i < vertices[c].Length; i++) { Vector4f transformed = Vector3f.Transform(new Vector3f(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z; } break; default: throw new NotImplementedException("Unknown ThingRenderMode"); } } } // This updates the visual thing public virtual void Update() { RenderDevice graphics = General.Map.Graphics; // Do we need to update the geometry buffer? if(updategeo) { //mxd. Trash geometry buffers if(geobuffers != null) foreach(VertexBuffer geobuffer in geobuffers) geobuffer.Dispose(); // Any vertics? if(vertices.Length > 0) { geobuffers = new VertexBuffer[vertices.Length]; for(int i = 0; i < vertices.Length; i++) { // Make a new buffer geobuffers[i] = new VertexBuffer(); // Fill the buffer graphics.SetBufferData(geobuffers[i], vertices[i]); } } //mxd. Check if thing is light CheckLightState(); // Done updategeo = false; } //mxd. Need to update thing cage? if(updatecage) { // Trash cage buffer if(cagebuffer != null) cagebuffer.Dispose(); cagebuffer = null; // Make a new cage List cageverts; if(sizeless) { WorldVertex v0 = new WorldVertex(-thing.Size + position_v3.X, -thing.Size + position_v3.Y, position_v3.Z); WorldVertex v1 = new WorldVertex(thing.Size + position_v3.X, thing.Size + position_v3.Y, position_v3.Z); WorldVertex v2 = new WorldVertex(thing.Size + position_v3.X, -thing.Size + position_v3.Y, position_v3.Z); WorldVertex v3 = new WorldVertex(-thing.Size + position_v3.X, thing.Size + position_v3.Y, position_v3.Z); WorldVertex v4 = new WorldVertex(position_v3.X, position_v3.Y, thing.Size + position_v3.Z); WorldVertex v5 = new WorldVertex(position_v3.X, position_v3.Y, -thing.Size + position_v3.Z); cageverts = new List(new[] { v0, v1, v2, v3, v4, v5 }); } else { float top = position_v3.Z + thing.Height; float bottom = position_v3.Z; WorldVertex v0 = new WorldVertex(-thing.Size + position_v3.X, -thing.Size + position_v3.Y, bottom); WorldVertex v1 = new WorldVertex(-thing.Size + position_v3.X, thing.Size + position_v3.Y, bottom); WorldVertex v2 = new WorldVertex(thing.Size + position_v3.X, thing.Size + position_v3.Y, bottom); WorldVertex v3 = new WorldVertex(thing.Size + position_v3.X, -thing.Size + position_v3.Y, bottom); WorldVertex v4 = new WorldVertex(-thing.Size + position_v3.X, -thing.Size + position_v3.Y, top); WorldVertex v5 = new WorldVertex(-thing.Size + position_v3.X, thing.Size + position_v3.Y, top); WorldVertex v6 = new WorldVertex(thing.Size + position_v3.X, thing.Size + position_v3.Y, top); WorldVertex v7 = new WorldVertex(thing.Size + position_v3.X, -thing.Size + position_v3.Y, top); cageverts = new List(new[] { v0, v1, v1, v2, v2, v3, v3, v0, v4, v5, v5, v6, v6, v7, v7, v4, v0, v4, v1, v5, v2, v6, v3, v7 }); } // Make new arrow if(Thing.IsDirectional) { Matrix transform = Matrix.Scaling(thing.Size, thing.Size, thing.Size) * (Matrix.RotationY(-Thing.RollRad) * Matrix.RotationX(-Thing.PitchRad) * Matrix.RotationZ(Thing.Angle)) * (sizeless ? position : position * Matrix.Translation(0.0f, 0.0f, thingheight / 2f)); WorldVertex a0 = new WorldVertex(Vector3D.Transform(0.0f, 0.0f, 0.0f, transform)); //start WorldVertex a1 = new WorldVertex(Vector3D.Transform(0.0f, -1.5f, 0.0f, transform)); //end WorldVertex a2 = new WorldVertex(Vector3D.Transform(0.2f, -1.1f, 0.2f, transform)); WorldVertex a3 = new WorldVertex(Vector3D.Transform(-0.2f, -1.1f, 0.2f, transform)); WorldVertex a4 = new WorldVertex(Vector3D.Transform(0.2f, -1.1f, -0.2f, transform)); WorldVertex a5 = new WorldVertex(Vector3D.Transform(-0.2f, -1.1f, -0.2f, transform)); cageverts.AddRange(new[] { a0, a1, a1, a2, a1, a3, a1, a4, a1, a5 }); } // Create buffer WorldVertex[] cv = cageverts.ToArray(); cagelength = cv.Length / 2; cagebuffer = new VertexBuffer(); graphics.SetBufferData(cagebuffer, cv); // Done updatecage = false; } } //mxd protected void CheckLightState() { //mxd. Check if thing is light if (thing.DynamicLightType != null) { isGldefsLight = false; lightInterval = -1; UpdateLight(); } //check if we have light from GLDEFS else if(General.Map.Data.GldefsEntries.ContainsKey(thing.Type)) { isGldefsLight = true; UpdateGldefsLight(); UpdateBoundingBox(lightRadius, lightRadius * 2); } else { UpdateBoundingBox((int)thing.Size, thingheight); lightType = null; lightRadius = -1; lightSpotRadius1 = lightSpotRadius2 = -1; lightPrimaryRadius = -1; lightSecondaryRadius = -1; lightInterval = -1; isGldefsLight = false; } } //mxd. Update light info public void UpdateLight() { lightType = thing.DynamicLightType; if (lightType == null) return; GZGeneral.LightData ld = lightType; if (ld.LightDef != GZGeneral.LightDef.VAVOOM_GENERIC && ld.LightDef != GZGeneral.LightDef.VAVOOM_COLORED) //if it's gzdoom light { if (ld.LightType == GZGeneral.LightType.POINT) { if (ld.LightDef != GZGeneral.LightDef.POINT_SUBTRACTIVE) // normal, additive, attenuated { //lightColor.Alpha used in shader to perform some calculations based on light type lightColor = new Color4( thing.Args[0] / DYNLIGHT_INTENSITY_SCALER, thing.Args[1] / DYNLIGHT_INTENSITY_SCALER, thing.Args[2] / DYNLIGHT_INTENSITY_SCALER, (float)ld.LightRenderStyle / 100.0f); } else // negative { lightColor = new Color4( thing.Args[0] / SUBLIGHT_INTENSITY_SCALER, thing.Args[1] / SUBLIGHT_INTENSITY_SCALER, thing.Args[2] / SUBLIGHT_INTENSITY_SCALER, (float)ld.LightRenderStyle / 100.0f); } } else { int c1, c2, c3; if (thing.Fields.ContainsKey("arg0str")) { PixelColor pc; ZDoom.ZDTextParser.GetColorFromString(thing.Fields["arg0str"].Value.ToString(), out pc); c1 = pc.r; c2 = pc.g; c3 = pc.b; } else { c1 = (thing.Args[0] & 0xFF0000) >> 16; c2 = (thing.Args[0] & 0x00FF00) >> 8; c3 = (thing.Args[0] & 0x0000FF); } if (ld.LightDef != GZGeneral.LightDef.SPOT_SUBTRACTIVE) { lightColor = new Color4( c1 / DYNLIGHT_INTENSITY_SCALER, c2 / DYNLIGHT_INTENSITY_SCALER, c3 / DYNLIGHT_INTENSITY_SCALER, (float)ld.LightRenderStyle / 100.0f); } else { lightColor = new Color4( c1 / SUBLIGHT_INTENSITY_SCALER, c2 / SUBLIGHT_INTENSITY_SCALER, c3 / SUBLIGHT_INTENSITY_SCALER, (float)ld.LightRenderStyle / 100.0f); } } if(lightType.LightModifier == GZGeneral.LightModifier.SECTOR) { int scaler = 1; if(thing.Sector != null) scaler = thing.Sector.Brightness / 4; lightPrimaryRadius = (thing.Args[3] * scaler); } else { lightPrimaryRadius = (thing.Args[3] * 2); //works... that.. way in GZDoom if (lightType.LightAnimated) lightSecondaryRadius = (thing.Args[4] * 2); } if (lightType.LightType == GZGeneral.LightType.SPOT) { lightSpotRadius1 = (thing.Args[1]); lightSpotRadius2 = (thing.Args[2]); } } else //it's one of vavoom lights { if(lightType.LightDef == GZGeneral.LightDef.VAVOOM_COLORED) { lightColor = new Color4( thing.Args[1] / DYNLIGHT_INTENSITY_SCALER, thing.Args[2] / DYNLIGHT_INTENSITY_SCALER, thing.Args[3] / DYNLIGHT_INTENSITY_SCALER, (float)ld.LightRenderStyle / 100.0f); } else { lightColor = new Color4(0.5f, 0.5f, 0.5f, (float)ld.LightRenderStyle / 100.0f); } lightPrimaryRadius = (thing.Args[0] * 8); } UpdateLightRadius(); UpdateBoundingBox(lightRadius, lightRadius * 2); } //mxd private void UpdateGldefsLight() { DynamicLightData light = General.Map.Data.GldefsEntries[thing.Type]; GZGeneral.LightData ld = light.Type; //apply settings lightColor = new Color4(light.Color.Red, light.Color.Green, light.Color.Blue, (float)ld.LightRenderStyle / 100.0f); Vector2D o = new Vector2D(light.Offset.X, light.Offset.Y).GetRotated(thing.Angle - Angle2D.PIHALF); lightOffset = new Vector3f(o.x, o.y, light.Offset.Z); lightType = light.Type; if(ld.LightModifier == GZGeneral.LightModifier.SECTOR) { lightPrimaryRadius = light.Interval * thing.Sector.Brightness / 5.0f; } else { lightPrimaryRadius = light.PrimaryRadius; lightSecondaryRadius = light.SecondaryRadius; } lightInterval = light.Interval; UpdateLightRadius(lightInterval); } //mxd public void UpdateLightRadius() { UpdateLightRadius( (lightInterval != -1 ? lightInterval : thing.AngleDoom) ); } //mxd private void UpdateLightRadius(int interval) { if(lightType == null) return; if(General.Settings.GZDrawLightsMode == LightRenderMode.ALL || !lightType.LightAnimated) { lightRadius = lightPrimaryRadius; return; } if(interval == 0) { lightRadius = 0; return; } float rMin = Math.Min(lightPrimaryRadius, lightSecondaryRadius); float rMax = Math.Max(lightPrimaryRadius, lightSecondaryRadius); float diff = rMax - rMin; switch(lightType.LightModifier) { case GZGeneral.LightModifier.PULSE: lightDelta = ((float)Math.Sin(Clock.CurrentTime / (interval * 4.0f)) + 1.0f) / 2.0f; //just playing by the eye here... in [0.0 ... 1.0] interval lightRadius = rMin + diff * lightDelta; break; case GZGeneral.LightModifier.FLICKER: float fdelta = (float)Math.Sin(Clock.CurrentTime / 0.1f); //just playing by the eye here... if(Math.Sign(fdelta) != Math.Sign(lightDelta)) { lightDelta = fdelta; lightRadius = (General.Random(0, 359) < interval ? rMax : rMin); } break; case GZGeneral.LightModifier.FLICKERRANDOM: float rdelta = (float)Math.Sin(Clock.CurrentTime / (interval * 9.0f)); //just playing by the eye here... if(Math.Sign(rdelta) != Math.Sign(lightDelta)) { lightRadius = rMin + (General.Random(0, (int) (diff * 10))) / 10.0f; } lightDelta = rdelta; break; } } //mxd. update bounding box public void UpdateBoundingBox() { if(lightType != null && lightRadius > thing.Size) UpdateBoundingBox(lightRadius, lightRadius * 2); } private void UpdateBoundingBox(float width, float height) { boundingBox = new Vector3D[9]; boundingBox[0] = CenterV3D; if (Thing.RenderMode != ThingRenderMode.MODEL && Thing.RenderMode != ThingRenderMode.VOXEL) { float h2 = height / 2.0f; boundingBox[1] = new Vector3D(position_v3.X - width, position_v3.Y - width, Center.Z - h2); boundingBox[2] = new Vector3D(position_v3.X + width, position_v3.Y - width, Center.Z - h2); boundingBox[3] = new Vector3D(position_v3.X - width, position_v3.Y + width, Center.Z - h2); boundingBox[4] = new Vector3D(position_v3.X + width, position_v3.Y + width, Center.Z - h2); boundingBox[5] = new Vector3D(position_v3.X - width, position_v3.Y - width, Center.Z + h2); boundingBox[6] = new Vector3D(position_v3.X + width, position_v3.Y - width, Center.Z + h2); boundingBox[7] = new Vector3D(position_v3.X - width, position_v3.Y + width, Center.Z + h2); boundingBox[8] = new Vector3D(position_v3.X + width, position_v3.Y + width, Center.Z + h2); } else { GZModel model = General.Map?.Data?.ModeldefEntries[Thing.Type]?.Model; if (model != null) { Vector3D offs = new Vector3D(position_v3.X, position_v3.Y, position_v3.Z); boundingBox[5] = new Vector3D(model.BBox.MinX, model.BBox.MinY, model.BBox.MaxZ) + offs; boundingBox[6] = new Vector3D(model.BBox.MaxX, model.BBox.MinY, model.BBox.MaxZ) + offs; boundingBox[7] = new Vector3D(model.BBox.MinX, model.BBox.MaxY, model.BBox.MaxZ) + offs; boundingBox[8] = new Vector3D(model.BBox.MaxX, model.BBox.MaxY, model.BBox.MaxZ) + offs; boundingBox[1] = new Vector3D(model.BBox.MinX, model.BBox.MinY, model.BBox.MinZ) + offs; boundingBox[2] = new Vector3D(model.BBox.MaxX, model.BBox.MinY, model.BBox.MinZ) + offs; boundingBox[3] = new Vector3D(model.BBox.MinX, model.BBox.MaxY, model.BBox.MinZ) + offs; boundingBox[4] = new Vector3D(model.BBox.MaxX, model.BBox.MaxY, model.BBox.MinZ) + offs; boundingBox[0] = new Vector3D(0.5f * (model.BBox.MinX + model.BBox.MaxX), 0.5f * (model.BBox.MinY + model.BBox.MaxY), 0.5f * (model.BBox.MinZ + model.BBox.MaxZ)) + offs; } } } //mxd. This updates the sprite frame to be rendered internal void UpdateSpriteFrame() { if(textures.Length != 8) spriteframe = 0; else spriteframe = (General.ClampAngle((int)Angle2D.RadToDeg((General.Map.VisualCamera.Position - thing.Position).GetAngleXY()) - thing.AngleDoom + 292)) / 45; // Convert to [0..7] range; 292 == 270 + 45/2 } /// /// This is called when the thing must be tested for line intersection. This should reject /// as fast as possible to rule out all geometry that certainly does not touch the line. /// public virtual bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) { return false; } /// /// This is called when the thing must be tested for line intersection. This should perform /// accurate hit detection and set u_ray to the position on the ray where this hits the geometry. /// public virtual bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) { return false; } #endregion } }