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;