mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-21 04:11:53 +02:00
#23 refactoring of shader system
This commit is contained in:
167
src/cache.h
167
src/cache.h
@@ -11,6 +11,7 @@
|
|||||||
#define FOG_DIST (18 * 1024)
|
#define FOG_DIST (18 * 1024)
|
||||||
#define WATER_FOG_DIST (8 * 1024)
|
#define WATER_FOG_DIST (8 * 1024)
|
||||||
//#define WATER_USE_GRID
|
//#define WATER_USE_GRID
|
||||||
|
#define UNDERWATER_COLOR "#define UNDERWATER_COLOR vec3(0.6, 0.9, 0.9)\n"
|
||||||
|
|
||||||
const char SHADER[] =
|
const char SHADER[] =
|
||||||
#include "shader.glsl"
|
#include "shader.glsl"
|
||||||
@@ -29,46 +30,55 @@ const char GUI[] =
|
|||||||
;
|
;
|
||||||
|
|
||||||
struct ShaderCache {
|
struct ShaderCache {
|
||||||
|
enum Effect { FX_NONE = 0, FX_UNDERWATER = 1, FX_ALPHA_TEST = 2, FX_CLIP_PLANE = 4 };
|
||||||
|
|
||||||
IGame *game;
|
IGame *game;
|
||||||
Shader *shaders[Core::passMAX][Shader::MAX][2][2][2];
|
Shader *shaders[Core::passMAX][Shader::MAX][(FX_UNDERWATER | FX_ALPHA_TEST | FX_CLIP_PLANE) + 1];
|
||||||
|
|
||||||
ShaderCache(IGame *game) : game(game) {
|
ShaderCache(IGame *game) : game(game) {
|
||||||
memset(shaders, 0, sizeof(shaders));
|
memset(shaders, 0, sizeof(shaders));
|
||||||
|
|
||||||
LOG("shader: cache warm up...\n");
|
LOG("shader: cache warm up...\n");
|
||||||
compile(Core::passShadow , Shader::ENTITY, false, false, false);
|
if (Core::settings.shadows)
|
||||||
|
compile(Core::passShadow, Shader::ENTITY, FX_NONE);
|
||||||
|
|
||||||
compile(Core::passAmbient, Shader::ROOM, false, false, false);
|
if (Core::settings.ambient) {
|
||||||
compile(Core::passAmbient, Shader::ROOM, false, true, false);
|
compile(Core::passAmbient, Shader::ROOM, FX_NONE);
|
||||||
compile(Core::passAmbient, Shader::ROOM, false, false, true);
|
compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST);
|
||||||
compile(Core::passAmbient, Shader::ROOM, false, true, true);
|
compile(Core::passAmbient, Shader::ROOM, FX_CLIP_PLANE);
|
||||||
compile(Core::passAmbient, Shader::ROOM, true, false, false);
|
compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE);
|
||||||
compile(Core::passAmbient, Shader::SPRITE, false, true, false);
|
compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER);
|
||||||
compile(Core::passAmbient, Shader::SPRITE, false, true, true);
|
compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE);
|
||||||
|
compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST);
|
||||||
|
compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE);
|
||||||
|
}
|
||||||
|
|
||||||
compile(Core::passCompose, Shader::ROOM, false, false, false);
|
if (Core::settings.water) {
|
||||||
compile(Core::passCompose, Shader::ROOM, false, true, false);
|
compile(Core::passWater, Shader::WATER_MASK, FX_NONE);
|
||||||
compile(Core::passCompose, Shader::ROOM, false, true, true);
|
compile(Core::passWater, Shader::WATER_STEP, FX_NONE);
|
||||||
compile(Core::passCompose, Shader::ROOM, false, false, true);
|
compile(Core::passWater, Shader::WATER_CAUSTICS, FX_NONE);
|
||||||
compile(Core::passCompose, Shader::ROOM, true, false, false);
|
compile(Core::passWater, Shader::WATER_COMPOSE, FX_NONE);
|
||||||
compile(Core::passCompose, Shader::ROOM, true, false, true);
|
compile(Core::passWater, Shader::WATER_DROP, FX_NONE);
|
||||||
compile(Core::passCompose, Shader::ENTITY, false, false, false);
|
}
|
||||||
compile(Core::passCompose, Shader::ENTITY, false, false, true);
|
|
||||||
compile(Core::passCompose, Shader::ENTITY, true, false, false);
|
|
||||||
compile(Core::passCompose, Shader::ENTITY, true, false, true);
|
|
||||||
compile(Core::passCompose, Shader::SPRITE, false, true, false);
|
|
||||||
compile(Core::passCompose, Shader::SPRITE, false, true, true);
|
|
||||||
compile(Core::passCompose, Shader::SPRITE, true, true, false);
|
|
||||||
compile(Core::passCompose, Shader::SPRITE, true, true, true);
|
|
||||||
compile(Core::passCompose, Shader::FLASH, false, true, false);
|
|
||||||
|
|
||||||
compile(Core::passFilter , Shader::FILTER_DOWNSAMPLE, false, false, false);
|
compile(Core::passFilter, Shader::FILTER_DOWNSAMPLE, FX_NONE);
|
||||||
|
|
||||||
compile(Core::passWater , Shader::WATER_MASK, false, false, false);
|
compile(Core::passCompose, Shader::ROOM, FX_NONE);
|
||||||
compile(Core::passWater , Shader::WATER_STEP, false, false, false);
|
compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST);
|
||||||
compile(Core::passWater , Shader::WATER_CAUSTICS, false, false, false);
|
compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE);
|
||||||
compile(Core::passWater , Shader::WATER_COMPOSE, false, false, false);
|
compile(Core::passCompose, Shader::ROOM, FX_CLIP_PLANE);
|
||||||
compile(Core::passWater , Shader::WATER_DROP, false, false, false);
|
compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER);
|
||||||
|
compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE);
|
||||||
|
compile(Core::passCompose, Shader::ENTITY, FX_NONE);
|
||||||
|
compile(Core::passCompose, Shader::ENTITY, FX_CLIP_PLANE);
|
||||||
|
compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER);
|
||||||
|
compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER | FX_CLIP_PLANE);
|
||||||
|
compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST);
|
||||||
|
compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE);
|
||||||
|
compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST);
|
||||||
|
compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST | FX_CLIP_PLANE);
|
||||||
|
compile(Core::passCompose, Shader::FLASH, FX_ALPHA_TEST);
|
||||||
|
compile(Core::passCompose, Shader::MIRROR, FX_NONE);
|
||||||
|
|
||||||
LOG("shader: cache is ready\n");
|
LOG("shader: cache is ready\n");
|
||||||
}
|
}
|
||||||
@@ -76,25 +86,26 @@ struct ShaderCache {
|
|||||||
~ShaderCache() {
|
~ShaderCache() {
|
||||||
for (int pass = 0; pass < Core::passMAX; pass++)
|
for (int pass = 0; pass < Core::passMAX; pass++)
|
||||||
for (int type = 0; type < Shader::MAX; type++)
|
for (int type = 0; type < Shader::MAX; type++)
|
||||||
for (int caustics = 0; caustics < 2; caustics++)
|
for (int fx = 0; fx < sizeof(shaders[Core::passMAX][Shader::MAX]) / sizeof(shaders[Core::passMAX][Shader::MAX][FX_NONE]); fx++)
|
||||||
for (int alphaTest = 0; alphaTest < 2; alphaTest++)
|
delete shaders[pass][type][fx];
|
||||||
for (int clipPlane = 0; clipPlane < 2; clipPlane++)
|
|
||||||
delete shaders[pass][type][caustics][alphaTest][clipPlane];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader* compile(Core::Pass pass, Shader::Type type, bool caustics = false, bool alphaTest = false, bool clipPlane = false) {
|
Shader* compile(Core::Pass pass, Shader::Type type, int fx) {
|
||||||
char def[1024], ext[255];
|
char def[1024], ext[255];
|
||||||
ext[0] = 0;
|
ext[0] = 0;
|
||||||
|
if (Core::settings.shadows) {
|
||||||
if (Core::support.shadowSampler) {
|
if (Core::support.shadowSampler) {
|
||||||
#ifdef MOBILE
|
#ifdef MOBILE
|
||||||
strcat(ext, "#extension GL_EXT_shadow_samplers : require\n");
|
strcat(ext, "#extension GL_EXT_shadow_samplers : require\n");
|
||||||
#endif
|
#endif
|
||||||
strcat(ext, "#define SHADOW_SAMPLER\n");
|
strcat(ext, "#define SHADOW_SAMPLER\n");
|
||||||
} else
|
} else {
|
||||||
if (Core::support.depthTexture)
|
if (Core::support.depthTexture)
|
||||||
strcat(ext, "#define SHADOW_DEPTH\n");
|
strcat(ext, "#define SHADOW_DEPTH\n");
|
||||||
else
|
else
|
||||||
strcat(ext, "#define SHADOW_COLOR\n");
|
strcat(ext, "#define SHADOW_COLOR\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char *passNames[] = { "COMPOSE", "SHADOW", "AMBIENT", "FILTER", "WATER" };
|
const char *passNames[] = { "COMPOSE", "SHADOW", "AMBIENT", "FILTER", "WATER" };
|
||||||
const char *src = NULL;
|
const char *src = NULL;
|
||||||
@@ -109,16 +120,20 @@ struct ShaderCache {
|
|||||||
int animTexRangesCount = game->getMesh()->animTexRangesCount;
|
int animTexRangesCount = game->getMesh()->animTexRangesCount;
|
||||||
int animTexOffsetsCount = game->getMesh()->animTexOffsetsCount;
|
int animTexOffsetsCount = game->getMesh()->animTexOffsetsCount;
|
||||||
sprintf(def, "%s#define PASS_%s\n#define TYPE_%s\n#define MAX_LIGHTS %d\n#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n#define FOG_DIST (1.0/%d.0)\n#define WATER_FOG_DIST (1.0/%d.0)\n", ext, passNames[pass], typ, MAX_LIGHTS, animTexRangesCount, animTexOffsetsCount, FOG_DIST, WATER_FOG_DIST);
|
sprintf(def, "%s#define PASS_%s\n#define TYPE_%s\n#define MAX_LIGHTS %d\n#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n#define FOG_DIST (1.0/%d.0)\n#define WATER_FOG_DIST (1.0/%d.0)\n", ext, passNames[pass], typ, MAX_LIGHTS, animTexRangesCount, animTexOffsetsCount, FOG_DIST, WATER_FOG_DIST);
|
||||||
if (caustics) strcat(def, "#define CAUSTICS\n");
|
if (fx & FX_UNDERWATER) strcat(def, "#define UNDERWATER\n" UNDERWATER_COLOR);
|
||||||
if (alphaTest) strcat(def, "#define ALPHA_TEST\n");
|
if (fx & FX_ALPHA_TEST) strcat(def, "#define ALPHA_TEST\n");
|
||||||
if (clipPlane) strcat(def, "#define CLIP_PLANE\n");
|
if (fx & FX_CLIP_PLANE) strcat(def, "#define CLIP_PLANE\n");
|
||||||
|
if (Core::settings.ambient) strcat(def, "#define OPT_AMBIENT\n");
|
||||||
|
if (Core::settings.lighting) strcat(def, "#define OPT_LIGHTING\n");
|
||||||
|
if (Core::settings.shadows) strcat(def, "#define OPT_SHADOW\n");
|
||||||
|
if (Core::settings.water) strcat(def, "#define OPT_WATER\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Core::passWater : {
|
case Core::passWater : {
|
||||||
static const char *typeNames[] = { "DROP", "STEP", "CAUSTICS", "MASK", "COMPOSE" };
|
static const char *typeNames[] = { "DROP", "STEP", "CAUSTICS", "MASK", "COMPOSE" };
|
||||||
src = WATER;
|
src = WATER;
|
||||||
typ = typeNames[type];
|
typ = typeNames[type];
|
||||||
sprintf(def, "%s#define PASS_%s\n#define WATER_%s\n#define WATER_FOG_DIST (1.0/%d.0)\n", ext, passNames[pass], typ, WATER_FOG_DIST);
|
sprintf(def, "%s#define PASS_%s\n#define WATER_%s\n#define WATER_FOG_DIST (1.0/%d.0)\n" UNDERWATER_COLOR, ext, passNames[pass], typ, WATER_FOG_DIST);
|
||||||
#ifdef WATER_USE_GRID
|
#ifdef WATER_USE_GRID
|
||||||
strcat(def, "#define WATER_USE_GRID\n");
|
strcat(def, "#define WATER_USE_GRID\n");
|
||||||
#endif
|
#endif
|
||||||
@@ -133,17 +148,26 @@ struct ShaderCache {
|
|||||||
}
|
}
|
||||||
default : ASSERT(false);
|
default : ASSERT(false);
|
||||||
}
|
}
|
||||||
LOG("shader: compile %s -> %s %s%s%s\n", passNames[pass], typ, caustics ? "caustics " : "", alphaTest ? "alphaTest " : "", clipPlane ? "clipPlane" : "");
|
LOG("shader: compile %s -> %s %s%s%s\n", passNames[pass], typ, (fx & FX_UNDERWATER) ? "underwater " : "", (fx & FX_ALPHA_TEST) ? "alphaTest " : "", (fx & FX_CLIP_PLANE) ? "clipPlane" : "");
|
||||||
return shaders[pass][type][caustics][alphaTest][clipPlane] = new Shader(src, def);
|
return shaders[pass][type][fx] = new Shader(src, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(Core::Pass pass, Shader::Type type, bool caustics = false, bool alphaTest = false, bool clipPlane = false) {
|
void bind(Core::Pass pass, Shader::Type type, int fx) {
|
||||||
Core::pass = pass;
|
Core::pass = pass;
|
||||||
Shader *shader = shaders[pass][type][caustics][alphaTest][clipPlane];
|
Shader *shader = shaders[pass][type][fx];
|
||||||
if (!shader)
|
if (!shader)
|
||||||
shader = compile(pass, type, caustics, alphaTest, clipPlane);
|
shader = compile(pass, type, fx);
|
||||||
ASSERT(shader != NULL);
|
ASSERT(shader != NULL);
|
||||||
shader->bind();
|
shader->bind();
|
||||||
|
// TODO: bindable uniform block
|
||||||
|
shader->setParam(uViewProj, Core::mViewProj);
|
||||||
|
shader->setParam(uLightProj, Core::mLightProj);
|
||||||
|
shader->setParam(uViewInv, Core::mViewInv);
|
||||||
|
shader->setParam(uViewPos, Core::viewPos);
|
||||||
|
shader->setParam(uParam, Core::params);
|
||||||
|
MeshBuilder *mesh = game->getMesh();
|
||||||
|
shader->setParam(uAnimTexRanges, mesh->animTexRanges[0], mesh->animTexRangesCount);
|
||||||
|
shader->setParam(uAnimTexOffsets, mesh->animTexOffsets[0], mesh->animTexOffsetsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -220,7 +244,7 @@ struct AmbientCache {
|
|||||||
// second pass - downsample it
|
// second pass - downsample it
|
||||||
Core::setDepthTest(false);
|
Core::setDepthTest(false);
|
||||||
|
|
||||||
game->setShader(Core::passFilter, Shader::FILTER_DOWNSAMPLE, false);
|
game->setShader(Core::passFilter, Shader::FILTER_DOWNSAMPLE);
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
int size = 64 >> (i << 1);
|
int size = 64 >> (i << 1);
|
||||||
@@ -291,7 +315,6 @@ struct WaterCache {
|
|||||||
TR::Level *level;
|
TR::Level *level;
|
||||||
Texture *refract;
|
Texture *refract;
|
||||||
Texture *reflect;
|
Texture *reflect;
|
||||||
vec3 color;
|
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
int from, to, caust;
|
int from, to, caust;
|
||||||
@@ -312,6 +335,7 @@ struct WaterCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init(IGame *game) {
|
void init(IGame *game) {
|
||||||
|
|
||||||
TR::Level *level = game->getLevel();
|
TR::Level *level = game->getLevel();
|
||||||
TR::Room &r = level->rooms[to]; // underwater room
|
TR::Room &r = level->rooms[to]; // underwater room
|
||||||
|
|
||||||
@@ -356,21 +380,22 @@ struct WaterCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m[(x - minX) + w * (z - minZ)] = hasWater ? (hasFlow ? 0xFFFF : 0xFF00) : 0;
|
m[(x - minX) + w * (z - minZ)] = hasWater ? (hasFlow ? 0xF81F : 0xF800) : 0;
|
||||||
}
|
}
|
||||||
mask = new Texture(w, h, Texture::RGB16, false, m, false);
|
|
||||||
delete[] m;
|
|
||||||
|
|
||||||
size = vec3(float((maxX - minX) * 512), 1.0f, float((maxZ - minZ) * 512)); // half size
|
size = vec3(float((maxX - minX) * 512), 1.0f, float((maxZ - minZ) * 512)); // half size
|
||||||
pos = vec3(r.info.x + minX * 1024 + size.x, float(posY), r.info.z + minZ * 1024 + size.z);
|
pos = vec3(r.info.x + minX * 1024 + size.x, float(posY), r.info.z + minZ * 1024 + size.z);
|
||||||
|
|
||||||
data[0] = new Texture(w * 64, h * 64, Texture::RGBA_HALF, false);
|
data[0] = new Texture(w * 64, h * 64, Texture::RGBA_HALF, false);
|
||||||
data[1] = new Texture(data[0]->width, data[0]->height, Texture::RGBA_HALF, false);
|
data[1] = new Texture(w * 64, h * 64, Texture::RGBA_HALF, false);
|
||||||
caustics = new Texture(512, 512, Texture::RGB16, false);
|
caustics = new Texture(512, 512, Texture::RGB16, false);
|
||||||
|
mask = new Texture(w, h, Texture::RGB16, false, m, false);
|
||||||
|
delete[] m;
|
||||||
|
|
||||||
blank = false;
|
blank = false;
|
||||||
|
|
||||||
Core::setTarget(data[0], true);
|
// Core::setTarget(data[0], true);
|
||||||
Core::invalidateTarget(false, true);
|
// Core::invalidateTarget(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void free() {
|
void free() {
|
||||||
@@ -394,7 +419,7 @@ struct WaterCache {
|
|||||||
Drop(const vec3 &pos, float radius, float strength) : pos(pos), radius(radius), strength(strength) {}
|
Drop(const vec3 &pos, float radius, float strength) : pos(pos), radius(radius), strength(strength) {}
|
||||||
} drops[MAX_DROPS];
|
} drops[MAX_DROPS];
|
||||||
|
|
||||||
WaterCache(IGame *game) : game(game), level(game->getLevel()), refract(NULL), color(0.6f, 0.9f, 0.9f), count(0), checkVisibility(false), dropCount(0) {
|
WaterCache(IGame *game) : game(game), level(game->getLevel()), refract(NULL), count(0), checkVisibility(false), dropCount(0) {
|
||||||
reflect = new Texture(512, 512, Texture::RGB16, false);
|
reflect = new Texture(512, 512, Texture::RGB16, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +519,7 @@ struct WaterCache {
|
|||||||
void drop(Item &item) {
|
void drop(Item &item) {
|
||||||
if (!dropCount) return;
|
if (!dropCount) return;
|
||||||
|
|
||||||
game->setShader(Core::passWater, Shader::WATER_DROP, false);
|
game->setShader(Core::passWater, Shader::WATER_DROP);
|
||||||
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 1.0f, 1.0f));
|
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 1.0f, 1.0f));
|
||||||
|
|
||||||
vec3 rPosScale[2] = { vec3(0.0f), vec3(1.0f) };
|
vec3 rPosScale[2] = { vec3(0.0f), vec3(1.0f) };
|
||||||
@@ -520,7 +545,7 @@ struct WaterCache {
|
|||||||
void step(Item &item) {
|
void step(Item &item) {
|
||||||
if (item.timer < SIMULATE_TIMESTEP) return;
|
if (item.timer < SIMULATE_TIMESTEP) return;
|
||||||
|
|
||||||
game->setShader(Core::passWater, Shader::WATER_STEP, false);
|
game->setShader(Core::passWater, Shader::WATER_STEP);
|
||||||
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 1.0f, 1.0f));
|
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 1.0f, 1.0f));
|
||||||
Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, 0));
|
Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, 0));
|
||||||
|
|
||||||
@@ -537,7 +562,7 @@ struct WaterCache {
|
|||||||
|
|
||||||
|
|
||||||
// calc caustics
|
// calc caustics
|
||||||
game->setShader(Core::passWater, Shader::WATER_CAUSTICS, false);
|
game->setShader(Core::passWater, Shader::WATER_CAUSTICS);
|
||||||
vec3 rPosScale[2] = { vec3(0.0f), vec3(1.0f / PLANE_DETAIL) };
|
vec3 rPosScale[2] = { vec3(0.0f), vec3(1.0f / PLANE_DETAIL) };
|
||||||
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
|
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
|
||||||
|
|
||||||
@@ -557,11 +582,19 @@ struct WaterCache {
|
|||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
|
|
||||||
// mask underwater geometry by zero alpha
|
// mask underwater geometry by zero alpha
|
||||||
game->setShader(Core::passWater, Shader::WATER_MASK, false);
|
for (int i = 0; i < count; i++) {
|
||||||
|
Item &item = items[i];
|
||||||
|
if (item.visible && item.blank)
|
||||||
|
item.init(game);
|
||||||
|
}
|
||||||
|
Core::setTarget(NULL);
|
||||||
|
|
||||||
|
game->setShader(Core::passWater, Shader::WATER_MASK);
|
||||||
Core::active.shader->setParam(uTexParam, vec4(1.0f));
|
Core::active.shader->setParam(uTexParam, vec4(1.0f));
|
||||||
Core::setCulling(cfNone);
|
|
||||||
Core::setDepthWrite(false);
|
|
||||||
Core::setColorWrite(false, false, false, true);
|
Core::setColorWrite(false, false, false, true);
|
||||||
|
Core::setDepthWrite(false);
|
||||||
|
Core::setCulling(cfNone);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
Item &item = items[i];
|
Item &item = items[i];
|
||||||
@@ -587,11 +620,6 @@ struct WaterCache {
|
|||||||
Item &item = items[i];
|
Item &item = items[i];
|
||||||
if (!item.visible) continue;
|
if (!item.visible) continue;
|
||||||
|
|
||||||
if (item.blank) {
|
|
||||||
item.init(game);
|
|
||||||
item.blank = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// render mirror reflection
|
// render mirror reflection
|
||||||
Core::setTarget(reflect, true);
|
Core::setTarget(reflect, true);
|
||||||
vec3 p = item.pos;
|
vec3 p = item.pos;
|
||||||
@@ -639,13 +667,10 @@ struct WaterCache {
|
|||||||
Core::lightColor[0] = vec4(lum, lum, lum, float(light.radius) * float(light.radius));
|
Core::lightColor[0] = vec4(lum, lum, lum, float(light.radius) * float(light.radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
game->setShader(Core::passWater, Shader::WATER_COMPOSE, false);
|
game->setShader(Core::passWater, Shader::WATER_COMPOSE);
|
||||||
Core::active.shader->setParam(uViewProj, Core::mViewProj);
|
|
||||||
Core::active.shader->setParam(uViewPos, Core::viewPos);
|
|
||||||
Core::active.shader->setParam(uLightPos, Core::lightPos[0], 1);
|
Core::active.shader->setParam(uLightPos, Core::lightPos[0], 1);
|
||||||
Core::active.shader->setParam(uLightColor, Core::lightColor[0], 1);
|
Core::active.shader->setParam(uLightColor, Core::lightColor[0], 1);
|
||||||
Core::active.shader->setParam(uParam, vec4(float(Core::width) / refract->width, float(Core::height) / refract->height, 0.05f, 0.02f));
|
Core::active.shader->setParam(uParam, vec4(float(Core::width) / refract->width, float(Core::height) / refract->height, 0.05f, 0.02f));
|
||||||
Core::active.shader->setParam(uColor, vec4(color * 0.2f, 1.0f));
|
|
||||||
|
|
||||||
float sx = item.size.x * DETAIL / (item.data[0]->width / 2);
|
float sx = item.size.x * DETAIL / (item.data[0]->width / 2);
|
||||||
float sz = item.size.z * DETAIL / (item.data[0]->height / 2);
|
float sz = item.size.z * DETAIL / (item.data[0]->height / 2);
|
||||||
@@ -659,7 +684,7 @@ struct WaterCache {
|
|||||||
#ifdef WATER_USE_GRID
|
#ifdef WATER_USE_GRID
|
||||||
vec3 rPosScale[2] = { item.pos, item.size * vec3(1.0f / PLANE_DETAIL, 512.0f, 1.0f / PLANE_DETAIL) };
|
vec3 rPosScale[2] = { item.pos, item.size * vec3(1.0f / PLANE_DETAIL, 512.0f, 1.0f / PLANE_DETAIL) };
|
||||||
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
|
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
|
||||||
level->mesh->renderPlane();
|
game->getMesh()->renderPlane();
|
||||||
#else
|
#else
|
||||||
Core::active.shader->setParam(uPosScale, item.pos, 2);
|
Core::active.shader->setParam(uPosScale, item.pos, 2);
|
||||||
game->getMesh()->renderQuad();
|
game->getMesh()->renderQuad();
|
||||||
@@ -716,4 +741,6 @@ struct LightCache {
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#undef UNDERWATER_COLOR
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -161,7 +161,7 @@ struct Camera : Controller {
|
|||||||
if (Input::down[ikS]) v = v - d;
|
if (Input::down[ikS]) v = v - d;
|
||||||
if (Input::down[ikA]) v = v + d.cross(vec3(0, 1, 0));
|
if (Input::down[ikA]) v = v + d.cross(vec3(0, 1, 0));
|
||||||
if (Input::down[ikD]) v = v - d.cross(vec3(0, 1, 0));
|
if (Input::down[ikD]) v = v - d.cross(vec3(0, 1, 0));
|
||||||
pos = pos + v.normal() * (Core::deltaTime * 512.0 * 10.0f);
|
pos = pos + v.normal() * (Core::deltaTime * 512.0f * 10.0f);
|
||||||
|
|
||||||
mViewInv.identity();
|
mViewInv.identity();
|
||||||
mViewInv.translate(pos);
|
mViewInv.translate(pos);
|
||||||
|
@@ -24,7 +24,7 @@ struct IGame {
|
|||||||
virtual void setWaterParams(float height) {}
|
virtual void setWaterParams(float height) {}
|
||||||
virtual void updateParams() {}
|
virtual void updateParams() {}
|
||||||
virtual void waterDrop(const vec3 &pos, float radius, float strength) {}
|
virtual void waterDrop(const vec3 &pos, float radius, float strength) {}
|
||||||
virtual void setShader(Core::Pass pass, Shader::Type type, bool caustics = false, bool alphaTest = false) {}
|
virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {}
|
||||||
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
||||||
virtual void renderCompose(int roomIndex) {}
|
virtual void renderCompose(int roomIndex) {}
|
||||||
};
|
};
|
||||||
@@ -487,8 +487,8 @@ struct Controller {
|
|||||||
return matrix;
|
return matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void renderShadow(MeshBuilder *mesh, const vec3 &pos, const vec3 &offset, const vec3 &size, float angle) {
|
void renderShadow(MeshBuilder *mesh, const vec3 &pos, const vec3 &offset, const vec3 &size, float angle) {
|
||||||
|
/*
|
||||||
mat4 m;
|
mat4 m;
|
||||||
m.identity();
|
m.identity();
|
||||||
m.translate(pos);
|
m.translate(pos);
|
||||||
@@ -496,12 +496,36 @@ struct Controller {
|
|||||||
m.translate(vec3(offset.x, 0.0f, offset.z));
|
m.translate(vec3(offset.x, 0.0f, offset.z));
|
||||||
m.scale(vec3(size.x, 0.0f, size.z) * (1.0f / 1024.0f));
|
m.scale(vec3(size.x, 0.0f, size.z) * (1.0f / 1024.0f));
|
||||||
|
|
||||||
Core::active.shader->setParam(uModel, m);
|
game->setShader(Core::pass, Shader::FLASH, false, false);
|
||||||
Core::active.shader->setParam(uColor, vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
Core::active.shader->setParam(uBasis, Basis(m));
|
||||||
|
Core::active.shader->setParam(uColor, vec4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||||
Core::active.shader->setParam(uAmbient, vec3(0.0f));
|
Core::active.shader->setParam(uAmbient, vec3(0.0f));
|
||||||
|
|
||||||
|
Core::setBlending(bmMultiply);
|
||||||
mesh->renderShadowSpot();
|
mesh->renderShadowSpot();
|
||||||
}
|
Core::setBlending(bmNone);
|
||||||
*/
|
*/
|
||||||
|
mat4 m = Core::mViewProj;
|
||||||
|
m.translate(pos);
|
||||||
|
m.rotateY(angle);
|
||||||
|
m.translate(vec3(offset.x, 0.0f, offset.z));
|
||||||
|
m.scale(vec3(size.x, 0.0f, size.z) * (1.0f / 1024.0f));
|
||||||
|
|
||||||
|
Basis b;
|
||||||
|
b.identity();
|
||||||
|
|
||||||
|
game->setShader(Core::pass, Shader::FLASH, false, false);
|
||||||
|
Core::active.shader->setParam(uViewProj, m);
|
||||||
|
Core::active.shader->setParam(uBasis, b);
|
||||||
|
Core::active.shader->setParam(uMaterial, vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||||
|
Core::active.shader->setParam(uAmbient, vec3(0.0f));
|
||||||
|
|
||||||
|
Core::setBlending(bmAlpha);
|
||||||
|
mesh->renderShadowSpot();
|
||||||
|
Core::setBlending(bmNone);
|
||||||
|
|
||||||
|
Core::active.shader->setParam(uViewProj, Core::mViewProj);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO: animation.calcJoints
|
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO: animation.calcJoints
|
||||||
mat4 matrix = getMatrix();
|
mat4 matrix = getMatrix();
|
||||||
@@ -540,13 +564,12 @@ struct Controller {
|
|||||||
|
|
||||||
frameIndex = Core::frameIndex;
|
frameIndex = Core::frameIndex;
|
||||||
|
|
||||||
/* // blob shadow // TODO: fake AO
|
// blob shadow // TODO: fake AO
|
||||||
if (TR::castShadow(entity.type)) {
|
if (!Core::settings.shadows && Core::pass == Core::passCompose && TR::castShadow(entity.type)) {
|
||||||
TR::Level::FloorInfo info;
|
TR::Level::FloorInfo info;
|
||||||
level->getFloorInfo(entity.room, entity.x, entity.y, entity.z, info);
|
level->getFloorInfo(entity.room, entity.x, entity.y, entity.z, info);
|
||||||
renderShadow(mesh, vec3(float(entity.x), info.floor - 16.0f, float(entity.z)), box.center(), box.size() * 0.8f, angle.y);
|
renderShadow(mesh, vec3(float(entity.x), info.floor - 16.0f, float(entity.z)), box.center(), box.size() * 0.8f, angle.y);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
56
src/core.h
56
src/core.h
@@ -183,6 +183,9 @@ struct Texture;
|
|||||||
#define PROFILE_LABEL(id, name, label)
|
#define PROFILE_LABEL(id, name, label)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum CullMode { cfNone, cfBack, cfFront };
|
||||||
|
enum BlendMode { bmNone, bmAlpha, bmAdd, bmMultiply, bmScreen };
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
int width, height;
|
int width, height;
|
||||||
int frameIndex;
|
int frameIndex;
|
||||||
@@ -192,7 +195,7 @@ namespace Core {
|
|||||||
vec3 viewPos;
|
vec3 viewPos;
|
||||||
vec3 lightPos[MAX_LIGHTS];
|
vec3 lightPos[MAX_LIGHTS];
|
||||||
vec4 lightColor[MAX_LIGHTS];
|
vec4 lightColor[MAX_LIGHTS];
|
||||||
vec4 color;
|
vec4 params;
|
||||||
|
|
||||||
Texture *blackTex, *whiteTex;
|
Texture *blackTex, *whiteTex;
|
||||||
|
|
||||||
@@ -212,7 +215,10 @@ namespace Core {
|
|||||||
Shader *shader;
|
Shader *shader;
|
||||||
Texture *textures[8];
|
Texture *textures[8];
|
||||||
Texture *target;
|
Texture *target;
|
||||||
|
int targetFace;
|
||||||
GLuint VAO;
|
GLuint VAO;
|
||||||
|
BlendMode blendMode;
|
||||||
|
CullMode cullMode;
|
||||||
} active;
|
} active;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -230,15 +236,19 @@ namespace Core {
|
|||||||
bool texHalf, texHalfLinear;
|
bool texHalf, texHalfLinear;
|
||||||
bool shaderBinary;
|
bool shaderBinary;
|
||||||
} support;
|
} support;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool ambient;
|
||||||
|
bool lighting;
|
||||||
|
bool shadows;
|
||||||
|
bool water;
|
||||||
|
} settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
|
||||||
enum CullMode { cfNone, cfBack, cfFront };
|
|
||||||
enum BlendMode { bmNone, bmAlpha, bmAdd, bmMultiply, bmScreen };
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
bool extSupport(const char *str, const char *ext) {
|
bool extSupport(const char *str, const char *ext) {
|
||||||
@@ -353,10 +363,10 @@ namespace Core {
|
|||||||
|
|
||||||
frameIndex = 0;
|
frameIndex = 0;
|
||||||
|
|
||||||
uint32 data = 0xFF000000;
|
uint32 data = 0x00000000;
|
||||||
blackTex = new Texture(1, 1, Texture::RGBA, false, &data);
|
blackTex = new Texture(1, 1, Texture::RGBA, false, &data, false);
|
||||||
data = 0xFFFFFFFF;
|
data = 0xFFFFFFFF;
|
||||||
whiteTex = new Texture(1, 1, Texture::RGBA, false, &data);
|
whiteTex = new Texture(1, 1, Texture::RGBA, false, &data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void free() {
|
void free() {
|
||||||
@@ -396,17 +406,23 @@ namespace Core {
|
|||||||
return cache.count++;
|
return cache.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear(bool clearColor, bool clearDepth, const vec4 &color) {
|
void clear(bool clearColor, bool clearDepth) {
|
||||||
glClearColor(color.x, color.y, color.z, color.w);
|
|
||||||
if (GLbitfield mask = (clearColor ? GL_COLOR_BUFFER_BIT : 0) | (clearDepth ? GL_DEPTH_BUFFER_BIT : 0))
|
if (GLbitfield mask = (clearColor ? GL_COLOR_BUFFER_BIT : 0) | (clearDepth ? GL_DEPTH_BUFFER_BIT : 0))
|
||||||
glClear(mask);
|
glClear(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setClearColor(const vec4 &color) {
|
||||||
|
glClearColor(color.x, color.y, color.z, color.w);
|
||||||
|
}
|
||||||
|
|
||||||
void setViewport(int x, int y, int width, int height) {
|
void setViewport(int x, int y, int width, int height) {
|
||||||
glViewport(x, y, width, height);
|
glViewport(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCulling(CullMode mode) {
|
void setCulling(CullMode mode) {
|
||||||
|
if (active.cullMode == mode)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case cfNone :
|
case cfNone :
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
@@ -418,11 +434,16 @@ namespace Core {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode != cfNone)
|
if (mode != cfNone && active.cullMode == cfNone)
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
|
|
||||||
|
active.cullMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBlending(BlendMode mode) {
|
void setBlending(BlendMode mode) {
|
||||||
|
if (active.blendMode == mode)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case bmNone :
|
case bmNone :
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
@@ -441,8 +462,10 @@ namespace Core {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode != bmNone)
|
if (mode != bmNone && active.blendMode == bmNone)
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
|
|
||||||
|
active.blendMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setColorWrite(bool r, bool g, bool b, bool a) {
|
void setColorWrite(bool r, bool g, bool b, bool a) {
|
||||||
@@ -473,6 +496,9 @@ namespace Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setTarget(Texture *target, bool clear = false, int face = 0) {
|
void setTarget(Texture *target, bool clear = false, int face = 0) {
|
||||||
|
if (target == active.target && face == active.targetFace)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glColorMask(true, true, true, true);
|
glColorMask(true, true, true, true);
|
||||||
@@ -493,10 +519,12 @@ namespace Core {
|
|||||||
glColorMask(false, false, false, false);
|
glColorMask(false, false, false, false);
|
||||||
setViewport(0, 0, target->width, target->height);
|
setViewport(0, 0, target->width, target->height);
|
||||||
}
|
}
|
||||||
active.target = target;
|
|
||||||
|
|
||||||
if (clear)
|
if (clear)
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
|
||||||
|
active.target = target;
|
||||||
|
active.targetFace = face;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyTarget(Texture *texture, int xOffset, int yOffset, int x, int y, int width, int height) {
|
void copyTarget(Texture *texture, int xOffset, int yOffset, int x, int y, int width, int height) {
|
||||||
@@ -506,7 +534,9 @@ namespace Core {
|
|||||||
|
|
||||||
void resetStates() {
|
void resetStates() {
|
||||||
memset(&active, 0, sizeof(active));
|
memset(&active, 0, sizeof(active));
|
||||||
glEnable(GL_DEPTH_TEST);
|
setDepthTest(true);
|
||||||
|
active.blendMode = bmAlpha;
|
||||||
|
active.cullMode = cfNone;
|
||||||
setCulling(cfFront);
|
setCulling(cfFront);
|
||||||
setBlending(bmNone);
|
setBlending(bmNone);
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,12 @@ namespace Game {
|
|||||||
|
|
||||||
void init(Stream *lvl, Stream *snd) {
|
void init(Stream *lvl, Stream *snd) {
|
||||||
Core::init();
|
Core::init();
|
||||||
|
|
||||||
|
Core::settings.ambient = true;
|
||||||
|
Core::settings.lighting = true;
|
||||||
|
Core::settings.shadows = true;
|
||||||
|
Core::settings.water = Core::support.texFloat || Core::support.texHalf;
|
||||||
|
|
||||||
level = NULL;
|
level = NULL;
|
||||||
startLevel(lvl, snd, false, false);
|
startLevel(lvl, snd, false, false);
|
||||||
}
|
}
|
||||||
|
@@ -213,6 +213,7 @@ struct Lara : Character {
|
|||||||
Inventory inventory;
|
Inventory inventory;
|
||||||
int lastPickUp;
|
int lastPickUp;
|
||||||
int viewTarget;
|
int viewTarget;
|
||||||
|
int roomPrev; // water out from room
|
||||||
|
|
||||||
struct Braid {
|
struct Braid {
|
||||||
Lara *lara;
|
Lara *lara;
|
||||||
@@ -405,7 +406,7 @@ struct Lara : Character {
|
|||||||
|
|
||||||
if (level->extra.braid > -1)
|
if (level->extra.braid > -1)
|
||||||
braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f));
|
braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f));
|
||||||
//reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool)
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
//reset(14, vec3(40448, 3584, 60928), PI * 0.5f, true); // gym (pool)
|
//reset(14, vec3(40448, 3584, 60928), PI * 0.5f, true); // gym (pool)
|
||||||
|
|
||||||
@@ -1063,6 +1064,7 @@ struct Lara : Character {
|
|||||||
|
|
||||||
if (h >= 0 && h <= (256 + 128) && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) {
|
if (h >= 0 && h <= (256 + 128) && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) {
|
||||||
alignToWall(LARA_RADIUS);
|
alignToWall(LARA_RADIUS);
|
||||||
|
roomPrev = getRoomIndex();
|
||||||
getEntity().room = roomAbove;
|
getEntity().room = roomAbove;
|
||||||
pos.y = float(info.floor);
|
pos.y = float(info.floor);
|
||||||
specular = LARA_WET_SPECULAR;
|
specular = LARA_WET_SPECULAR;
|
||||||
@@ -1555,6 +1557,7 @@ struct Lara : Character {
|
|||||||
|
|
||||||
if (state != STATE_SURF_TREAD && state != STATE_SURF_LEFT && state != STATE_SURF_RIGHT && state != STATE_SURF_SWIM && state != STATE_SURF_BACK && state != STATE_STOP) {
|
if (state != STATE_SURF_TREAD && state != STATE_SURF_LEFT && state != STATE_SURF_RIGHT && state != STATE_SURF_SWIM && state != STATE_SURF_BACK && state != STATE_STOP) {
|
||||||
game->waterDrop(pos, 128.0f, 0.2f);
|
game->waterDrop(pos, 128.0f, 0.2f);
|
||||||
|
specular = LARA_WET_SPECULAR;
|
||||||
return animation.setAnim(ANIM_TO_ONWATER);
|
return animation.setAnim(ANIM_TO_ONWATER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2011,7 +2014,7 @@ struct Lara : Character {
|
|||||||
Basis b(basis);
|
Basis b(basis);
|
||||||
b.rotate(quat(vec3(1, 0, 0), -PI * 0.5f));
|
b.rotate(quat(vec3(1, 0, 0), -PI * 0.5f));
|
||||||
b.translate(offset);
|
b.translate(offset);
|
||||||
Core::active.shader->setParam(uColor, vec4(lum, lum, lum, alpha));
|
Core::active.shader->setParam(uMaterial, vec4(lum, 0.0f, 0.0f, alpha));
|
||||||
Core::active.shader->setParam(uBasis, b);
|
Core::active.shader->setParam(uBasis, b);
|
||||||
mesh->renderModel(level->extra.muzzleFlash);
|
mesh->renderModel(level->extra.muzzleFlash);
|
||||||
}
|
}
|
||||||
|
187
src/level.h
187
src/level.h
@@ -24,12 +24,12 @@ struct Level : IGame {
|
|||||||
Camera *camera;
|
Camera *camera;
|
||||||
Texture *shadow;
|
Texture *shadow;
|
||||||
|
|
||||||
struct {
|
struct Params {
|
||||||
float time;
|
float time;
|
||||||
float waterHeight;
|
float waterHeight;
|
||||||
float clipSign;
|
float clipSign;
|
||||||
float clipHeight;
|
float clipHeight;
|
||||||
} params;
|
} *params = (Params*)&Core::params;
|
||||||
|
|
||||||
ShaderCache *shaderCache;
|
ShaderCache *shaderCache;
|
||||||
AmbientCache *ambientCache;
|
AmbientCache *ambientCache;
|
||||||
@@ -49,24 +49,25 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void setClipParams(float clipSign, float clipHeight) {
|
virtual void setClipParams(float clipSign, float clipHeight) {
|
||||||
params.clipSign = clipSign;
|
params->clipSign = clipSign;
|
||||||
params.clipHeight = clipHeight;
|
params->clipHeight = clipHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setWaterParams(float height) {
|
virtual void setWaterParams(float height) {
|
||||||
params.waterHeight = height;
|
params->waterHeight = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void updateParams() {
|
virtual void updateParams() {
|
||||||
Core::active.shader->setParam(uParam, *((vec4*)¶ms));
|
Core::active.shader->setParam(uParam, Core::params);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void waterDrop(const vec3 &pos, float radius, float strength) {
|
virtual void waterDrop(const vec3 &pos, float radius, float strength) {
|
||||||
|
if (waterCache)
|
||||||
waterCache->addDrop(pos, radius, strength);
|
waterCache->addDrop(pos, radius, strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setShader(Core::Pass pass, Shader::Type type, bool caustics = false, bool alphaTest = false) {
|
virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {
|
||||||
shaderCache->bind(pass, type, caustics, alphaTest, params.clipHeight != NO_CLIP_PLANE);
|
shaderCache->bind(pass, type, (underwater ? ShaderCache::FX_UNDERWATER : 0) | (alphaTest ? ShaderCache::FX_ALPHA_TEST : 0) | (params->clipHeight != NO_CLIP_PLANE ? ShaderCache::FX_CLIP_PLANE : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {
|
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {
|
||||||
@@ -88,13 +89,14 @@ struct Level : IGame {
|
|||||||
|
|
||||||
Core::setDepthTest(true);
|
Core::setDepthTest(true);
|
||||||
Core::setDepthWrite(true);
|
Core::setDepthWrite(true);
|
||||||
|
if (shadow)
|
||||||
shadow->bind(sShadow);
|
shadow->bind(sShadow);
|
||||||
renderScene(roomIndex);
|
renderScene(roomIndex);
|
||||||
}
|
}
|
||||||
//==============================
|
//==============================
|
||||||
|
|
||||||
Level(Stream &stream, bool demo, bool home) : level(stream, demo), lara(NULL) {
|
Level(Stream &stream, bool demo, bool home) : level(stream, demo), lara(NULL) {
|
||||||
params.time = 0.0f;
|
params->time = 0.0f;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
Debug::init();
|
Debug::init();
|
||||||
@@ -204,8 +206,9 @@ struct Level : IGame {
|
|||||||
|
|
||||||
level.cameraController = camera;
|
level.cameraController = camera;
|
||||||
|
|
||||||
ambientCache = new AmbientCache(this);
|
ambientCache = Core::settings.ambient ? new AmbientCache(this) : NULL;
|
||||||
waterCache = new WaterCache(this);
|
waterCache = Core::settings.water ? new WaterCache(this) : NULL;
|
||||||
|
shadow = Core::settings.shadows ? new Texture(1024, 1024, Texture::SHADOW, false) : NULL;
|
||||||
|
|
||||||
initReflections();
|
initReflections();
|
||||||
for (int i = 0; i < level.soundSourcesCount; i++) {
|
for (int i = 0; i < level.soundSourcesCount; i++) {
|
||||||
@@ -236,8 +239,6 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initTextures() {
|
void initTextures() {
|
||||||
shadow = new Texture(1024, 1024, Texture::SHADOW, false);
|
|
||||||
|
|
||||||
if (!level.tilesCount) {
|
if (!level.tilesCount) {
|
||||||
atlas = NULL;
|
atlas = NULL;
|
||||||
return;
|
return;
|
||||||
@@ -327,95 +328,28 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LEVEL_EDITOR
|
void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) {
|
||||||
struct Light {
|
|
||||||
vec3 pos;
|
|
||||||
float radius;
|
|
||||||
float intensity;
|
|
||||||
} lights[255];
|
|
||||||
int lightsCount = 0;
|
|
||||||
int lightIntensity = 0.5f;
|
|
||||||
float lightRadius = 4096.0f;
|
|
||||||
|
|
||||||
int findLight(const vec3 &pos, float maxDist) const {
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < lightsCount; i++) {
|
|
||||||
float d = (lights[i].pos - pos).length();
|
|
||||||
if (d < maxDist) {
|
|
||||||
maxDist = d;
|
|
||||||
index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addLight(const vec3 &pos, float radius) {
|
|
||||||
int index = findLight(pos, 1024.0f);
|
|
||||||
if (index > -1) {
|
|
||||||
lightRadius = lights[index].radius;
|
|
||||||
lightIntensity = lights[index].intensity;
|
|
||||||
} else
|
|
||||||
index = lightsCount++;
|
|
||||||
|
|
||||||
lights[index].pos = pos;
|
|
||||||
lights[index].radius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeLight(const vec3 &pos) {
|
|
||||||
int index = findLight(pos, 1024.0f);
|
|
||||||
if (index > -1)
|
|
||||||
lights[index] = lights[--lightsCount];
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateLight(const vec3 &pos, float addRadius, float addIntensity) {
|
|
||||||
int index = findLight(pos, 1024.0f);
|
|
||||||
if (index > -1) {
|
|
||||||
lights[index].radius = max(lights[index].radius + addRadius, 256.0f);
|
|
||||||
lights[index].intensity = clamp(lights[index].intensity + addIntensity, 0.0f, 1.0f);
|
|
||||||
lightRadius = lights[index].radius;
|
|
||||||
lightIntensity = lights[index].intensity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateEditor() {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderEditor() {
|
|
||||||
#ifdef _DEBUG
|
|
||||||
Debug::begin();
|
|
||||||
|
|
||||||
Debug::end();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setRoomParams(int roomIndex, float intensity, Shader::Type type, bool alphaTest = false) {
|
|
||||||
if (Core::pass == Core::passShadow) {
|
if (Core::pass == Core::passShadow) {
|
||||||
setShader(Core::pass, type, false);
|
setShader(Core::pass, type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TR::Room &room = level.rooms[roomIndex];
|
TR::Room &room = level.rooms[roomIndex];
|
||||||
|
|
||||||
|
if (room.flags.water)
|
||||||
|
setWaterParams(float(room.info.yTop));
|
||||||
|
else
|
||||||
|
setWaterParams(NO_CLIP_PLANE);
|
||||||
|
|
||||||
setShader(Core::pass, type, room.flags.water, alphaTest);
|
setShader(Core::pass, type, room.flags.water, alphaTest);
|
||||||
|
|
||||||
if (room.flags.water) {
|
if (room.flags.water) {
|
||||||
Core::color = vec4(waterCache->color, intensity);
|
if (waterCache)
|
||||||
/*
|
|
||||||
// trace to water surface room
|
|
||||||
int wrIndex = roomIndex;
|
|
||||||
|
|
||||||
room.sectors[sx * room.zSectors + sz];
|
|
||||||
|
|
||||||
int sx = room.xSectors
|
|
||||||
*/
|
|
||||||
waterCache->bindCaustics(roomIndex);
|
waterCache->bindCaustics(roomIndex);
|
||||||
} else
|
setWaterParams(float(room.info.yTop));
|
||||||
Core::color = vec4(1.0f, 1.0f, 1.0f, intensity);
|
}
|
||||||
|
|
||||||
Core::active.shader->setParam(uColor, Core::color);
|
Core::active.shader->setParam(uMaterial, vec4(diffuse, ambient, specular, alpha));
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderRoom(int roomIndex, int from = TR::NO_ROOM) {
|
void renderRoom(int roomIndex, int from = TR::NO_ROOM) {
|
||||||
@@ -427,7 +361,7 @@ struct Level : IGame {
|
|||||||
|
|
||||||
// room geometry & sprites
|
// room geometry & sprites
|
||||||
if (!room.flags.rendered) { // skip if already rendered
|
if (!room.flags.rendered) { // skip if already rendered
|
||||||
if (room.flags.water)
|
if (waterCache && room.flags.water)
|
||||||
waterCache->setVisible(roomIndex);
|
waterCache->setVisible(roomIndex);
|
||||||
|
|
||||||
room.flags.rendered = true;
|
room.flags.rendered = true;
|
||||||
@@ -445,7 +379,7 @@ struct Level : IGame {
|
|||||||
|
|
||||||
Core::setBlending(transp ? bmAlpha : bmNone);
|
Core::setBlending(transp ? bmAlpha : bmNone);
|
||||||
|
|
||||||
setRoomParams(roomIndex, intensityf(room.ambient), Shader::ROOM, transp > 0);
|
setRoomParams(roomIndex, Shader::ROOM, 1.0f, intensityf(room.ambient), 0.0f, 1.0f, transp > 0);
|
||||||
Shader *sh = Core::active.shader;
|
Shader *sh = Core::active.shader;
|
||||||
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
@@ -458,7 +392,7 @@ struct Level : IGame {
|
|||||||
// render room sprites
|
// render room sprites
|
||||||
if (range.sprites.iCount) {
|
if (range.sprites.iCount) {
|
||||||
Core::setBlending(bmAlpha);
|
Core::setBlending(bmAlpha);
|
||||||
setRoomParams(roomIndex, 1.0, Shader::SPRITE, true);
|
setRoomParams(roomIndex, Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, true);
|
||||||
Shader *sh = Core::active.shader;
|
Shader *sh = Core::active.shader;
|
||||||
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
@@ -494,7 +428,7 @@ struct Level : IGame {
|
|||||||
|
|
||||||
frustum = *camFrustum;
|
frustum = *camFrustum;
|
||||||
if (frustum.clipByPortal(v, 4, p.normal)) {
|
if (frustum.clipByPortal(v, 4, p.normal)) {
|
||||||
if ((level.rooms[roomIndex].flags.water ^ level.rooms[p.roomIndex].flags.water) && v[0].y == v[1].y && v[0].y == v[2].y)
|
if (waterCache &&(level.rooms[roomIndex].flags.water ^ level.rooms[p.roomIndex].flags.water) && v[0].y == v[1].y && v[0].y == v[2].y)
|
||||||
waterCache->setVisible(roomIndex, p.roomIndex);
|
waterCache->setVisible(roomIndex, p.roomIndex);
|
||||||
renderRoom(p.roomIndex, roomIndex);
|
renderRoom(p.roomIndex, roomIndex);
|
||||||
}
|
}
|
||||||
@@ -556,6 +490,7 @@ struct Level : IGame {
|
|||||||
|
|
||||||
Controller *controller = (Controller*)entity.controller;
|
Controller *controller = (Controller*)entity.controller;
|
||||||
|
|
||||||
|
|
||||||
TR::Room &room = level.rooms[entity.room];
|
TR::Room &room = level.rooms[entity.room];
|
||||||
if (!room.flags.rendered || entity.flags.invisible || entity.flags.rendered)
|
if (!room.flags.rendered || entity.flags.invisible || entity.flags.rendered)
|
||||||
return;
|
return;
|
||||||
@@ -566,25 +501,29 @@ struct Level : IGame {
|
|||||||
if (entity.type == TR::Entity::CRYSTAL)
|
if (entity.type == TR::Entity::CRYSTAL)
|
||||||
type = Shader::MIRROR;
|
type = Shader::MIRROR;
|
||||||
|
|
||||||
setRoomParams(entity.room, isModel ? controller->specular : intensityf(lum), type, !isModel);
|
int roomIndex = entity.room;
|
||||||
|
// if (entity.type == TR::Entity::LARA && ((Lara*)entity.controller)->state == Lara::STATE_WATER_OUT)
|
||||||
|
// roomIndex = ((Lara*)entity.controller)->roomPrev;
|
||||||
|
|
||||||
|
setRoomParams(roomIndex, type, 1.0f, intensityf(lum), controller->specular, 1.0f, isModel ? mesh->models[entity.modelIndex].opaque : true);
|
||||||
|
|
||||||
if (isModel) { // model
|
if (isModel) { // model
|
||||||
vec3 pos = controller->getPos();
|
vec3 pos = controller->getPos();
|
||||||
|
if (Core::settings.ambient) {
|
||||||
AmbientCache::Cube cube;
|
AmbientCache::Cube cube;
|
||||||
if (Core::frameIndex != controller->frameIndex) {
|
if (Core::frameIndex != controller->frameIndex) {
|
||||||
ambientCache->getAmbient(entity.room, pos, cube);
|
ambientCache->getAmbient(entity.room, pos, cube);
|
||||||
if (cube.status == AmbientCache::Cube::READY)
|
if (cube.status == AmbientCache::Cube::READY)
|
||||||
memcpy(controller->ambient, cube.colors, sizeof(cube.colors)); // store last calculated ambient into controller
|
memcpy(controller->ambient, cube.colors, sizeof(cube.colors)); // store last calculated ambient into controller
|
||||||
}
|
}
|
||||||
getLight(pos, entity.room);
|
|
||||||
Core::active.shader->setParam(uAmbient, controller->ambient[0], 6);
|
Core::active.shader->setParam(uAmbient, controller->ambient[0], 6);
|
||||||
|
}
|
||||||
|
getLight(pos, entity.room);
|
||||||
} else { // sprite
|
} else { // sprite
|
||||||
Core::lightPos[0] = vec3(0);
|
Core::lightPos[0] = vec3(0);
|
||||||
Core::lightColor[0] = vec4(0, 0, 0, 1);
|
Core::lightColor[0] = vec4(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::active.shader->setParam(uViewProj, Core::mViewProj);
|
|
||||||
Core::active.shader->setParam(uLightProj, Core::mLightProj);
|
|
||||||
Core::active.shader->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
Core::active.shader->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
Core::active.shader->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
Core::active.shader->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
|
|
||||||
@@ -592,7 +531,7 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
params.time += Core::deltaTime;
|
params->time += Core::deltaTime;
|
||||||
|
|
||||||
for (int i = 0; i < level.entitiesCount; i++) {
|
for (int i = 0; i < level.entitiesCount; i++) {
|
||||||
TR::Entity &e = level.entities[i];
|
TR::Entity &e = level.entities[i];
|
||||||
@@ -601,7 +540,7 @@ struct Level : IGame {
|
|||||||
if (controller) {
|
if (controller) {
|
||||||
controller->update();
|
controller->update();
|
||||||
|
|
||||||
if (e.type == TR::Entity::WATERFALL && ((Waterfall*)controller)->drop) { // add water drops for waterfalls
|
if (waterCache && e.type == TR::Entity::WATERFALL && ((Waterfall*)controller)->drop) { // add water drops for waterfalls
|
||||||
Waterfall *w = (Waterfall*)controller;
|
Waterfall *w = (Waterfall*)controller;
|
||||||
waterCache->addDrop(w->dropPos, w->dropRadius, w->dropStrength);
|
waterCache->addDrop(w->dropPos, w->dropRadius, w->dropStrength);
|
||||||
}
|
}
|
||||||
@@ -610,6 +549,7 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
camera->update();
|
camera->update();
|
||||||
|
if (waterCache)
|
||||||
waterCache->update();
|
waterCache->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,22 +567,6 @@ struct Level : IGame {
|
|||||||
if (!Core::support.VAO)
|
if (!Core::support.VAO)
|
||||||
mesh->bind();
|
mesh->bind();
|
||||||
//Core::mViewProj = Core::mLightProj;
|
//Core::mViewProj = Core::mLightProj;
|
||||||
// set frame constants for all shaders
|
|
||||||
for (int i = 0; i < sizeof(shaderCache->shaders[Core::pass]) / sizeof(shaderCache->shaders[Core::pass][0]); i++) {
|
|
||||||
Shader **ptr = &shaderCache->shaders[Core::pass][i][0][0][0];
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
Shader *sh = *ptr++;
|
|
||||||
if (!sh) continue;
|
|
||||||
sh->bind();
|
|
||||||
sh->setParam(uViewProj, Core::mViewProj);
|
|
||||||
sh->setParam(uLightProj, Core::mLightProj);
|
|
||||||
sh->setParam(uViewInv, Core::mViewInv);
|
|
||||||
sh->setParam(uViewPos, Core::viewPos);
|
|
||||||
sh->setParam(uParam, *((vec4*)¶ms));
|
|
||||||
sh->setParam(uAnimTexRanges, mesh->animTexRanges[0], mesh->animTexRangesCount);
|
|
||||||
sh->setParam(uAnimTexOffsets, mesh->animTexOffsets[0], mesh->animTexOffsetsCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Core::basis.identity();
|
Core::basis.identity();
|
||||||
|
|
||||||
// clear visibility flag for rooms
|
// clear visibility flag for rooms
|
||||||
@@ -731,35 +655,42 @@ struct Level : IGame {
|
|||||||
shadow->unbind(sShadow);
|
shadow->unbind(sShadow);
|
||||||
bool colorShadow = shadow->format == Texture::Format::RGBA ? true : false;
|
bool colorShadow = shadow->format == Texture::Format::RGBA ? true : false;
|
||||||
if (colorShadow)
|
if (colorShadow)
|
||||||
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
|
Core::setClearColor(vec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||||
Core::setTarget(shadow, true);
|
Core::setTarget(shadow, true);
|
||||||
Core::setCulling(cfBack);
|
Core::setCulling(cfBack);
|
||||||
renderScene(roomIndex);
|
renderScene(roomIndex);
|
||||||
Core::invalidateTarget(!colorShadow, colorShadow);
|
Core::invalidateTarget(!colorShadow, colorShadow);
|
||||||
Core::setCulling(cfFront);
|
Core::setCulling(cfFront);
|
||||||
if (colorShadow)
|
if (colorShadow)
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
Core::setClearColor(vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
Core::invalidateTarget(true, true);
|
Core::invalidateTarget(true, true);
|
||||||
params.clipHeight = NO_CLIP_PLANE;
|
params->clipHeight = NO_CLIP_PLANE;
|
||||||
params.clipSign = 1.0f;
|
params->clipSign = 1.0f;
|
||||||
params.waterHeight = params.clipHeight;
|
params->waterHeight = params->clipHeight;
|
||||||
Core::resetStates();
|
Core::resetStates();
|
||||||
|
|
||||||
|
if (ambientCache)
|
||||||
ambientCache->precessQueue();
|
ambientCache->precessQueue();
|
||||||
|
if (waterCache)
|
||||||
waterCache->reset();
|
waterCache->reset();
|
||||||
|
if (shadow)
|
||||||
renderShadows(lara->getRoomIndex());
|
renderShadows(lara->getRoomIndex());
|
||||||
|
|
||||||
Core::setTarget(NULL, true);
|
Core::setTarget(NULL, true);
|
||||||
Core::setViewport(0, 0, Core::width, Core::height);
|
Core::setViewport(0, 0, Core::width, Core::height);
|
||||||
|
|
||||||
|
if (waterCache)
|
||||||
waterCache->checkVisibility = true;
|
waterCache->checkVisibility = true;
|
||||||
renderCompose(camera->getRoomIndex());
|
|
||||||
waterCache->checkVisibility = false;
|
|
||||||
|
|
||||||
|
renderCompose(camera->getRoomIndex());
|
||||||
|
|
||||||
|
if (waterCache) {
|
||||||
|
waterCache->checkVisibility = false;
|
||||||
waterCache->render();
|
waterCache->render();
|
||||||
|
}
|
||||||
|
|
||||||
// Core::mViewInv = camera->mViewInv;
|
// Core::mViewInv = camera->mViewInv;
|
||||||
// Core::mView = Core::mViewInv.inverse();
|
// Core::mView = Core::mViewInv.inverse();
|
||||||
@@ -832,8 +763,8 @@ struct Level : IGame {
|
|||||||
glEnd();
|
glEnd();
|
||||||
Core::setDepthTest(true);
|
Core::setDepthTest(true);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
@@ -871,7 +802,7 @@ struct Level : IGame {
|
|||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
*/
|
|
||||||
|
|
||||||
Core::setBlending(bmAlpha);
|
Core::setBlending(bmAlpha);
|
||||||
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
|
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
|
||||||
@@ -886,7 +817,7 @@ struct Level : IGame {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
static int dbg_ambient = 0;
|
static int dbg_ambient = 0;
|
||||||
dbg_ambient = int(params.time * 2) % 4;
|
dbg_ambient = int(params->time * 2) % 4;
|
||||||
|
|
||||||
shadow->unbind(sShadow);
|
shadow->unbind(sShadow);
|
||||||
atlas->bind(sDiffuse);
|
atlas->bind(sDiffuse);
|
||||||
|
50
src/mesh.h
50
src/mesh.h
@@ -138,7 +138,10 @@ struct MeshBuilder {
|
|||||||
MeshRange sprites;
|
MeshRange sprites;
|
||||||
MeshRange **meshes;
|
MeshRange **meshes;
|
||||||
} *rooms;
|
} *rooms;
|
||||||
MeshRange *models;
|
struct ModelRange {
|
||||||
|
MeshRange geometry;
|
||||||
|
bool opaque;
|
||||||
|
} *models;
|
||||||
MeshRange *sequences;
|
MeshRange *sequences;
|
||||||
// procedured
|
// procedured
|
||||||
MeshRange shadowBlob;
|
MeshRange shadowBlob;
|
||||||
@@ -180,6 +183,7 @@ struct MeshBuilder {
|
|||||||
iCount += d.rCount * 6 + d.tCount * 3;
|
iCount += d.rCount * 6 + d.tCount * 3;
|
||||||
vCount += d.rCount * 4 + d.tCount * 3;
|
vCount += d.rCount * 4 + d.tCount * 3;
|
||||||
|
|
||||||
|
if (Core::settings.water)
|
||||||
roomRemoveWaterSurfaces(r, iCount, vCount);
|
roomRemoveWaterSurfaces(r, iCount, vCount);
|
||||||
|
|
||||||
for (int j = 0; j < r.meshesCount; j++) {
|
for (int j = 0; j < r.meshesCount; j++) {
|
||||||
@@ -203,7 +207,7 @@ struct MeshBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get models info
|
// get models info
|
||||||
models = new MeshRange[level.modelsCount];
|
models = new ModelRange[level.modelsCount];
|
||||||
for (int i = 0; i < level.modelsCount; i++) {
|
for (int i = 0; i < level.modelsCount; i++) {
|
||||||
TR::Model &model = level.models[i];
|
TR::Model &model = level.models[i];
|
||||||
for (int j = 0; j < model.mCount; j++) {
|
for (int j = 0; j < model.mCount; j++) {
|
||||||
@@ -311,21 +315,23 @@ struct MeshBuilder {
|
|||||||
// build models geometry
|
// build models geometry
|
||||||
for (int i = 0; i < level.modelsCount; i++) {
|
for (int i = 0; i < level.modelsCount; i++) {
|
||||||
TR::Model &model = level.models[i];
|
TR::Model &model = level.models[i];
|
||||||
MeshRange &range = models[i];
|
ModelRange &range = models[i];
|
||||||
int vStart = vCount;
|
int vStart = vCount;
|
||||||
range.vStart = vStart;
|
range.geometry.vStart = vStart;
|
||||||
range.iStart = iCount;
|
range.geometry.iStart = iCount;
|
||||||
|
range.opaque = true;
|
||||||
|
|
||||||
for (int j = 0; j < model.mCount; j++) {
|
for (int j = 0; j < model.mCount; j++) {
|
||||||
int index = level.meshOffsets[model.mStart + j];
|
int index = level.meshOffsets[model.mStart + j];
|
||||||
if (!index && model.mStart + j > 0) continue;
|
if (!index && model.mStart + j > 0) continue;
|
||||||
aCount++;
|
aCount++;
|
||||||
TR::Mesh &mesh = level.meshes[index];
|
TR::Mesh &mesh = level.meshes[index];
|
||||||
buildMesh( true, mesh, level, indices, vertices, iCount, vCount, vStart, j, 0, 0, 0, 0);
|
bool opaque = buildMesh(true, mesh, level, indices, vertices, iCount, vCount, vStart, j, 0, 0, 0, 0);
|
||||||
|
if (!opaque)
|
||||||
buildMesh(false, mesh, level, indices, vertices, iCount, vCount, vStart, j, 0, 0, 0, 0);
|
buildMesh(false, mesh, level, indices, vertices, iCount, vCount, vStart, j, 0, 0, 0, 0);
|
||||||
|
range.opaque &= opaque;
|
||||||
}
|
}
|
||||||
|
range.geometry.iCount = iCount - range.geometry.iStart;
|
||||||
range.iCount = iCount - range.iStart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// build sprite sequences
|
// build sprite sequences
|
||||||
@@ -429,7 +435,7 @@ struct MeshBuilder {
|
|||||||
for (int i = 0; i < level.spriteSequencesCount; i++)
|
for (int i = 0; i < level.spriteSequencesCount; i++)
|
||||||
mesh->initRange(sequences[i]);
|
mesh->initRange(sequences[i]);
|
||||||
for (int i = 0; i < level.modelsCount; i++)
|
for (int i = 0; i < level.modelsCount; i++)
|
||||||
mesh->initRange(models[i]);
|
mesh->initRange(models[i].geometry);
|
||||||
mesh->initRange(shadowBlob);
|
mesh->initRange(shadowBlob);
|
||||||
mesh->initRange(bar);
|
mesh->initRange(bar);
|
||||||
mesh->initRange(quad);
|
mesh->initRange(quad);
|
||||||
@@ -538,8 +544,9 @@ struct MeshBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void buildRoom(bool opaque, const TR::Room &room, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) {
|
bool buildRoom(bool opaque, const TR::Room &room, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) {
|
||||||
const TR::Room::Data &d = room.data;
|
const TR::Room::Data &d = room.data;
|
||||||
|
bool isOpaque = true;
|
||||||
|
|
||||||
for (int j = 0; j < d.rCount; j++) {
|
for (int j = 0; j < d.rCount; j++) {
|
||||||
TR::Rectangle &f = d.rectangles[j];
|
TR::Rectangle &f = d.rectangles[j];
|
||||||
@@ -547,6 +554,9 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
||||||
|
|
||||||
|
if (t.attribute != 0)
|
||||||
|
isOpaque = false;
|
||||||
|
|
||||||
if (opaque != (t.attribute == 0))
|
if (opaque != (t.attribute == 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -570,6 +580,9 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
||||||
|
|
||||||
|
if (t.attribute != 0)
|
||||||
|
isOpaque = false;
|
||||||
|
|
||||||
if (opaque != (t.attribute == 0))
|
if (opaque != (t.attribute == 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -586,16 +599,22 @@ struct MeshBuilder {
|
|||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isOpaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buildMesh(bool opaque, const TR::Mesh &mesh, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir) {
|
bool buildMesh(bool opaque, const TR::Mesh &mesh, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir) {
|
||||||
TR::Color24 COLOR_WHITE = { 255, 255, 255 };
|
TR::Color24 COLOR_WHITE = { 255, 255, 255 };
|
||||||
|
bool isOpaque = true;
|
||||||
|
|
||||||
for (int j = 0; j < mesh.rCount; j++) {
|
for (int j = 0; j < mesh.rCount; j++) {
|
||||||
TR::Rectangle &f = mesh.rectangles[j];
|
TR::Rectangle &f = mesh.rectangles[j];
|
||||||
bool textured = !(f.texture & 0x8000);
|
bool textured = !(f.texture & 0x8000);
|
||||||
TR::ObjectTexture &t = textured ? level.objectTextures[f.texture] : whiteTile;
|
TR::ObjectTexture &t = textured ? level.objectTextures[f.texture] : whiteTile;
|
||||||
|
|
||||||
|
if (t.attribute != 0)
|
||||||
|
isOpaque = false;
|
||||||
|
|
||||||
if (opaque != (t.attribute == 0))
|
if (opaque != (t.attribute == 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -619,6 +638,9 @@ struct MeshBuilder {
|
|||||||
bool textured = !(f.texture & 0x8000);
|
bool textured = !(f.texture & 0x8000);
|
||||||
TR::ObjectTexture &t = textured ? level.objectTextures[f.texture] : whiteTile;
|
TR::ObjectTexture &t = textured ? level.objectTextures[f.texture] : whiteTile;
|
||||||
|
|
||||||
|
if (t.attribute != 0)
|
||||||
|
isOpaque = false;
|
||||||
|
|
||||||
if (opaque != (t.attribute == 0))
|
if (opaque != (t.attribute == 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -636,6 +658,8 @@ struct MeshBuilder {
|
|||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isOpaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 getTexCoord(const TR::ObjectTexture &tex) {
|
vec2 getTexCoord(const TR::ObjectTexture &tex) {
|
||||||
@@ -791,7 +815,7 @@ struct MeshBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void renderModel(int modelIndex) {
|
void renderModel(int modelIndex) {
|
||||||
mesh->render(models[modelIndex]);
|
mesh->render(models[modelIndex].geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderSprite(int sequenceIndex, int frame) {
|
void renderSprite(int sequenceIndex, int frame) {
|
||||||
@@ -860,7 +884,7 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
|
|
||||||
Core::active.shader->setParam(uColor, color);
|
Core::active.shader->setParam(uMaterial, vec4(1.0, 0.0, 0.0, 1.0));
|
||||||
//text = "a: b";
|
//text = "a: b";
|
||||||
Basis basis;
|
Basis basis;
|
||||||
basis.identity();
|
basis.identity();
|
||||||
|
151
src/shader.glsl
151
src/shader.glsl
@@ -18,12 +18,13 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
varying vec4 vLightProj;
|
varying vec4 vLightProj;
|
||||||
#endif
|
#endif
|
||||||
varying vec4 vColor;
|
varying vec4 vColor;
|
||||||
|
uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
uniform vec3 uViewPos;
|
uniform vec3 uViewPos;
|
||||||
uniform vec4 uParam;
|
uniform vec4 uParam; // x - time, y - water height, z - clip plane sign, w - clip plane height
|
||||||
#ifdef CAUSTICS
|
#ifdef UNDERWATER
|
||||||
uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ
|
uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -82,6 +83,15 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
|
|
||||||
#ifndef PASS_SHADOW
|
#ifndef PASS_SHADOW
|
||||||
vColor = aColor;
|
vColor = aColor;
|
||||||
|
vColor.xyz *= uMaterial.x; // apply diffuse intensity
|
||||||
|
#ifdef TYPE_MIRROR
|
||||||
|
vColor.xyz *= vec3(0.6, 0.6, 4.0); // blue color dodge for crystal
|
||||||
|
#endif
|
||||||
|
#ifdef UNDERWATER
|
||||||
|
#ifndef TYPE_ENTITY
|
||||||
|
vColor.xyz *= UNDERWATER_COLOR; // underwater blue color
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
@@ -95,7 +105,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
vTexCoord.xy = (aTexCoord.xy + offset) * TEXCOORD_SCALE; // first frame + offset * isAnimated
|
vTexCoord.xy = (aTexCoord.xy + offset) * TEXCOORD_SCALE; // first frame + offset * isAnimated
|
||||||
vNormal = vec4(mulQuat(rBasisRot, aNormal.xyz), aNormal.w);
|
vNormal = vec4(mulQuat(rBasisRot, aNormal.xyz), aNormal.w);
|
||||||
|
|
||||||
#ifdef CAUSTICS
|
#ifdef UNDERWATER
|
||||||
float sum = coord.x + coord.y + coord.z;
|
float sum = coord.x + coord.y + coord.z;
|
||||||
vColor.xyz *= abs(sin(sum / 512.0 + uParam.x)) * 1.5 + 0.5; // color dodge
|
vColor.xyz *= abs(sin(sum / 512.0 + uParam.x)) * 1.5 + 0.5; // color dodge
|
||||||
#endif
|
#endif
|
||||||
@@ -105,7 +115,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
vNormal = vec4(uViewPos.xyz - coord.xyz, 0.0);
|
vNormal = vec4(uViewPos.xyz - coord.xyz, 0.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CAUSTICS
|
#ifdef UNDERWATER
|
||||||
vTexCoord.zw = clamp((coord.xz - uRoomSize.xy) / (uRoomSize.zw - uRoomSize.xy), vec2(0.0), vec2(1.0));
|
vTexCoord.zw = clamp((coord.xz - uRoomSize.xy) / (uRoomSize.zw - uRoomSize.xy), vec2(0.0), vec2(1.0));
|
||||||
#else
|
#else
|
||||||
vTexCoord.zw = vec2(1.0);
|
vTexCoord.zw = vec2(1.0);
|
||||||
@@ -124,13 +134,22 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
uniform sampler2D sDiffuse;
|
uniform sampler2D sDiffuse;
|
||||||
|
|
||||||
|
#if defined(UNDERWATER) && defined(OPT_WATER)
|
||||||
uniform sampler2D sReflect;
|
uniform sampler2D sReflect;
|
||||||
uniform vec4 uColor;
|
#endif
|
||||||
|
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
|
#ifdef TYPE_MIRROR
|
||||||
uniform samplerCube sEnvironment;
|
uniform samplerCube sEnvironment;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPT_AMBIENT
|
||||||
|
uniform vec3 uAmbient[6];
|
||||||
|
#endif
|
||||||
|
|
||||||
uniform vec3 uLightPos[MAX_LIGHTS];
|
uniform vec3 uLightPos[MAX_LIGHTS];
|
||||||
uniform vec4 uLightColor[MAX_LIGHTS];
|
uniform vec4 uLightColor[MAX_LIGHTS];
|
||||||
uniform vec3 uAmbient[6];
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PASS_SHADOW
|
#ifdef PASS_SHADOW
|
||||||
@@ -147,6 +166,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
|
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
|
|
||||||
|
#ifdef OPT_SHADOW
|
||||||
#ifdef SHADOW_SAMPLER
|
#ifdef SHADOW_SAMPLER
|
||||||
uniform sampler2DShadow sShadow;
|
uniform sampler2DShadow sShadow;
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
@@ -185,43 +205,25 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
float getShadow(vec4 lightProj) {
|
float getShadow(vec4 lightProj) {
|
||||||
vec3 poissonDisk[16];
|
|
||||||
poissonDisk[ 0] = vec3(-0.94201624, -0.39906216, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 1] = vec3( 0.94558609, -0.76890725, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 2] = vec3(-0.09418410, -0.92938870, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 3] = vec3( 0.34495938, 0.29387760, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 4] = vec3(-0.91588581, 0.45771432, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 5] = vec3(-0.81544232, -0.87912464, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 6] = vec3(-0.38277543, 0.27676845, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 7] = vec3( 0.97484398, 0.75648379, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 8] = vec3( 0.44323325, -0.97511554, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[ 9] = vec3( 0.53742981, -0.47373420, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[10] = vec3(-0.26496911, -0.41893023, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[11] = vec3( 0.79197514, 0.19090188, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[12] = vec3(-0.24188840, 0.99706507, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[13] = vec3(-0.81409955, 0.91437590, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[14] = vec3( 0.19984126, 0.78641367, 0.0) * (1.5 / 1024.0);
|
|
||||||
poissonDisk[15] = vec3( 0.14383161, -0.14100790, 0.0) * (1.5 / 1024.0);
|
|
||||||
|
|
||||||
float rShadow = 0.0;
|
|
||||||
vec3 p = lightProj.xyz / lightProj.w;
|
vec3 p = lightProj.xyz / lightProj.w;
|
||||||
|
|
||||||
rShadow += SHADOW(p + poissonDisk[0]);
|
float rShadow = 0.0;
|
||||||
rShadow += SHADOW(p + poissonDisk[1]);
|
rShadow += SHADOW(p + (vec3(-0.94201624, -0.39906216, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[2]);
|
rShadow += SHADOW(p + (vec3( 0.94558609, -0.76890725, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[3]);
|
rShadow += SHADOW(p + (vec3(-0.09418410, -0.92938870, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[4]);
|
rShadow += SHADOW(p + (vec3( 0.34495938, 0.29387760, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[5]);
|
rShadow += SHADOW(p + (vec3(-0.91588581, 0.45771432, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[6]);
|
rShadow += SHADOW(p + (vec3(-0.81544232, -0.87912464, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[7]);
|
rShadow += SHADOW(p + (vec3(-0.38277543, 0.27676845, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[8]);
|
rShadow += SHADOW(p + (vec3( 0.97484398, 0.75648379, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[9]);
|
rShadow += SHADOW(p + (vec3( 0.44323325, -0.97511554, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[10]);
|
rShadow += SHADOW(p + (vec3( 0.53742981, -0.47373420, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[11]);
|
rShadow += SHADOW(p + (vec3(-0.26496911, -0.41893023, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[12]);
|
rShadow += SHADOW(p + (vec3( 0.79197514, 0.19090188, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[13]);
|
rShadow += SHADOW(p + (vec3(-0.24188840, 0.99706507, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[14]);
|
rShadow += SHADOW(p + (vec3(-0.81409955, 0.91437590, 0.0) * (1.5 / 1024.0)));
|
||||||
rShadow += SHADOW(p + poissonDisk[15]);
|
rShadow += SHADOW(p + (vec3( 0.19984126, 0.78641367, 0.0) * (1.5 / 1024.0)));
|
||||||
|
rShadow += SHADOW(p + (vec3( 0.14383161, -0.14100790, 0.0) * (1.5 / 1024.0)));
|
||||||
|
|
||||||
rShadow /= 16.0;
|
rShadow /= 16.0;
|
||||||
|
|
||||||
@@ -234,6 +236,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
float getShadow() {
|
float getShadow() {
|
||||||
return min(dot(vNormal.xyz, uLightPos[0].xyz - vCoord), vLightProj.w) > 0.0 ? getShadow(vLightProj) : 1.0;
|
return min(dot(vNormal.xyz, uLightPos[0].xyz - vCoord), vLightProj.w) > 0.0 ? getShadow(vLightProj) : 1.0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
vec3 calcLight(vec3 normal, vec3 pos, vec4 color) {
|
vec3 calcLight(vec3 normal, vec3 pos, vec4 color) {
|
||||||
vec3 lv = pos - vCoord.xyz;
|
vec3 lv = pos - vCoord.xyz;
|
||||||
@@ -249,6 +252,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
return vec3(spec);
|
return vec3(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OPT_AMBIENT
|
||||||
vec3 calcAmbient(vec3 n) {
|
vec3 calcAmbient(vec3 n) {
|
||||||
vec3 sqr = n * n;
|
vec3 sqr = n * n;
|
||||||
vec3 pos = step(0.0, n);
|
vec3 pos = step(0.0, n);
|
||||||
@@ -256,11 +260,12 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) +
|
sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) +
|
||||||
sqr.z * mix(uAmbient[5], uAmbient[4], pos.z);
|
sqr.z * mix(uAmbient[5], uAmbient[4], pos.z);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CAUSTICS
|
#if defined(UNDERWATER) && defined(OPT_WATER)
|
||||||
float calcCaustics(vec3 n) {
|
float calcCaustics(vec3 n) {
|
||||||
vec2 fade = smoothstep(uRoomSize.xy, uRoomSize.xy + vec2(256.0), vCoord.xz) * (1.0 - smoothstep(uRoomSize.zw - vec2(256.0), uRoomSize.zw, vCoord.xz));
|
vec2 fade = smoothstep(uRoomSize.xy, uRoomSize.xy + vec2(256.0), vCoord.xz) * (1.0 - smoothstep(uRoomSize.zw - vec2(256.0), uRoomSize.zw, vCoord.xz));
|
||||||
return texture2D(sReflect, vTexCoord.zw).r * (max(0.0, -n.y)) * fade.x * fade.y;
|
return texture2D(sReflect, vTexCoord.zw).g * (max(0.0, -n.y)) * fade.x * fade.y;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -285,6 +290,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
#ifdef ALPHA_TEST
|
#ifdef ALPHA_TEST
|
||||||
if (color.w <= 0.1)
|
if (color.w <= 0.1)
|
||||||
discard;
|
discard;
|
||||||
|
//color.xyz *= vec3(1.0, 0.0, 0.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PASS_SHADOW
|
#ifdef PASS_SHADOW
|
||||||
@@ -294,9 +300,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
gl_FragColor = vec4(1.0);
|
gl_FragColor = vec4(1.0);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
color.xyz *= uColor.xyz;
|
|
||||||
color.xyz *= vColor.xyz;
|
color.xyz *= vColor.xyz;
|
||||||
|
|
||||||
color.xyz *= color.xyz; // to "linear" space
|
color.xyz *= color.xyz; // to "linear" space
|
||||||
|
|
||||||
#ifdef PASS_AMBIENT
|
#ifdef PASS_AMBIENT
|
||||||
@@ -308,42 +312,62 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
vec3 viewVec = normalize(vViewVec);
|
vec3 viewVec = normalize(vViewVec);
|
||||||
vec3 light = vec3(0.0);
|
vec3 light = vec3(0.0);
|
||||||
|
|
||||||
|
#ifdef OPT_LIGHTING
|
||||||
light += calcLight(normal, uLightPos[1], uLightColor[1]);
|
light += calcLight(normal, uLightPos[1], uLightColor[1]);
|
||||||
light += calcLight(normal, uLightPos[2], uLightColor[2]);
|
light += calcLight(normal, uLightPos[2], uLightColor[2]);
|
||||||
|
#endif
|
||||||
|
|
||||||
// apply lighting
|
// apply lighting
|
||||||
#ifdef TYPE_SPRITE
|
#ifdef TYPE_SPRITE
|
||||||
light += vColor.w * uColor.w;
|
light += vColor.w * uMaterial.y; // apply diffuse intensity
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TYPE_ROOM
|
#ifdef TYPE_ROOM
|
||||||
light += mix(min(uColor.w, vColor.w), vColor.w, getShadow());
|
#ifdef OPT_SHADOW
|
||||||
#ifdef CAUSTICS
|
light += mix(min(uMaterial.y, vColor.w), vColor.w, getShadow());
|
||||||
|
#else
|
||||||
|
light += vColor.w;
|
||||||
|
#endif
|
||||||
|
#if defined(UNDERWATER) && defined(OPT_WATER)
|
||||||
light += calcCaustics(normal);
|
light += calcCaustics(normal);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TYPE_ENTITY
|
#ifdef TYPE_ENTITY
|
||||||
vec3 rAmbient = calcAmbient(normal);
|
vec3 mainLight = calcLight(normal, uLightPos[0], uLightColor[0]);
|
||||||
|
float mainSpec = uMaterial.z;
|
||||||
|
|
||||||
|
#ifdef OPT_SHADOW
|
||||||
float rShadow = getShadow();
|
float rShadow = getShadow();
|
||||||
light += calcLight(normal, uLightPos[0], uLightColor[0]) * rShadow + rAmbient;
|
mainLight *= rShadow;
|
||||||
color.xyz += calcSpecular(normal, viewVec, uLightPos[0], uLightColor[0], uColor.w * rShadow + 0.03);
|
mainSpec *= rShadow;
|
||||||
#ifdef CAUSTICS
|
|
||||||
light += calcCaustics(normal);
|
|
||||||
#endif
|
#endif
|
||||||
|
light += mainLight;
|
||||||
|
|
||||||
|
#ifdef OPT_AMBIENT
|
||||||
|
light += calcAmbient(normal);
|
||||||
|
#else
|
||||||
|
light += uMaterial.y;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TYPE_MIRROR
|
#ifdef UNDERWATER
|
||||||
vec3 rv = reflect(-viewVec, normal);
|
if (vCoord.y > uParam.y) {
|
||||||
color.xyz = uColor.xyz * pow(abs(textureCube(sEnvironment, normalize(rv)).xyz), vec3(2.2));
|
color.xyz *= UNDERWATER_COLOR;
|
||||||
light.xyz = vec3(1.0);
|
#ifdef OPT_WATER
|
||||||
|
light += calcCaustics(normal);
|
||||||
|
#endif
|
||||||
|
} else
|
||||||
|
color.xyz += calcSpecular(normal, viewVec, uLightPos[0], uLightColor[0], mainSpec + 0.03);
|
||||||
|
#else
|
||||||
|
color.xyz += calcSpecular(normal, viewVec, uLightPos[0], uLightColor[0], mainSpec + 0.03);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
color.xyz *= light;
|
color.xyz *= light;
|
||||||
|
|
||||||
#else // ifndef TYPE_FLASH
|
#else // ifndef TYPE_FLASH
|
||||||
|
|
||||||
color.w *= uColor.w;
|
color.w *= uMaterial.w;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -351,14 +375,21 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords
|
|||||||
color.xyz = sqrt(color.xyz); // back to "gamma" space
|
color.xyz = sqrt(color.xyz); // back to "gamma" space
|
||||||
|
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
|
#ifdef TYPE_MIRROR
|
||||||
|
vec3 rv = reflect(-normalize(vViewVec), normalize(vNormal.xyz));
|
||||||
|
color.xyz = vColor.xyz * textureCube(sEnvironment, normalize(rv)).xyz;
|
||||||
|
#endif
|
||||||
|
|
||||||
color.xyz = applyFog(color.xyz, vec3(0.0), length(vViewVec) * FOG_DIST);
|
color.xyz = applyFog(color.xyz, vec3(0.0), length(vViewVec) * FOG_DIST);
|
||||||
#ifdef CAUSTICS
|
#if defined(UNDERWATER) && defined(OPT_WATER)
|
||||||
float d = abs((vCoord.y - max(uViewPos.y, uParam.y)) / normalize(vViewVec).y);
|
float d = abs((vCoord.y - max(uViewPos.y, uParam.y)) / normalize(vViewVec).y);
|
||||||
d *= step(0.0, vCoord.y - uParam.y);
|
d *= step(0.0, vCoord.y - uParam.y);
|
||||||
color.xyz = applyFog(color.xyz, uColor.xyz * 0.2, d * WATER_FOG_DIST);
|
color.xyz = applyFog(color.xyz, UNDERWATER_COLOR * 0.2, d * WATER_FOG_DIST);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
color.xyz *= 1.5; // add contrast
|
||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
58
src/shader.h
58
src/shader.h
@@ -3,13 +3,53 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
enum AttribType { aCoord, aTexCoord, aNormal, aColor, aMAX };
|
#define SHADER_ATTRIBS(E) \
|
||||||
enum SamplerType { sDiffuse, sNormal, sReflect, sShadow, sEnvironment, sMask, sMAX };
|
E( aCoord ) \
|
||||||
enum UniformType { uParam, uTexParam, uViewProj, uViewInv, uBasis, uLightProj, uColor, uAmbient, uViewPos, uLightPos, uLightColor, uAnimTexRanges, uAnimTexOffsets, uRoomSize, uPosScale, uMAX };
|
E( aTexCoord ) \
|
||||||
|
E( aNormal ) \
|
||||||
|
E( aColor )
|
||||||
|
|
||||||
const char *AttribName[aMAX] = { "aCoord", "aTexCoord", "aNormal", "aColor" };
|
#define SHADER_SAMPLERS(E) \
|
||||||
const char *SamplerName[sMAX] = { "sDiffuse", "sNormal", "sReflect", "sShadow", "sEnvironment", "sMask" };
|
E( sDiffuse ) \
|
||||||
const char *UniformName[uMAX] = { "uParam", "uTexParam", "uViewProj", "uViewInv", "uBasis", "uLightProj", "uColor", "uAmbient", "uViewPos", "uLightPos", "uLightColor", "uAnimTexRanges", "uAnimTexOffsets", "uRoomSize", "uPosScale" };
|
E( sNormal ) \
|
||||||
|
E( sReflect ) \
|
||||||
|
E( sShadow ) \
|
||||||
|
E( sEnvironment ) \
|
||||||
|
E( sMask )
|
||||||
|
|
||||||
|
#define SHADER_UNIFORMS(E) \
|
||||||
|
E( uParam ) \
|
||||||
|
E( uTexParam ) \
|
||||||
|
E( uViewProj ) \
|
||||||
|
E( uViewInv ) \
|
||||||
|
E( uBasis ) \
|
||||||
|
E( uLightProj ) \
|
||||||
|
E( uMaterial ) \
|
||||||
|
E( uAmbient ) \
|
||||||
|
E( uViewPos ) \
|
||||||
|
E( uLightPos ) \
|
||||||
|
E( uLightColor ) \
|
||||||
|
E( uAnimTexRanges ) \
|
||||||
|
E( uAnimTexOffsets ) \
|
||||||
|
E( uRoomSize ) \
|
||||||
|
E( uPosScale )
|
||||||
|
|
||||||
|
#define ENUM(v) v,
|
||||||
|
#define STR(v) #v,
|
||||||
|
|
||||||
|
enum AttribType { SHADER_ATTRIBS(ENUM) aMAX };
|
||||||
|
enum SamplerType { SHADER_SAMPLERS(ENUM) sMAX };
|
||||||
|
enum UniformType { SHADER_UNIFORMS(ENUM) uMAX };
|
||||||
|
|
||||||
|
const char *AttribName[aMAX] = { SHADER_ATTRIBS(STR) };
|
||||||
|
const char *SamplerName[sMAX] = { SHADER_SAMPLERS(STR) };
|
||||||
|
const char *UniformName[uMAX] = { SHADER_UNIFORMS(STR) };
|
||||||
|
|
||||||
|
#undef SHADER_ATTRIBS
|
||||||
|
#undef SHADER_SAMPLERS
|
||||||
|
#undef SHADER_UNIFORMS
|
||||||
|
#undef ENUM
|
||||||
|
#undef STR
|
||||||
|
|
||||||
struct Shader {
|
struct Shader {
|
||||||
GLuint ID;
|
GLuint ID;
|
||||||
@@ -26,7 +66,7 @@ struct Shader {
|
|||||||
|
|
||||||
Shader(const char *source, const char *defines = "") {
|
Shader(const char *source, const char *defines = "") {
|
||||||
char fileName[255];
|
char fileName[255];
|
||||||
|
//LOG(hello[0]);
|
||||||
// generate shader file path
|
// generate shader file path
|
||||||
if (Core::support.shaderBinary) {
|
if (Core::support.shaderBinary) {
|
||||||
uint32 hash = fnv32(defines, strlen(defines), fnv32(source, strlen(source)));
|
uint32 hash = fnv32(defines, strlen(defines), fnv32(source, strlen(source)));
|
||||||
@@ -131,11 +171,13 @@ struct Shader {
|
|||||||
memset(params, 0, sizeof(params));
|
memset(params, 0, sizeof(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind() {
|
bool bind() {
|
||||||
if (Core::active.shader != this) {
|
if (Core::active.shader != this) {
|
||||||
Core::active.shader = this;
|
Core::active.shader = this;
|
||||||
glUseProgram(ID);
|
glUseProgram(ID);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool checkParam(UniformType uType, const void *value, int size) {
|
inline bool checkParam(UniformType uType, const void *value, int size) {
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
enum Format : uint32 { RGBA, RGB16, RGBA16, RGBA_FLOAT, RGBA_HALF, DEPTH, SHADOW, MAX };
|
enum Format : uint32 { LUMINANCE, RGBA, RGB16, RGBA16, RGBA_FLOAT, RGBA_HALF, DEPTH, SHADOW, MAX };
|
||||||
|
|
||||||
GLuint ID;
|
GLuint ID;
|
||||||
int width, height;
|
int width, height;
|
||||||
@@ -38,14 +38,14 @@ struct Texture {
|
|||||||
|
|
||||||
if (format == RGBA_HALF) {
|
if (format == RGBA_HALF) {
|
||||||
if (Core::support.texHalf)
|
if (Core::support.texHalf)
|
||||||
filter = Core::support.texHalfLinear;
|
filter = filter && Core::support.texHalfLinear;
|
||||||
else
|
else
|
||||||
format = RGBA_FLOAT;
|
format = RGBA_FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format == RGBA_FLOAT) {
|
if (format == RGBA_FLOAT) {
|
||||||
if (Core::support.texFloat)
|
if (Core::support.texFloat)
|
||||||
filter = Core::support.texFloatLinear;
|
filter = filter && Core::support.texFloatLinear;
|
||||||
else
|
else
|
||||||
format = RGBA;
|
format = RGBA;
|
||||||
}
|
}
|
||||||
@@ -66,6 +66,7 @@ struct Texture {
|
|||||||
GLuint ifmt, fmt;
|
GLuint ifmt, fmt;
|
||||||
GLenum type;
|
GLenum type;
|
||||||
} formats[MAX] = {
|
} formats[MAX] = {
|
||||||
|
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE }, // LUMINANCE
|
||||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // RGBA
|
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // RGBA
|
||||||
{ GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 }, // RGB16
|
{ GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 }, // RGB16
|
||||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, // RGBA16
|
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, // RGBA16
|
||||||
@@ -88,15 +89,20 @@ struct Texture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bind(int sampler) {
|
void bind(int sampler) {
|
||||||
|
if (Core::active.textures[sampler] != this) {
|
||||||
Core::active.textures[sampler] = this;
|
Core::active.textures[sampler] = this;
|
||||||
glActiveTexture(GL_TEXTURE0 + sampler);
|
glActiveTexture(GL_TEXTURE0 + sampler);
|
||||||
glBindTexture(cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, ID);
|
glBindTexture(cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, ID);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void unbind(int sampler) {
|
void unbind(int sampler) {
|
||||||
|
if (Core::active.textures[sampler]) {
|
||||||
|
Core::active.textures[sampler] = NULL;
|
||||||
glActiveTexture(GL_TEXTURE0 + sampler);
|
glActiveTexture(GL_TEXTURE0 + sampler);
|
||||||
glBindTexture(cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, 0);
|
glBindTexture(cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -301,7 +301,7 @@ struct Crystal : Controller {
|
|||||||
|
|
||||||
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) {
|
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) {
|
||||||
Shader *sh = Core::active.shader;
|
Shader *sh = Core::active.shader;
|
||||||
sh->setParam(uColor, vec4(0.4f, 0.4f, 16.0f, 1.0f)); // blue color dodge
|
sh->setParam(uMaterial, vec4(1.0f));
|
||||||
environment->bind(sEnvironment);
|
environment->bind(sEnvironment);
|
||||||
Controller::render(frustum, mesh, type, caustics);
|
Controller::render(frustum, mesh, type, caustics);
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ uniform vec3 uPosScale[2];
|
|||||||
|
|
||||||
uniform vec4 uTexParam;
|
uniform vec4 uTexParam;
|
||||||
uniform vec4 uParam;
|
uniform vec4 uParam;
|
||||||
uniform vec4 uColor;
|
|
||||||
|
|
||||||
uniform sampler2D sNormal;
|
uniform sampler2D sNormal;
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ uniform sampler2D sNormal;
|
|||||||
vec4 calc() {
|
vec4 calc() {
|
||||||
vec2 tc = gl_FragCoord.xy * uTexParam.xy;
|
vec2 tc = gl_FragCoord.xy * uTexParam.xy;
|
||||||
|
|
||||||
if (texture2D(sMask, tc).x == 0.0)
|
if (texture2D(sMask, tc).x < 0.5)
|
||||||
return vec4(0.0);
|
return vec4(0.0);
|
||||||
|
|
||||||
vec4 v = texture2D(sDiffuse, tc); // height, speed, normal.xz
|
vec4 v = texture2D(sDiffuse, tc); // height, speed, normal.xz
|
||||||
@@ -135,11 +134,11 @@ uniform sampler2D sNormal;
|
|||||||
float rOldArea = length(dFdx(vOldPos.xyz)) * length(dFdy(vOldPos.xyz));
|
float rOldArea = length(dFdx(vOldPos.xyz)) * length(dFdy(vOldPos.xyz));
|
||||||
float rNewArea = length(dFdx(vNewPos.xyz)) * length(dFdy(vNewPos.xyz));
|
float rNewArea = length(dFdx(vNewPos.xyz)) * length(dFdy(vNewPos.xyz));
|
||||||
float value = clamp(rOldArea / rNewArea * 0.2, 0.0, 1.0) * vOldPos.w;
|
float value = clamp(rOldArea / rNewArea * 0.2, 0.0, 1.0) * vOldPos.w;
|
||||||
return vec4(vec3(value), 1.0);
|
return vec4(0.0, value, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 mask() {
|
vec4 mask() {
|
||||||
return vec4(0.0);
|
return vec4(1.0, 1.0, 1.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 compose() {
|
vec4 compose() {
|
||||||
@@ -164,11 +163,11 @@ uniform sampler2D sNormal;
|
|||||||
|
|
||||||
float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0);
|
float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0);
|
||||||
|
|
||||||
vec4 color = mix(refr, refl, fresnel) + spec;
|
vec4 color = mix(refr, refl, fresnel) + spec * 1.5;
|
||||||
|
|
||||||
float d = abs((vCoord.y - uViewPos.y) / normalize(vViewVec).y);
|
float d = abs((vCoord.y - uViewPos.y) / normalize(vViewVec).y);
|
||||||
d *= step(0.0, uViewPos.y - vCoord.y); // apply fog only when camera is underwater
|
d *= step(0.0, uViewPos.y - vCoord.y); // apply fog only when camera is underwater
|
||||||
color.xyz = applyFog(color.xyz, uColor.xyz, d * WATER_FOG_DIST);
|
color.xyz = applyFog(color.xyz, UNDERWATER_COLOR * 0.2, d * WATER_FOG_DIST);
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user