mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2025-02-05 23:31:24 +00:00
7435d4bd5b
Probably fixed probable I/O race condition when loading images. Fixed Visual mode stuttering due to floating point precision degradation when running the editor for several days without restarting (internal timer is now reset when saving the map or creating a new one). Fixed, Nodes Viewer, cosmetic: Nodes Viewer window position was reset after pressing the "Rebuild Nodes" button. Added Eternity Game configurations by printz. Updated ZDoom_ACS.cfg (CheckClass). Updated ZDoom ACC (CheckClass).
1882 lines
65 KiB
C#
1882 lines
65 KiB
C#
|
|
#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 System.Drawing;
|
|
using CodeImp.DoomBuilder.Config;
|
|
using CodeImp.DoomBuilder.Data;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.GZBuilder.Data;
|
|
using CodeImp.DoomBuilder.GZBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.GZBuilder.MD3;
|
|
using CodeImp.DoomBuilder.GZBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.VisualModes;
|
|
using SlimDX;
|
|
using SlimDX.Direct3D9;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.Rendering
|
|
{
|
|
internal sealed class Renderer3D : Renderer, IRenderer3D
|
|
{
|
|
#region ================== Constants
|
|
|
|
private const float PROJ_NEAR_PLANE = 1f;
|
|
private const float FOG_RANGE = 0.9f;
|
|
internal const float GZDOOM_VERTICAL_VIEW_STRETCH = 1.2f;
|
|
internal const float GZDOOM_INVERTED_VERTICAL_VIEW_STRETCH = 1.0f / GZDOOM_VERTICAL_VIEW_STRETCH;
|
|
|
|
private const int SHADERPASS_LIGHT = 17; //mxd
|
|
private const int SHADERPASS_SKYBOX = 5; //mxd
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// Matrices
|
|
private Matrix projection;
|
|
private Matrix view3d;
|
|
private Matrix viewproj; //mxd
|
|
private Matrix billboard;
|
|
private Matrix view2d;
|
|
private Matrix world;
|
|
private Vector3D cameraposition;
|
|
private int shaderpass;
|
|
|
|
// Window size
|
|
private Size windowsize;
|
|
|
|
// Frustum
|
|
private ProjectedFrustum2D frustum;
|
|
|
|
// Thing cage
|
|
private bool renderthingcages;
|
|
//mxd
|
|
private VisualVertexHandle vertexhandle;
|
|
private int[] lightOffsets;
|
|
|
|
// Crosshair
|
|
private FlatVertex[] crosshairverts;
|
|
private bool crosshairbusy;
|
|
|
|
// Highlighting
|
|
private IVisualPickable highlighted;
|
|
private float highlightglow;
|
|
private float highlightglowinv;
|
|
private bool showselection;
|
|
private bool showhighlight;
|
|
|
|
//mxd. Solid geometry to be rendered. Must be sorted by sector.
|
|
private Dictionary<ImageData, List<VisualGeometry>> solidgeo;
|
|
|
|
//mxd. Masked geometry to be rendered. Must be sorted by sector.
|
|
private Dictionary<ImageData, List<VisualGeometry>> maskedgeo;
|
|
|
|
//mxd. Translucent geometry to be rendered. Must be sorted by camera distance.
|
|
private List<VisualGeometry> translucentgeo;
|
|
|
|
//mxd. Geometry to be rendered as skybox.
|
|
private List<VisualGeometry> skygeo;
|
|
|
|
//mxd. Solid things to be rendered (currently(?) there won't be any). Must be sorted by sector.
|
|
private Dictionary<ImageData, List<VisualThing>> solidthings;
|
|
|
|
//mxd. Masked things to be rendered. Must be sorted by sector.
|
|
private Dictionary<ImageData, List<VisualThing>> maskedthings;
|
|
|
|
//mxd. Translucent things to be rendered. Must be sorted by camera distance.
|
|
private List<VisualThing> translucentthings;
|
|
|
|
//mxd. Things with attached dynamic lights
|
|
private List<VisualThing> lightthings;
|
|
|
|
//mxd. Things, which should be rendered as models
|
|
private Dictionary<ModelData, List<VisualThing>> maskedmodelthings;
|
|
|
|
//mxd. Things, which should be rendered as translucent models
|
|
private List<VisualThing> translucentmodelthings;
|
|
|
|
//mxd. All things. Used to render thing cages
|
|
private List<VisualThing> allthings;
|
|
|
|
//mxd. Visual vertices
|
|
private List<VisualVertex> visualvertices;
|
|
|
|
//mxd. Event lines
|
|
private List<Line3D> eventlines;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
public ProjectedFrustum2D Frustum2D { get { return frustum; } }
|
|
public bool DrawThingCages { get { return renderthingcages; } set { renderthingcages = value; } }
|
|
public bool ShowSelection { get { return showselection; } set { showselection = value; } }
|
|
public bool ShowHighlight { get { return showhighlight; } set { showhighlight = value; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
// Constructor
|
|
internal Renderer3D(D3DDevice graphics) : base(graphics)
|
|
{
|
|
// Initialize
|
|
CreateProjection();
|
|
CreateMatrices2D();
|
|
renderthingcages = true;
|
|
showselection = true;
|
|
showhighlight = true;
|
|
eventlines = new List<Line3D>(); //mxd
|
|
|
|
// Dummy frustum
|
|
frustum = new ProjectedFrustum2D(new Vector2D(), 0.0f, 0.0f, PROJ_NEAR_PLANE,
|
|
General.Settings.ViewDistance, Angle2D.DegToRad(General.Settings.VisualFOV));
|
|
|
|
// We have no destructor
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
// Disposer
|
|
public override void Dispose()
|
|
{
|
|
// Not already disposed?
|
|
if(!isdisposed)
|
|
{
|
|
// Clean up
|
|
if (vertexhandle != null) vertexhandle.Dispose(); //mxd
|
|
|
|
// Done
|
|
base.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Management
|
|
|
|
// This is called before a device is reset
|
|
// (when resized or display adapter was changed)
|
|
public override void UnloadResource()
|
|
{
|
|
crosshairverts = null;
|
|
}
|
|
|
|
// This is called resets when the device is reset
|
|
// (when resized or display adapter was changed)
|
|
public override void ReloadResource()
|
|
{
|
|
CreateMatrices2D();
|
|
}
|
|
|
|
// This makes screen vertices for display
|
|
private void CreateCrosshairVerts(Size texturesize)
|
|
{
|
|
// Determine coordinates
|
|
float width = windowsize.Width;
|
|
float height = windowsize.Height;
|
|
RectangleF rect = new RectangleF((float)Math.Round((width - texturesize.Width) * 0.5f), (float)Math.Round((height - texturesize.Height) * 0.5f), texturesize.Width, texturesize.Height);
|
|
|
|
// Make vertices
|
|
crosshairverts = new FlatVertex[4];
|
|
crosshairverts[0].x = rect.Left;
|
|
crosshairverts[0].y = rect.Top;
|
|
crosshairverts[0].c = -1;
|
|
crosshairverts[1].x = rect.Right;
|
|
crosshairverts[1].y = rect.Top;
|
|
crosshairverts[1].c = -1;
|
|
crosshairverts[1].u = 1.0f;
|
|
crosshairverts[2].x = rect.Left;
|
|
crosshairverts[2].y = rect.Bottom;
|
|
crosshairverts[2].c = -1;
|
|
crosshairverts[2].v = 1.0f;
|
|
crosshairverts[3].x = rect.Right;
|
|
crosshairverts[3].y = rect.Bottom;
|
|
crosshairverts[3].c = -1;
|
|
crosshairverts[3].u = 1.0f;
|
|
crosshairverts[3].v = 1.0f;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Resources
|
|
//mxd
|
|
internal void UpdateVertexHandle()
|
|
{
|
|
if (vertexhandle != null)
|
|
{
|
|
vertexhandle.UnloadResource();
|
|
vertexhandle.ReloadResource();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ================== Presentation
|
|
|
|
// This creates the projection
|
|
internal void CreateProjection()
|
|
{
|
|
// Calculate aspect
|
|
float screenheight = General.Map.Graphics.RenderTarget.ClientSize.Height * (General.Settings.GZStretchView ? GZDOOM_INVERTED_VERTICAL_VIEW_STRETCH : 1.0f); //mxd
|
|
float aspect = General.Map.Graphics.RenderTarget.ClientSize.Width / screenheight;
|
|
|
|
// The DirectX PerspectiveFovRH matrix method calculates the scaling in X and Y as follows:
|
|
// yscale = 1 / tan(fovY / 2)
|
|
// xscale = yscale / aspect
|
|
// The fov specified in the method is the FOV over Y, but we want the user to specify the FOV
|
|
// over X, so calculate what it would be over Y first;
|
|
float fov = Angle2D.DegToRad(General.Settings.VisualFOV);
|
|
float reversefov = 1.0f / (float)Math.Tan(fov / 2.0f);
|
|
float reversefovy = reversefov * aspect;
|
|
float fovy = (float)Math.Atan(1.0f / reversefovy) * 2.0f;
|
|
|
|
// Make the projection matrix
|
|
projection = Matrix.PerspectiveFovRH(fovy, aspect, PROJ_NEAR_PLANE, General.Settings.ViewDistance);
|
|
viewproj = view3d * projection; //mxd
|
|
}
|
|
|
|
// This creates matrices for a camera view
|
|
public void PositionAndLookAt(Vector3D pos, Vector3D lookat)
|
|
{
|
|
// Calculate delta vector
|
|
cameraposition = pos;
|
|
Vector3D delta = lookat - pos;
|
|
float anglexy = delta.GetAngleXY();
|
|
float anglez = delta.GetAngleZ();
|
|
|
|
// Create frustum
|
|
frustum = new ProjectedFrustum2D(pos, anglexy, anglez, PROJ_NEAR_PLANE,
|
|
General.Settings.ViewDistance, Angle2D.DegToRad(General.Settings.VisualFOV));
|
|
|
|
// Make the view matrix
|
|
view3d = Matrix.LookAtRH(D3DDevice.V3(pos), D3DDevice.V3(lookat), new Vector3(0f, 0f, 1f));
|
|
viewproj = view3d * projection; //mxd
|
|
|
|
// Make the billboard matrix
|
|
billboard = Matrix.RotationZ(anglexy + Angle2D.PI);
|
|
}
|
|
|
|
// This creates 2D view matrix
|
|
private void CreateMatrices2D()
|
|
{
|
|
windowsize = graphics.RenderTarget.ClientSize;
|
|
Matrix scaling = Matrix.Scaling((1f / windowsize.Width) * 2f, (1f / windowsize.Height) * -2f, 1f);
|
|
Matrix translate = Matrix.Translation(-(float)windowsize.Width * 0.5f, -(float)windowsize.Height * 0.5f, 0f);
|
|
view2d = translate * scaling;
|
|
}
|
|
|
|
// This applies the matrices
|
|
private void ApplyMatrices3D()
|
|
{
|
|
graphics.Shaders.World3D.WorldViewProj = world * viewproj; //mxd. Multiplication is ~2x faster than "world * view3d * projection";
|
|
}
|
|
|
|
// This sets the appropriate view matrix
|
|
public void ApplyMatrices2D()
|
|
{
|
|
graphics.Device.SetTransform(TransformState.World, world);
|
|
graphics.Device.SetTransform(TransformState.Projection, Matrix.Identity);
|
|
graphics.Device.SetTransform(TransformState.View, view2d);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Start / Finish
|
|
|
|
// This starts rendering
|
|
public bool Start()
|
|
{
|
|
// Start drawing
|
|
if(graphics.StartRendering(true, General.Colors.Background.ToColorValue(), graphics.BackBuffer, graphics.DepthBuffer))
|
|
{
|
|
// Beginning renderstates
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.None);
|
|
graphics.Device.SetRenderState(RenderState.ZEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.FogEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.FogDensity, 1.0f);
|
|
graphics.Device.SetRenderState(RenderState.FogColor, General.Colors.Background.ToInt());
|
|
graphics.Device.SetRenderState(RenderState.FogStart, General.Settings.ViewDistance * FOG_RANGE);
|
|
graphics.Device.SetRenderState(RenderState.FogEnd, General.Settings.ViewDistance);
|
|
graphics.Device.SetRenderState(RenderState.FogTableMode, FogMode.Linear);
|
|
graphics.Device.SetRenderState(RenderState.RangeFogEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
|
graphics.Shaders.World3D.HighlightColor = new Color4(); //mxd
|
|
|
|
// Texture addressing
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap);
|
|
|
|
// Matrices
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
|
|
// Highlight
|
|
if(General.Settings.AnimateVisualSelection)
|
|
{
|
|
highlightglow = (float)Math.Sin(Clock.CurrentTime / 100.0f) * 0.1f + 0.4f;
|
|
highlightglowinv = -highlightglow + 0.8f;
|
|
}
|
|
else
|
|
{
|
|
highlightglow = 0.4f;
|
|
highlightglowinv = 0.3f;
|
|
}
|
|
|
|
// Determine shader pass to use
|
|
shaderpass = (fullbrightness ? 1 : 0);
|
|
|
|
// Create crosshair vertices
|
|
if(crosshairverts == null)
|
|
CreateCrosshairVerts(new Size(General.Map.Data.Crosshair3D.Width, General.Map.Data.Crosshair3D.Height));
|
|
|
|
//mxd. Crate vertex handle
|
|
if (vertexhandle == null) vertexhandle = new VisualVertexHandle();
|
|
|
|
// Ready
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Can't render now
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This begins rendering world geometry
|
|
public void StartGeometry()
|
|
{
|
|
// Make collections
|
|
solidgeo = new Dictionary<ImageData, List<VisualGeometry>>(); //mxd
|
|
maskedgeo = new Dictionary<ImageData, List<VisualGeometry>>(); //mxd
|
|
translucentgeo = new List<VisualGeometry>(); //mxd
|
|
skygeo = new List<VisualGeometry>(); //mxd
|
|
|
|
solidthings = new Dictionary<ImageData, List<VisualThing>>(); //mxd
|
|
maskedthings = new Dictionary<ImageData, List<VisualThing>>(); //mxd
|
|
translucentthings = new List<VisualThing>(); //mxd
|
|
|
|
maskedmodelthings = new Dictionary<ModelData, List<VisualThing>>(); //mxd
|
|
translucentmodelthings = new List<VisualThing>(); //mxd
|
|
lightthings = new List<VisualThing>(); //mxd
|
|
allthings = new List<VisualThing>(); //mxd
|
|
}
|
|
|
|
// This ends rendering world geometry
|
|
public void FinishGeometry()
|
|
{
|
|
//mxd. Sort lights
|
|
if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && lightthings.Count > 0)
|
|
UpdateLights();
|
|
|
|
// Initial renderstates
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.Counterclockwise);
|
|
graphics.Device.SetRenderState(RenderState.ZEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
|
graphics.Shaders.World3D.Begin();
|
|
|
|
//mxd. SKY PASS
|
|
if (skygeo.Count > 0)
|
|
{
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
RenderSky(skygeo);
|
|
}
|
|
|
|
// SOLID PASS
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
RenderSinglePass(solidgeo, solidthings);
|
|
|
|
//mxd. Render models, without backface culling
|
|
if(maskedmodelthings.Count > 0)
|
|
{
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.None);
|
|
RenderModels();
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.Counterclockwise);
|
|
}
|
|
|
|
// MASK PASS
|
|
if(maskedgeo.Count > 0 || maskedthings.Count > 0)
|
|
{
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, true);
|
|
RenderSinglePass(maskedgeo, maskedthings);
|
|
}
|
|
|
|
//mxd. LIGHT PASS
|
|
if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && lightthings.Count > 0)
|
|
{
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
|
|
RenderLights(solidgeo, lightthings);
|
|
RenderLights(maskedgeo, lightthings);
|
|
}
|
|
|
|
// ALPHA AND ADDITIVE PASS
|
|
if(translucentgeo.Count > 0 || translucentthings.Count > 0)
|
|
{
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
RenderTranslucentPass(translucentgeo, translucentthings);
|
|
}
|
|
|
|
//mxd. Render translucent models, with backface culling
|
|
if(translucentmodelthings.Count > 0)
|
|
{
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
RenderTranslucentModels();
|
|
}
|
|
|
|
// THING CAGES
|
|
if(renderthingcages)
|
|
{
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
RenderThingCages();
|
|
}
|
|
|
|
//mxd. Visual vertices
|
|
RenderVertices();
|
|
|
|
//mxd. Event lines
|
|
if(General.Settings.GZShowEventLines) RenderArrows(eventlines);
|
|
|
|
// Remove references
|
|
graphics.Shaders.World3D.Texture1 = null;
|
|
|
|
// Done
|
|
graphics.Shaders.World3D.End();
|
|
|
|
//mxd. Trash collections
|
|
solidgeo = null;
|
|
maskedgeo = null;
|
|
translucentgeo = null;
|
|
skygeo = null;
|
|
|
|
solidthings = null;
|
|
maskedthings = null;
|
|
translucentthings = null;
|
|
|
|
allthings = null;
|
|
lightthings = null;
|
|
maskedmodelthings = null;
|
|
translucentmodelthings = null;
|
|
|
|
visualvertices = null;
|
|
}
|
|
|
|
//mxd
|
|
private void UpdateLights()
|
|
{
|
|
if(lightthings.Count > General.Settings.GZMaxDynamicLights)
|
|
{
|
|
// Calculate distance to camera
|
|
foreach(VisualThing t in lightthings) t.CalculateCameraDistance(cameraposition);
|
|
|
|
// Sort by it, closer ones first
|
|
lightthings.Sort((t1, t2) => Math.Sign(t1.CameraDistance - t2.CameraDistance));
|
|
|
|
// Gather the closest
|
|
List<VisualThing> tl = new List<VisualThing>(General.Settings.GZMaxDynamicLights);
|
|
for(int i = 0; i < General.Settings.GZMaxDynamicLights; i++) tl.Add(lightthings[i]);
|
|
lightthings = tl;
|
|
}
|
|
|
|
// Sort things by light render style
|
|
lightthings.Sort((t1, t2) => Math.Sign(t1.LightRenderStyle - t2.LightRenderStyle));
|
|
lightOffsets = new int[3];
|
|
|
|
foreach(VisualThing t in lightthings)
|
|
{
|
|
//add light to apropriate array.
|
|
switch(t.LightRenderStyle)
|
|
{
|
|
case DynamicLightRenderStyle.NORMAL:
|
|
case DynamicLightRenderStyle.VAVOOM: lightOffsets[0]++; break;
|
|
case DynamicLightRenderStyle.ADDITIVE: lightOffsets[1]++; break;
|
|
default: lightOffsets[2]++; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//mxd.
|
|
//I never particularly liked old ThingCages, so I wrote this instead.
|
|
//It should render faster and it has fancy arrow! :)
|
|
private void RenderThingCages()
|
|
{
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.SourceAlpha);
|
|
|
|
graphics.Shaders.World3D.BeginPass(16);
|
|
|
|
foreach(VisualThing t in allthings)
|
|
{
|
|
// Setup color
|
|
Color4 thingcolor;
|
|
if(t.Selected && showselection)
|
|
{
|
|
thingcolor = General.Colors.Selection3D.ToColorValue();
|
|
}
|
|
else
|
|
{
|
|
thingcolor = t.CageColor;
|
|
if(t != highlighted) thingcolor.Alpha = 0.6f;
|
|
}
|
|
graphics.Shaders.World3D.VertexColor = thingcolor;
|
|
|
|
//Render cage
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
graphics.Device.SetStreamSource(0, t.CageBuffer, 0, WorldVertex.Stride);
|
|
graphics.Device.DrawPrimitives(PrimitiveType.LineList, 0, t.CageLength);
|
|
}
|
|
|
|
// Done
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
|
}
|
|
|
|
//mxd
|
|
private void RenderVertices()
|
|
{
|
|
if(visualvertices == null) return;
|
|
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.SourceAlpha);
|
|
|
|
graphics.Shaders.World3D.BeginPass(16);
|
|
|
|
foreach(VisualVertex v in visualvertices)
|
|
{
|
|
world = v.Position;
|
|
ApplyMatrices3D();
|
|
|
|
// Setup color
|
|
Color4 color;
|
|
if(v.Selected && showselection)
|
|
{
|
|
color = General.Colors.Selection3D.ToColorValue();
|
|
}
|
|
else
|
|
{
|
|
color = v.HaveHeightOffset ? General.Colors.InfoLine.ToColorValue() : General.Colors.Vertices.ToColorValue();
|
|
if(v != highlighted) color.Alpha = 0.6f;
|
|
}
|
|
graphics.Shaders.World3D.VertexColor = color;
|
|
|
|
//Commence drawing!!11
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
graphics.Device.SetStreamSource(0, v.CeilingVertex ? vertexhandle.Upper : vertexhandle.Lower, 0, WorldVertex.Stride);
|
|
graphics.Device.DrawPrimitives(PrimitiveType.LineList, 0, 8);
|
|
}
|
|
|
|
// Done
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
|
}
|
|
|
|
//mxd
|
|
private void RenderArrows(ICollection<Line3D> lines)
|
|
{
|
|
// Calculate required points count
|
|
if(lines.Count == 0) return;
|
|
int pointscount = 0;
|
|
foreach(Line3D line in lines) pointscount += (line.RenderArrowhead ? 6 : 2); // 4 extra points for the arrowhead
|
|
if(pointscount < 2) return;
|
|
|
|
//create vertices
|
|
WorldVertex[] verts = new WorldVertex[pointscount];
|
|
const float scaler = 20f;
|
|
pointscount = 0;
|
|
|
|
foreach(Line3D line in lines)
|
|
{
|
|
int color = line.Color.ToInt();
|
|
|
|
// Add regular points
|
|
verts[pointscount].x = line.Start.x;
|
|
verts[pointscount].y = line.Start.y;
|
|
verts[pointscount].z = line.Start.z;
|
|
verts[pointscount].c = color;
|
|
pointscount++;
|
|
|
|
verts[pointscount].x = line.End.x;
|
|
verts[pointscount].y = line.End.y;
|
|
verts[pointscount].z = line.End.z;
|
|
verts[pointscount].c = color;
|
|
pointscount++;
|
|
|
|
// Add arrowhead
|
|
if(line.RenderArrowhead)
|
|
{
|
|
float nz = line.GetDelta().GetNormal().z * scaler;
|
|
float angle = line.GetAngle();
|
|
Vector3D a1 = new Vector3D(line.End.x - scaler * (float)Math.Sin(angle - 0.46f), line.End.y + scaler * (float)Math.Cos(angle - 0.46f), line.End.z - nz);
|
|
Vector3D a2 = new Vector3D(line.End.x - scaler * (float)Math.Sin(angle + 0.46f), line.End.y + scaler * (float)Math.Cos(angle + 0.46f), line.End.z - nz);
|
|
|
|
verts[pointscount] = verts[pointscount - 1];
|
|
verts[pointscount + 1].x = a1.x;
|
|
verts[pointscount + 1].y = a1.y;
|
|
verts[pointscount + 1].z = a1.z;
|
|
verts[pointscount + 1].c = color;
|
|
|
|
verts[pointscount + 2] = verts[pointscount - 1];
|
|
verts[pointscount + 3].x = a2.x;
|
|
verts[pointscount + 3].y = a2.y;
|
|
verts[pointscount + 3].z = a2.z;
|
|
verts[pointscount + 3].c = color;
|
|
|
|
pointscount += 4;
|
|
}
|
|
}
|
|
|
|
VertexBuffer vb = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * verts.Length, Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default);
|
|
DataStream s = vb.Lock(0, WorldVertex.Stride * verts.Length, LockFlags.Discard);
|
|
s.WriteRange(verts);
|
|
vb.Unlock();
|
|
s.Dispose();
|
|
|
|
//begin rendering
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.ZWriteEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.SourceAlpha);
|
|
|
|
graphics.Shaders.World3D.BeginPass(15);
|
|
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
|
|
//render
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
graphics.Device.SetStreamSource(0, vb, 0, WorldVertex.Stride);
|
|
graphics.Device.DrawPrimitives(PrimitiveType.LineList, 0, pointscount / 2);
|
|
|
|
// Done
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
|
vb.Dispose();
|
|
}
|
|
|
|
// This performs a single render pass
|
|
private void RenderSinglePass(Dictionary<ImageData, List<VisualGeometry>> geopass, Dictionary<ImageData, List<VisualThing>> thingspass)
|
|
{
|
|
ImageData curtexture;
|
|
int currentshaderpass = shaderpass;
|
|
int highshaderpass = shaderpass + 2;
|
|
|
|
// Begin rendering with this shader
|
|
graphics.Shaders.World3D.BeginPass(shaderpass);
|
|
|
|
// Render the geometry collected
|
|
foreach(KeyValuePair<ImageData, List<VisualGeometry>> group in geopass)
|
|
{
|
|
// What texture to use?
|
|
if(group.Key is UnknownImage)
|
|
curtexture = General.Map.Data.UnknownTexture3D;
|
|
else if(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.Shaders.World3D.Texture1 = curtexture.Texture;
|
|
|
|
//mxd. Sort geometry by sector index
|
|
group.Value.Sort((g1, g2) => g1.Sector.Sector.FixedIndex - g2.Sector.Sector.FixedIndex);
|
|
|
|
// Go for all geometry that uses this texture
|
|
VisualSector sector = null;
|
|
|
|
foreach(VisualGeometry g in group.Value)
|
|
{
|
|
// Changing sector?
|
|
if(!object.ReferenceEquals(g.Sector, sector))
|
|
{
|
|
// Update the sector if needed
|
|
if(g.Sector.NeedsUpdateGeo) g.Sector.Update();
|
|
|
|
// Only do this sector when a vertexbuffer is created
|
|
//mxd. No Map means that sector was deleted recently, I suppose
|
|
if(g.Sector.GeometryBuffer != null && g.Sector.Sector.Map != null)
|
|
{
|
|
// Change current sector
|
|
sector = g.Sector;
|
|
|
|
// Set stream source
|
|
graphics.Device.SetStreamSource(0, sector.GeometryBuffer, 0, WorldVertex.Stride);
|
|
}
|
|
else
|
|
{
|
|
sector = null;
|
|
}
|
|
}
|
|
|
|
if(sector != null)
|
|
{
|
|
// Determine the shader pass we want to use for this object
|
|
int wantedshaderpass = (((g == highlighted) && showhighlight) || (g.Selected && showselection)) ? highshaderpass : shaderpass;
|
|
|
|
//mxd. Render fog?
|
|
if (General.Settings.GZDrawFog && !fullbrightness && sector.Sector.FogMode != SectorFogMode.NONE)
|
|
wantedshaderpass += 8;
|
|
|
|
// Switch shader pass?
|
|
if(currentshaderpass != wantedshaderpass)
|
|
{
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Shaders.World3D.BeginPass(wantedshaderpass);
|
|
currentshaderpass = wantedshaderpass;
|
|
|
|
//mxd. Set variables for fog rendering?
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.World = world;
|
|
}
|
|
}
|
|
|
|
//mxd. Set variables for fog rendering?
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, g.FogFactor);
|
|
graphics.Shaders.World3D.LightColor = sector.Sector.FogColor;
|
|
}
|
|
|
|
// Set the colors to use
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((g == highlighted) && showhighlight, (g.Selected && showselection));
|
|
|
|
// Apply changes
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
|
|
// Render!
|
|
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get things for this pass
|
|
if(thingspass.Count > 0)
|
|
{
|
|
// Texture addressing
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Clamp);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Clamp);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Clamp);
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); //mxd. Disable backside culling, because otherwise sprites with positive ScaleY and negative ScaleX will be facing away from the camera...
|
|
|
|
Color4 vertexcolor = new Color4(); //mxd
|
|
|
|
// Render things collected
|
|
foreach(KeyValuePair<ImageData, List<VisualThing>> group in thingspass)
|
|
{
|
|
if(group.Key is UnknownImage) continue;
|
|
|
|
// What texture to use?
|
|
if(!group.Key.IsImageLoaded || group.Key.IsDisposed)
|
|
curtexture = General.Map.Data.Hourglass3D;
|
|
else
|
|
curtexture = group.Key;
|
|
|
|
// Create Direct3D texture if still needed
|
|
if((curtexture.Texture == null) || curtexture.Texture.Disposed)
|
|
curtexture.CreateTexture();
|
|
|
|
// Apply texture
|
|
graphics.Shaders.World3D.Texture1 = curtexture.Texture;
|
|
|
|
// Render all things with this texture
|
|
foreach(VisualThing t in group.Value)
|
|
{
|
|
// Update buffer if needed
|
|
t.Update();
|
|
|
|
// Only do this sector when a vertexbuffer is created
|
|
if(t.GeometryBuffer != null)
|
|
{
|
|
// Determine the shader pass we want to use for this object
|
|
int wantedshaderpass = (((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass;
|
|
|
|
//mxd. If fog is enagled, switch to shader, which calculates it
|
|
if (General.Settings.GZDrawFog && !fullbrightness && t.Thing.Sector != null && t.Thing.Sector.FogMode != SectorFogMode.NONE)
|
|
wantedshaderpass += 8;
|
|
|
|
//mxd. Create the matrix for positioning
|
|
world = CreateThingPositionMatrix(t);
|
|
|
|
//mxd. If current thing is light - set it's color to light color
|
|
if(Array.IndexOf(GZBuilder.GZGeneral.GZ_LIGHTS, t.Thing.SRB2Type) != -1 && !fullbrightness)
|
|
{
|
|
wantedshaderpass += 4; // Render using one of passes, which uses World3D.VertexColor
|
|
vertexcolor = t.LightColor;
|
|
}
|
|
//mxd. Check if Thing is affected by dynamic lights and set color accordingly
|
|
else if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && lightthings.Count > 0)
|
|
{
|
|
Color4 litcolor = GetLitColorForThing(t);
|
|
if(litcolor.ToArgb() != 0)
|
|
{
|
|
wantedshaderpass += 4; // Render using one of passes, which uses World3D.VertexColor
|
|
vertexcolor = new Color4(t.VertexColor) + litcolor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vertexcolor = new Color4();
|
|
}
|
|
|
|
// Switch shader pass?
|
|
if(currentshaderpass != wantedshaderpass)
|
|
{
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Shaders.World3D.BeginPass(wantedshaderpass);
|
|
currentshaderpass = wantedshaderpass;
|
|
}
|
|
|
|
//mxd. Set variables for fog rendering?
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.World = world;
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
|
|
}
|
|
|
|
// Set the colors to use
|
|
if(t.Thing.Sector != null) graphics.Shaders.World3D.LightColor = t.Thing.Sector.FogColor;
|
|
graphics.Shaders.World3D.VertexColor = vertexcolor;
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((t == highlighted) && showhighlight, (t.Selected && showselection));
|
|
|
|
// Apply changes
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Texture addressing
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap);
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.Counterclockwise); //mxd
|
|
}
|
|
|
|
// Done rendering with this shader
|
|
graphics.Shaders.World3D.EndPass();
|
|
}
|
|
|
|
//mxd
|
|
private void RenderTranslucentPass(List<VisualGeometry> geopass, List<VisualThing> thingspass)
|
|
{
|
|
int currentshaderpass = shaderpass;
|
|
int highshaderpass = shaderpass + 2;
|
|
|
|
// Sort geometry by camera distance. First vertex of the BoundingBox is it's center
|
|
if(General.Map.VisualCamera.Sector != null)
|
|
{
|
|
// If the camera is inside a sector, compare z coordinates
|
|
translucentgeo.Sort(delegate(VisualGeometry vg1, VisualGeometry vg2)
|
|
{
|
|
if (vg1 == vg2) return 0;
|
|
float camdist1, camdist2;
|
|
|
|
if((vg1.GeometryType == VisualGeometryType.FLOOR || vg1.GeometryType == VisualGeometryType.CEILING)
|
|
&& General.Map.VisualCamera.Sector.Index == vg1.Sector.Sector.Index)
|
|
{
|
|
camdist1 = Math.Abs(General.Map.VisualCamera.Position.z - vg1.BoundingBox[0].z);
|
|
}
|
|
else
|
|
{
|
|
camdist1 = (General.Map.VisualCamera.Position - vg1.BoundingBox[0]).GetLengthSq();
|
|
}
|
|
|
|
if((vg2.GeometryType == VisualGeometryType.FLOOR || vg2.GeometryType == VisualGeometryType.CEILING)
|
|
&& General.Map.VisualCamera.Sector.Index == vg2.Sector.Sector.Index)
|
|
{
|
|
camdist2 = Math.Abs(General.Map.VisualCamera.Position.z - vg2.BoundingBox[0].z);
|
|
}
|
|
else
|
|
{
|
|
camdist2 = (General.Map.VisualCamera.Position - vg2.BoundingBox[0]).GetLengthSq();
|
|
}
|
|
|
|
return (int)(camdist2 - camdist1);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
translucentgeo.Sort(delegate (VisualGeometry vg1, VisualGeometry vg2)
|
|
{
|
|
if (vg1 == vg2) return 0;
|
|
return (int)((General.Map.VisualCamera.Position - vg2.BoundingBox[0]).GetLengthSq()
|
|
- (General.Map.VisualCamera.Position - vg1.BoundingBox[0]).GetLengthSq());
|
|
});
|
|
}
|
|
|
|
ImageData curtexture;
|
|
VisualSector sector = null;
|
|
RenderPass currentpass = RenderPass.Solid;
|
|
long curtexturename = 0;
|
|
float fogfactor = -1;
|
|
|
|
// Begin rendering with this shader
|
|
graphics.Shaders.World3D.BeginPass(shaderpass);
|
|
|
|
// Go for all geometry
|
|
foreach(VisualGeometry g in geopass)
|
|
{
|
|
// Change blend mode?
|
|
if(g.RenderPass != currentpass)
|
|
{
|
|
switch(g.RenderPass)
|
|
{
|
|
case RenderPass.Additive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.Subtractive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Subtract);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.ReverseSubtractive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.ReverseSubtract);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.Alpha:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
|
|
break;
|
|
}
|
|
|
|
currentpass = g.RenderPass;
|
|
}
|
|
|
|
// Change texture?
|
|
if(g.Texture.LongName != curtexturename)
|
|
{
|
|
// What texture to use?
|
|
if(g.Texture is UnknownImage)
|
|
curtexture = General.Map.Data.UnknownTexture3D;
|
|
else if(g.Texture.IsImageLoaded && !g.Texture.IsDisposed)
|
|
curtexture = g.Texture;
|
|
else
|
|
curtexture = General.Map.Data.Hourglass3D;
|
|
|
|
// Create Direct3D texture if still needed
|
|
if((curtexture.Texture == null) || curtexture.Texture.Disposed)
|
|
curtexture.CreateTexture();
|
|
|
|
// Apply texture
|
|
graphics.Shaders.World3D.Texture1 = curtexture.Texture;
|
|
curtexturename = g.Texture.LongName;
|
|
}
|
|
|
|
// Changing sector?
|
|
if(!object.ReferenceEquals(g.Sector, sector))
|
|
{
|
|
// Update the sector if needed
|
|
if(g.Sector.NeedsUpdateGeo) g.Sector.Update();
|
|
|
|
// Only do this sector when a vertexbuffer is created
|
|
//mxd. No Map means that sector was deleted recently, I suppose
|
|
if(g.Sector.GeometryBuffer != null && g.Sector.Sector.Map != null)
|
|
{
|
|
// Change current sector
|
|
sector = g.Sector;
|
|
|
|
// Set stream source
|
|
graphics.Device.SetStreamSource(0, sector.GeometryBuffer, 0, WorldVertex.Stride);
|
|
}
|
|
else
|
|
{
|
|
sector = null;
|
|
}
|
|
}
|
|
|
|
if(sector != null)
|
|
{
|
|
// Determine the shader pass we want to use for this object
|
|
int wantedshaderpass = (((g == highlighted) && showhighlight) || (g.Selected && showselection)) ? highshaderpass : shaderpass;
|
|
|
|
//mxd. Render fog?
|
|
if (General.Settings.GZDrawFog && !fullbrightness && sector.Sector.FogMode != SectorFogMode.NONE)
|
|
wantedshaderpass += 8;
|
|
|
|
// Switch shader pass?
|
|
if(currentshaderpass != wantedshaderpass)
|
|
{
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Shaders.World3D.BeginPass(wantedshaderpass);
|
|
currentshaderpass = wantedshaderpass;
|
|
|
|
//mxd. Set variables for fog rendering?
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.World = world;
|
|
}
|
|
}
|
|
|
|
// Set variables for fog rendering?
|
|
if(wantedshaderpass > 7 && g.FogFactor != fogfactor)
|
|
{
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, g.FogFactor);
|
|
fogfactor = g.FogFactor;
|
|
}
|
|
|
|
// Set the colors to use
|
|
graphics.Shaders.World3D.LightColor = sector.Sector.FogColor;
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((g == highlighted) && showhighlight, (g.Selected && showselection));
|
|
|
|
// Apply changes
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
|
|
// Render!
|
|
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
|
}
|
|
}
|
|
|
|
// Get things for this pass
|
|
if(thingspass.Count > 0)
|
|
{
|
|
// Texture addressing
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Clamp);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Clamp);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Clamp);
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); //mxd. Disable backside culling, because otherwise sprites with positive ScaleY and negative ScaleX will be facing away from the camera...
|
|
|
|
// Sort geometry by camera distance. First vertex of the BoundingBox is it's center
|
|
thingspass.Sort(delegate (VisualThing vt1, VisualThing vt2)
|
|
{
|
|
if (vt1 == vt2) return 0;
|
|
return (int)((General.Map.VisualCamera.Position - vt2.BoundingBox[0]).GetLengthSq()
|
|
- (General.Map.VisualCamera.Position - vt1.BoundingBox[0]).GetLengthSq());
|
|
});
|
|
|
|
// Reset vars
|
|
currentpass = RenderPass.Solid;
|
|
curtexturename = 0;
|
|
Color4 vertexcolor = new Color4();
|
|
fogfactor = -1;
|
|
|
|
// Render things collected
|
|
foreach(VisualThing t in thingspass)
|
|
{
|
|
if(t.Texture is UnknownImage) continue;
|
|
|
|
// Change blend mode?
|
|
if(t.RenderPass != currentpass)
|
|
{
|
|
switch(t.RenderPass)
|
|
{
|
|
case RenderPass.Additive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.Subtractive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Subtract);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.ReverseSubtractive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.ReverseSubtract);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.Alpha:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
|
|
break;
|
|
}
|
|
|
|
currentpass = t.RenderPass;
|
|
}
|
|
|
|
// Change texture?
|
|
if(t.Texture.LongName != curtexturename)
|
|
{
|
|
// What texture to use?
|
|
if(t.Texture.IsImageLoaded && !t.Texture.IsDisposed)
|
|
curtexture = t.Texture;
|
|
else
|
|
curtexture = General.Map.Data.Hourglass3D;
|
|
|
|
// Create Direct3D texture if still needed
|
|
if((curtexture.Texture == null) || curtexture.Texture.Disposed)
|
|
curtexture.CreateTexture();
|
|
|
|
// Apply texture
|
|
graphics.Shaders.World3D.Texture1 = curtexture.Texture;
|
|
curtexturename = t.Texture.LongName;
|
|
}
|
|
|
|
// Update buffer if needed
|
|
t.Update();
|
|
|
|
// Only do this sector when a vertexbuffer is created
|
|
if(t.GeometryBuffer != null)
|
|
{
|
|
// Determine the shader pass we want to use for this object
|
|
int wantedshaderpass = (((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass;
|
|
|
|
//mxd. if fog is enagled, switch to shader, which calculates it
|
|
if (General.Settings.GZDrawFog && !fullbrightness && t.Thing.Sector != null && t.Thing.Sector.FogMode != SectorFogMode.NONE)
|
|
wantedshaderpass += 8;
|
|
|
|
//mxd. Create the matrix for positioning
|
|
world = CreateThingPositionMatrix(t);
|
|
|
|
//mxd. If current thing is light - set it's color to light color
|
|
if(Array.IndexOf(GZBuilder.GZGeneral.GZ_LIGHTS, t.Thing.SRB2Type) != -1 && !fullbrightness)
|
|
{
|
|
wantedshaderpass += 4; // Render using one of passes, which uses World3D.VertexColor
|
|
vertexcolor = t.LightColor;
|
|
}
|
|
//mxd. Check if Thing is affected by dynamic lights and set color accordingly
|
|
else if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && lightthings.Count > 0)
|
|
{
|
|
Color4 litcolor = GetLitColorForThing(t);
|
|
if(litcolor.ToArgb() != 0)
|
|
{
|
|
wantedshaderpass += 4; // Render using one of passes, which uses World3D.VertexColor
|
|
vertexcolor = new Color4(t.VertexColor) + litcolor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vertexcolor = new Color4();
|
|
}
|
|
|
|
// Switch shader pass?
|
|
if(currentshaderpass != wantedshaderpass)
|
|
{
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Shaders.World3D.BeginPass(wantedshaderpass);
|
|
currentshaderpass = wantedshaderpass;
|
|
}
|
|
|
|
//mxd. Set variables for fog rendering?
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.World = world;
|
|
if(t.FogFactor != fogfactor)
|
|
{
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
|
|
fogfactor = t.FogFactor;
|
|
}
|
|
}
|
|
|
|
// Set the colors to use
|
|
graphics.Shaders.World3D.LightColor = t.Thing.Sector.FogColor;
|
|
graphics.Shaders.World3D.VertexColor = vertexcolor;
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((t == highlighted) && showhighlight, (t.Selected && showselection));
|
|
|
|
// Apply changes
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Texture addressing
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap);
|
|
graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap);
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.Counterclockwise); //mxd
|
|
}
|
|
|
|
// Reset source blend blending operation
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
|
|
// Done rendering with this shader
|
|
graphics.Shaders.World3D.EndPass();
|
|
}
|
|
|
|
//mxd
|
|
private Matrix CreateThingPositionMatrix(VisualThing t)
|
|
{
|
|
//mxd. Create the matrix for positioning
|
|
if(t.Info.RenderMode == Thing.SpriteRenderMode.NORMAL) // Apply billboarding?
|
|
{
|
|
if(t.Info.XYBillboard)
|
|
{
|
|
return Matrix.Translation(0f, 0f, -t.LocalCenterZ)
|
|
* Matrix.RotationX(Angle2D.PI - General.Map.VisualCamera.AngleZ)
|
|
* Matrix.Translation(0f, 0f, t.LocalCenterZ)
|
|
* billboard
|
|
* Matrix.Scaling(t.Thing.ScaleX, t.Thing.ScaleX, t.Thing.ScaleY)
|
|
* t.Position;
|
|
}
|
|
else
|
|
{
|
|
return billboard
|
|
* Matrix.Scaling(t.Thing.ScaleX, t.Thing.ScaleX, t.Thing.ScaleY)
|
|
* t.Position;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Matrix.Scaling(t.Thing.ScaleX, t.Thing.ScaleX, t.Thing.ScaleY)
|
|
* t.Position;
|
|
}
|
|
}
|
|
|
|
//mxd. Dynamic lights pass!
|
|
private void RenderLights(Dictionary<ImageData, List<VisualGeometry>> geometrytolit, List<VisualThing> lights)
|
|
{
|
|
// Anything to do?
|
|
if(geometrytolit.Count == 0) return;
|
|
|
|
graphics.Shaders.World3D.World = Matrix.Identity;
|
|
graphics.Shaders.World3D.BeginPass(SHADERPASS_LIGHT);
|
|
|
|
VisualSector sector = null;
|
|
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.One);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.BlendFactor);
|
|
|
|
foreach(KeyValuePair<ImageData, List<VisualGeometry>> group in geometrytolit)
|
|
{
|
|
if(group.Key.Texture == null) continue;
|
|
graphics.Shaders.World3D.Texture1 = group.Key.Texture;
|
|
|
|
foreach(VisualGeometry g in group.Value)
|
|
{
|
|
// Changing sector?
|
|
if(!object.ReferenceEquals(g.Sector, sector))
|
|
{
|
|
// Only do this sector when a vertexbuffer is created
|
|
// mxd. no Map means that sector was deleted recently, I suppose
|
|
if(g.Sector.GeometryBuffer != null && g.Sector.Sector.Map != null)
|
|
{
|
|
// Change current sector
|
|
sector = g.Sector;
|
|
|
|
// Set stream source
|
|
graphics.Device.SetStreamSource(0, sector.GeometryBuffer, 0, WorldVertex.Stride);
|
|
}
|
|
else
|
|
{
|
|
sector = null;
|
|
}
|
|
}
|
|
|
|
if(sector == null) continue;
|
|
|
|
//normal lights
|
|
int count = lightOffsets[0];
|
|
Vector4 lpr;
|
|
if(lightOffsets[0] > 0)
|
|
{
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
if(BoundingBoxesIntersect(g.BoundingBox, lights[i].BoundingBox))
|
|
{
|
|
lpr = new Vector4(lights[i].Center, lights[i].LightRadius);
|
|
if(lpr.W == 0) continue;
|
|
graphics.Shaders.World3D.LightColor = lights[i].LightColor;
|
|
graphics.Shaders.World3D.LightPositionAndRadius = lpr;
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
|
}
|
|
}
|
|
}
|
|
|
|
//additive lights
|
|
if(lightOffsets[1] > 0)
|
|
{
|
|
count += lightOffsets[1];
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
|
|
for(int i = lightOffsets[0]; i < count; i++)
|
|
{
|
|
if(BoundingBoxesIntersect(g.BoundingBox, lights[i].BoundingBox))
|
|
{
|
|
lpr = new Vector4(lights[i].Center, lights[i].LightRadius);
|
|
if(lpr.W == 0) continue;
|
|
graphics.Shaders.World3D.LightColor = lights[i].LightColor;
|
|
graphics.Shaders.World3D.LightPositionAndRadius = lpr;
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
|
}
|
|
}
|
|
}
|
|
|
|
//negative lights
|
|
if(lightOffsets[2] > 0)
|
|
{
|
|
count += lightOffsets[2];
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.ReverseSubtract);
|
|
|
|
for(int i = lightOffsets[0] + lightOffsets[1]; i < count; i++)
|
|
{
|
|
if(BoundingBoxesIntersect(g.BoundingBox, lights[i].BoundingBox))
|
|
{
|
|
lpr = new Vector4(lights[i].Center, lights[i].LightRadius);
|
|
if(lpr.W == 0) continue;
|
|
Color4 lc = lights[i].LightColor;
|
|
graphics.Shaders.World3D.LightColor = new Color4(lc.Alpha, (lc.Green + lc.Blue) / 2, (lc.Red + lc.Blue) / 2, (lc.Green + lc.Red) / 2);
|
|
graphics.Shaders.World3D.LightPositionAndRadius = lpr;
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
}
|
|
|
|
//mxd. render models
|
|
private void RenderModels()
|
|
{
|
|
int shaderpass = (fullbrightness ? 1 : 4);
|
|
int currentshaderpass = shaderpass;
|
|
int highshaderpass = shaderpass + 2;
|
|
|
|
// Begin rendering with this shader
|
|
graphics.Shaders.World3D.BeginPass(currentshaderpass);
|
|
|
|
foreach(KeyValuePair<ModelData, List<VisualThing>> group in maskedmodelthings)
|
|
{
|
|
foreach(VisualThing t in group.Value)
|
|
{
|
|
t.Update();
|
|
|
|
Color4 vertexcolor = new Color4(t.VertexColor);
|
|
|
|
// Check if model is affected by dynamic lights and set color accordingly
|
|
if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && lightthings.Count > 0)
|
|
graphics.Shaders.World3D.VertexColor = vertexcolor + GetLitColorForThing(t);
|
|
else
|
|
graphics.Shaders.World3D.VertexColor = vertexcolor;
|
|
|
|
// Determine the shader pass we want to use for this object
|
|
int wantedshaderpass = ((((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass);
|
|
|
|
//mxd. if fog is enagled, switch to shader, which calculates it
|
|
if (General.Settings.GZDrawFog && !fullbrightness && t.Thing.Sector != null && t.Thing.Sector.FogMode != SectorFogMode.NONE)
|
|
wantedshaderpass += 8;
|
|
|
|
// Switch shader pass?
|
|
if(currentshaderpass != wantedshaderpass)
|
|
{
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Shaders.World3D.BeginPass(wantedshaderpass);
|
|
currentshaderpass = wantedshaderpass;
|
|
}
|
|
|
|
// Set the colors to use
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((t == highlighted) && showhighlight, (t.Selected && showselection));
|
|
|
|
// Create the matrix for positioning / rotation
|
|
float sx = t.Thing.ScaleX * t.Thing.ActorScale.Width;
|
|
float sy = t.Thing.ScaleY * t.Thing.ActorScale.Height;
|
|
|
|
Matrix modelscale = Matrix.Scaling(sx, sx, sy);
|
|
Matrix modelrotation = Matrix.RotationY(-t.Thing.RollRad) * Matrix.RotationX(t.Thing.PitchRad) * Matrix.RotationZ(t.Thing.Angle);
|
|
|
|
world = General.Map.Data.ModeldefEntries[t.Thing.SRB2Type].Transform * modelscale * modelrotation * t.Position;
|
|
ApplyMatrices3D();
|
|
|
|
//mxd. Set variables for fog rendering
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.World = world;
|
|
if(t.Thing.Sector != null) graphics.Shaders.World3D.LightColor = t.Thing.Sector.FogColor;
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
|
|
}
|
|
|
|
for(int i = 0; i < group.Key.Model.Meshes.Count; i++)
|
|
{
|
|
graphics.Shaders.World3D.Texture1 = group.Key.Model.Textures[i];
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
|
|
// Render!
|
|
group.Key.Model.Meshes[i].DrawSubset(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
graphics.Shaders.World3D.EndPass();
|
|
}
|
|
|
|
//mxd. Render additive/translucent models
|
|
private void RenderTranslucentModels()
|
|
{
|
|
int shaderpass = (fullbrightness ? 1 : 4);
|
|
int currentshaderpass = shaderpass;
|
|
int highshaderpass = shaderpass + 2;
|
|
RenderPass currentpass = RenderPass.Solid;
|
|
|
|
// Sort models by camera distance. First vertex of the BoundingBox is it's center
|
|
translucentmodelthings.Sort((vt1, vt2) => (int)((General.Map.VisualCamera.Position - vt2.BoundingBox[0]).GetLengthSq()
|
|
- (General.Map.VisualCamera.Position - vt1.BoundingBox[0]).GetLengthSq()));
|
|
|
|
// Begin rendering with this shader
|
|
graphics.Shaders.World3D.BeginPass(currentshaderpass);
|
|
|
|
foreach(VisualThing t in translucentmodelthings)
|
|
{
|
|
// Change blend mode?
|
|
if(t.RenderPass != currentpass)
|
|
{
|
|
switch(t.RenderPass)
|
|
{
|
|
case RenderPass.Additive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.Subtractive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Subtract);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.ReverseSubtractive:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.ReverseSubtract);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One);
|
|
break;
|
|
|
|
case RenderPass.Alpha:
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
|
|
break;
|
|
}
|
|
|
|
currentpass = t.RenderPass;
|
|
}
|
|
|
|
t.Update();
|
|
Color4 vertexcolor = new Color4(t.VertexColor);
|
|
|
|
// Check if model is affected by dynamic lights and set color accordingly
|
|
if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && lightthings.Count > 0)
|
|
graphics.Shaders.World3D.VertexColor = vertexcolor + GetLitColorForThing(t);
|
|
else
|
|
graphics.Shaders.World3D.VertexColor = vertexcolor;
|
|
|
|
// Determine the shader pass we want to use for this object
|
|
int wantedshaderpass = ((((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass);
|
|
|
|
//mxd. if fog is enagled, switch to shader, which calculates it
|
|
if (General.Settings.GZDrawFog && !fullbrightness && t.Thing.Sector != null && t.Thing.Sector.FogMode != SectorFogMode.NONE)
|
|
wantedshaderpass += 8;
|
|
|
|
// Switch shader pass?
|
|
if(currentshaderpass != wantedshaderpass)
|
|
{
|
|
graphics.Shaders.World3D.EndPass();
|
|
graphics.Shaders.World3D.BeginPass(wantedshaderpass);
|
|
currentshaderpass = wantedshaderpass;
|
|
}
|
|
|
|
// Set the colors to use
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((t == highlighted) && showhighlight, (t.Selected && showselection));
|
|
|
|
// Create the matrix for positioning / rotation
|
|
float sx = t.Thing.ScaleX * t.Thing.ActorScale.Width;
|
|
float sy = t.Thing.ScaleY * t.Thing.ActorScale.Height;
|
|
|
|
Matrix modelscale = Matrix.Scaling(sx, sx, sy);
|
|
Matrix modelrotation = Matrix.RotationY(-t.Thing.RollRad) * Matrix.RotationX(t.Thing.PitchRad) * Matrix.RotationZ(t.Thing.Angle);
|
|
|
|
world = General.Map.Data.ModeldefEntries[t.Thing.SRB2Type].Transform * modelscale * modelrotation * t.Position;
|
|
ApplyMatrices3D();
|
|
|
|
//mxd. Set variables for fog rendering
|
|
if(wantedshaderpass > 7)
|
|
{
|
|
graphics.Shaders.World3D.World = world;
|
|
if(t.Thing.Sector != null) graphics.Shaders.World3D.LightColor = t.Thing.Sector.FogColor;
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
|
|
}
|
|
|
|
GZModel model = General.Map.Data.ModeldefEntries[t.Thing.SRB2Type].Model;
|
|
for(int i = 0; i < model.Meshes.Count; i++)
|
|
{
|
|
graphics.Shaders.World3D.Texture1 = model.Textures[i];
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
|
|
// Render!
|
|
model.Meshes[i].DrawSubset(0);
|
|
}
|
|
}
|
|
|
|
// Reset source blend and blending operation
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
|
|
|
|
graphics.Shaders.World3D.EndPass();
|
|
}
|
|
|
|
//mxd
|
|
private void RenderSky(IEnumerable<VisualGeometry> geo)
|
|
{
|
|
VisualSector sector = null;
|
|
|
|
// Set render settings
|
|
graphics.Shaders.World3D.BeginPass(SHADERPASS_SKYBOX);
|
|
graphics.Shaders.World3D.Texture1 = General.Map.Data.SkyBox;
|
|
graphics.Shaders.World3D.World = world;
|
|
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, 0f);
|
|
|
|
foreach (VisualGeometry g in geo)
|
|
{
|
|
// Changing sector?
|
|
if (!object.ReferenceEquals(g.Sector, sector))
|
|
{
|
|
// Update the sector if needed
|
|
if (g.Sector.NeedsUpdateGeo) g.Sector.Update();
|
|
|
|
// Only do this sector when a vertexbuffer is created
|
|
//mxd. No Map means that sector was deleted recently, I suppose
|
|
if (g.Sector.GeometryBuffer != null && g.Sector.Sector.Map != null)
|
|
{
|
|
// Change current sector
|
|
sector = g.Sector;
|
|
|
|
// Set stream source
|
|
graphics.Device.SetStreamSource(0, sector.GeometryBuffer, 0, WorldVertex.Stride);
|
|
}
|
|
else
|
|
{
|
|
sector = null;
|
|
}
|
|
}
|
|
|
|
if (sector != null)
|
|
{
|
|
// Set the colors to use
|
|
graphics.Shaders.World3D.HighlightColor = CalculateHighlightColor((g == highlighted) && showhighlight, (g.Selected && showselection));
|
|
|
|
// Apply changes
|
|
graphics.Shaders.World3D.ApplySettings();
|
|
|
|
// Render!
|
|
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
|
}
|
|
}
|
|
|
|
graphics.Shaders.World3D.EndPass();
|
|
}
|
|
|
|
//mxd. This gets color from dynamic lights based on distance to thing.
|
|
//thing position must be in absolute cordinates
|
|
//(thing.Position.Z value is relative to floor of the sector the thing is in)
|
|
private Color4 GetLitColorForThing(VisualThing t)
|
|
{
|
|
Color4 litColor = new Color4();
|
|
foreach(VisualThing lt in lightthings)
|
|
{
|
|
// Don't light self
|
|
if(General.Map.Data.GldefsEntries.ContainsKey(t.Thing.SRB2Type) && General.Map.Data.GldefsEntries[t.Thing.SRB2Type].DontLightSelf && t.Thing.Index == lt.Thing.Index)
|
|
continue;
|
|
|
|
float distSquared = Vector3.DistanceSquared(lt.Center, t.PositionV3);
|
|
float radiusSquared = lt.LightRadius * lt.LightRadius;
|
|
if(distSquared < radiusSquared)
|
|
{
|
|
int sign = (lt.LightRenderStyle == DynamicLightRenderStyle.NEGATIVE ? -1 : 1);
|
|
float scaler = 1 - distSquared / radiusSquared * lt.LightColor.Alpha;
|
|
litColor.Red += lt.LightColor.Red * scaler * sign;
|
|
litColor.Green += lt.LightColor.Green * scaler * sign;
|
|
litColor.Blue += lt.LightColor.Blue * scaler * sign;
|
|
}
|
|
}
|
|
|
|
return litColor;
|
|
}
|
|
|
|
// This calculates the highlight/selection color
|
|
private Color4 CalculateHighlightColor(bool ishighlighted, bool isselected)
|
|
{
|
|
if (!ishighlighted && !isselected) return new Color4(); //mxd
|
|
Color4 highlightcolor = isselected ? General.Colors.Selection.ToColorValue() : General.Colors.Highlight.ToColorValue();
|
|
highlightcolor.Alpha = ishighlighted ? highlightglowinv : highlightglow;
|
|
return highlightcolor;
|
|
}
|
|
|
|
// This finishes rendering
|
|
public void Finish()
|
|
{
|
|
General.Plugins.OnPresentDisplayBegin();
|
|
|
|
// Done
|
|
graphics.FinishRendering();
|
|
graphics.Present();
|
|
highlighted = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Rendering
|
|
|
|
// This sets the highlighted object for the rendering
|
|
public void SetHighlightedObject(IVisualPickable obj)
|
|
{
|
|
highlighted = obj;
|
|
}
|
|
|
|
// This collects a visual sector's geometry for rendering
|
|
public void AddSectorGeometry(VisualGeometry g)
|
|
{
|
|
// Must have a texture and vertices
|
|
if(g.Texture != null && g.Triangles > 0)
|
|
{
|
|
if (g.RenderAsSky && General.Settings.GZDrawSky)
|
|
{
|
|
skygeo.Add(g);
|
|
}
|
|
else
|
|
{
|
|
switch (g.RenderPass)
|
|
{
|
|
case RenderPass.Solid:
|
|
if (!solidgeo.ContainsKey(g.Texture))
|
|
solidgeo.Add(g.Texture, new List<VisualGeometry>());
|
|
solidgeo[g.Texture].Add(g);
|
|
break;
|
|
|
|
case RenderPass.Mask:
|
|
if (!maskedgeo.ContainsKey(g.Texture))
|
|
maskedgeo.Add(g.Texture, new List<VisualGeometry>());
|
|
maskedgeo[g.Texture].Add(g);
|
|
break;
|
|
|
|
case RenderPass.Additive:
|
|
case RenderPass.Subtractive:
|
|
case RenderPass.ReverseSubtractive:
|
|
case RenderPass.Alpha:
|
|
translucentgeo.Add(g);
|
|
break;
|
|
|
|
default:
|
|
throw new NotImplementedException("Geometry rendering of " + g.RenderPass + " render pass is not implemented!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This collects a visual sector's geometry for rendering
|
|
public void AddThingGeometry(VisualThing t)
|
|
{
|
|
//mxd. Gather lights
|
|
if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && t.LightType != DynamicLightType.NONE)
|
|
{
|
|
t.UpdateLightRadius();
|
|
if(t.LightRadius > 0)
|
|
{
|
|
if(Array.IndexOf(GZBuilder.GZGeneral.GZ_ANIMATED_LIGHT_TYPES, t.LightType) != -1)
|
|
t.UpdateBoundingBox();
|
|
lightthings.Add(t);
|
|
}
|
|
}
|
|
|
|
//mxd. Gather models
|
|
if(t.Thing.IsModel &&
|
|
(General.Settings.GZDrawModelsMode == ModelRenderMode.ALL ||
|
|
General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER ||
|
|
(General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected)))
|
|
{
|
|
switch(t.RenderPass)
|
|
{
|
|
case RenderPass.Mask:
|
|
case RenderPass.Solid:
|
|
ModelData mde = General.Map.Data.ModeldefEntries[t.Thing.SRB2Type];
|
|
if(!maskedmodelthings.ContainsKey(mde)) maskedmodelthings.Add(mde, new List<VisualThing>());
|
|
maskedmodelthings[mde].Add(t);
|
|
break;
|
|
|
|
case RenderPass.Additive:
|
|
case RenderPass.Subtractive:
|
|
case RenderPass.ReverseSubtractive:
|
|
case RenderPass.Alpha:
|
|
translucentmodelthings.Add(t);
|
|
break;
|
|
|
|
default:
|
|
throw new NotImplementedException("Thing model rendering of " + t.RenderPass + " render pass is not implemented!");
|
|
}
|
|
}
|
|
// Gather regular things
|
|
else if(t.Texture != null) //Must have a texture!
|
|
{
|
|
//mxd
|
|
switch(t.RenderPass)
|
|
{
|
|
case RenderPass.Solid:
|
|
if(!solidthings.ContainsKey(t.Texture)) solidthings.Add(t.Texture, new List<VisualThing>());
|
|
solidthings[t.Texture].Add(t);
|
|
break;
|
|
|
|
case RenderPass.Mask:
|
|
if(!maskedthings.ContainsKey(t.Texture)) maskedthings.Add(t.Texture, new List<VisualThing>());
|
|
maskedthings[t.Texture].Add(t);
|
|
break;
|
|
|
|
case RenderPass.Additive:
|
|
case RenderPass.Subtractive:
|
|
case RenderPass.ReverseSubtractive:
|
|
case RenderPass.Alpha:
|
|
translucentthings.Add(t);
|
|
break;
|
|
|
|
default:
|
|
throw new NotImplementedException("Thing rendering of " + t.RenderPass + " render pass is not implemented!");
|
|
}
|
|
}
|
|
|
|
//mxd. Add to the plain list
|
|
allthings.Add(t);
|
|
}
|
|
|
|
//mxd
|
|
public void SetVisualVertices(List<VisualVertex> verts) { visualvertices = verts; }
|
|
|
|
//mxd
|
|
public void SetEventLines(List<Line3D> lines) { eventlines = lines; }
|
|
|
|
//mxd
|
|
private static bool BoundingBoxesIntersect(Vector3D[] bbox1, Vector3D[] bbox2)
|
|
{
|
|
Vector3D dist = bbox1[0] - bbox2[0];
|
|
|
|
Vector3D halfSize1 = bbox1[0] - bbox1[1];
|
|
Vector3D halfSize2 = bbox2[0] - bbox2[1];
|
|
|
|
return (halfSize1.x + halfSize2.x >= Math.Abs(dist.x) && halfSize1.y + halfSize2.y >= Math.Abs(dist.y) && halfSize1.z + halfSize2.z >= Math.Abs(dist.z));
|
|
}
|
|
|
|
// This renders the crosshair
|
|
public void RenderCrosshair()
|
|
{
|
|
//mxd
|
|
world = Matrix.Identity;
|
|
ApplyMatrices3D();
|
|
|
|
// Set renderstates
|
|
graphics.Device.SetRenderState(RenderState.CullMode, Cull.None);
|
|
graphics.Device.SetRenderState(RenderState.ZEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false);
|
|
graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
|
|
graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
|
|
graphics.Device.SetTransform(TransformState.World, Matrix.Identity);
|
|
graphics.Device.SetTransform(TransformState.Projection, Matrix.Identity);
|
|
ApplyMatrices2D();
|
|
|
|
// Texture
|
|
if(crosshairbusy)
|
|
{
|
|
if(General.Map.Data.CrosshairBusy3D.Texture == null) General.Map.Data.CrosshairBusy3D.CreateTexture();
|
|
graphics.Shaders.Display2D.Texture1 = General.Map.Data.CrosshairBusy3D.Texture;
|
|
}
|
|
else
|
|
{
|
|
if(General.Map.Data.Crosshair3D.Texture == null) General.Map.Data.Crosshair3D.CreateTexture();
|
|
graphics.Shaders.Display2D.Texture1 = General.Map.Data.Crosshair3D.Texture;
|
|
}
|
|
|
|
// Draw
|
|
graphics.Shaders.Display2D.Begin();
|
|
graphics.Shaders.Display2D.SetSettings(1.0f, 1.0f, 0.0f, 1.0f, true);
|
|
graphics.Shaders.Display2D.BeginPass(1);
|
|
graphics.Device.DrawUserPrimitives(PrimitiveType.TriangleStrip, 0, 2, crosshairverts);
|
|
graphics.Shaders.Display2D.EndPass();
|
|
graphics.Shaders.Display2D.End();
|
|
}
|
|
|
|
// This switches fog on and off
|
|
public void SetFogMode(bool usefog)
|
|
{
|
|
graphics.Device.SetRenderState(RenderState.FogEnable, usefog);
|
|
}
|
|
|
|
// This siwtches crosshair busy icon on and off
|
|
public void SetCrosshairBusy(bool busy)
|
|
{
|
|
crosshairbusy = busy;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|