diff --git a/Builder.sln.DotSettings.user b/Builder.sln.DotSettings.user index 66ccfb21..9af4ab9e 100644 --- a/Builder.sln.DotSettings.user +++ b/Builder.sln.DotSettings.user @@ -4,4 +4,6 @@ DO_NOT_SHOW UDMF <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> \ No newline at end of file + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + True + True \ No newline at end of file diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index 94e8b858..28070d03 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -209,6 +209,7 @@ Component + @@ -1307,6 +1308,7 @@ + @@ -1365,6 +1367,7 @@ + diff --git a/Source/Core/BuilderMono.csproj b/Source/Core/BuilderMono.csproj index 0af6f83a..66d5d4db 100644 --- a/Source/Core/BuilderMono.csproj +++ b/Source/Core/BuilderMono.csproj @@ -206,6 +206,7 @@ Component + diff --git a/Source/Core/Config/ProgramConfiguration.cs b/Source/Core/Config/ProgramConfiguration.cs index 50ecf744..57e41880 100755 --- a/Source/Core/Config/ProgramConfiguration.cs +++ b/Source/Core/Config/ProgramConfiguration.cs @@ -146,6 +146,9 @@ namespace CodeImp.DoomBuilder.Config private bool dynamicgridsize; private int ignoredremoterevision; + //volte + private bool classicRendering; + // These are not stored in the configuration, only used at runtime private int defaultbrightness; private int defaultfloorheight; @@ -270,6 +273,9 @@ namespace CodeImp.DoomBuilder.Config public bool RenderGrid { get { return rendergrid; } internal set { rendergrid = value; } } //mxd public bool DynamicGridSize { get { return dynamicgridsize; } internal set { dynamicgridsize = value; } } //mxd internal int IgnoredRemoteRevision { get { return ignoredremoterevision; } set { ignoredremoterevision = value; } } //mxd + + //volte + public bool ClassicRendering { get { return classicRendering; } internal set { classicRendering = value; } } //mxd. Left here for compatibility reasons... public string DefaultTexture { get { return General.Map != null ? General.Map.Options.DefaultWallTexture : "-"; } set { if(General.Map != null) General.Map.Options.DefaultWallTexture = value; } } @@ -404,6 +410,9 @@ namespace CodeImp.DoomBuilder.Config dynamicgridsize = cfg.ReadSetting("dynamicgridsize", true); //mxd ignoredremoterevision = cfg.ReadSetting("ignoredremoterevision", 0); //mxd + // volte + classicRendering = cfg.ReadSetting("classicrendering", false); + //mxd. Sector defaults defaultceilheight = cfg.ReadSetting("defaultceilheight", 128); defaultfloorheight = cfg.ReadSetting("defaultfloorheight", 0); @@ -544,6 +553,9 @@ namespace CodeImp.DoomBuilder.Config cfg.WriteSetting("dynamicgridsize", dynamicgridsize); //mxd cfg.WriteSetting("ignoredremoterevision", ignoredremoterevision); //mxd + //volte + cfg.WriteSetting("classicrendering", classicRendering); + //mxd. Sector defaults cfg.WriteSetting("defaultceilheight", defaultceilheight); cfg.WriteSetting("defaultfloorheight", defaultfloorheight); diff --git a/Source/Core/Data/ColorMap.cs b/Source/Core/Data/ColorMap.cs new file mode 100644 index 00000000..06a7faaa --- /dev/null +++ b/Source/Core/Data/ColorMap.cs @@ -0,0 +1,107 @@ +#region ================== Copyright + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.CompilerServices; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class ColorMap + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private PixelColor[] colors; + + #endregion + + #region ================== Properties + + public PixelColor this[int index] { get { return colors[index]; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ColorMap() + { + colors = new PixelColor[34 * 256]; + for(int i = 0; i < 34 * 256; i++) + { + // Set colors to gray + colors[i].r = 127; + colors[i].g = 127; + colors[i].b = 127; + colors[i].a = 255; + } + } + + // Constructor + public ColorMap(Stream stream, Playpal palette) + { + BinaryReader reader = new BinaryReader(stream); + var colors = new List(); + + // Read all palette entries + stream.Seek(0, SeekOrigin.Begin); + while (stream.Position < stream.Length) + { + // Read colors + var index = reader.ReadByte(); + var color = palette[index]; + colors.Add(color); + } + + this.colors = colors.ToArray(); + } + + #endregion + + #region ================== Methods + + public Bitmap CreateBitmap() { + var width = 256; + var height = (int)Math.Ceiling((float)colors.Length / 256); + var bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int index = width * y + x; + bitmap.SetPixel(x, y, colors[index].ToColor()); + } + } + return bitmap; + } + + #endregion + } +} diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs index ed7852f6..eece1803 100755 --- a/Source/Core/Data/DataManager.cs +++ b/Source/Core/Data/DataManager.cs @@ -66,7 +66,8 @@ namespace CodeImp.DoomBuilder.Data // Palette private Playpal palette; - + private ColorMap mainColormap; + // Textures, Flats and Sprites private Dictionary textures; private Dictionary texturenamesshorttofull; //mxd @@ -163,6 +164,7 @@ namespace CodeImp.DoomBuilder.Data internal IEnumerable Containers { get { return containers; } } public Playpal Palette { get { return palette; } } + public ColorMap MainColorMap { get { return mainColormap; } } public ICollection Textures { get { return textures.Values; } } public ICollection Flats { get { return flats.Values; } } public List TextureNames { get { return texturenames; } } @@ -428,6 +430,7 @@ namespace CodeImp.DoomBuilder.Data // Load stuff LoadX11R6RGB(); //mxd LoadPalette(); + LoadMainColorMap(); Dictionary cachedparsers = new Dictionary(); //mxd int texcount = LoadTextures(texturesonly, texturenamesshorttofull, cachedparsers); int flatcount = LoadFlats(flatsonly, flatnamesshorttofull, cachedparsers); @@ -839,6 +842,24 @@ namespace CodeImp.DoomBuilder.Data } } + private void LoadMainColorMap() + { + // Go for all opened containers + for(int i = containers.Count - 1; i >= 0; i--) + { + // Load palette + mainColormap = containers[i].LoadMainColorMap(palette); + if(mainColormap != null) break; + } + + // Make empty palette when still no palette found + if(mainColormap == null) + { + General.ErrorLogger.Add(ErrorType.Warning, "None of the loaded resources define a colormap. Did you forget to configure an IWAD for this game configuration?"); + mainColormap = new ColorMap(); + } + } + #endregion #region ================== Colormaps diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs index 7bef45e9..932ec8c0 100755 --- a/Source/Core/Data/DataReader.cs +++ b/Source/Core/Data/DataReader.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.IO; using CodeImp.DoomBuilder.Compilers; using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.ZDoom; #endregion @@ -164,6 +165,8 @@ namespace CodeImp.DoomBuilder.Data // When implemented, this should find and load a PLAYPAL palette public virtual Playpal LoadPalette() { return null; } + + public virtual ColorMap LoadMainColorMap(Playpal palette) { return null; } #endregion diff --git a/Source/Core/Data/ImageData.cs b/Source/Core/Data/ImageData.cs index 1e97762c..2d0a288b 100755 --- a/Source/Core/Data/ImageData.cs +++ b/Source/Core/Data/ImageData.cs @@ -37,7 +37,8 @@ namespace CodeImp.DoomBuilder.Data public abstract unsafe class ImageData : IDisposable { #region ================== Constants - + + public const int TEXTURE_INDEXED = 1; #endregion #region ================== Variables @@ -61,6 +62,7 @@ namespace CodeImp.DoomBuilder.Data protected bool hasPatchWithSameName; //mxd protected int namewidth; // biwa protected int shortnamewidth; // biwa + protected bool wantIndexed; // volte //mxd. Hashing private static int hashcounter; @@ -85,6 +87,7 @@ namespace CodeImp.DoomBuilder.Data private int mipmaplevels; // 0 = all mipmaps protected bool dynamictexture; private Texture texture; + private Texture indexedTexture; // Disposing protected bool isdisposed; @@ -105,7 +108,8 @@ namespace CodeImp.DoomBuilder.Data public bool HasPatchWithSameName { get { return hasPatchWithSameName; } } //mxd internal bool HasLongName { get { return hasLongName; } } //mxd public bool UseColorCorrection { get { return usecolorcorrection; } set { usecolorcorrection = value; } } - public Texture Texture { get { return GetTexture(); } } + public Texture Texture { get { return GetTexture(false); } } + public Texture IndexedTexture { get { return GetTexture(true); } } public bool IsPreviewLoaded { get @@ -176,6 +180,7 @@ namespace CodeImp.DoomBuilder.Data previewbitmap?.Dispose(); spritepreviewbitmap?.Dispose(); texture?.Dispose(); + indexedTexture?.Dispose(); loadedbitmap = null; previewbitmap = null; spritepreviewbitmap = null; @@ -330,6 +335,7 @@ namespace CodeImp.DoomBuilder.Data loadedbitmap?.Dispose(); texture?.Dispose(); + indexedTexture?.Dispose(); imagestate = ImageLoadState.Ready; loadedbitmap = loadResult.bitmap; alphatest = loadResult.alphatest; @@ -676,30 +682,78 @@ namespace CodeImp.DoomBuilder.Data loadResult.bitmap.UnlockBits(bmpdata); } - Texture GetTexture() + Texture GetTexture(bool indexed = false) { - if (texture != null) - return texture; - else if (imagestate == ImageLoadState.Loading) - return General.Map.Data.LoadingTexture; - else if (loadfailed) - return General.Map.Data.FailedTexture; + if (indexed && indexedTexture != null) + return indexedTexture; + if (!indexed && texture != null) + return texture; - if (imagestate == ImageLoadState.None) - { - General.Map.Data.QueueLoadImage(this); - return General.Map.Data.LoadingTexture; - } + if (indexed && !wantIndexed) + { + // indexed texture requested, but we didn't generate it, so we have to reload the image + ReleaseTexture(); + imagestate = ImageLoadState.None; + wantIndexed = true; + } + + if (imagestate == ImageLoadState.Loading) + return General.Map.Data.LoadingTexture; + if (loadfailed) + return General.Map.Data.FailedTexture; - texture = new Texture(General.Map.Graphics, loadedbitmap); + if (imagestate == ImageLoadState.None) + { + General.Map.Data.QueueLoadImage(this); + return General.Map.Data.LoadingTexture; + } + + if (loadedbitmap == null) + { + return General.Map.Data.LoadingTexture; + } - loadedbitmap.Dispose(); - loadedbitmap = null; + texture = new Texture(General.Map.Graphics, loadedbitmap); + if (wantIndexed) + { + Bitmap indexedBitmap = CreateIndexedBitmap(loadedbitmap, General.Map.Data.Palette); + indexedTexture = new Texture(General.Map.Graphics, indexedBitmap); + indexedTexture.UserData = TEXTURE_INDEXED; + } + + loadedbitmap.Dispose(); + loadedbitmap = null; #if DEBUG texture.Tag = name; //mxd. Helps with tracking undisposed resources... #endif - return texture; + + return indexed ? indexedTexture : texture; + } + + Bitmap CreateIndexedBitmap(Bitmap original, Playpal palette) + { + int[] indices = new int[original.Width * original.Height]; + + Bitmap indexed = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb); + BitmapData indata = original.LockBits(new Rectangle(0, 0, original.Size.Width, original.Size.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + PixelColor* inpixels = (PixelColor*)indata.Scan0.ToPointer(); + General.Colors.QuantizeColorsToPlaypal(inpixels, indices, palette); + + BitmapData outdata = indexed.LockBits(new Rectangle(0, 0, indexed.Size.Width, indexed.Size.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + PixelColor *outpixels = (PixelColor*)outdata.Scan0.ToPointer(); + for (int i = 0; i < indices.Length; i++) + { + outpixels[i].r = (byte)indices[i]; + outpixels[i].g = 0; + outpixels[i].b = 0; + outpixels[i].a = inpixels[i].a; + } + + original.UnlockBits(indata); + indexed.UnlockBits(outdata); + + return indexed; } // This updates a dynamic texture @@ -718,6 +772,8 @@ namespace CodeImp.DoomBuilder.Data { texture?.Dispose(); texture = null; + indexedTexture?.Dispose(); + indexedTexture = null; } // This returns a preview image diff --git a/Source/Core/Data/PK3StructuredReader.cs b/Source/Core/Data/PK3StructuredReader.cs index c932e976..99cc69ba 100755 --- a/Source/Core/Data/PK3StructuredReader.cs +++ b/Source/Core/Data/PK3StructuredReader.cs @@ -151,6 +151,37 @@ namespace CodeImp.DoomBuilder.Data // Done return palette; } + + // This loads the COLORMAP + public override ColorMap LoadMainColorMap(Playpal palette) + { + // Error when suspended + if(issuspended) throw new Exception("Data reader is suspended"); + + // Colormap from wad(s) + ColorMap colormap = null; + foreach(WADReader wr in wads) + { + ColorMap wadcolormap = wr.LoadMainColorMap(palette); + if(wadcolormap != null) return wadcolormap; + } + + // Find in root directory + string foundfile = FindFirstFile("COLORMAP", false); + if((foundfile != null) && FileExists(foundfile)) + { + MemoryStream stream = LoadFile(foundfile); + + if(stream.Length >= 256) //mxd + colormap = new ColorMap(stream, palette); + else + General.ErrorLogger.Add(ErrorType.Warning, "Warning: invalid colormap \"" + foundfile + "\""); + stream.Dispose(); + } + + // Done + return colormap; + } #endregion diff --git a/Source/Core/Data/Playpal.cs b/Source/Core/Data/Playpal.cs index d90cc0fb..92b719ec 100755 --- a/Source/Core/Data/Playpal.cs +++ b/Source/Core/Data/Playpal.cs @@ -16,6 +16,9 @@ #region ================== Namespaces +using System; +using System.Drawing; +using System.Drawing.Imaging; using System.IO; using CodeImp.DoomBuilder.Rendering; @@ -38,7 +41,7 @@ namespace CodeImp.DoomBuilder.Data #region ================== Properties public PixelColor this[int index] { get { return colors[index]; } } - + #endregion #region ================== Constructor / Disposer @@ -84,6 +87,38 @@ namespace CodeImp.DoomBuilder.Data #region ================== Methods + public Bitmap CreateBitmap() { + var bitmap = new Bitmap(16, 16, PixelFormat.Format32bppRgb); + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + int index = 16 * y + x; + bitmap.SetPixel(x, y, colors[index].ToColor()); + } + } + return bitmap; + } + + public int FindClosestColor(PixelColor match) + { + float minDist = 99999; + int minIndex = 0; + for (int i = 0; i < colors.Length; i++) + { + PixelColor color = colors[i]; + float dr = (float)match.r - (float)color.r; + float dg = (float)match.g - (float)color.g; + float db = (float)match.b - (float)color.b; + float sqDist = dr * dr + dg * dg + db * db; + if (sqDist < minDist) + { + minIndex = i; + minDist = sqDist; + } + } + + return minIndex; + } + #endregion } } diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs index a0e60b36..2a14be00 100755 --- a/Source/Core/Data/WADReader.cs +++ b/Source/Core/Data/WADReader.cs @@ -26,6 +26,7 @@ using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Data.Scripting; using CodeImp.DoomBuilder.GZBuilder.Data; using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.ZDoom; #endregion @@ -308,6 +309,21 @@ namespace CodeImp.DoomBuilder.Data return null; // No palette } + public override ColorMap LoadMainColorMap(Playpal palette) + { + // Error when suspended + if(issuspended) throw new Exception("Data reader is suspended"); + + // Look for a lump named COLORMAP + Lump lump = file.FindLump("COLORMAP"); + if (lump != null) + { + using (Stream s = lump.GetSafeStream()) + return new ColorMap(s, palette); + } + return null; // No palette + } + #endregion #region ================== Colormaps diff --git a/Source/Core/Properties/Resources.Designer.cs b/Source/Core/Properties/Resources.Designer.cs index dacd671f..0b487770 100755 --- a/Source/Core/Properties/Resources.Designer.cs +++ b/Source/Core/Properties/Resources.Designer.cs @@ -210,6 +210,16 @@ namespace CodeImp.DoomBuilder.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ClassicRendering { + get { + object obj = ResourceManager.GetObject("ClassicRendering", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/Source/Core/Properties/Resources.resx b/Source/Core/Properties/Resources.resx index 0af1b28f..b52d9d29 100755 --- a/Source/Core/Properties/Resources.resx +++ b/Source/Core/Properties/Resources.resx @@ -532,6 +532,9 @@ ..\Resources\Sky.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\ClassicRendering.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\Pin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/Source/Core/Rendering/ColorCollection.cs b/Source/Core/Rendering/ColorCollection.cs index 1ffef750..ae5d2c5b 100755 --- a/Source/Core/Rendering/ColorCollection.cs +++ b/Source/Core/Rendering/ColorCollection.cs @@ -19,6 +19,7 @@ using System; using System.Globalization; using System.Drawing; +using CodeImp.DoomBuilder.Data; using Configuration = CodeImp.DoomBuilder.IO.Configuration; #endregion @@ -275,6 +276,12 @@ namespace CodeImp.DoomBuilder.Rendering cp->b = correctiontable[cp->b]; } } + + // This quantizes an image to a PLAYPAL lump, putting indices into an array of integers. + internal unsafe void QuantizeColorsToPlaypal(PixelColor* inPixels, int[] indices, Playpal playpal) + { + System.Threading.Tasks.Parallel.For(0, indices.Length, (i) => indices[i] = playpal.FindClosestColor(inPixels[i])); + } // This clamps a value between 0 and 1 private static float Saturate(float v) diff --git a/Source/Core/Rendering/IRenderer3D.cs b/Source/Core/Rendering/IRenderer3D.cs index 9beb3c60..8d18a7c8 100755 --- a/Source/Core/Rendering/IRenderer3D.cs +++ b/Source/Core/Rendering/IRenderer3D.cs @@ -17,6 +17,7 @@ #region ================== Namespaces using System.Collections.Generic; +using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.VisualModes; @@ -45,7 +46,8 @@ namespace CodeImp.DoomBuilder.Rendering // Rendering methods int CalculateBrightness(int level); int CalculateBrightness(int level, Sidedef sd); //mxd - + + void SetClassicLightingColorMap(ColorMap colormap); void SetHighlightedObject(IVisualPickable obj); void AddSectorGeometry(VisualGeometry g); void AddThingGeometry(VisualThing t); diff --git a/Source/Core/Rendering/RenderDevice.cs b/Source/Core/Rendering/RenderDevice.cs index bb64ecfa..c7088273 100755 --- a/Source/Core/Rendering/RenderDevice.cs +++ b/Source/Core/Rendering/RenderDevice.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Windows.Forms; using CodeImp.DoomBuilder.Controls; @@ -69,6 +70,11 @@ namespace CodeImp.DoomBuilder.Rendering DeclareUniform(UniformName.sectorfogcolor, "sectorfogcolor", UniformType.Vec4f); DeclareUniform(UniformName.lightsEnabled, "lightsEnabled", UniformType.Float); DeclareUniform(UniformName.slopeHandleLength, "slopeHandleLength", UniformType.Float); + + // volte: classic rendering + DeclareUniform(UniformName.drawPaletted, "drawPaletted", UniformType.Int); + DeclareUniform(UniformName.colormapSize, "colormapSize", UniformType.Vec2i); + DeclareUniform(UniformName.lightLevel, "lightLevel", UniformType.Int); // 2d fsaa CompileShader(ShaderName.display2d_fsaa, "display2d.shader", "display2d_fsaa"); @@ -90,6 +96,10 @@ namespace CodeImp.DoomBuilder.Rendering CompileShader(ShaderName.world3d_vertex_color, "world3d.shader", "world3d_vertex_color"); CompileShader(ShaderName.world3d_main_vertexcolor, "world3d.shader", "world3d_main_vertexcolor"); CompileShader(ShaderName.world3d_constant_color, "world3d.shader", "world3d_constant_color"); + + // classic rendering + CompileShader(ShaderName.world3d_classic, "world3d.shader", "world3d_classic"); + CompileShader(ShaderName.world3d_classic_highlight, "world3d.shader", "world3d_classic_highlight"); // skybox shader CompileShader(ShaderName.world3d_skybox, "world3d_skybox.shader", "world3d_skybox"); @@ -125,7 +135,11 @@ namespace CodeImp.DoomBuilder.Rendering Handle = RenderDevice_New(display, RenderTarget.Handle); if (Handle == IntPtr.Zero) - throw new RenderDeviceException(string.Format("Could not create render device: {0}", BuilderNative_GetError())); + { + StringBuilder sb = new StringBuilder(4096); + BuilderNative_GetError(sb, sb.Capacity); + throw new RenderDeviceException(string.Format("Could not create render device: {0}", sb)); + } } public bool Disposed { get { return Handle == IntPtr.Zero; } } @@ -133,7 +147,11 @@ namespace CodeImp.DoomBuilder.Rendering void ThrowIfFailed(bool result) { if (!result) - throw new RenderDeviceException(BuilderNative_GetError()); + { + StringBuilder sb = new StringBuilder(4096); + BuilderNative_GetError(sb, sb.Capacity); + throw new RenderDeviceException(sb.ToString()); + } } public void Dispose() @@ -359,24 +377,24 @@ namespace CodeImp.DoomBuilder.Rendering RenderDevice_SetZWriteEnable(Handle, value); } - public void SetTexture(BaseTexture value) + public void SetTexture(BaseTexture value, int unit = 0) { - RenderDevice_SetTexture(Handle, value != null ? value.Handle : IntPtr.Zero); + RenderDevice_SetTexture(Handle, unit, value != null ? value.Handle : IntPtr.Zero); } - public void SetSamplerFilter(TextureFilter filter) + public void SetSamplerFilter(TextureFilter filter, int unit = 0) { - SetSamplerFilter(filter, filter, MipmapFilter.None, 0.0f); + SetSamplerFilter(filter, filter, MipmapFilter.None, 0.0f, unit); } - public void SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) + public void SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy, int unit = 0) { - RenderDevice_SetSamplerFilter(Handle, minfilter, magfilter, mipfilter, maxanisotropy); + RenderDevice_SetSamplerFilter(Handle, unit, minfilter, magfilter, mipfilter, maxanisotropy); } - public void SetSamplerState(TextureAddress address) + public void SetSamplerState(TextureAddress address, int unit = 0) { - RenderDevice_SetSamplerState(Handle, address); + RenderDevice_SetSamplerState(Handle, unit, address); } public void DrawIndexed(PrimitiveType type, int startIndex, int primitiveCount) @@ -564,7 +582,7 @@ namespace CodeImp.DoomBuilder.Rendering static extern void RenderDevice_DeclareShader(IntPtr handle, ShaderName index, string name, string vertexShader, string fragShader); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - static extern string BuilderNative_GetError(); + static extern void BuilderNative_GetError(StringBuilder str, int length); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] static extern bool RenderDevice_SetShader(IntPtr handle, ShaderName name); @@ -615,13 +633,13 @@ namespace CodeImp.DoomBuilder.Rendering static extern void RenderDevice_SetZWriteEnable(IntPtr handle, bool value); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - static extern void RenderDevice_SetTexture(IntPtr handle, IntPtr texture); + static extern void RenderDevice_SetTexture(IntPtr handle, int unit, IntPtr texture); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - static extern void RenderDevice_SetSamplerFilter(IntPtr handle, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy); + static extern void RenderDevice_SetSamplerFilter(IntPtr handle, int unit, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - static extern void RenderDevice_SetSamplerState(IntPtr handle, TextureAddress address); + static extern void RenderDevice_SetSamplerState(IntPtr handle, int unit, TextureAddress address); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] static extern bool RenderDevice_Draw(IntPtr handle, PrimitiveType type, int startIndex, int primitiveCount); @@ -735,7 +753,10 @@ namespace CodeImp.DoomBuilder.Rendering world3d_main_highlight_fog_vertexcolor, world3d_vertex_color, world3d_constant_color, - world3d_slope_handle + world3d_slope_handle, + world3d_classic, + world3d_p19, + world3d_classic_highlight } public enum UniformType : int @@ -778,7 +799,10 @@ namespace CodeImp.DoomBuilder.Rendering fogcolor, sectorfogcolor, lightsEnabled, - slopeHandleLength + slopeHandleLength, + drawPaletted, + colormapSize, + lightLevel } public enum VertexFormat : int { Flat, World } diff --git a/Source/Core/Rendering/Renderer.cs b/Source/Core/Rendering/Renderer.cs index b6c6d478..0aa2e21e 100755 --- a/Source/Core/Rendering/Renderer.cs +++ b/Source/Core/Rendering/Renderer.cs @@ -47,7 +47,7 @@ namespace CodeImp.DoomBuilder.Rendering // Disposing public bool IsDisposed { get { return isdisposed; } } public static bool FullBrightness { get { return fullbrightness; } set { fullbrightness = value; } } //mxd - + #endregion #region ================== Constructor / Disposer diff --git a/Source/Core/Rendering/Renderer3D.cs b/Source/Core/Rendering/Renderer3D.cs index 4a710bdc..e845921a 100755 --- a/Source/Core/Rendering/Renderer3D.cs +++ b/Source/Core/Rendering/Renderer3D.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Drawing.Drawing2D; using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.GZBuilder.Data; @@ -26,6 +27,7 @@ using CodeImp.DoomBuilder.GZBuilder.MD3; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.VisualModes; using CodeImp.DoomBuilder.GZBuilder; +using ColorMap = System.Drawing.Imaging.ColorMap; #endregion @@ -64,7 +66,10 @@ namespace CodeImp.DoomBuilder.Rendering //mxd private VisualVertexHandle vertexhandle; private int[] lightOffsets; - + + private Data.ColorMap classicLightingColorMap = null; + private Texture classicLightingColorMapTex = null; + // Slope handle private VisualSlopeHandle visualslopehandle; @@ -135,6 +140,11 @@ namespace CodeImp.DoomBuilder.Rendering public bool ShowSelection { get { return showselection; } set { showselection = value; } } public bool ShowHighlight { get { return showhighlight; } set { showhighlight = value; } } + protected bool UseIndexedTexture + { + get { return General.Settings.ClassicRendering && !fullbrightness; } + } + #endregion #region ================== Constructor / Disposer @@ -294,6 +304,25 @@ namespace CodeImp.DoomBuilder.Rendering billboard = Matrix.RotationZ((float)(anglexy + Angle2D.PI)); } + + // volte: Set the active colormap for classic lighting + public void SetClassicLightingColorMap(Data.ColorMap colormap) + { + if (colormap == classicLightingColorMap) + { + return; + } + classicLightingColorMap = colormap; + if (classicLightingColorMap == null) + { + classicLightingColorMapTex = null; + } + else + { + classicLightingColorMapTex = new Texture(graphics, classicLightingColorMap.CreateBitmap()); + } + } + // This creates 2D view matrix private void CreateMatrices2D() { @@ -302,7 +331,7 @@ namespace CodeImp.DoomBuilder.Rendering Matrix translate = Matrix.Translation(-(float)windowsize.Width * 0.5f, -(float)windowsize.Height * 0.5f, 0f); view2d = translate * scaling; } - + #endregion #region ================== Start / Finish @@ -327,13 +356,15 @@ namespace CodeImp.DoomBuilder.Rendering graphics.SetUniform(UniformName.fogcolor, General.Colors.Background.ToColorValue()); graphics.SetUniform(UniformName.texturefactor, new Color4(1f, 1f, 1f, 1f)); graphics.SetUniform(UniformName.highlightcolor, new Color4()); //mxd - TextureFilter texFilter = General.Settings.VisualBilinear ? TextureFilter.Linear : TextureFilter.Nearest; - graphics.SetSamplerFilter(texFilter, texFilter, MipmapFilter.Linear, General.Settings.FilterAnisotropy); + TextureFilter texFilter = (!General.Settings.ClassicRendering && General.Settings.VisualBilinear) ? TextureFilter.Linear : TextureFilter.Nearest; + MipmapFilter mipFilter = General.Settings.ClassicRendering ? MipmapFilter.None : MipmapFilter.Linear; + float aniso = General.Settings.ClassicRendering ? 0 : General.Settings.FilterAnisotropy; + graphics.SetSamplerFilter(texFilter, texFilter, mipFilter, aniso); // Texture addressing graphics.SetSamplerState(TextureAddress.Wrap); - // Matrices + // Matrices world = Matrix.Identity; graphics.SetUniform(UniformName.projection, ref projection); graphics.SetUniform(UniformName.world, ref world); @@ -352,7 +383,18 @@ namespace CodeImp.DoomBuilder.Rendering } // Determine shader pass to use - shaderpass = (fullbrightness ? ShaderName.world3d_fullbright : ShaderName.world3d_main); + if (fullbrightness) + { + shaderpass = ShaderName.world3d_fullbright; + } + else if (General.Settings.ClassicRendering) + { + shaderpass = ShaderName.world3d_classic; + } + else + { + shaderpass = ShaderName.world3d_main; + } // Create crosshair vertices if(crosshairverts == null) @@ -787,14 +829,25 @@ namespace CodeImp.DoomBuilder.Rendering bool hadlights = false; + // volte: Set textures for classic lighting + if (classicLightingColorMap != null) + { + graphics.SetUniform(UniformName.colormapSize, new Vector2i(classicLightingColorMapTex.Width, classicLightingColorMapTex.Height)); + graphics.SetTexture(classicLightingColorMapTex, 1); + graphics.SetSamplerFilter(TextureFilter.Nearest, TextureFilter.Nearest, MipmapFilter.None, 0, 1); + graphics.SetSamplerState(TextureAddress.Clamp, 1); + } + // Render the geometry collected foreach (KeyValuePair> group in geopass) { curtexture = group.Key; - // Apply texture - graphics.SetTexture(curtexture.Texture); - + // Apply texture + Texture texture = UseIndexedTexture ? curtexture.IndexedTexture : curtexture.Texture; + graphics.SetTexture(texture); + graphics.SetUniform(UniformName.drawPaletted, texture.UserData == ImageData.TEXTURE_INDEXED); + //mxd. Sort geometry by sector index group.Value.Sort((g1, g2) => g1.Sector.Sector.FixedIndex - g2.Sector.Sector.FixedIndex); @@ -880,7 +933,7 @@ namespace CodeImp.DoomBuilder.Rendering ShaderName wantedshaderpass = (((g == highlighted) && showhighlight) || (g.Selected && showselection)) ? highshaderpass : shaderpass; //mxd. Render fog? - if(General.Settings.GZDrawFog && !fullbrightness && sector.Sector.FogMode != SectorFogMode.NONE) + if(General.Settings.GZDrawFog && !fullbrightness && !General.Settings.ClassicRendering && sector.Sector.FogMode != SectorFogMode.NONE) wantedshaderpass += 8; // Switch shader pass? @@ -895,6 +948,9 @@ namespace CodeImp.DoomBuilder.Rendering graphics.SetUniform(UniformName.modelnormal, Matrix.Identity); } } + + // volte: set sector light level for classic rendering mode + graphics.SetUniform(UniformName.lightLevel, sector.Sector.Brightness); //mxd. Set variables for fog rendering? if(wantedshaderpass > ShaderName.world3d_p7) @@ -933,8 +989,10 @@ namespace CodeImp.DoomBuilder.Rendering curtexture = group.Key; - // Apply texture - graphics.SetTexture(curtexture.Texture); + // Apply texture + Texture texture = UseIndexedTexture ? curtexture.IndexedTexture : curtexture.Texture; + graphics.SetTexture(texture); + graphics.SetUniform(UniformName.drawPaletted, texture.UserData == ImageData.TEXTURE_INDEXED); // Render all things with this texture foreach(VisualThing t in group.Value) @@ -958,20 +1016,20 @@ namespace CodeImp.DoomBuilder.Rendering ShaderName 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) + if(General.Settings.GZDrawFog && !fullbrightness && !General.Settings.ClassicRendering && 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(t.LightType != null && t.LightType.LightInternal && !fullbrightness) + if(t.LightType != null && t.LightType.LightInternal && !fullbrightness && !General.Settings.ClassicRendering) { 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) + else if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && !General.Settings.ClassicRendering && lightthings.Count > 0) { Color4 litcolor = GetLitColorForThing(t); if(litcolor.ToArgb() != 0) @@ -1000,7 +1058,11 @@ namespace CodeImp.DoomBuilder.Rendering } // Set the colors to use - if(t.Thing.Sector != null) graphics.SetUniform(UniformName.sectorfogcolor, t.Thing.Sector.FogColor); + if (t.Thing.Sector != null) + { + graphics.SetUniform(UniformName.lightLevel, t.Thing.Sector.Brightness); + graphics.SetUniform(UniformName.sectorfogcolor, t.Thing.Sector.FogColor); + } graphics.SetUniform(UniformName.vertexColor, vertexcolor); graphics.SetUniform(UniformName.highlightcolor, CalculateHighlightColor((t == highlighted) && showhighlight, (t.Selected && showselection))); @@ -1162,8 +1224,11 @@ namespace CodeImp.DoomBuilder.Rendering { curtexture = g.Texture; - // Apply texture - graphics.SetTexture(curtexture.Texture); + // Apply texture + Texture texture = UseIndexedTexture ? curtexture.IndexedTexture : curtexture.Texture; + graphics.SetTexture(texture); + graphics.SetUniform(UniformName.drawPaletted, texture.UserData == ImageData.TEXTURE_INDEXED); + curtexturename = g.Texture.LongName; } @@ -1195,7 +1260,7 @@ namespace CodeImp.DoomBuilder.Rendering ShaderName wantedshaderpass = (((g == highlighted) && showhighlight) || (g.Selected && showselection)) ? highshaderpass : shaderpass; //mxd. Render fog? - if (General.Settings.GZDrawFog && !fullbrightness && sector.Sector.FogMode != SectorFogMode.NONE) + if (General.Settings.GZDrawFog && !fullbrightness && !General.Settings.ClassicRendering && sector.Sector.FogMode != SectorFogMode.NONE) wantedshaderpass += 8; // Switch shader pass? @@ -1294,8 +1359,11 @@ namespace CodeImp.DoomBuilder.Rendering { curtexture = t.Texture; - // Apply texture - graphics.SetTexture(curtexture.Texture); + // Apply texture + Texture texture = UseIndexedTexture ? curtexture.IndexedTexture : curtexture.Texture; + graphics.SetTexture(texture); + graphics.SetUniform(UniformName.drawPaletted, texture.UserData == ImageData.TEXTURE_INDEXED); + curtexturename = t.Texture.LongName; } @@ -1306,20 +1374,20 @@ namespace CodeImp.DoomBuilder.Rendering ShaderName 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) + if(General.Settings.GZDrawFog && !fullbrightness && !General.Settings.ClassicRendering && 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(t.LightType != null && t.LightType.LightInternal && !fullbrightness) + if(t.LightType != null && t.LightType.LightInternal && !fullbrightness && !General.Settings.ClassicRendering) { 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) + else if(General.Settings.GZDrawLightsMode != LightRenderMode.NONE && !fullbrightness && !General.Settings.ClassicRendering && lightthings.Count > 0) { Color4 litcolor = GetLitColorForThing(t); if(litcolor.ToArgb() != 0) @@ -1506,7 +1574,7 @@ namespace CodeImp.DoomBuilder.Rendering ShaderName wantedshaderpass = ((((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass); // 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) + if (General.Settings.GZDrawFog && !fullbrightness && !General.Settings.ClassicRendering && t.Thing.Sector != null && t.Thing.Sector.FogMode != SectorFogMode.NONE) wantedshaderpass += 8; // Switch shader pass? diff --git a/Source/Core/Rendering/Texture.cs b/Source/Core/Rendering/Texture.cs index d99f7c54..217cfcdc 100644 --- a/Source/Core/Rendering/Texture.cs +++ b/Source/Core/Rendering/Texture.cs @@ -1,111 +1,112 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Runtime.InteropServices; - -namespace CodeImp.DoomBuilder.Rendering -{ - public enum TextureFormat : int - { - Rgba8, - Bgra8, - Rg16f, - Rgba16f, - R32f, - Rg32f, - Rgb32f, - Rgba32f, - D32f_S8, - D24_S8 - } - - public class BaseTexture : IDisposable - { - public BaseTexture() - { - Handle = Texture_New(); - if (Handle == IntPtr.Zero) - throw new Exception("Texture_New failed"); - } - - ~BaseTexture() - { - Dispose(); - } - - public bool Disposed { get { return Handle == IntPtr.Zero; } } - - public void Dispose() - { - if (!Disposed) - { - Texture_Delete(Handle); - Handle = IntPtr.Zero; - } - } - - internal IntPtr Handle; - - [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - protected static extern IntPtr Texture_New(); - - [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - protected static extern void Texture_Delete(IntPtr handle); - - [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - protected static extern void Texture_Set2DImage(IntPtr handle, int width, int height, TextureFormat format); - - [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - protected static extern void Texture_SetCubeImage(IntPtr handle, int size, TextureFormat format); - } - - public class Texture : BaseTexture - { - public Texture(int width, int height, TextureFormat format) - { - Width = width; - Height = height; - Format = format; - Texture_Set2DImage(Handle, Width, Height, Format); - } - - public Texture(RenderDevice device, System.Drawing.Bitmap bitmap) - { - Width = bitmap.Width; - Height = bitmap.Height; - Format = TextureFormat.Bgra8; - Texture_Set2DImage(Handle, Width, Height, Format); - device.SetPixels(this, bitmap); - } - - public Texture(RenderDevice device, System.Drawing.Image image) - { - using (var bitmap = new System.Drawing.Bitmap(image)) - { - Width = bitmap.Width; - Height = bitmap.Height; - Format = TextureFormat.Bgra8; - Texture_Set2DImage(Handle, Width, Height, Format); - device.SetPixels(this, bitmap); - } - } - - public int Width { get; private set; } - public int Height { get; private set; } - public TextureFormat Format { get; private set; } - - public object Tag { get; set; } - } - - public class CubeTexture : BaseTexture - { - public CubeTexture(RenderDevice device, int size) - { - Texture_SetCubeImage(Handle, size, TextureFormat.Bgra8); - } - } - - public enum CubeMapFace : int { PositiveX, PositiveY, PositiveZ, NegativeX, NegativeY, NegativeZ } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace CodeImp.DoomBuilder.Rendering +{ + public enum TextureFormat : int + { + Rgba8, + Bgra8, + Rg16f, + Rgba16f, + R32f, + Rg32f, + Rgb32f, + Rgba32f, + D32f_S8, + D24_S8 + } + + public class BaseTexture : IDisposable + { + public BaseTexture() + { + Handle = Texture_New(); + if (Handle == IntPtr.Zero) + throw new Exception("Texture_New failed"); + } + + ~BaseTexture() + { + Dispose(); + } + + public bool Disposed { get { return Handle == IntPtr.Zero; } } + + public void Dispose() + { + if (!Disposed) + { + Texture_Delete(Handle); + Handle = IntPtr.Zero; + } + } + + internal IntPtr Handle; + + [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] + protected static extern IntPtr Texture_New(); + + [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] + protected static extern void Texture_Delete(IntPtr handle); + + [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] + protected static extern void Texture_Set2DImage(IntPtr handle, int width, int height, TextureFormat format); + + [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] + protected static extern void Texture_SetCubeImage(IntPtr handle, int size, TextureFormat format); + } + + public class Texture : BaseTexture + { + public Texture(int width, int height, TextureFormat format) + { + Width = width; + Height = height; + Format = format; + Texture_Set2DImage(Handle, Width, Height, Format); + } + + public Texture(RenderDevice device, System.Drawing.Bitmap bitmap) + { + Width = bitmap.Width; + Height = bitmap.Height; + Format = TextureFormat.Bgra8; + Texture_Set2DImage(Handle, Width, Height, Format); + device.SetPixels(this, bitmap); + } + + public Texture(RenderDevice device, System.Drawing.Image image) + { + using (var bitmap = new System.Drawing.Bitmap(image)) + { + Width = bitmap.Width; + Height = bitmap.Height; + Format = TextureFormat.Bgra8; + Texture_Set2DImage(Handle, Width, Height, Format); + device.SetPixels(this, bitmap); + } + } + + public int Width { get; private set; } + public int Height { get; private set; } + public TextureFormat Format { get; private set; } + + public object Tag { get; set; } + public int UserData { get; set; } + } + + public class CubeTexture : BaseTexture + { + public CubeTexture(RenderDevice device, int size) + { + Texture_SetCubeImage(Handle, size, TextureFormat.Bgra8); + } + } + + public enum CubeMapFace : int { PositiveX, PositiveY, PositiveZ, NegativeX, NegativeY, NegativeZ } +} diff --git a/Source/Core/Resources/Actions.cfg b/Source/Core/Resources/Actions.cfg index e739692b..9690d234 100755 --- a/Source/Core/Resources/Actions.cfg +++ b/Source/Core/Resources/Actions.cfg @@ -1330,6 +1330,16 @@ gztogglevisualvertices default = 262230; } +toggleclassicrendering +{ + title = "Toggle classic rendering"; + category = "visual"; + description = "When enabled, attempts to simulate classic Doom rendering with banded light and palettized textures."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + gztoggleenhancedrendering { title = "Toggle Enhanced Rendering Effects"; diff --git a/Source/Core/Resources/ClassicRendering.png b/Source/Core/Resources/ClassicRendering.png new file mode 100644 index 00000000..81b944c8 Binary files /dev/null and b/Source/Core/Resources/ClassicRendering.png differ diff --git a/Source/Core/Resources/world3d.shader b/Source/Core/Resources/world3d.shader index 84503bd7..0e54d50b 100755 --- a/Source/Core/Resources/world3d.shader +++ b/Source/Core/Resources/world3d.shader @@ -6,7 +6,7 @@ uniforms mat4 modelnormal; vec4 campos; - vec4 highlightcolor; + vec4 highlightcolor; vec4 stencilColor; float desaturation; @@ -16,30 +16,90 @@ uniforms vec4 vertexColor; sampler2D texture1; + sampler2D texture2; + sampler2D texture3; + + // classic lighting related + int drawPaletted; + ivec2 colormapSize; + int lightLevel; // dynamic light related - vec4 lightPosAndRadius[64]; - vec4 lightOrientation[64]; // this is a vector that points in light's direction - vec2 light2Radius[64]; // this is used with spotlights - vec4 lightColor[64]; - float ignoreNormals; - float lightsEnabled; - - // Slope handle length - float slopeHandleLength; - + vec4 lightPosAndRadius[64]; + vec4 lightOrientation[64]; // this is a vector that points in light's direction + vec2 light2Radius[64]; // this is used with spotlights + vec4 lightColor[64]; + float ignoreNormals; + float lightsEnabled; + + // Slope handle length + float slopeHandleLength; + } functions { - // This adds fog color to current pixel color - vec4 getFogColor(vec3 PosW, vec4 color) - { - float fogdist = max(16.0, distance(PosW, campos.xyz)); - float fogfactor = exp2(campos.w * fogdist); - - color.rgb = mix(sectorfogcolor.rgb, color.rgb, fogfactor); - return color; + vec4 getColorMappedColor(int entry, int depth) + { + vec2 uv = vec2((float(entry) + 0.5) / colormapSize.x, (float(depth) + 0.5) / colormapSize.y); + vec4 colormapColor = texture(texture2, uv); + return colormapColor; + } + + int classicLightLevelToColorMapOffset(int lightLevel, vec3 position, vec3 normal) + { + const int LIGHTLEVELS = 16; + const int LIGHTSEGSHIFT = 4; + const int NUMCOLORMAPS = 32; + const int MAXLIGHTSCALE = 48; + const int DISTMAP = 2; + + int scaledLightLevel = lightLevel >> LIGHTSEGSHIFT; + + bool isFlat = abs(dot(normal, vec3(0, 0, 1))) > 1e-3; + + if (abs(dot(normal, vec3(0, 1, 0))) < 1e-3) + { + scaledLightLevel++; + } + else if (abs(dot(normal, vec3(1, 0, 0))) < 1e-3) + { + scaledLightLevel--; + } + + int level; + float dist = distance(position, campos.xyz); + + if (!isFlat) + { + int startmap = int(((LIGHTLEVELS-1-scaledLightLevel)*2)*NUMCOLORMAPS/LIGHTLEVELS); + + // same calculation as Eternity Engine + int index = int(2560.0 / dist); + if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; + level = startmap - index / DISTMAP; + } + else + { + // same calculation as Eternity Engine + float startmap = 2.0 * (30.0 - lightLevel / 8.0f); + level = int(startmap - (1280.0f / dist)) + 1; + } + + + if (level < 0) level = 0; + if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; + return level; + } + + // This adds fog color to current pixel color + vec4 getFogColor(vec3 PosW, vec4 color) + { + float fogdist = max(16.0, distance(PosW, campos.xyz)); + float fogfactor = exp2(campos.w * fogdist); + + color.rgb = mix(sectorfogcolor.rgb, color.rgb, fogfactor); + return color; } vec4 desaturate(vec4 texel) @@ -51,20 +111,20 @@ functions vec3 getOneDynLightContribution(vec3 PosW, vec3 Normal, vec3 light, vec4 lColor, vec4 lPosAndRadius, vec4 lOrientation, vec2 l2Radius) { - //is face facing away from light source? - // update 01.02.2017: offset the equation by 3px back to try to emulate GZDoom's broken visibility check. - float diffuseContribution = dot(Normal, normalize(lPosAndRadius.xyz - PosW + Normal * 3.0)); - if (diffuseContribution < 0.0 && (ignoreNormals == 0 || (lColor.a > 0.979 && lColor.a < 0.981))) // attenuated light and facing away - return light; - - diffuseContribution = max(diffuseContribution, 0.0); // to make sure - - //is pixel in light range? - float dist = distance(PosW, lPosAndRadius.xyz); - - if (dist > lPosAndRadius.w) - return light; - + //is face facing away from light source? + // update 01.02.2017: offset the equation by 3px back to try to emulate GZDoom's broken visibility check. + float diffuseContribution = dot(Normal, normalize(lPosAndRadius.xyz - PosW + Normal * 3.0)); + if (diffuseContribution < 0.0 && (ignoreNormals == 0 || (lColor.a > 0.979 && lColor.a < 0.981))) // attenuated light and facing away + return light; + + diffuseContribution = max(diffuseContribution, 0.0); // to make sure + + //is pixel in light range? + float dist = distance(PosW, lPosAndRadius.xyz); + + if (dist > lPosAndRadius.w) + return light; + float power = 1.0; power *= max(lPosAndRadius.w - dist, 0.0) / lPosAndRadius.w; @@ -74,18 +134,18 @@ functions float cosDir = dot(lightDirection, lOrientation.xyz); float df = smoothstep(l2Radius.y, l2Radius.x, cosDir); power *= df; - } - + } + if (lColor.a > 0.979 && lColor.a < 0.981) // attenuated light 98% - power *= diffuseContribution; - - // for w/e reason GZDoom also does this - power *= lColor.a; - - if (lColor.a >= 1) - return light.rgb - lColor.rgb * power; - - return light.rgb + lColor.rgb * power; + power *= diffuseContribution; + + // for w/e reason GZDoom also does this + power *= lColor.a; + + if (lColor.a >= 1) + return light.rgb - lColor.rgb * power; + + return light.rgb + lColor.rgb * power; } @@ -136,11 +196,11 @@ shader world3d_main vertex { - v2f.viewpos = view * world * vec4(in.Position, 1.0); - gl_Position = projection * v2f.viewpos; - v2f.PosW = (world * vec4(in.Position, 1.0)).xyz; - v2f.Color = in.Color; - v2f.UV = in.TextureCoordinate; + v2f.viewpos = view * world * vec4(in.Position, 1.0); + gl_Position = projection * v2f.viewpos; + v2f.PosW = (world * vec4(in.Position, 1.0)).xyz; + v2f.Color = in.Color; + v2f.UV = in.TextureCoordinate; v2f.Normal = normalize((modelnormal * vec4(in.Normal, 1.0)).xyz); } @@ -163,15 +223,15 @@ shader world3d_fullbright extends world3d_main { fragment { - vec4 tcolor = texture(texture1, v2f.UV); - tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); - tcolor.a *= v2f.Color.a; - out.FragColor = tcolor; - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + vec4 tcolor = texture(texture1, v2f.UV); + tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); + tcolor.a *= v2f.Color.a; + out.FragColor = tcolor; + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -180,25 +240,25 @@ shader world3d_main_highlight extends world3d_main { fragment { - vec4 tcolor = texture(texture1, v2f.UV); - tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); + vec4 tcolor = texture(texture1, v2f.UV); + tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); tcolor = getDynLightContribution(tcolor, v2f.Color, v2f.PosW, v2f.Normal); - if (tcolor.a == 0.0) - { - out.FragColor = tcolor; - } - else - { - // Blend texture color and vertex color - vec4 ncolor = desaturate(tcolor); - - out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (ncolor.rgb - 0.4 * highlightcolor.a), max(v2f.Color.a + 0.25, 0.5)); - } - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + if (tcolor.a == 0.0) + { + out.FragColor = tcolor; + } + else + { + // Blend texture color and vertex color + vec4 ncolor = desaturate(tcolor); + + out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (ncolor.rgb - 0.4 * highlightcolor.a), max(v2f.Color.a + 0.25, 0.5)); + } + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -207,24 +267,24 @@ shader world3d_fullbright_highlight extends world3d_fullbright { fragment { - vec4 tcolor = texture(texture1, v2f.UV); - tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); - if(tcolor.a == 0.0) - { - out.FragColor = tcolor; - } - else - { - // Blend texture color and vertex color - vec4 ncolor = tcolor * v2f.Color; - - out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (tcolor.rgb - 0.4 * highlightcolor.a), max(v2f.Color.a + 0.25, 0.5)); - } - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + vec4 tcolor = texture(texture1, v2f.UV); + tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); + if(tcolor.a == 0.0) + { + out.FragColor = tcolor; + } + else + { + // Blend texture color and vertex color + vec4 ncolor = tcolor * v2f.Color; + + out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (tcolor.rgb - 0.4 * highlightcolor.a), max(v2f.Color.a + 0.25, 0.5)); + } + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -233,12 +293,12 @@ shader world3d_vertex_color extends world3d_main { fragment { - out.FragColor = v2f.Color; - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + out.FragColor = v2f.Color; + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -247,9 +307,9 @@ shader world3d_main_vertexcolor extends world3d_main { vertex { - v2f.viewpos = view * world * vec4(in.Position, 1.0); - gl_Position = projection * v2f.viewpos; - v2f.Color = vertexColor; + v2f.viewpos = view * world * vec4(in.Position, 1.0); + gl_Position = projection * v2f.viewpos; + v2f.Color = vertexColor; v2f.UV = in.TextureCoordinate; } } @@ -258,12 +318,12 @@ shader world3d_constant_color extends world3d_main_vertexcolor { fragment { - out.FragColor = vertexColor; - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + out.FragColor = vertexColor; + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0f) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -274,9 +334,9 @@ shader world3d_highlight_vertexcolor extends world3d_main_highlight { vertex { - v2f.viewpos = view * world * vec4(in.Position, 1.0); - gl_Position = projection * v2f.viewpos; - v2f.Color = vertexColor; + v2f.viewpos = view * world * vec4(in.Position, 1.0); + gl_Position = projection * v2f.viewpos; + v2f.Color = vertexColor; v2f.UV = in.TextureCoordinate; } } @@ -285,22 +345,22 @@ shader world3d_main_fog extends world3d_main { fragment { - vec4 tcolor = texture(texture1, v2f.UV); - tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); - tcolor = getDynLightContribution(tcolor, v2f.Color, v2f.PosW, v2f.Normal); - if (tcolor.a == 0.0) - { - out.FragColor = tcolor; - } - else - { - out.FragColor = desaturate(getFogColor(v2f.PosW, tcolor)); - } - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + vec4 tcolor = texture(texture1, v2f.UV); + tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); + tcolor = getDynLightContribution(tcolor, v2f.Color, v2f.PosW, v2f.Normal); + if (tcolor.a == 0.0) + { + out.FragColor = tcolor; + } + else + { + out.FragColor = desaturate(getFogColor(v2f.PosW, tcolor)); + } + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -309,25 +369,25 @@ shader world3d_main_highlight_fog extends world3d_main_fog { fragment { - vec4 tcolor = texture(texture1, v2f.UV); - tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); - tcolor = vec4(getDynLightContribution(tcolor, v2f.Color, v2f.PosW, v2f.Normal).rgb, tcolor.a); - if (tcolor.a == 0.0) - { - out.FragColor = tcolor; - } - else - { - // Blend texture color and vertex color - vec4 ncolor = desaturate(getFogColor(v2f.PosW, tcolor)); - - out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (ncolor.rgb - 0.4 * highlightcolor.a), max(ncolor.a + 0.25, 0.5)); - } - - #if defined(ALPHA_TEST) - if (out.FragColor.a < 0.5) discard; - #endif - + vec4 tcolor = texture(texture1, v2f.UV); + tcolor = mix(tcolor, vec4(stencilColor.rgb, tcolor.a), stencilColor.a); + tcolor = vec4(getDynLightContribution(tcolor, v2f.Color, v2f.PosW, v2f.Normal).rgb, tcolor.a); + if (tcolor.a == 0.0) + { + out.FragColor = tcolor; + } + else + { + // Blend texture color and vertex color + vec4 ncolor = desaturate(getFogColor(v2f.PosW, tcolor)); + + out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (ncolor.rgb - 0.4 * highlightcolor.a), max(ncolor.a + 0.25, 0.5)); + } + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + if (fogsettings.x >= 0.0) out.FragColor = mix(out.FragColor, fogcolor, clamp((-v2f.viewpos.z - fogsettings.x) / (fogsettings.y - fogsettings.x), 0.0, 1.0)); } } @@ -338,11 +398,11 @@ shader world3d_main_fog_vertexcolor extends world3d_main_fog { vertex { - v2f.viewpos = view * world * vec4(in.Position, 1.0); - gl_Position = projection * v2f.viewpos; - v2f.PosW = (world * vec4(in.Position, 1.0)).xyz; - v2f.Color = vertexColor; - v2f.UV = in.TextureCoordinate; + v2f.viewpos = view * world * vec4(in.Position, 1.0); + gl_Position = projection * v2f.viewpos; + v2f.PosW = (world * vec4(in.Position, 1.0)).xyz; + v2f.Color = vertexColor; + v2f.UV = in.TextureCoordinate; v2f.Normal = normalize((modelnormal * vec4(in.Normal, 1.0)).xyz); } } @@ -351,11 +411,11 @@ shader world3d_main_highlight_fog_vertexcolor extends world3d_main_highlight_fog { vertex { - v2f.viewpos = view * world * vec4(in.Position, 1.0); - gl_Position = projection * v2f.viewpos; - v2f.PosW = (world * vec4(in.Position, 1.0)).xyz; - v2f.Color = vertexColor; - v2f.UV = in.TextureCoordinate; + v2f.viewpos = view * world * vec4(in.Position, 1.0); + gl_Position = projection * v2f.viewpos; + v2f.PosW = (world * vec4(in.Position, 1.0)).xyz; + v2f.Color = vertexColor; + v2f.UV = in.TextureCoordinate; v2f.Normal = normalize((modelnormal * vec4(in.Normal, 1.0)).xyz); } } @@ -370,4 +430,67 @@ shader world3d_slope_handle extends world3d_vertex_color v2f.Color = in.Color * vertexColor; v2f.UV = in.TextureCoordinate; } +} + + +shader world3d_classic extends world3d_main +{ + fragment + { + vec4 pcolor; + + if (bool(drawPaletted)) + { + vec4 color = texture(texture1, v2f.UV); + int entry = int(color.r * 255); + float alpha = color.a; + int colorMapOffset = classicLightLevelToColorMapOffset(lightLevel, v2f.PosW, v2f.Normal); + pcolor = getColorMappedColor(entry, colorMapOffset); + pcolor.a = alpha; + } + else + { + pcolor = texture(texture1, v2f.UV); + } + + out.FragColor = pcolor; + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + } +} + +shader world3d_classic_highlight extends world3d_main +{ + fragment + { + vec4 pcolor; + + if (bool(drawPaletted)) + { + vec4 color = texture(texture1, v2f.UV); + int entry = int(color.r * 255); + float alpha = color.a; + int modifiedLightLevel = max(lightLevel, 128); + int colorMapOffset = classicLightLevelToColorMapOffset(modifiedLightLevel, v2f.PosW, v2f.Normal); + pcolor = getColorMappedColor(entry, colorMapOffset); + pcolor.a = alpha; + } + else + { + pcolor = texture(texture1, v2f.UV); + } + + out.FragColor = pcolor; + + if (pcolor.a > 0.0) + { + out.FragColor = vec4(highlightcolor.rgb * highlightcolor.a + (pcolor.rgb - 0.4 * highlightcolor.a), max(pcolor.a + 0.25, 0.5)); + } + + #if defined(ALPHA_TEST) + if (out.FragColor.a < 0.5) discard; + #endif + } } \ No newline at end of file diff --git a/Source/Core/Windows/MainForm.Designer.cs b/Source/Core/Windows/MainForm.Designer.cs index 759036c5..90e0deef 100755 --- a/Source/Core/Windows/MainForm.Designer.cs +++ b/Source/Core/Windows/MainForm.Designer.cs @@ -191,6 +191,7 @@ namespace CodeImp.DoomBuilder.Windows this.itemallmdl = new System.Windows.Forms.ToolStripMenuItem(); this.itemtogglefog = new System.Windows.Forms.ToolStripMenuItem(); this.itemtogglesky = new System.Windows.Forms.ToolStripMenuItem(); + this.itemtoggleclassicrendering = new System.Windows.Forms.ToolStripMenuItem(); this.itemtoggleeventlines = new System.Windows.Forms.ToolStripMenuItem(); this.itemtogglevisualverts = new System.Windows.Forms.ToolStripMenuItem(); this.buttonfullbrightness = new System.Windows.Forms.ToolStripButton(); @@ -224,6 +225,7 @@ namespace CodeImp.DoomBuilder.Windows this.modelsshowall = new System.Windows.Forms.ToolStripMenuItem(); this.buttontogglefog = new System.Windows.Forms.ToolStripButton(); this.buttontogglesky = new System.Windows.Forms.ToolStripButton(); + this.buttontoggleclassicrendering = new System.Windows.Forms.ToolStripButton(); this.buttontoggleeventlines = new System.Windows.Forms.ToolStripButton(); this.buttontogglevisualvertices = new System.Windows.Forms.ToolStripButton(); this.separatorgzmodes = new System.Windows.Forms.ToolStripSeparator(); @@ -798,6 +800,7 @@ namespace CodeImp.DoomBuilder.Windows this.itemmodelmodes, this.itemtogglefog, this.itemtogglesky, + this.itemtoggleclassicrendering, this.itemtoggleeventlines, this.itemtogglevisualverts, this.separatorhelpers, @@ -1395,6 +1398,7 @@ namespace CodeImp.DoomBuilder.Windows this.modelrendermode, this.buttontogglefog, this.buttontogglesky, + this.buttontoggleclassicrendering, this.buttontoggleeventlines, this.buttontogglevisualvertices, this.separatorgzmodes, @@ -1797,6 +1801,16 @@ namespace CodeImp.DoomBuilder.Windows this.itemtogglesky.Text = "Render sky (Visual mode)"; this.itemtogglesky.Click += new System.EventHandler(this.InvokeTaggedAction); // + // itemclassicrendering + // + this.itemtoggleclassicrendering.CheckOnClick = true; + this.itemtoggleclassicrendering.Image = global::CodeImp.DoomBuilder.Properties.Resources.ClassicRendering; + this.itemtoggleclassicrendering.Name = "itemtoggleclassicrendering"; + this.itemtoggleclassicrendering.Size = new System.Drawing.Size(273, 22); + this.itemtoggleclassicrendering.Tag = "builder_toggleclassicrendering"; + this.itemtoggleclassicrendering.Text = "Classic rendering (Visual mode)"; + this.itemtoggleclassicrendering.Click += new System.EventHandler(this.InvokeTaggedAction); + // // itemeventlines // this.itemtoggleeventlines.CheckOnClick = true; @@ -2170,6 +2184,18 @@ namespace CodeImp.DoomBuilder.Windows this.buttontogglesky.Text = "Render Sky (Visual mode)"; this.buttontogglesky.Click += new System.EventHandler(this.InvokeTaggedAction); // + // buttontoggleclassicrendering + // + this.buttontoggleclassicrendering.CheckOnClick = true; + this.buttontoggleclassicrendering.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttontoggleclassicrendering.Image = global::CodeImp.DoomBuilder.Properties.Resources.ClassicRendering; + this.buttontoggleclassicrendering.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttontoggleclassicrendering.Name = "buttontoggleclassicrendering"; + this.buttontoggleclassicrendering.Size = new System.Drawing.Size(23, 20); + this.buttontoggleclassicrendering.Tag = "builder_toggleclassicrendering"; + this.buttontoggleclassicrendering.Text = "Classic Rendering (Visual mode)"; + this.buttontoggleclassicrendering.Click += new System.EventHandler(this.InvokeTaggedAction); + // // buttontoggleeventlines // this.buttontoggleeventlines.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; @@ -3013,6 +3039,7 @@ namespace CodeImp.DoomBuilder.Windows private System.Windows.Forms.ToolStripSeparator seperatorgeometry; private System.Windows.Forms.ToolStripButton buttontogglefog; private System.Windows.Forms.ToolStripButton buttontogglesky; + private System.Windows.Forms.ToolStripButton buttontoggleclassicrendering; private System.Windows.Forms.ToolStripStatusLabel warnsLabel; private System.Windows.Forms.ToolStripMenuItem itemReloadModedef; private System.Windows.Forms.ToolStripMenuItem itemReloadGldefs; @@ -3072,6 +3099,7 @@ namespace CodeImp.DoomBuilder.Windows private System.Windows.Forms.ToolStripMenuItem itemallmdl; private System.Windows.Forms.ToolStripMenuItem itemtogglefog; private System.Windows.Forms.ToolStripMenuItem itemtogglesky; + private System.Windows.Forms.ToolStripMenuItem itemtoggleclassicrendering; private System.Windows.Forms.ToolStripMenuItem itemtoggleeventlines; private System.Windows.Forms.ToolStripMenuItem itemtogglevisualverts; private ToolStripMenuItem itemimport; diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs index 47d47e60..63080077 100755 --- a/Source/Core/Windows/MainForm.cs +++ b/Source/Core/Windows/MainForm.cs @@ -2164,6 +2164,7 @@ namespace CodeImp.DoomBuilder.Windows buttonsplitjoinedsectors.Checked = General.Settings.SplitJoinedSectors; //mxd buttonautoclearsidetextures.Visible = General.Settings.ToolbarGeometry && maploaded; //mxd buttontest.Visible = General.Settings.ToolbarTesting && maploaded; + buttontoggleclassicrendering.Visible = General.Settings.ToolbarViewModes && maploaded; //mxd modelrendermode.Visible = General.Settings.GZToolbarGZDoom && maploaded; @@ -2330,6 +2331,7 @@ namespace CodeImp.DoomBuilder.Windows buttontogglefog.Checked = General.Settings.GZDrawFog; buttontogglesky.Checked = General.Settings.GZDrawSky; + buttontoggleclassicrendering.Checked = General.Settings.ClassicRendering; buttontoggleeventlines.Checked = General.Settings.GZShowEventLines; buttontogglevisualvertices.Visible = General.Map.UDMF; buttontogglevisualvertices.Checked = General.Settings.GZShowVisualVertices; @@ -3141,6 +3143,7 @@ namespace CodeImp.DoomBuilder.Windows itemtogglefixedthingsscale.Checked = General.Settings.FixedThingsScale; //mxd itemtogglefog.Checked = General.Settings.GZDrawFog; itemtogglesky.Checked = General.Settings.GZDrawSky; + itemtoggleclassicrendering.Checked = General.Settings.ClassicRendering; itemtoggleeventlines.Checked = General.Settings.GZShowEventLines; itemtogglevisualverts.Visible = (General.Map != null && General.Map.UDMF); itemtogglevisualverts.Checked = General.Settings.GZShowVisualVertices; @@ -3214,6 +3217,20 @@ namespace CodeImp.DoomBuilder.Windows General.MainWindow.RedrawDisplay(); General.MainWindow.UpdateGZDoomPanel(); } + + //mxd + [BeginAction("toggleclassicrendering")] + internal void ToggleClassicRendering() + { + General.Settings.ClassicRendering = !General.Settings.ClassicRendering; + + itemtoggleclassicrendering.Checked = General.Settings.ClassicRendering; + buttontoggleclassicrendering.Checked = General.Settings.ClassicRendering; + + General.MainWindow.DisplayStatus(StatusType.Action, "Classic rendering is " + (General.Settings.ClassicRendering ? "ENABLED" : "DISABLED")); + General.MainWindow.RedrawDisplay(); + General.MainWindow.UpdateGZDoomPanel(); + } [BeginAction("gztoggleeventlines")] internal void ToggleEventLines() diff --git a/Source/Native/Backend.cpp b/Source/Native/Backend.cpp index abe31c14..b361b149 100644 --- a/Source/Native/Backend.cpp +++ b/Source/Native/Backend.cpp @@ -76,9 +76,12 @@ extern "C" Backend::Get()->DeleteRenderDevice(device); } - const char* BuilderNative_GetError() + void BuilderNative_GetError(char *out, int len) { - return GetError(); + std::string result = mLastError; + result = result.substr(0, len); + std::copy(result.begin(), result.end(), out); + out[std::min(len - 1, (int)result.size())] = 0; } void RenderDevice_DeclareUniform(RenderDevice* device, UniformName name, const char* variablename, UniformType type) @@ -161,19 +164,19 @@ extern "C" device->SetZWriteEnable(value); } - void RenderDevice_SetTexture(RenderDevice* device, Texture* texture) + void RenderDevice_SetTexture(RenderDevice* device, int unit, Texture* texture) { - device->SetTexture(texture); + device->SetTexture(unit, texture); } - void RenderDevice_SetSamplerFilter(RenderDevice* device, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) + void RenderDevice_SetSamplerFilter(RenderDevice* device, int unit, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) { - device->SetSamplerFilter(minfilter, magfilter, mipfilter, maxanisotropy); + device->SetSamplerFilter(unit, minfilter, magfilter, mipfilter, maxanisotropy); } - void RenderDevice_SetSamplerState(RenderDevice* device, TextureAddress address) + void RenderDevice_SetSamplerState(RenderDevice* device, int unit, TextureAddress address) { - device->SetSamplerState(address); + device->SetSamplerState(unit, address); } bool RenderDevice_Draw(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount) diff --git a/Source/Native/Backend.h b/Source/Native/Backend.h index b5c18292..2d6ebbca 100644 --- a/Source/Native/Backend.h +++ b/Source/Native/Backend.h @@ -83,9 +83,9 @@ public: virtual void SetMultisampleAntialias(bool value) = 0; virtual void SetZEnable(bool value) = 0; virtual void SetZWriteEnable(bool value) = 0; - virtual void SetTexture(Texture* texture) = 0; - virtual void SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) = 0; - virtual void SetSamplerState(TextureAddress address) = 0; + virtual void SetTexture(int unit, Texture* texture) = 0; + virtual void SetSamplerFilter(int unit, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) = 0; + virtual void SetSamplerState(int unit, TextureAddress address) = 0; virtual bool Draw(PrimitiveType type, int startIndex, int primitiveCount) = 0; virtual bool DrawIndexed(PrimitiveType type, int startIndex, int primitiveCount) = 0; virtual bool DrawData(PrimitiveType type, int startIndex, int primitiveCount, const void* data) = 0; diff --git a/Source/Native/OpenGL/GLRenderDevice.cpp b/Source/Native/OpenGL/GLRenderDevice.cpp index ebd37497..f2f8f656 100644 --- a/Source/Native/OpenGL/GLRenderDevice.cpp +++ b/Source/Native/OpenGL/GLRenderDevice.cpp @@ -113,16 +113,15 @@ GLRenderDevice::~GLRenderDevice() glDeleteVertexArrays(1, &handle); } - for (auto& it : mSamplers) + for (auto& it : mTextureUnit) { - for (GLuint handle : it.second.WrapModes) - { - if (handle != 0) - glDeleteSamplers(1, &handle); - } + GLuint &handle = it.SamplerHandle; + if (handle != 0) + { + glDeleteSamplers(1, &handle); + } } - mShaderManager->ReleaseResources(); Context->ClearCurrent(); } @@ -264,30 +263,49 @@ void GLRenderDevice::SetZWriteEnable(bool value) } } -void GLRenderDevice::SetTexture(Texture* texture) +void GLRenderDevice::SetTexture(int unit, Texture* texture) { - if (mTextureUnit.Tex != texture) + if (mTextureUnit[unit].Tex != texture) { - mTextureUnit.Tex = static_cast(texture); + mTextureUnit[unit].Tex = static_cast(texture); mNeedApply = true; mTexturesChanged = true; } } -void GLRenderDevice::SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) +void GLRenderDevice::SetSamplerFilter(int unit, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) { - SamplerFilterKey key; - key.MinFilter = GetGLMinFilter(minfilter, mipfilter); - key.MagFilter = (magfilter == TextureFilter::Nearest) ? GL_NEAREST : GL_LINEAR; - key.MaxAnisotropy = maxanisotropy; - if (mSamplerFilterKey != key) - { - mSamplerFilterKey = key; - mSamplerFilter = &mSamplers[mSamplerFilterKey]; + bool dirty = false; + + if (mTextureUnit[unit].MinFilter != minfilter) + { + mTextureUnit[unit].MinFilter = minfilter; + dirty = true; + } + + if (mTextureUnit[unit].MagFilter != magfilter) + { + mTextureUnit[unit].MagFilter = magfilter; + dirty = true; + } + + if (mTextureUnit[unit].MipFilter != mipfilter) + { + mTextureUnit[unit].MipFilter = mipfilter; + dirty = true; + } - mNeedApply = true; - mTexturesChanged = true; - } + if ( mTextureUnit[unit].MaxAnisotropy != maxanisotropy) + { + mTextureUnit[unit].MaxAnisotropy = maxanisotropy; + dirty = true; + } + + if (dirty) + { + mNeedApply = true; + mTexturesChanged = true; + } } GLint GLRenderDevice::GetGLMinFilter(TextureFilter filter, MipmapFilter mipfilter) @@ -315,11 +333,20 @@ GLint GLRenderDevice::GetGLMinFilter(TextureFilter filter, MipmapFilter mipfilte } } -void GLRenderDevice::SetSamplerState(TextureAddress address) +GLRenderDevice::SamplerFilterKey GLRenderDevice::GetSamplerFilterKey(TextureFilter filter, MipmapFilter mipFilter, float maxAnisotropy) { - if (mTextureUnit.WrapMode != address) + SamplerFilterKey key; + key.MinFilter = GetGLMinFilter(filter, mipFilter); + key.MagFilter = (filter == TextureFilter::Linear) ? GL_LINEAR : GL_NEAREST; + key.MaxAnisotropy = maxAnisotropy; + return key; +} + +void GLRenderDevice::SetSamplerState(int unit, TextureAddress address) +{ + if (mTextureUnit[unit].WrapMode != address) { - mTextureUnit.WrapMode = address; + mTextureUnit[unit].WrapMode = address; mNeedApply = true; mTexturesChanged = true; } @@ -901,42 +928,47 @@ bool GLRenderDevice::ApplyUniforms() bool GLRenderDevice::ApplyTextures() { - glActiveTexture(GL_TEXTURE0); - if (mTextureUnit.Tex) - { - GLenum target = mTextureUnit.Tex->IsCubeTexture() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + bool hasError = false; + for (int index = 0; index < 10; index++) + { + TextureUnit &unit = mTextureUnit[index]; + if (unit.Tex) + { + glActiveTexture(GL_TEXTURE0 + index); + GLenum target = unit.Tex->IsCubeTexture() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + + glBindTexture(target, unit.Tex->GetTexture(this)); - glBindTexture(target, mTextureUnit.Tex->GetTexture(this)); + SamplerFilterKey key = GetSamplerFilterKey(unit.MagFilter, unit.MipFilter, unit.MaxAnisotropy); + SamplerFilter &filter = mSamplers[key]; + GLuint &samplerHandle = filter.WrapModes[(int)unit.WrapMode]; - GLuint& samplerHandle = mSamplerFilter->WrapModes[(int)mTextureUnit.WrapMode]; - if (samplerHandle == 0) - { - static const int wrapMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE }; + if (samplerHandle == 0) + { + static const int wrapMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE }; - glGenSamplers(1, &samplerHandle); - glSamplerParameteri(samplerHandle, GL_TEXTURE_MIN_FILTER, mSamplerFilterKey.MinFilter); - glSamplerParameteri(samplerHandle, GL_TEXTURE_MAG_FILTER, mSamplerFilterKey.MagFilter); - glSamplerParameteri(samplerHandle, GL_TEXTURE_WRAP_S, wrapMode[(int)mTextureUnit.WrapMode]); - glSamplerParameteri(samplerHandle, GL_TEXTURE_WRAP_T, wrapMode[(int)mTextureUnit.WrapMode]); - glSamplerParameteri(samplerHandle, GL_TEXTURE_WRAP_R, wrapMode[(int)mTextureUnit.WrapMode]); - if (mSamplerFilterKey.MaxAnisotropy > 0.0f) - glSamplerParameterf(samplerHandle, GL_TEXTURE_MAX_ANISOTROPY_EXT, mSamplerFilterKey.MaxAnisotropy); - } - - if (mTextureUnit.SamplerHandle != samplerHandle) - { - mTextureUnit.SamplerHandle = samplerHandle; - glBindSampler(0, samplerHandle); - } - } - else - { - glBindTexture(GL_TEXTURE_2D, 0); - } - - mTexturesChanged = false; - - return CheckGLError(); + glGenSamplers(1, &samplerHandle); + glSamplerParameteri(samplerHandle, GL_TEXTURE_MIN_FILTER, key.MinFilter); + glSamplerParameteri(samplerHandle, GL_TEXTURE_MAG_FILTER, key.MagFilter); + glSamplerParameteri(samplerHandle, GL_TEXTURE_WRAP_S, wrapMode[(int)unit.WrapMode]); + glSamplerParameteri(samplerHandle, GL_TEXTURE_WRAP_T, wrapMode[(int)unit.WrapMode]); + glSamplerParameteri(samplerHandle, GL_TEXTURE_WRAP_R, wrapMode[(int)unit.WrapMode]); + if (key.MaxAnisotropy > 0.0f) + glSamplerParameterf(samplerHandle, GL_TEXTURE_MAX_ANISOTROPY_EXT, key.MaxAnisotropy); + } + + if (unit.SamplerHandle != samplerHandle) + { + unit.SamplerHandle = samplerHandle; + glBindSampler(index, samplerHandle); + } + } + + hasError |= CheckGLError(); + } + + mTexturesChanged = false; + return hasError; } std::mutex& GLRenderDevice::GetMutex() diff --git a/Source/Native/OpenGL/GLRenderDevice.h b/Source/Native/OpenGL/GLRenderDevice.h index e91ac9fa..c943bffe 100644 --- a/Source/Native/OpenGL/GLRenderDevice.h +++ b/Source/Native/OpenGL/GLRenderDevice.h @@ -54,9 +54,9 @@ public: void SetMultisampleAntialias(bool value) override; void SetZEnable(bool value) override; void SetZWriteEnable(bool value) override; - void SetTexture(Texture* texture) override; - void SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) override; - void SetSamplerState(TextureAddress address) override; + void SetTexture(int unit, Texture* texture) override; + void SetSamplerFilter(int unit, TextureFilter minfilter, TextureFilter magfilter, MipmapFilter mipfilter, float maxanisotropy) override; + void SetSamplerState(int unit, TextureAddress address) override; bool Draw(PrimitiveType type, int startIndex, int primitiveCount) override; bool DrawIndexed(PrimitiveType type, int startIndex, int primitiveCount) override; bool DrawData(PrimitiveType type, int startIndex, int primitiveCount, const void* data) override; @@ -114,13 +114,17 @@ public: std::vector IndexBuffers; std::vector Textures; } mDeleteList; - + struct TextureUnit { GLTexture* Tex = nullptr; TextureAddress WrapMode = TextureAddress::Wrap; GLuint SamplerHandle = 0; - } mTextureUnit; + TextureFilter MinFilter = TextureFilter::Nearest; + TextureFilter MagFilter = TextureFilter::Nearest; + MipmapFilter MipFilter = MipmapFilter::None; + float MaxAnisotropy = 1; + } mTextureUnit[10]; struct SamplerFilterKey { @@ -132,15 +136,15 @@ public: bool operator==(const SamplerFilterKey& b) const { return memcmp(this, &b, sizeof(SamplerFilterKey)) == 0; } bool operator!=(const SamplerFilterKey& b) const { return memcmp(this, &b, sizeof(SamplerFilterKey)) != 0; } }; - + struct SamplerFilter - { - GLuint WrapModes[2] = { 0, 0 }; - }; + { + GLuint WrapModes[2] = { 0, 0 }; + }; - std::map mSamplers; - SamplerFilterKey mSamplerFilterKey; - SamplerFilter* mSamplerFilter = nullptr; + std::map mSamplers; + + SamplerFilterKey GetSamplerFilterKey(TextureFilter filter, MipmapFilter mipFilter, float maxAnisotropy); int mVertexBuffer = -1; int64_t mVertexBufferStartIndex = 0; diff --git a/Source/Native/OpenGL/GLShader.cpp b/Source/Native/OpenGL/GLShader.cpp index f819562c..32d005f9 100644 --- a/Source/Native/OpenGL/GLShader.cpp +++ b/Source/Native/OpenGL/GLShader.cpp @@ -41,6 +41,8 @@ bool GLShader::CheckCompile(GLRenderDevice* device) CreateProgram(device); glUseProgram(mProgram); glUniform1i(glGetUniformLocation(mProgram, "texture1"), 0); + glUniform1i(glGetUniformLocation(mProgram, "texture2"), 1); + glUniform1i(glGetUniformLocation(mProgram, "texture3"), 2); glUseProgram(0); } diff --git a/Source/Native/OpenGL/GLTexture.cpp b/Source/Native/OpenGL/GLTexture.cpp index 6d2628c4..b9a0f80c 100644 --- a/Source/Native/OpenGL/GLTexture.cpp +++ b/Source/Native/OpenGL/GLTexture.cpp @@ -74,7 +74,7 @@ bool GLTexture::SetPixels(GLRenderDevice* device, const void* data) glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, mTexture); glTexImage2D(GL_TEXTURE_2D, 0, ToInternalFormat(mFormat), mWidth, mHeight, 0, ToDataFormat(mFormat), ToDataType(mFormat), data); - if (data != nullptr) + if (data != nullptr) glGenerateMipmap(GL_TEXTURE_2D); // diff --git a/Source/Native/Precomp.h b/Source/Native/Precomp.h index 569fc710..88cb80b1 100644 --- a/Source/Native/Precomp.h +++ b/Source/Native/Precomp.h @@ -24,9 +24,11 @@ #define _CRT_SECURE_NO_WARNINGS #include +#include #include #include #include +#include #ifdef WIN32 #include diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs index e2917e11..62cbcb1a 100755 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs @@ -1614,6 +1614,8 @@ namespace CodeImp.DoomBuilder.BuilderModes // This draws a frame public override void OnRedrawDisplay() { + renderer.SetClassicLightingColorMap(General.Map.Data.MainColorMap); + // Start drawing if(renderer.Start()) {