diff --git a/levels/avaraline-strict-mode/alf/layout/io.alf b/levels/avaraline-strict-mode/alf/layout/io.alf index a1f98a5f6..1f6d77b72 100644 --- a/levels/avaraline-strict-mode/alf/layout/io.alf +++ b/levels/avaraline-strict-mode/alf/layout/io.alf @@ -1,5 +1,5 @@ - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - - - - + + + + + + + diff --git a/levels/avaraline-strict-mode/alf/layout/not-nul.alf b/levels/avaraline-strict-mode/alf/layout/not-nul.alf index a5bc65862..21b4f4576 100644 --- a/levels/avaraline-strict-mode/alf/layout/not-nul.alf +++ b/levels/avaraline-strict-mode/alf/layout/not-nul.alf @@ -1,275 +1,279 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + diff --git a/platform/macos/Info.plist b/platform/macos/Info.plist index 3e2cf897a..443267707 100644 --- a/platform/macos/Info.plist +++ b/platform/macos/Info.plist @@ -2,7 +2,34 @@ + CFBundleDocumentTypes + + + NSHighResolutionCapable + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.xml + + UTTypeIcons + + UTTypeIconBadgeName + + + UTTypeIdentifier + net.avaraline.alf + UTTypeTagSpecification + + public.filename-extension + + alf + + + + diff --git a/rsrc/aftershock/default.avarascript b/rsrc/aftershock/default.avarascript index 6c4e92ed6..a85d6c0c9 100644 --- a/rsrc/aftershock/default.avarascript +++ b/rsrc/aftershock/default.avarascript @@ -265,8 +265,10 @@ // Material properties: defaultMaterial.specular = 0 defaultMaterial.shininess = 0 - baseMaterial.specular = defaultMaterial.specular - baseMaterial.shininess = defaultMaterial.shininess + defaultMaterial.glow = 0 + baseMaterial.specular = 0 + baseMaterial.shininess = 0 + baseMaterial.glow = 0 // Advanced weapon powers: grenadePower = 2.05 diff --git a/rsrc/default.avarascript b/rsrc/default.avarascript index 2e56b83ee..c079938f1 100644 --- a/rsrc/default.avarascript +++ b/rsrc/default.avarascript @@ -265,8 +265,10 @@ // Material properties: defaultMaterial.specular = 0 defaultMaterial.shininess = 0 + defaultMaterial.glow = 0 baseMaterial.specular = 0 baseMaterial.shininess = 0 + baseMaterial.glow = 0 // Advanced weapon powers: grenadePower = 2.25 diff --git a/rsrc/shaders/hud_vert.glsl b/rsrc/shaders/hud_vert.glsl index 2b903d6d5..3b9e9c57b 100644 --- a/rsrc/shaders/hud_vert.glsl +++ b/rsrc/shaders/hud_vert.glsl @@ -1,7 +1,11 @@ layout(location = 0) in vec3 vertexPosition_modelspace; layout(location = 1) in vec4 vertexColor; layout(location = 2) in vec4 vertexSpecular; -layout(location = 3) in vec3 vertexNormal; +layout(location = 3) in float vertexGlow; +layout(location = 4) in float vertexReserved1; +layout(location = 5) in float vertexReserved2; +layout(location = 6) in float vertexReserved3; +layout(location = 7) in vec3 vertexNormal; uniform mat4 view; uniform mat4 proj; @@ -9,6 +13,7 @@ uniform mat4 model; out vec4 fragmentColor; out vec4 fragmentSpecular; +out float fragmentGlow; out vec3 fragmentNormal; void main() { @@ -16,5 +21,6 @@ void main() { gl_Position = proj * (pos * model * view); fragmentColor = vertexColor; fragmentSpecular = vertexSpecular; + fragmentGlow = vertexGlow; fragmentNormal = vertexNormal; } diff --git a/rsrc/shaders/sky_frag.glsl b/rsrc/shaders/sky_frag.glsl index 2c57d454d..3daba2ce7 100644 --- a/rsrc/shaders/sky_frag.glsl +++ b/rsrc/shaders/sky_frag.glsl @@ -1,3 +1,4 @@ +#define GROUND_PHI 0.0 #define MAX_LIGHTS 4 in vec3 tex_coord; @@ -45,8 +46,10 @@ vec3 apply_fog(vec3 color, vec3 viewDir, float dist) { } vec3 draw_celestial(vec3 inColor, float phi, float gradientHeight, vec3 offsetFragPos, int i) { - if (lightCelestialRadius[i] == 0.0 || phi <= 0.0) return inColor; + if (lightCelestialRadius[i] == 0.0 || phi <= GROUND_PHI) return inColor; float dist = distance(offsetFragPos, lightPos[i]); + float coronaRadius = lightCelestialRadius[i] * (1 + length(lightDir[i])); + //float horizonRadius = coronaRadius * (1 - clamp(phi, 0.0, 1.0)); return mix( inColor, mix( @@ -54,7 +57,9 @@ vec3 draw_celestial(vec3 inColor, float phi, float gradientHeight, vec3 offsetFr lightColor[i] * gradientHeight + horizonColor * (1.0 - gradientHeight), float(phi < highAlt) ), - float(dist <= lightCelestialRadius[i]) + pow(clamp(1 - ((dist - lightCelestialRadius[i]) / (coronaRadius - lightCelestialRadius[i])), 0.0, 1.0), 4) + //clamp(1 - (lightCelestialRadius[i] / coronaRadius), 0.0, 1.0) + //float(dist <= lightCelestialRadius[i]) ); } @@ -105,7 +110,7 @@ void main() float(phi > highAlt) ), groundColor + spec(viewDir), - float(phi <= 0.0) + float(phi <= GROUND_PHI) ), 1.0 ); @@ -119,7 +124,7 @@ void main() mix( -dot(camPos, vec3(0, 1, 0)) / dot(-viewDir, vec3(0, 1, 0)), maxHazeDist, - float(phi >= 0.0) + float(phi >= GROUND_PHI) ), maxHazeDist ); diff --git a/rsrc/shaders/world_frag.glsl b/rsrc/shaders/world_frag.glsl index d07f3bf7d..2b5c83827 100644 --- a/rsrc/shaders/world_frag.glsl +++ b/rsrc/shaders/world_frag.glsl @@ -4,6 +4,7 @@ in vec4 fragmentColor; in vec3 fragmentSpecular; in float fragmentShininess; +in float fragmentGlow; in vec3 fragmentNormal; in vec3 fragPos; @@ -81,9 +82,13 @@ float noise() { vec4 light_color(vec3 viewDir) { return mix( - ambient * vec4(ambientColor, 1.0) * fragmentColor, - vec4((ambient * ambientColor) + diffuse() + spec(viewDir), 1.0) * fragmentColor, - float(lightsActive) + mix( + ambient * vec4(ambientColor, 1.0) * fragmentColor, + vec4((ambient * ambientColor) + diffuse() + spec(viewDir), 1.0) * fragmentColor, + float(lightsActive) + ), + fragmentColor, + float(fragmentGlow > 0.0) ); } diff --git a/rsrc/shaders/world_vert.glsl b/rsrc/shaders/world_vert.glsl index 78d1575f5..8bbd88aea 100644 --- a/rsrc/shaders/world_vert.glsl +++ b/rsrc/shaders/world_vert.glsl @@ -1,9 +1,14 @@ layout(location = 0) in vec3 vertexPosition_modelspace; layout(location = 1) in vec4 vertexColor; layout(location = 2) in vec4 vertexSpecular; -layout(location = 3) in vec3 vertexNormal; +layout(location = 3) in float vertexGlow; +layout(location = 4) in float vertexReserved1; +layout(location = 5) in float vertexReserved2; +layout(location = 6) in float vertexReserved3; +layout(location = 7) in vec3 vertexNormal; uniform float maxShininess; +uniform float maxGlow; uniform mat4 model; uniform mat4 view; uniform mat4 proj; @@ -12,6 +17,7 @@ uniform mat3 normalTransform; out vec4 fragmentColor; out vec3 fragmentSpecular; out float fragmentShininess; +out float fragmentGlow; out vec3 fragmentNormal; out vec3 fragPos; @@ -21,6 +27,7 @@ void main() { fragmentColor = vertexColor; fragmentSpecular = vertexSpecular.rgb; fragmentShininess = vertexSpecular.a * maxShininess; + fragmentGlow = vertexGlow * maxGlow; fragmentNormal = vertexNormal * normalTransform; fragPos = (pos * model).xyz; } diff --git a/src/bsp/CBSPPart.cpp b/src/bsp/CBSPPart.cpp index 5f754976f..839ed294e 100644 --- a/src/bsp/CBSPPart.cpp +++ b/src/bsp/CBSPPart.cpp @@ -108,8 +108,9 @@ void CBSPPart::IBSPPart(short resId) { original = baseMaterial; current = baseMaterial; nlohmann::json const &mat = doc["materials"][i]; - ARGBColor color = ARGBColor(0x00ffffff); // Default to invisible "white." + ARGBColor color = defaultMaterial.GetColor(); ARGBColor spec = defaultMaterial.GetSpecular().WithA(defaultMaterial.GetShininess()); + uint8_t glow = defaultMaterial.GetGlow(); if (mat.find("base") != mat.end()) { color = ARGBColor::Parse(mat["base"]) @@ -137,6 +138,13 @@ void CBSPPart::IBSPPart(short resId) { spec = baseMaterial.GetSpecular().WithA(baseMaterial.GetShininess()); } current = current.WithSpecular(spec).WithShininess(spec.GetA()); + + glow = mat.value("glow", 0); + original = original.WithGlow(glow); + if (glow == defaultMaterial.GetGlow()) { + glow = baseMaterial.GetGlow(); + } + current = current.WithGlow(glow); materialTable.push_back(MaterialRecord(original, current)); } @@ -476,6 +484,30 @@ void CBSPPart::ReplaceShininessForColor(ARGBColor origColor, uint8_t newShinines if (shininessReplaced && vData) vData->Replace(*this); } +void CBSPPart::ReplaceGlowForColor(ARGBColor origColor, uint8_t newGlow) { + bool glowReplaced = false; + for (auto &material : materialTable) { + if (material.original.GetColor() == origColor) { + material.current = material.current.WithGlow(newGlow); + glowReplaced = true; + } + } + // (No need to check for alpha here.) + if (glowReplaced && vData) vData->Replace(*this); +} + +void CBSPPart::ReplaceAllGlow(uint8_t newGlow) { + bool glowReplaced = false; + for (auto &material : materialTable) { + if (material.current.GetGlow() != newGlow) { + glowReplaced = true; + } + material.current = material.current.WithGlow(newGlow); + } + // (No need to check for alpha here.) + if (glowReplaced && vData) vData->Replace(*this); +} + void CBSPPart::ReplaceMaterial(Material origMaterial, Material newMaterial) { bool materialReplaced = false; for (auto &material : materialTable) { diff --git a/src/bsp/CBSPPart.h b/src/bsp/CBSPPart.h index c563a9863..337671995 100644 --- a/src/bsp/CBSPPart.h +++ b/src/bsp/CBSPPart.h @@ -214,6 +214,8 @@ class CBSPPart { virtual void ReplaceMaterialForColor(ARGBColor origColor, Material newMaterial); virtual void ReplaceSpecularForColor(ARGBColor origColor, ARGBColor newSpecular); virtual void ReplaceShininessForColor(ARGBColor origColor, uint8_t newShininess); + virtual void ReplaceGlowForColor(ARGBColor origColor, uint8_t newGlow); + virtual void ReplaceAllGlow(uint8_t newGlow); virtual void ReplaceMaterial(Material origMaterial, Material newMaterial); virtual void ReplaceAllMaterials(Material newMaterial); diff --git a/src/game/CScout.cpp b/src/game/CScout.cpp index da6ed5bc4..e3c04e313 100644 --- a/src/game/CScout.cpp +++ b/src/game/CScout.cpp @@ -45,6 +45,16 @@ CScout::CScout(CAbstractPlayer *thePlayer, short theTeam, ARGBColor longTeamColo partCount = 1; LoadPart(0, kScoutBSP); partList[0]->ReplaceColor(*ColorManager::getMarkerColor(0), longTeamColor); + for (CSmartPart **thePart = partList; *thePart; thePart++) { + (*thePart)->ReplaceSpecularForColor(*ColorManager::getMarkerColor(0), ARGBColor(0x333333)); + (*thePart)->ReplaceShininessForColor(*ColorManager::getMarkerColor(0), 4); + (*thePart)->ReplaceSpecularForColor(*ColorManager::getMarkerColor(1), ARGBColor(0x454545)); + (*thePart)->ReplaceShininessForColor(*ColorManager::getMarkerColor(1), 8); + (*thePart)->ReplaceSpecularForColor(*ColorManager::getMarkerColor(2), ARGBColor(0xa6a6a6)); + (*thePart)->ReplaceShininessForColor(*ColorManager::getMarkerColor(2), 48); + (*thePart)->ReplaceSpecularForColor(*ColorManager::getMarkerColor(3), ARGBColor(0x808080)); + (*thePart)->ReplaceShininessForColor(*ColorManager::getMarkerColor(3), 24); + } hitSoundId = 220; diff --git a/src/game/CSmart.cpp b/src/game/CSmart.cpp index 358eb0de3..6e5bdebde 100644 --- a/src/game/CSmart.cpp +++ b/src/game/CSmart.cpp @@ -60,6 +60,7 @@ long CSmart::Arm(CSmartPart *aPart) { shields = FIX3(100); partList[0]->ReplaceColor(*ColorManager::getMarkerColor(1), ColorManager::getMissileArmedColor()); + partList[0]->ReplaceGlowForColor(*ColorManager::getMarkerColor(1), 16); targetIdent = 0; targetPart = NULL; diff --git a/src/game/CWorldShader.h b/src/game/CWorldShader.h index 0358cde3d..003ddbfa7 100644 --- a/src/game/CWorldShader.h +++ b/src/game/CWorldShader.h @@ -45,5 +45,5 @@ class CWorldShader { CWorldShader(); - virtual void Reset(); + void Reset(); }; diff --git a/src/level/LevelLoader.cpp b/src/level/LevelLoader.cpp index 792d0e15b..ac025a686 100644 --- a/src/level/LevelLoader.cpp +++ b/src/level/LevelLoader.cpp @@ -77,6 +77,7 @@ Material GetDefaultMaterial() { defaultMaterial = defaultMaterial.WithSpecular(*specular); } defaultMaterial = defaultMaterial.WithShininessVar(iDefaultMaterialShininess); + defaultMaterial = defaultMaterial.WithGlowVar(iDefaultMaterialGlow); return defaultMaterial; } @@ -87,6 +88,7 @@ Material GetBaseMaterial() { baseMaterial = baseMaterial.WithSpecular(*specular); } baseMaterial = baseMaterial.WithShininessVar(iBaseMaterialShininess); + baseMaterial = baseMaterial.WithGlowVar(iBaseMaterialGlow); return baseMaterial; } @@ -153,6 +155,8 @@ struct ALFWalker: pugi::xml_tree_walker { attr = "material.0.specular"; } else if (attr.compare("material.shininess") == 0) { attr = "material.0.shininess"; + } else if (attr.compare("material.glow") == 0) { + attr = "material.0.glow"; } // XML attributes can't have brackets, so we turn light.0.i into light[0].i std::regex subscript("\\.(\\d+)"); @@ -274,6 +278,14 @@ struct ALFWalker: pugi::xml_tree_walker { palette[3] = palette[3].WithShininessVar(iBaseMaterialShininess); } + if (!node.attribute("baseMaterial.glow").empty()) { + // When baseMaterial properties are set, apply it to all mats. + palette[0] = palette[0].WithGlowVar(iBaseMaterialGlow); + palette[1] = palette[1].WithGlowVar(iBaseMaterialGlow); + palette[2] = palette[2].WithGlowVar(iBaseMaterialGlow); + palette[3] = palette[3].WithGlowVar(iBaseMaterialGlow); + } + if (!node.attribute("material.specular").empty()) { const std::optional color = ReadColorVar("material[0].specular"); if (color) { @@ -328,6 +340,26 @@ struct ALFWalker: pugi::xml_tree_walker { if (!node.attribute("material.3.shininess").empty()) { palette[3] = palette[3].WithShininessVar("material[3].shininess"); } + + if (!node.attribute("material.glow").empty()) { + palette[0] = palette[0].WithGlowVar("material[0].glow"); + } + + if (!node.attribute("material.0.glow").empty()) { + palette[0] = palette[0].WithGlowVar("material[0].glow"); + } + + if (!node.attribute("material.1.glow").empty()) { + palette[1] = palette[1].WithGlowVar("material[1].glow"); + } + + if (!node.attribute("material.2.glow").empty()) { + palette[2] = palette[2].WithGlowVar("material[2].glow"); + } + + if (!node.attribute("material.3.glow").empty()) { + palette[3] = palette[3].WithGlowVar("material[3].glow"); + } if (!node.attribute("x").empty() && !node.attribute("z").empty() && !node.attribute("w").empty() && !node.attribute("d").empty()) { diff --git a/src/level/LevelLoader.h b/src/level/LevelLoader.h index 7e8f74490..fdd084b12 100644 --- a/src/level/LevelLoader.h +++ b/src/level/LevelLoader.h @@ -274,8 +274,10 @@ enum { // Materials iDefaultMaterialSpecular, iDefaultMaterialShininess, + iDefaultMaterialGlow, iBaseMaterialSpecular, iBaseMaterialShininess, + iBaseMaterialGlow, // Advanced weapons iGrenadePower, diff --git a/src/render/LegacyOpenGLRenderer.cpp b/src/render/LegacyOpenGLRenderer.cpp index 6ffad6df9..d7ab75533 100644 --- a/src/render/LegacyOpenGLRenderer.cpp +++ b/src/render/LegacyOpenGLRenderer.cpp @@ -187,6 +187,7 @@ void LegacyOpenGLRenderer::ApplyLights() AdjustAmbient(*worldShader, ambientIntensity); worldShader->SetFloat3("ambientColor", ambientRGB); worldShader->SetFloat("maxShininess", MAX_SHININESS_EXP); + worldShader->SetFloat("maxGlow", MAX_GLOW); worldShader->SetBool("lightsActive", true); skyShader->Use(); @@ -507,10 +508,22 @@ void LegacyOpenGLRenderer::Draw(OpenGLShader &shader, const CBSPPart &part, floa // Specular! glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (4 * sizeof(uint8_t)))); glEnableVertexAttribArray(2); + + // Glow! + glVertexAttribPointer(3, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (8 * sizeof(uint8_t)))); + glEnableVertexAttribArray(3); + + // Reserved + glVertexAttribPointer(4, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (9 * sizeof(uint8_t)))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(5, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (10 * sizeof(uint8_t)))); + glEnableVertexAttribArray(5); + glVertexAttribPointer(6, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (11 * sizeof(uint8_t)))); + glEnableVertexAttribArray(6); // Normal! - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)((3 * sizeof(float)) + (8 * sizeof(uint8_t)))); - glEnableVertexAttribArray(3); + glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)((3 * sizeof(float)) + (12 * sizeof(uint8_t)))); + glEnableVertexAttribArray(7); // Custom, per-object lighting and depth testing! float extraAmbient = ToFloat(part.extraAmbient); @@ -545,6 +558,11 @@ void LegacyOpenGLRenderer::Draw(OpenGLShader &shader, const CBSPPart &part, floa glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glDisableVertexAttribArray(4); + glDisableVertexAttribArray(5); + glDisableVertexAttribArray(6); + glDisableVertexAttribArray(7); __glCheckErrors(); // Restore previous lighting and depth testing state. diff --git a/src/render/Material.h b/src/render/Material.h index 07c5394dd..7ed1b91c5 100644 --- a/src/render/Material.h +++ b/src/render/Material.h @@ -8,22 +8,27 @@ #include #include +#define MAX_GLOW 256.0f #define MAX_SHININESS_EXP 1024.0f class Material { public: void* operator new(std::size_t) = delete; - bool operator==(const Material& other) { return color == other.color && specular == other.specular; } - bool operator!=(const Material& other) { return color != other.color && specular != other.specular; } - constexpr Material(ARGBColor color = 0xFF000000, ARGBColor specular = 0x00000000): color(color), specular(specular) {} + bool operator==(const Material& other) { return color == other.color && specular == other.specular && glow == other.glow; } + bool operator!=(const Material& other) { return color != other.color && specular != other.specular && glow != other.glow; } + constexpr Material(ARGBColor color = 0xFFFFFFFF, ARGBColor specular = 0x00000000, uint8_t glow = 0): color(color), specular(specular), glow(glow) {} inline ARGBColor GetColor() const { return color; } inline ARGBColor GetSpecular() const { return specular.WithA(0x00); } + inline uint8_t GetGlow() const { return glow; } inline bool HasAlpha() const { return color.HasAlpha(); } inline Material WithColor(ARGBColor c) const { - return Material(c, specular); + return Material(c, specular, glow); } inline Material WithSpecular(ARGBColor s) const { - return Material(color, s.WithA(specular.GetA())); + return Material(color, s.WithA(specular.GetA()), glow); + } + inline Material WithGlow(uint8_t g) const { + return Material(color, specular, g); } // Convenience getters. @@ -38,40 +43,59 @@ class Material { // Convenience setters. inline Material WithA(uint8_t a) const { - return Material(color.WithA(a), specular); + return Material(color.WithA(a), specular, glow); } inline Material WithR(uint8_t r) const { - return Material(color.WithR(r), specular); + return Material(color.WithR(r), specular, glow); } inline Material WithG(uint8_t g) const { - return Material(color.WithG(g), specular); + return Material(color.WithG(g), specular, glow); } inline Material WithB(uint8_t b) const { - return Material(color.WithB(b), specular); + return Material(color.WithB(b), specular, glow); } inline Material WithSpecR(uint8_t r) const { - return Material(color, specular.WithR(r)); + return Material(color, specular.WithR(r), glow); } inline Material WithSpecG(uint8_t g) const { - return Material(color, specular.WithG(g)); + return Material(color, specular.WithG(g), glow); } inline Material WithSpecB(uint8_t b) const { - return Material(color, specular.WithB(b)); + return Material(color, specular.WithB(b), glow); } inline Material WithShininess(uint8_t s) const { - return Material(color, specular.WithA(s)); + return Material(color, specular.WithA(s), glow); + } + inline Material WithGlowVar(short index) { + Fixed glow = ReadFixedMaterialVar(index); + return Material(color, specular, ConstrainGlow(glow)); + } + inline Material WithGlowVar(const char *s) { + Fixed glow = ReadFixedMaterialVar(s); + return Material(color, specular, ConstrainGlow(glow)); } inline Material WithShininessVar(short index) { Fixed shininess = ReadFixedMaterialVar(index); - return Material(color, specular.WithA(ConstrainShininess(shininess))); + return Material(color, specular.WithA(ConstrainShininess(shininess)), glow); } inline Material WithShininessVar(const char *s) { Fixed shininess = ReadFixedMaterialVar(s); - return Material(color, specular.WithA(ConstrainShininess(shininess))); + return Material(color, specular.WithA(ConstrainShininess(shininess)), glow); } private: - ARGBColor color = 0xFF000000; + ARGBColor color = 0xFFFFFFFF; ARGBColor specular = 0x00000000; // Alpha channel = shininess value + uint8_t glow = 0; + + /** + * Constrain input values for glow. + * + * @param glow The fixed-point glow value. Valid range is 0 to MAX_GLOW. + * @return the nearest approximate value that we can cram into 8 bits, i.e. scaled to a range of 0 - 255 + */ + inline uint8_t ConstrainGlow(Fixed glow) { + return std::round(std::clamp(ToFloat(glow), 0.0f, MAX_GLOW) * 255 / MAX_GLOW); + } /** * Constrain input values for shininess. diff --git a/src/render/ModernOpenGLRenderer.cpp b/src/render/ModernOpenGLRenderer.cpp index 4c234d5bd..8d910ac78 100644 --- a/src/render/ModernOpenGLRenderer.cpp +++ b/src/render/ModernOpenGLRenderer.cpp @@ -230,6 +230,7 @@ void ModernOpenGLRenderer::ApplyLights() AdjustAmbient(*worldShader, ambientIntensity); worldShader->SetFloat3("ambientColor", ambientRGB); worldShader->SetFloat("maxShininess", MAX_SHININESS_EXP); + worldShader->SetFloat("maxGlow", MAX_GLOW); worldShader->SetBool("lightsActive", true); skyShader->Use(); @@ -618,10 +619,22 @@ void ModernOpenGLRenderer::Draw(OpenGLShader &shader, const CBSPPart &part, floa // Specular! glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (4 * sizeof(uint8_t)))); glEnableVertexAttribArray(2); + + // Glow! + glVertexAttribPointer(3, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (8 * sizeof(uint8_t)))); + glEnableVertexAttribArray(3); + + // Reserved + glVertexAttribPointer(4, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (9 * sizeof(uint8_t)))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(5, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (10 * sizeof(uint8_t)))); + glEnableVertexAttribArray(5); + glVertexAttribPointer(6, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLData), (void *)((3 * sizeof(float)) + (11 * sizeof(uint8_t)))); + glEnableVertexAttribArray(6); // Normal! - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)((3 * sizeof(float)) + (8 * sizeof(uint8_t)))); - glEnableVertexAttribArray(3); + glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)((3 * sizeof(float)) + (12 * sizeof(uint8_t)))); + glEnableVertexAttribArray(7); // Custom, per-object lighting and depth testing! float extraAmbient = ToFloat(part.extraAmbient); @@ -656,6 +669,11 @@ void ModernOpenGLRenderer::Draw(OpenGLShader &shader, const CBSPPart &part, floa glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glDisableVertexAttribArray(4); + glDisableVertexAttribArray(5); + glDisableVertexAttribArray(6); + glDisableVertexAttribArray(7); // Restore previous lighting and depth testing state. if (part.privateAmbient != -1 || extraAmbient > 0) { diff --git a/src/render/OpenGLVertices.cpp b/src/render/OpenGLVertices.cpp index 9636a311e..607d9ee8d 100644 --- a/src/render/OpenGLVertices.cpp +++ b/src/render/OpenGLVertices.cpp @@ -170,6 +170,7 @@ void OpenGLVertices::Append(const CBSPPart &part) vertex.specG = material.GetSpecG(); vertex.specB = material.GetSpecB(); vertex.specS = material.GetShininess(); + vertex.glow = material.GetGlow(); vertex.nx = poly.normal.x; vertex.ny = poly.normal.y; vertex.nz = poly.normal.z; @@ -199,6 +200,7 @@ void OpenGLVertices::Append(const CBSPPart &part) vertex.specG = material.GetSpecG(); vertex.specB = material.GetSpecB(); vertex.specS = material.GetShininess(); + vertex.glow = material.GetGlow(); vertex.nx = -poly.normal.x; vertex.ny = -poly.normal.y; vertex.nz = -poly.normal.z; diff --git a/src/render/OpenGLVertices.h b/src/render/OpenGLVertices.h index 84003e78c..949065ef8 100644 --- a/src/render/OpenGLVertices.h +++ b/src/render/OpenGLVertices.h @@ -19,6 +19,10 @@ struct GLData { uint8_t specG = 0; uint8_t specB = 0; uint8_t specS = 0; + uint8_t glow = 0; + uint8_t reserved1 = 0; + uint8_t reserved2 = 0; + uint8_t reserved3 = 0; float nx = 0.0f; float ny = 0.0f; float nz = 0.0f;