/* ** gldefs.cpp ** GLDEFS parser ** **--------------------------------------------------------------------------- ** Copyright 2003 Timothy Stump ** Copyright 2005-2018 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include #include "i_system.h" #include "doomtype.h" #include "c_cvars.h" #include "c_dispatch.h" #include "m_random.h" #include "sc_man.h" #include "templates.h" #include "w_wad.h" #include "gi.h" #include "r_state.h" #include "stats.h" #include "zstring.h" #include "d_dehacked.h" #include "v_text.h" #include "g_levellocals.h" #include "a_dynlight.h" #include "textures/skyboxtexture.h" #include "gl/shaders/gl_postprocessshader.h" void AddLightDefaults(FLightDefaults *defaults); void AddLightAssociation(const char *actor, const char *frame, const char *light); void InitializeActorLights(TArray &LightAssociations); extern TArray usershaders; extern TDeletingArray LightDefaults; //----------------------------------------------------------------------------- // // ParseVavoomSkybox // //----------------------------------------------------------------------------- static void ParseVavoomSkybox() { int lump = Wads.CheckNumForName("SKYBOXES"); if (lump < 0) return; FScanner sc(lump); while (sc.GetString()) { int facecount=0; int maplump = -1; bool error = false; FSkyBox * sb = new FSkyBox; sb->Name = sc.String; sb->Name.ToUpper(); sb->fliptop = true; sc.MustGetStringName("{"); while (!sc.CheckString("}")) { if (facecount<6) { sc.MustGetStringName("{"); sc.MustGetStringName("map"); sc.MustGetString(); maplump = Wads.CheckNumForFullName(sc.String, true); FTexture *tex = TexMan.FindTexture(sc.String, ETextureType::Wall, FTextureManager::TEXMAN_TryAny); if (tex == NULL) { sc.ScriptMessage("Texture '%s' not found in Vavoom skybox '%s'\n", sc.String, sb->Name.GetChars()); error = true; } sb->faces[facecount] = tex; sc.MustGetStringName("}"); } facecount++; } if (facecount != 6) { sc.ScriptError("%s: Skybox definition requires 6 faces", sb->Name.GetChars()); } sb->SetSize(); if (!error) TexMan.AddTexture(sb); } } //========================================================================== // // light definition keywords // //========================================================================== static const char *LightTags[]= { "color", "size", "secondarySize", "offset", "chance", "interval", "scale", "frame", "light", "{", "}", "subtractive", "additive", "halo", "dontlightself", "attenuate", "dontlightactors", "spot", nullptr }; enum { LIGHTTAG_COLOR, LIGHTTAG_SIZE, LIGHTTAG_SECSIZE, LIGHTTAG_OFFSET, LIGHTTAG_CHANCE, LIGHTTAG_INTERVAL, LIGHTTAG_SCALE, LIGHTTAG_FRAME, LIGHTTAG_LIGHT, LIGHTTAG_OPENBRACE, LIGHTTAG_CLOSEBRACE, LIGHTTAG_SUBTRACTIVE, LIGHTTAG_ADDITIVE, LIGHTTAG_HALO, LIGHTTAG_DONTLIGHTSELF, LIGHTTAG_ATTENUATE, LIGHTTAG_DONTLIGHTACTORS, LIGHTTAG_SPOT }; //========================================================================== // // Top level keywords // //========================================================================== // these are the core types available in the *DEFS lump static const char *CoreKeywords[]= { "pointlight", "pulselight", "flickerlight", "flickerlight2", "sectorlight", "object", "clearlights", "shader", "clearshaders", "skybox", "glow", "brightmap", "disable_fullbright", "hardwareshader", "detail", "#include", "material", nullptr }; enum { LIGHT_POINT, LIGHT_PULSE, LIGHT_FLICKER, LIGHT_FLICKER2, LIGHT_SECTOR, LIGHT_OBJECT, LIGHT_CLEAR, TAG_SHADER, TAG_CLEARSHADERS, TAG_SKYBOX, TAG_GLOW, TAG_BRIGHTMAP, TAG_DISABLE_FB, TAG_HARDWARESHADER, TAG_DETAIL, TAG_INCLUDE, TAG_MATERIAL }; //========================================================================== // // GLDEFS parser // //========================================================================== class GLDefsParser { FScanner sc; int workingLump; int ScriptDepth = 0; TArray &LightAssociations; //========================================================================== // // This is only here so any shader definition for ZDoomGL can be skipped // There is no functionality for this stuff! // //========================================================================== bool ParseShader() { int ShaderDepth = 0; if (sc.GetString()) { char *tmp; tmp = strstr(sc.String, "{"); while (tmp) { ShaderDepth++; tmp++; tmp = strstr(tmp, "{"); } tmp = strstr(sc.String, "}"); while (tmp) { ShaderDepth--; tmp++; tmp = strstr(tmp, "}"); } if (ShaderDepth == 0) return true; } return false; } //========================================================================== // // Parses a GLBoom+ detail texture definition // // Syntax is this: // detail // { // (walls | flats) [default_detail_name [width [height [offset_x [offset_y]]]]] // { // texture_name [detail_name [width [height [offset_x [offset_y]]]]] // } // } // This merely parses the block and returns no error if valid. The feature // is not actually implemented, so nothing else happens. // // The semantics of this are too horrible to comprehend (default detail texture???) // so if this ever gets done, the parser will look different. //========================================================================== void ParseDetailTexture() { while (!sc.CheckToken('}')) { sc.MustGetString(); if (sc.Compare("walls") || sc.Compare("flats")) { if (!sc.CheckToken('{')) { sc.MustGetString(); // Default detail texture if (sc.CheckFloat()) // Width if (sc.CheckFloat()) // Height if (sc.CheckFloat()) // OffsX if (sc.CheckFloat()) // OffsY { // Nothing } } else sc.UnGet(); sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetString(); // Texture if (sc.GetString()) // Detail texture { if (sc.CheckFloat()) // Width if (sc.CheckFloat()) // Height if (sc.CheckFloat()) // OffsX if (sc.CheckFloat()) // OffsY { // Nothing } } else sc.UnGet(); } } } } //========================================================================== // // // //========================================================================== float ParseFloat(FScanner &sc) { sc.MustGetFloat(); return float(sc.Float); } int ParseInt(FScanner &sc) { sc.MustGetNumber(); return sc.Number; } char *ParseString(FScanner &sc) { sc.GetString(); return sc.String; } void ParseTriple(FScanner &sc, float floatVal[3]) { for (int i = 0; i < 3; i++) { floatVal[i] = ParseFloat(sc); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void AddLightAssociation(const char *actor, const char *frame, const char *light) { FLightAssociation *temp; unsigned int i; FLightAssociation assoc(actor, frame, light); for (i = 0; i < LightAssociations.Size(); i++) { temp = &LightAssociations[i]; if (temp->ActorName() == assoc.ActorName()) { if (strcmp(temp->FrameName(), assoc.FrameName()) == 0) { temp->ReplaceLightName(assoc.Light()); return; } } } LightAssociations.Push(assoc); } //----------------------------------------------------------------------------- // // Note: The different light type parsers really could use some consolidation... // //----------------------------------------------------------------------------- void ParsePointLight() { int type; float floatTriple[3]; int intVal; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, PointLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_ADDITIVE: defaults->SetAdditive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: defaults->SetAttenuate(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; case LIGHTTAG_SPOT: { float innerAngle = ParseFloat(sc); float outerAngle = ParseFloat(sc); defaults->SetSpot(true); defaults->SetSpotInnerAngle(innerAngle); defaults->SetSpotOuterAngle(outerAngle); } break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParsePulseLight() { int type; float floatVal, floatTriple[3]; int intVal; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, PulseLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_INTERVAL: floatVal = ParseFloat(sc); defaults->SetParameter(floatVal * TICRATE); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: defaults->SetAttenuate(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; case LIGHTTAG_SPOT: { float innerAngle = ParseFloat(sc); float outerAngle = ParseFloat(sc); defaults->SetSpot(true); defaults->SetSpotInnerAngle(innerAngle); defaults->SetSpotOuterAngle(outerAngle); } break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } defaults->OrderIntensities(); AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParseFlickerLight() { int type; float floatVal, floatTriple[3]; int intVal; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, FlickerLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_CHANCE: floatVal = ParseFloat(sc); defaults->SetParameter(floatVal*360.); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: defaults->SetAttenuate(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; case LIGHTTAG_SPOT: { float innerAngle = ParseFloat(sc); float outerAngle = ParseFloat(sc); defaults->SetSpot(true); defaults->SetSpotInnerAngle(innerAngle); defaults->SetSpotOuterAngle(outerAngle); } break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } defaults->OrderIntensities(); AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParseFlickerLight2() { int type; float floatVal, floatTriple[3]; int intVal; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, RandomFlickerLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_INTERVAL: floatVal = ParseFloat(sc); defaults->SetParameter(floatVal * 360.); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: defaults->SetAttenuate(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; case LIGHTTAG_SPOT: { float innerAngle = ParseFloat(sc); float outerAngle = ParseFloat(sc); defaults->SetSpot(true); defaults->SetSpotInnerAngle(innerAngle); defaults->SetSpotOuterAngle(outerAngle); } break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } if (defaults->GetArg(LIGHT_SECONDARY_INTENSITY) < defaults->GetArg(LIGHT_INTENSITY)) { int v = defaults->GetArg(LIGHT_SECONDARY_INTENSITY); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_INTENSITY)); defaults->SetArg(LIGHT_INTENSITY, v); } AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParseSectorLight() { int type; float floatVal; float floatTriple[3]; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, SectorLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SCALE: floatVal = ParseFloat(sc); defaults->SetArg(LIGHT_SCALE, clamp((int)(floatVal * 255), 1, 1024)); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: defaults->SetAttenuate(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; case LIGHTTAG_SPOT: { float innerAngle = ParseFloat(sc); float outerAngle = ParseFloat(sc); defaults->SetSpot(true); defaults->SetSpotInnerAngle(innerAngle); defaults->SetSpotOuterAngle(outerAngle); } break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParseFrame(const FString &name) { int type, startDepth; FString frameName; // get name sc.GetString(); if (strlen(sc.String) > 8) { sc.ScriptError("Name longer than 8 characters: %s\n", sc.String); } frameName = sc.String; startDepth = ScriptDepth; // check for opening brace sc.GetString(); if (sc.Compare("{")) { ScriptDepth++; while (ScriptDepth > startDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_LIGHT: ParseString(sc); AddLightAssociation(name, frameName, sc.String); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParseObject() { int type; FString name; // get name sc.GetString(); name = sc.String; if (!PClass::FindActor(name)) sc.ScriptMessage("Warning: dynamic lights attached to non-existent actor %s\n", name.GetChars()); // check for opening brace sc.GetString(); if (sc.Compare("{")) { ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_FRAME: ParseFrame(name); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } } else { sc.ScriptError("Expected '{'.\n"); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void ParseGldefSkybox() { int facecount=0; sc.MustGetString(); FSkyBox * sb = new FSkyBox; sb->Name = sc.String; sb->Name.ToUpper(); if (sc.CheckString("fliptop")) { sb->fliptop = true; } sc.MustGetStringName("{"); while (!sc.CheckString("}")) { sc.MustGetString(); if (facecount<6) { sb->faces[facecount] = TexMan[TexMan.GetTexture(sc.String, ETextureType::Wall, FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_Overridable)]; } facecount++; } if (facecount != 3 && facecount != 6) { sc.ScriptError("%s: Skybox definition requires either 3 or 6 faces", sb->Name.GetChars()); } sb->SetSize(); TexMan.AddTexture(sb); } //=========================================================================== // // Reads glow definitions from GLDEFS // //=========================================================================== void ParseGlow() { sc.MustGetStringName("{"); while (!sc.CheckString("}")) { sc.MustGetString(); if (sc.Compare("FLATS")) { sc.MustGetStringName("{"); while (!sc.CheckString("}")) { sc.MustGetString(); FTextureID flump=TexMan.CheckForTexture(sc.String, ETextureType::Flat,FTextureManager::TEXMAN_TryAny); FTexture *tex = TexMan[flump]; if (tex) tex->bAutoGlowing = tex->bGlowing = tex->bFullbright = true; } } else if (sc.Compare("WALLS")) { sc.MustGetStringName("{"); while (!sc.CheckString("}")) { sc.MustGetString(); FTextureID flump=TexMan.CheckForTexture(sc.String, ETextureType::Wall,FTextureManager::TEXMAN_TryAny); FTexture *tex = TexMan[flump]; if (tex) tex->bAutoGlowing = tex->bGlowing = tex->bFullbright = true; } } else if (sc.Compare("TEXTURE")) { sc.SetCMode(true); sc.MustGetString(); FTextureID flump=TexMan.CheckForTexture(sc.String, ETextureType::Flat,FTextureManager::TEXMAN_TryAny); FTexture *tex = TexMan[flump]; sc.MustGetStringName(","); sc.MustGetString(); PalEntry color = V_GetColor(NULL, sc.String); //sc.MustGetStringName(","); //sc.MustGetNumber(); if (sc.CheckString(",")) { if (sc.CheckNumber()) { if (tex) tex->GlowHeight = sc.Number; if (!sc.CheckString(",")) goto skip_fb; } sc.MustGetStringName("fullbright"); if (tex) tex->bFullbright = true; } skip_fb: sc.SetCMode(false); if (tex && color != 0) { tex->bAutoGlowing = false; tex->bGlowing = true; tex->GlowColor = color; } } } } //========================================================================== // // Parses a brightmap definition // //========================================================================== void ParseBrightmap() { ETextureType type = ETextureType::Any; bool disable_fullbright=false; bool thiswad = false; bool iwad = false; FTexture *bmtex = NULL; sc.MustGetString(); if (sc.Compare("texture")) type = ETextureType::Wall; else if (sc.Compare("flat")) type = ETextureType::Flat; else if (sc.Compare("sprite")) type = ETextureType::Sprite; else sc.UnGet(); sc.MustGetString(); FTextureID no = TexMan.CheckForTexture(sc.String, type, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_Overridable); FTexture *tex = TexMan[no]; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetString(); if (sc.Compare("disablefullbright")) { // This can also be used without a brightness map to disable // fullbright in rotations that only use brightness maps on // other angles. disable_fullbright = true; } else if (sc.Compare("thiswad")) { // only affects textures defined in the WAD containing the definition file. thiswad = true; } else if (sc.Compare ("iwad")) { // only affects textures defined in the IWAD. iwad = true; } else if (sc.Compare ("map")) { sc.MustGetString(); if (bmtex != NULL) { Printf("Multiple brightmap definitions in texture %s\n", tex? tex->Name.GetChars() : "(null)"); } bmtex = TexMan.FindTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); if (bmtex == NULL) Printf("Brightmap '%s' not found in texture '%s'\n", sc.String, tex? tex->Name.GetChars() : "(null)"); } } if (!tex) { return; } if (thiswad || iwad) { bool useme = false; int lumpnum = tex->GetSourceLump(); if (lumpnum != -1) { if (iwad && Wads.GetLumpFile(lumpnum) <= Wads.GetIwadNum()) useme = true; if (thiswad && Wads.GetLumpFile(lumpnum) == workingLump) useme = true; } if (!useme) return; } if (bmtex != NULL) { /* I do not think this is needed any longer if (tex->bWarped != 0) { Printf("Cannot combine warping with brightmap on texture '%s'\n", tex->Name.GetChars()); return; } */ bmtex->bMasked = false; tex->Brightmap = bmtex; } tex->bDisableFullbright = disable_fullbright; } //========================================================================== // // Parses a material definition // //========================================================================== void ParseMaterial() { ETextureType type = ETextureType::Any; bool disable_fullbright = false; bool disable_fullbright_specified = false; bool thiswad = false; bool iwad = false; FTexture *textures[6]; const char *keywords[7] = { "brightmap", "normal", "specular", "metallic", "roughness", "ao", nullptr }; const char *notFound[6] = { "Brightmap", "Normalmap", "Specular texture", "Metallic texture", "Roughness texture", "Ambient occlusion texture" }; memset(textures, 0, sizeof(textures)); sc.MustGetString(); if (sc.Compare("texture")) type = ETextureType::Wall; else if (sc.Compare("flat")) type = ETextureType::Flat; else if (sc.Compare("sprite")) type = ETextureType::Sprite; else sc.UnGet(); sc.MustGetString(); FTextureID no = TexMan.CheckForTexture(sc.String, type, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_Overridable); FTexture *tex = TexMan[no]; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetString(); if (sc.Compare("disablefullbright")) { // This can also be used without a brightness map to disable // fullbright in rotations that only use brightness maps on // other angles. disable_fullbright = true; disable_fullbright_specified = true; } else if (sc.Compare("thiswad")) { // only affects textures defined in the WAD containing the definition file. thiswad = true; } else if (sc.Compare ("iwad")) { // only affects textures defined in the IWAD. iwad = true; } else if (sc.Compare("glossiness")) { sc.MustGetFloat(); if (tex) tex->Glossiness = (float)sc.Float; } else if (sc.Compare("specularlevel")) { sc.MustGetFloat(); if (tex) tex->SpecularLevel = (float)sc.Float; } else { for (int i = 0; keywords[i] != nullptr; i++) { if (sc.Compare (keywords[i])) { sc.MustGetString(); if (textures[i]) Printf("Multiple %s definitions in texture %s\n", keywords[i], tex? tex->Name.GetChars() : "(null)"); textures[i] = TexMan.FindTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); if (!textures[i]) Printf("%s '%s' not found in texture '%s'\n", notFound[i], sc.String, tex? tex->Name.GetChars() : "(null)"); break; } } } } if (!tex) { return; } if (thiswad || iwad) { bool useme = false; int lumpnum = tex->GetSourceLump(); if (lumpnum != -1) { if (iwad && Wads.GetLumpFile(lumpnum) <= Wads.GetIwadNum()) useme = true; if (thiswad && Wads.GetLumpFile(lumpnum) == workingLump) useme = true; } if (!useme) return; } FTexture **bindings[6] = { &tex->Brightmap, &tex->Normal, &tex->Specular, &tex->Metallic, &tex->Roughness, &tex->AmbientOcclusion }; for (int i = 0; keywords[i] != nullptr; i++) { if (textures[i]) { textures[i]->bMasked = false; *bindings[i] = textures[i]; } } if (disable_fullbright_specified) tex->bDisableFullbright = disable_fullbright; } //========================================================================== // // Parses a shader definition // //========================================================================== void ParseHardwareShader() { sc.MustGetString(); if (sc.Compare("postprocess")) { sc.MustGetString(); PostProcessShader shaderdesc; shaderdesc.Target = sc.String; shaderdesc.Target.ToLower(); bool validTarget = false; if (sc.Compare("beforebloom")) validTarget = true; if (sc.Compare("scene")) validTarget = true; if (sc.Compare("screen")) validTarget = true; if (!validTarget) sc.ScriptError("Invalid target '%s' for postprocess shader",sc.String); sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetString(); if (sc.Compare("shader")) { sc.MustGetString(); shaderdesc.ShaderLumpName = sc.String; sc.MustGetNumber(); shaderdesc.ShaderVersion = sc.Number; if (sc.Number > 450 || sc.Number < 330) sc.ScriptError("Shader version must be in range 330 to 450!"); } else if (sc.Compare("name")) { sc.MustGetString(); shaderdesc.Name = sc.String; } else if (sc.Compare("uniform")) { sc.MustGetString(); FString uniformType = sc.String; uniformType.ToLower(); sc.MustGetString(); FString uniformName = sc.String; PostProcessUniformType parsedType = PostProcessUniformType::Undefined; if (uniformType.Compare("int") == 0) parsedType = PostProcessUniformType::Int; else if (uniformType.Compare("float") == 0) parsedType = PostProcessUniformType::Float; else if (uniformType.Compare("vec2") == 0) parsedType = PostProcessUniformType::Vec2; else if (uniformType.Compare("vec3") == 0) parsedType = PostProcessUniformType::Vec3; else sc.ScriptError("Unrecognized uniform type '%s'", sc.String); if (parsedType != PostProcessUniformType::Undefined) shaderdesc.Uniforms[uniformName].Type = parsedType; } else if (sc.Compare("texture")) { sc.MustGetString(); FString textureName = sc.String; sc.MustGetString(); FString textureSource = sc.String; shaderdesc.Textures[textureName] = textureSource; } else if (sc.Compare("enabled")) { shaderdesc.Enabled = true; } else { sc.ScriptError("Unknown keyword '%s'", sc.String); } } PostProcessShaders.Push(shaderdesc); } else { ETextureType type = ETextureType::Any; if (sc.Compare("texture")) type = ETextureType::Wall; else if (sc.Compare("flat")) type = ETextureType::Flat; else if (sc.Compare("sprite")) type = ETextureType::Sprite; else sc.UnGet(); bool disable_fullbright = false; bool thiswad = false; bool iwad = false; int maplump = -1; FString maplumpname; float speed = 1.f; sc.MustGetString(); FTextureID no = TexMan.CheckForTexture(sc.String, type); FTexture *tex = TexMan[no]; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetString(); if (sc.Compare("shader")) { sc.MustGetString(); maplumpname = sc.String; } else if (sc.Compare("speed")) { sc.MustGetFloat(); speed = float(sc.Float); } } if (!tex) { return; } if (maplumpname.IsNotEmpty()) { if (tex->bWarped != 0) { Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->Name.GetChars()); return; } tex->shaderspeed = speed; for (unsigned i = 0; i < usershaders.Size(); i++) { if (!usershaders[i].CompareNoCase(maplumpname)) { tex->shaderindex = i + FIRST_USER_SHADER; return; } } tex->shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER; } } } public: //========================================================================== // // // //========================================================================== void DoParseDefs() { int recursion=0; int lump, type; // Get actor class name. while (true) { sc.SavePos(); if (!sc.GetToken ()) { return; } type = sc.MatchString(CoreKeywords); switch (type) { case TAG_INCLUDE: { sc.MustGetString(); // This is not using sc.Open because it can print a more useful error message when done here lump = Wads.CheckNumForFullName(sc.String, true); if (lump==-1) sc.ScriptError("Lump '%s' not found", sc.String); GLDefsParser newscanner(lump, LightAssociations); newscanner.DoParseDefs(); break; } case LIGHT_POINT: ParsePointLight(); break; case LIGHT_PULSE: ParsePulseLight(); break; case LIGHT_FLICKER: ParseFlickerLight(); break; case LIGHT_FLICKER2: ParseFlickerLight2(); break; case LIGHT_SECTOR: ParseSectorLight(); break; case LIGHT_OBJECT: ParseObject(); break; case LIGHT_CLEAR: // This has been intentionally removed break; case TAG_SHADER: ParseShader(); break; case TAG_CLEARSHADERS: break; case TAG_SKYBOX: ParseGldefSkybox(); break; case TAG_GLOW: ParseGlow(); break; case TAG_BRIGHTMAP: ParseBrightmap(); break; case TAG_MATERIAL: ParseMaterial(); break; case TAG_HARDWARESHADER: ParseHardwareShader(); break; case TAG_DETAIL: ParseDetailTexture(); break; case TAG_DISABLE_FB: { /* not implemented. sc.MustGetString(); const PClass *cls = PClass::FindClass(sc.String); if (cls) GetDefaultByType(cls)->renderflags |= RF_NEVERFULLBRIGHT; */ } break; default: sc.ScriptError("Error parsing defs. Unknown tag: %s.\n", sc.String); break; } } } GLDefsParser(int lumpnum, TArray &la) : sc(lumpnum), workingLump(lumpnum), LightAssociations(la) { } }; //========================================================================== // // // //========================================================================== void LoadGLDefs(const char *defsLump) { TArray LightAssociations; int workingLump, lastLump; static const char *gldefsnames[] = { "GLDEFS", defsLump, nullptr }; lastLump = 0; while ((workingLump = Wads.FindLumpMulti(gldefsnames, &lastLump)) != -1) { GLDefsParser sc(workingLump, LightAssociations); sc.DoParseDefs(); } InitializeActorLights(LightAssociations); } //========================================================================== // // // //========================================================================== void ParseGLDefs() { const char *defsLump = NULL; LightDefaults.Clear(); //gl_DestroyUserShaders(); function says 'todo' switch (gameinfo.gametype) { case GAME_Heretic: defsLump = "HTICDEFS"; break; case GAME_Hexen: defsLump = "HEXNDEFS"; break; case GAME_Strife: defsLump = "STRFDEFS"; break; case GAME_Doom: defsLump = "DOOMDEFS"; break; case GAME_Chex: defsLump = "CHEXDEFS"; break; default: // silence GCC break; } ParseVavoomSkybox(); LoadGLDefs(defsLump); }