mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-27 22:33:17 +00:00
9409843931
Now everything uses a function. This really wasn't what operators are supposef to be used for.
1783 lines
44 KiB
C++
1783 lines
44 KiB
C++
/*
|
|
** 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 <ctype.h>
|
|
#include "i_system.h"
|
|
#include "sc_man.h"
|
|
#include "templates.h"
|
|
#include "w_wad.h"
|
|
#include "gi.h"
|
|
#include "r_state.h"
|
|
#include "stats.h"
|
|
#include "v_text.h"
|
|
#include "g_levellocals.h"
|
|
#include "a_dynlight.h"
|
|
#include "textures/skyboxtexture.h"
|
|
#include "hwrenderer/postprocessing/hw_postprocessshader.h"
|
|
#include "hwrenderer/textures/hw_material.h"
|
|
|
|
void AddLightDefaults(FLightDefaults *defaults, double attnFactor);
|
|
void AddLightAssociation(const char *actor, const char *frame, const char *light);
|
|
void InitializeActorLights(TArray<FLightAssociation> &LightAssociations);
|
|
|
|
TArray<UserShaderDesc> usershaders;
|
|
extern TDeletingArray<FLightDefaults *> 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(sc.String);
|
|
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->GetName().GetChars());
|
|
error = true;
|
|
}
|
|
sb->faces[facecount] = tex;
|
|
sc.MustGetStringName("}");
|
|
}
|
|
facecount++;
|
|
}
|
|
if (facecount != 6)
|
|
{
|
|
sc.ScriptError("%s: Skybox definition requires 6 faces", sb->GetName().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",
|
|
"lightsizefactor",
|
|
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,
|
|
TAG_LIGHTSIZEFACTOR,
|
|
};
|
|
|
|
//==========================================================================
|
|
//
|
|
// GLDEFS parser
|
|
//
|
|
//==========================================================================
|
|
|
|
class GLDefsParser
|
|
{
|
|
FScanner sc;
|
|
int workingLump;
|
|
int ScriptDepth = 0;
|
|
TArray<FLightAssociation> &LightAssociations;
|
|
double lightSizeFactor = 1.;
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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>((int)(floatTriple[0] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
|
|
break;
|
|
case LIGHTTAG_OFFSET:
|
|
ParseTriple(sc, floatTriple);
|
|
defaults->SetOffset(floatTriple);
|
|
break;
|
|
case LIGHTTAG_SIZE:
|
|
intVal = clamp<int>(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, lightSizeFactor);
|
|
}
|
|
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>((int)(floatTriple[0] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
|
|
break;
|
|
case LIGHTTAG_OFFSET:
|
|
ParseTriple(sc, floatTriple);
|
|
defaults->SetOffset(floatTriple);
|
|
break;
|
|
case LIGHTTAG_SIZE:
|
|
intVal = clamp<int>(ParseInt(sc), 1, 1024);
|
|
defaults->SetArg(LIGHT_INTENSITY, intVal);
|
|
break;
|
|
case LIGHTTAG_SECSIZE:
|
|
intVal = clamp<int>(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, lightSizeFactor);
|
|
}
|
|
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>((int)(floatTriple[0] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
|
|
break;
|
|
case LIGHTTAG_OFFSET:
|
|
ParseTriple(sc, floatTriple);
|
|
defaults->SetOffset(floatTriple);
|
|
break;
|
|
case LIGHTTAG_SIZE:
|
|
intVal = clamp<int>(ParseInt(sc), 1, 1024);
|
|
defaults->SetArg(LIGHT_INTENSITY, intVal);
|
|
break;
|
|
case LIGHTTAG_SECSIZE:
|
|
intVal = clamp<int>(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, lightSizeFactor);
|
|
}
|
|
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>((int)(floatTriple[0] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
|
|
break;
|
|
case LIGHTTAG_OFFSET:
|
|
ParseTriple(sc, floatTriple);
|
|
defaults->SetOffset(floatTriple);
|
|
break;
|
|
case LIGHTTAG_SIZE:
|
|
intVal = clamp<int>(ParseInt(sc), 1, 1024);
|
|
defaults->SetArg(LIGHT_INTENSITY, intVal);
|
|
break;
|
|
case LIGHTTAG_SECSIZE:
|
|
intVal = clamp<int>(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, lightSizeFactor);
|
|
}
|
|
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>((int)(floatTriple[0] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
|
|
defaults->SetArg(LIGHT_BLUE, clamp<int>((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, lightSizeFactor);
|
|
}
|
|
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.GetTexture(TexMan.GetTextureID(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.GetTexture(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.GetTexture(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.GetTexture(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.GetTexture(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)
|
|
{
|
|
bmtex->bMasked = false;
|
|
tex->Brightmap = bmtex;
|
|
}
|
|
tex->bDisableFullbright = disable_fullbright;
|
|
}
|
|
|
|
void SetShaderIndex(FTexture *tex, unsigned index)
|
|
{
|
|
auto desc = usershaders[index - FIRST_USER_SHADER];
|
|
if (desc.disablealphatest)
|
|
{
|
|
tex->bTranslucent = true;
|
|
}
|
|
tex->shaderindex = index;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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;
|
|
|
|
UserShaderDesc usershader;
|
|
TArray<FString> texNameList;
|
|
TArray<int> texNameIndex;
|
|
float speed = 1.f;
|
|
|
|
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.GetTexture(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 if (sc.Compare("speed"))
|
|
{
|
|
sc.MustGetFloat();
|
|
speed = float(sc.Float);
|
|
}
|
|
else if (sc.Compare("shader"))
|
|
{
|
|
sc.MustGetString();
|
|
usershader.shader = sc.String;
|
|
}
|
|
else if (sc.Compare("texture"))
|
|
{
|
|
sc.MustGetString();
|
|
FString textureName = sc.String;
|
|
for (FString &texName : texNameList)
|
|
{
|
|
if (!texName.Compare(textureName))
|
|
{
|
|
sc.ScriptError("Trying to redefine custom hardware shader texture '%s' in texture '%s'\n", textureName.GetChars(), tex ? tex->Name.GetChars() : "(null)");
|
|
}
|
|
}
|
|
sc.MustGetString();
|
|
bool okay = false;
|
|
for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++)
|
|
{
|
|
if (!tex->CustomShaderTextures[i])
|
|
{
|
|
tex->CustomShaderTextures[i] = TexMan.FindTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny);
|
|
if (!tex->CustomShaderTextures[i])
|
|
{
|
|
sc.ScriptError("Custom hardware shader texture '%s' not found in texture '%s'\n", sc.String, tex ? tex->Name.GetChars() : "(null)");
|
|
}
|
|
|
|
texNameList.Push(textureName);
|
|
texNameIndex.Push(i);
|
|
okay = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!okay)
|
|
{
|
|
sc.ScriptError("Error: out of texture units in texture '%s'", tex ? tex->Name.GetChars() : "(null)");
|
|
}
|
|
}
|
|
else if (sc.Compare("define"))
|
|
{
|
|
sc.MustGetString();
|
|
FString defineName = sc.String;
|
|
FString defineValue = "";
|
|
if (sc.CheckToken('='))
|
|
{
|
|
sc.MustGetString();
|
|
defineValue = sc.String;
|
|
}
|
|
usershader.defines.AppendFormat("#define %s %s\n", defineName.GetChars(), defineValue.GetChars());
|
|
}
|
|
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;
|
|
|
|
if (usershader.shader.IsNotEmpty())
|
|
{
|
|
int firstUserTexture;
|
|
if (tex->Normal && tex->Specular)
|
|
{
|
|
usershader.shaderType = tex->Brightmap ? SHADER_SpecularBrightmap : SHADER_Specular;
|
|
firstUserTexture = tex->Brightmap ? 5 : 4;
|
|
}
|
|
else if (tex->Normal && tex->Metallic && tex->Roughness && tex->AmbientOcclusion)
|
|
{
|
|
usershader.shaderType = tex->Brightmap ? SHADER_PBRBrightmap : SHADER_PBR;
|
|
firstUserTexture = tex->Brightmap ? 7 : 6;
|
|
}
|
|
else
|
|
{
|
|
usershader.shaderType = tex->Brightmap ? SHADER_Brightmap : SHADER_Default;
|
|
firstUserTexture = tex->Brightmap ? 3 : 2;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < texNameList.Size(); i++)
|
|
{
|
|
usershader.defines.AppendFormat("#define %s texture%d\n", texNameList[i].GetChars(), texNameIndex[i] + firstUserTexture);
|
|
}
|
|
|
|
if (tex->isWarped() != 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].shader.CompareNoCase(usershader.shader) &&
|
|
usershaders[i].shaderType == usershader.shaderType &&
|
|
!usershaders[i].defines.Compare(usershader.defines))
|
|
{
|
|
SetShaderIndex(tex, i + FIRST_USER_SHADER);
|
|
return;
|
|
}
|
|
}
|
|
SetShaderIndex(tex, usershaders.Push(usershader) + FIRST_USER_SHADER);
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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;
|
|
UserShaderDesc desc;
|
|
desc.shaderType = SHADER_Default;
|
|
TArray<FString> texNameList;
|
|
TArray<int> texNameIndex;
|
|
float speed = 1.f;
|
|
|
|
sc.MustGetString();
|
|
FTextureID no = TexMan.CheckForTexture(sc.String, type);
|
|
FTexture *tex = TexMan.GetTexture(no);
|
|
|
|
sc.MustGetToken('{');
|
|
while (!sc.CheckToken('}'))
|
|
{
|
|
sc.MustGetString();
|
|
if (sc.Compare("shader"))
|
|
{
|
|
sc.MustGetString();
|
|
desc.shader = sc.String;
|
|
}
|
|
else if (sc.Compare("material"))
|
|
{
|
|
sc.MustGetString();
|
|
MaterialShaderIndex typeIndex[6] = { SHADER_Default, SHADER_Brightmap, SHADER_Specular, SHADER_SpecularBrightmap, SHADER_PBR, SHADER_PBRBrightmap };
|
|
const char *typeName[6] = { "normal", "brightmap", "specular", "specularbrightmap", "pbr", "pbrbrightmap" };
|
|
bool found = false;
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
if (sc.Compare(typeName[i]))
|
|
{
|
|
desc.shaderType = typeIndex[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
sc.ScriptError("Unknown material type '%s' specified\n", sc.String);
|
|
}
|
|
else if (sc.Compare("speed"))
|
|
{
|
|
sc.MustGetFloat();
|
|
speed = float(sc.Float);
|
|
}
|
|
else if (sc.Compare("texture"))
|
|
{
|
|
sc.MustGetString();
|
|
FString textureName = sc.String;
|
|
for(FString &texName : texNameList)
|
|
{
|
|
if(!texName.Compare(textureName))
|
|
{
|
|
sc.ScriptError("Trying to redefine custom hardware shader texture '%s' in texture '%s'\n", textureName.GetChars(), tex? tex->Name.GetChars() : "(null)");
|
|
}
|
|
}
|
|
sc.MustGetString();
|
|
bool okay = false;
|
|
for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++)
|
|
{
|
|
if (!tex->CustomShaderTextures[i])
|
|
{
|
|
tex->CustomShaderTextures[i] = TexMan.FindTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny);
|
|
if (!tex->CustomShaderTextures[i])
|
|
{
|
|
sc.ScriptError("Custom hardware shader texture '%s' not found in texture '%s'\n", sc.String, tex? tex->Name.GetChars() : "(null)");
|
|
}
|
|
|
|
texNameList.Push(textureName);
|
|
texNameIndex.Push(i);
|
|
okay = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!okay)
|
|
{
|
|
sc.ScriptError("Error: out of texture units in texture '%s'", tex? tex->Name.GetChars() : "(null)");
|
|
}
|
|
}
|
|
else if(sc.Compare("define"))
|
|
{
|
|
sc.MustGetString();
|
|
FString defineName = sc.String;
|
|
FString defineValue = "";
|
|
if(sc.CheckToken('='))
|
|
{
|
|
sc.MustGetString();
|
|
defineValue = sc.String;
|
|
}
|
|
desc.defines.AppendFormat("#define %s %s\n", defineName.GetChars(), defineValue.GetChars());
|
|
}
|
|
else if (sc.Compare("disablealphatest"))
|
|
{
|
|
desc.disablealphatest = true;
|
|
}
|
|
}
|
|
if (!tex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int firstUserTexture;
|
|
switch (desc.shaderType)
|
|
{
|
|
default:
|
|
case SHADER_Default: firstUserTexture = 2; break;
|
|
case SHADER_Brightmap: firstUserTexture = 3; break;
|
|
case SHADER_Specular: firstUserTexture = 4; break;
|
|
case SHADER_SpecularBrightmap: firstUserTexture = 5; break;
|
|
case SHADER_PBR: firstUserTexture = 6; break;
|
|
case SHADER_PBRBrightmap: firstUserTexture = 7; break;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < texNameList.Size(); i++)
|
|
{
|
|
desc.defines.AppendFormat("#define %s texture%d\n", texNameList[i].GetChars(), texNameIndex[i] + firstUserTexture);
|
|
}
|
|
|
|
if (desc.shader.IsNotEmpty())
|
|
{
|
|
if (tex->isWarped() != 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].shader.CompareNoCase(desc.shader) &&
|
|
usershaders[i].shaderType == desc.shaderType &&
|
|
!usershaders[i].defines.Compare(desc.defines))
|
|
{
|
|
SetShaderIndex(tex, i + FIRST_USER_SHADER);
|
|
return;
|
|
}
|
|
}
|
|
SetShaderIndex(tex, usershaders.Push(desc) + 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.lightSizeFactor = lightSizeFactor;
|
|
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_LIGHTSIZEFACTOR:
|
|
lightSizeFactor = ParseFloat(sc);
|
|
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<FLightAssociation> &la)
|
|
: sc(lumpnum), workingLump(lumpnum), LightAssociations(la)
|
|
{
|
|
}
|
|
};
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void LoadGLDefs(const char *defsLump)
|
|
{
|
|
TArray<FLightAssociation> 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);
|
|
}
|