mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-16 01:54:38 +02:00
#23 volumetric caustics effect
This commit is contained in:
49
src/cache.h
49
src/cache.h
@@ -91,8 +91,9 @@ struct ShaderCache {
|
|||||||
|
|
||||||
void prepareWater(int fx) {
|
void prepareWater(int fx) {
|
||||||
compile(Core::passWater, Shader::WATER_MASK, fx, RS_COLOR_WRITE_A | RS_DEPTH_TEST);
|
compile(Core::passWater, Shader::WATER_MASK, fx, RS_COLOR_WRITE_A | RS_DEPTH_TEST);
|
||||||
compile(Core::passWater, Shader::WATER_STEP, fx, RS_COLOR_WRITE);
|
compile(Core::passWater, Shader::WATER_SIMULATE, fx, RS_COLOR_WRITE);
|
||||||
compile(Core::passWater, Shader::WATER_DROP, fx, RS_COLOR_WRITE);
|
compile(Core::passWater, Shader::WATER_DROP, fx, RS_COLOR_WRITE);
|
||||||
|
compile(Core::passWater, Shader::WATER_RAYS, fx, RS_COLOR_WRITE | RS_DEPTH_TEST);
|
||||||
compile(Core::passWater, Shader::WATER_CAUSTICS, fx, RS_COLOR_WRITE);
|
compile(Core::passWater, Shader::WATER_CAUSTICS, fx, RS_COLOR_WRITE);
|
||||||
compile(Core::passWater, Shader::WATER_COMPOSE, fx, RS_COLOR_WRITE | RS_DEPTH_TEST);
|
compile(Core::passWater, Shader::WATER_COMPOSE, fx, RS_COLOR_WRITE | RS_DEPTH_TEST);
|
||||||
}
|
}
|
||||||
@@ -688,7 +689,7 @@ struct WaterCache {
|
|||||||
|
|
||||||
vec2 s(item.size.x * DETAIL * 2.0f, item.size.z * DETAIL * 2.0f);
|
vec2 s(item.size.x * DETAIL * 2.0f, item.size.z * DETAIL * 2.0f);
|
||||||
|
|
||||||
game->setShader(Core::passWater, Shader::WATER_STEP);
|
game->setShader(Core::passWater, Shader::WATER_SIMULATE);
|
||||||
Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, Core::params.x));
|
Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, Core::params.x));
|
||||||
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, s.x / item.data[0]->width, s.y / item.data[0]->height));
|
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, s.x / item.data[0]->width, s.y / item.data[0]->height));
|
||||||
|
|
||||||
@@ -739,9 +740,47 @@ struct WaterCache {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderRays() {
|
||||||
|
if (!visible) return;
|
||||||
|
PROFILE_MARKER("WATER_RAYS");
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Item &item = items[i];
|
||||||
|
if (!item.visible || !item.caustics) continue;
|
||||||
|
|
||||||
|
// render water plane
|
||||||
|
game->setShader(Core::passWater, Shader::WATER_RAYS);
|
||||||
|
|
||||||
|
item.caustics->bind(sReflect);
|
||||||
|
Core::ditherTex->bind(sMask);
|
||||||
|
|
||||||
|
vec3 bCenter = vec3(item.pos.x, item.pos.y + WATER_VOLUME_HEIGHT / 2, item.pos.z);
|
||||||
|
vec3 bSize = vec3(item.size.x, WATER_VOLUME_HEIGHT / 2, item.size.z);
|
||||||
|
Box box(bCenter - bSize, bCenter + bSize);
|
||||||
|
|
||||||
|
vec4 rPosScale[2] = { vec4(bCenter, 0.0), vec4(bSize, 1.0) };
|
||||||
|
|
||||||
|
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
|
||||||
|
Core::active.shader->setParam(uParam, vec4(level->rooms[item.to].getOffset(), 0.35f));
|
||||||
|
|
||||||
|
Core::setBlendMode(bmAdd);
|
||||||
|
Core::setCullMode(cmBack);
|
||||||
|
Core::setDepthWrite(false);
|
||||||
|
//Core::setDepthTest(false);
|
||||||
|
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||||
|
//game->getMesh()->renderBox();
|
||||||
|
game->getMesh()->renderWaterVolume(item.to);
|
||||||
|
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
//Core::setDepthTest(true);
|
||||||
|
Core::setDepthWrite(true);
|
||||||
|
Core::setCullMode(cmFront);
|
||||||
|
Core::setBlendMode(bmNone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void renderMask() {
|
void renderMask() {
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
PROFILE_MARKER("WATER_RENDER_MASK");
|
PROFILE_MARKER("WATER_MASK");
|
||||||
// mask underwater geometry by zero alpha
|
// mask underwater geometry by zero alpha
|
||||||
game->setShader(Core::passWater, Shader::WATER_MASK);
|
game->setShader(Core::passWater, Shader::WATER_MASK);
|
||||||
Core::active.shader->setParam(uTexParam, vec4(1.0f));
|
Core::active.shader->setParam(uTexParam, vec4(1.0f));
|
||||||
@@ -891,9 +930,9 @@ struct WaterCache {
|
|||||||
camera->setup(true);
|
camera->setup(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
void compose() {
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
PROFILE_MARKER("WATER_RENDER");
|
PROFILE_MARKER("WATER_COMPOSE");
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
Item &item = items[i];
|
Item &item = items[i];
|
||||||
if (!item.visible) continue;
|
if (!item.visible) continue;
|
||||||
|
@@ -523,7 +523,7 @@ struct Camera : ICamera {
|
|||||||
smooth = false;
|
smooth = false;
|
||||||
|
|
||||||
fov = firstPerson ? 90.0f : 65.0f;
|
fov = firstPerson ? 90.0f : 65.0f;
|
||||||
znear = firstPerson ? 8.0f : 32.0f;
|
znear = firstPerson ? 16.0f : 32.0f;
|
||||||
zfar = 45.0f * 1024.0f;
|
zfar = 45.0f * 1024.0f;
|
||||||
|
|
||||||
#ifdef _OS_PSP
|
#ifdef _OS_PSP
|
||||||
|
41
src/core.h
41
src/core.h
@@ -352,6 +352,7 @@ enum RenderState {
|
|||||||
|
|
||||||
// Texture image format
|
// Texture image format
|
||||||
enum TexFormat {
|
enum TexFormat {
|
||||||
|
FMT_LUMINANCE,
|
||||||
FMT_RGBA,
|
FMT_RGBA,
|
||||||
FMT_RGB16,
|
FMT_RGB16,
|
||||||
FMT_RGBA16,
|
FMT_RGBA16,
|
||||||
@@ -364,12 +365,13 @@ enum TexFormat {
|
|||||||
|
|
||||||
// Texture options
|
// Texture options
|
||||||
enum TexOption {
|
enum TexOption {
|
||||||
OPT_CUBEMAP = 1,
|
OPT_REPEAT = 1,
|
||||||
OPT_MIPMAPS = 2,
|
OPT_CUBEMAP = 2,
|
||||||
OPT_NEAREST = 4,
|
OPT_MIPMAPS = 4,
|
||||||
OPT_TARGET = 8,
|
OPT_NEAREST = 8,
|
||||||
OPT_VERTEX = 16,
|
OPT_TARGET = 16,
|
||||||
OPT_PROXY = 32,
|
OPT_VERTEX = 32,
|
||||||
|
OPT_PROXY = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pipeline State Object
|
// Pipeline State Object
|
||||||
@@ -384,6 +386,13 @@ struct PSO {
|
|||||||
|
|
||||||
typedef uint16 Index;
|
typedef uint16 Index;
|
||||||
|
|
||||||
|
struct Edge {
|
||||||
|
Index a, b;
|
||||||
|
|
||||||
|
Edge() {}
|
||||||
|
Edge(Index a, Index b) : a(a), b(b) {}
|
||||||
|
};
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
short4 coord; // xyz - position, w - joint index (for entities only)
|
short4 coord; // xyz - position, w - joint index (for entities only)
|
||||||
short4 normal; // xyz - vertex normal, w - unused
|
short4 normal; // xyz - vertex normal, w - unused
|
||||||
@@ -448,8 +457,9 @@ struct MeshRange {
|
|||||||
E( TYPE_MIRROR ) \
|
E( TYPE_MIRROR ) \
|
||||||
/* water sub-passes */ \
|
/* water sub-passes */ \
|
||||||
E( WATER_DROP ) \
|
E( WATER_DROP ) \
|
||||||
E( WATER_STEP ) \
|
E( WATER_SIMULATE ) \
|
||||||
E( WATER_CAUSTICS ) \
|
E( WATER_CAUSTICS ) \
|
||||||
|
E( WATER_RAYS ) \
|
||||||
E( WATER_MASK ) \
|
E( WATER_MASK ) \
|
||||||
E( WATER_COMPOSE ) \
|
E( WATER_COMPOSE ) \
|
||||||
/* filter types */ \
|
/* filter types */ \
|
||||||
@@ -509,7 +519,7 @@ namespace Core {
|
|||||||
vec4 fogParams;
|
vec4 fogParams;
|
||||||
vec4 contacts[MAX_CONTACTS];
|
vec4 contacts[MAX_CONTACTS];
|
||||||
|
|
||||||
Texture *whiteTex, *whiteCube, *blackTex;
|
Texture *whiteTex, *whiteCube, *blackTex, *ditherTex;
|
||||||
|
|
||||||
enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass;
|
enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass;
|
||||||
|
|
||||||
@@ -639,7 +649,19 @@ namespace Core {
|
|||||||
data = 0;
|
data = 0;
|
||||||
blackTex = new Texture(1, 1, FMT_RGBA, OPT_NEAREST, &data);
|
blackTex = new Texture(1, 1, FMT_RGBA, OPT_NEAREST, &data);
|
||||||
|
|
||||||
// init settings
|
uint8 ditherData[] = {
|
||||||
|
0x00, 0x7F, 0x1F, 0x9F, 0x07, 0x87, 0x27, 0xA7,
|
||||||
|
0xBF, 0x3F, 0xDF, 0x5F, 0xC7, 0x47, 0xE7, 0x67,
|
||||||
|
0x2F, 0xAF, 0x0F, 0x8F, 0x37, 0xB7, 0x17, 0x97,
|
||||||
|
0xEF, 0x6F, 0xCF, 0x4F, 0xF7, 0x77, 0xD7, 0x57,
|
||||||
|
0x0B, 0x8B, 0x2B, 0xAB, 0x03, 0x83, 0x23, 0xA3,
|
||||||
|
0xCB, 0x4B, 0xEB, 0x6B, 0xC3, 0x43, 0xE3, 0x63,
|
||||||
|
0x3B, 0xBB, 0x1B, 0x9B, 0x33, 0xB3, 0x13, 0x93,
|
||||||
|
0xFB, 0x7B, 0xDB, 0x5B, 0xF3, 0x73, 0xD3, 0x53,
|
||||||
|
};
|
||||||
|
ditherTex = new Texture(8, 8, FMT_LUMINANCE, OPT_REPEAT | OPT_NEAREST, &ditherData);
|
||||||
|
|
||||||
|
// init settings
|
||||||
settings.version = SETTINGS_VERSION;
|
settings.version = SETTINGS_VERSION;
|
||||||
|
|
||||||
settings.detail.setFilter (Core::Settings::HIGH);
|
settings.detail.setFilter (Core::Settings::HIGH);
|
||||||
@@ -737,6 +759,7 @@ namespace Core {
|
|||||||
delete whiteTex;
|
delete whiteTex;
|
||||||
delete whiteCube;
|
delete whiteCube;
|
||||||
delete blackTex;
|
delete blackTex;
|
||||||
|
delete ditherTex;
|
||||||
|
|
||||||
GAPI::deinit();
|
GAPI::deinit();
|
||||||
|
|
||||||
|
@@ -553,8 +553,8 @@ namespace GAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool border = isShadow && Core::support.texBorder;
|
bool border = isShadow && Core::support.texBorder;
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE);
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, (opt & OPT_REPEAT) ? GL_REPEAT : (border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE));
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE);
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, (opt & OPT_REPEAT) ? GL_REPEAT : (border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE));
|
||||||
if (border) {
|
if (border) {
|
||||||
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
|
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
|
||||||
@@ -567,6 +567,7 @@ namespace GAPI {
|
|||||||
GLuint ifmt, fmt;
|
GLuint ifmt, fmt;
|
||||||
GLenum type;
|
GLenum type;
|
||||||
} formats[FMT_MAX] = {
|
} formats[FMT_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
|
||||||
|
@@ -2031,10 +2031,14 @@ struct Level : IGame {
|
|||||||
Core::setBlendMode(bmNone);
|
Core::setBlendMode(bmNone);
|
||||||
if (water && waterCache && waterCache->visible) {
|
if (water && waterCache && waterCache->visible) {
|
||||||
Core::Pass pass = Core::pass;
|
Core::Pass pass = Core::pass;
|
||||||
|
if (!camera->isUnderwater())
|
||||||
|
waterCache->renderRays();
|
||||||
waterCache->renderMask();
|
waterCache->renderMask();
|
||||||
waterCache->copyScreenToRefract();
|
waterCache->copyScreenToRefract();
|
||||||
setMainLight(player);
|
setMainLight(player);
|
||||||
waterCache->render();
|
waterCache->compose();
|
||||||
|
if (camera->isUnderwater())
|
||||||
|
waterCache->renderRays();
|
||||||
|
|
||||||
Core::pass = pass;
|
Core::pass = pass;
|
||||||
setupBinding();
|
setupBinding();
|
||||||
|
238
src/mesh.h
238
src/mesh.h
@@ -14,6 +14,9 @@ TR::ObjectTexture &whiteTile = barTile[4]; // BAR_WHITE
|
|||||||
#define DOUBLE_SIDED 2
|
#define DOUBLE_SIDED 2
|
||||||
#define MAX_ROOM_DYN_FACES 512
|
#define MAX_ROOM_DYN_FACES 512
|
||||||
|
|
||||||
|
#define WATER_VOLUME_HEIGHT (768 * 2)
|
||||||
|
#define WATER_VOLUME_OFFSET 4
|
||||||
|
|
||||||
struct Mesh : GAPI::Mesh {
|
struct Mesh : GAPI::Mesh {
|
||||||
int aIndex;
|
int aIndex;
|
||||||
|
|
||||||
@@ -139,6 +142,7 @@ struct MeshBuilder {
|
|||||||
Geometry geometry[3]; // opaque, double-side alpha, additive
|
Geometry geometry[3]; // opaque, double-side alpha, additive
|
||||||
Dynamic dynamic[3]; // lists of dynamic polygons (with animated textures) like lava, waterfalls etc.
|
Dynamic dynamic[3]; // lists of dynamic polygons (with animated textures) like lava, waterfalls etc.
|
||||||
MeshRange sprites;
|
MeshRange sprites;
|
||||||
|
MeshRange waterVolume;
|
||||||
int split;
|
int split;
|
||||||
} *rooms;
|
} *rooms;
|
||||||
|
|
||||||
@@ -154,7 +158,7 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
// procedured
|
// procedured
|
||||||
MeshRange shadowBlob;
|
MeshRange shadowBlob;
|
||||||
MeshRange quad, circle;
|
MeshRange quad, circle, box;
|
||||||
MeshRange plane;
|
MeshRange plane;
|
||||||
|
|
||||||
int transparent;
|
int transparent;
|
||||||
@@ -263,6 +267,28 @@ struct MeshBuilder {
|
|||||||
iCount += CIRCLE_SEGS * 3;
|
iCount += CIRCLE_SEGS * 3;
|
||||||
vCount += CIRCLE_SEGS + 1;
|
vCount += CIRCLE_SEGS + 1;
|
||||||
|
|
||||||
|
// box
|
||||||
|
const Index boxIndices[] = {
|
||||||
|
2, 1, 0, 3, 2, 0,
|
||||||
|
4, 5, 6, 4, 6, 7,
|
||||||
|
8, 9, 10, 8, 10, 11,
|
||||||
|
14, 13, 12, 15, 14, 12,
|
||||||
|
16, 17, 18, 16, 18, 19,
|
||||||
|
22, 21, 20, 23, 22, 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
const short4 boxCoords[] = {
|
||||||
|
{-1, -1, 1, 0}, { 1, -1, 1, 0}, { 1, 1, 1, 0}, {-1, 1, 1, 0},
|
||||||
|
{ 1, 1, 1, 0}, { 1, 1, -1, 0}, { 1, -1, -1, 0}, { 1, -1, 1, 0},
|
||||||
|
{-1, -1, -1, 0}, { 1, -1, -1, 0}, { 1, 1, -1, 0}, {-1, 1, -1, 0},
|
||||||
|
{-1, -1, -1, 0}, {-1, -1, 1, 0}, {-1, 1, 1, 0}, {-1, 1, -1, 0},
|
||||||
|
{ 1, 1, 1, 0}, {-1, 1, 1, 0}, {-1, 1, -1, 0}, { 1, 1, -1, 0},
|
||||||
|
{-1, -1, -1, 0}, { 1, -1, -1, 0}, { 1, -1, 1, 0}, {-1, -1, 1, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
iCount += COUNT(boxIndices);
|
||||||
|
vCount += COUNT(boxCoords);
|
||||||
|
|
||||||
// detailed plane
|
// detailed plane
|
||||||
#ifdef GENERATE_WATER_PLANE
|
#ifdef GENERATE_WATER_PLANE
|
||||||
iCount += SQR(PLANE_DETAIL * 2) * 6;
|
iCount += SQR(PLANE_DETAIL * 2) * 6;
|
||||||
@@ -289,6 +315,10 @@ struct MeshBuilder {
|
|||||||
aCount++;
|
aCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
range.waterVolume.iCount = 0;
|
||||||
|
if (Core::settings.detail.water > Core::Settings::MEDIUM)
|
||||||
|
buildWaterVolume(i, indices, vertices, iCount, vCount, vStartRoom);
|
||||||
|
|
||||||
for (int transp = 0; transp < 3; transp++) { // opaque, opacity
|
for (int transp = 0; transp < 3; transp++) { // opaque, opacity
|
||||||
int blendMask = getBlendMask(transp);
|
int blendMask = getBlendMask(transp);
|
||||||
|
|
||||||
@@ -470,12 +500,11 @@ struct MeshBuilder {
|
|||||||
vertices[vCount + 3].texCoord = short4( 0, 0, 0, 0 );
|
vertices[vCount + 3].texCoord = short4( 0, 0, 0, 0 );
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
Vertex &v = vertices[vCount + i];
|
Vertex &v = vertices[vCount++];
|
||||||
v.normal = short4( 0, 0, 0, 0 );
|
v.normal = short4( 0, 0, 0, 0 );
|
||||||
v.color = ubyte4( 255, 255, 255, 255 );
|
v.color = ubyte4( 255, 255, 255, 255 );
|
||||||
v.light = ubyte4( 255, 255, 255, 255 );
|
v.light = ubyte4( 255, 255, 255, 255 );
|
||||||
}
|
}
|
||||||
vCount += 4;
|
|
||||||
|
|
||||||
// circle
|
// circle
|
||||||
circle.vStart = vStartCommon;
|
circle.vStart = vStartCommon;
|
||||||
@@ -503,6 +532,25 @@ struct MeshBuilder {
|
|||||||
vertices[vCount + CIRCLE_SEGS].coord = short4( 0, 0, 0, 0 );
|
vertices[vCount + CIRCLE_SEGS].coord = short4( 0, 0, 0, 0 );
|
||||||
vCount += CIRCLE_SEGS + 1;
|
vCount += CIRCLE_SEGS + 1;
|
||||||
|
|
||||||
|
// box
|
||||||
|
box.vStart = vStartCommon;
|
||||||
|
box.iStart = iCount;
|
||||||
|
box.iCount = COUNT(boxIndices);
|
||||||
|
|
||||||
|
baseIdx = vCount - vStartCommon;
|
||||||
|
|
||||||
|
for (int i = 0; i < COUNT(boxIndices); i++)
|
||||||
|
indices[iCount++] = baseIdx + boxIndices[i];
|
||||||
|
|
||||||
|
for (int i = 0; i < COUNT(boxCoords); i++) {
|
||||||
|
Vertex &v = vertices[vCount++];
|
||||||
|
v.coord = boxCoords[i];
|
||||||
|
v.normal = short4(0, 0, 0, 32767);
|
||||||
|
v.texCoord = short4(0, 0, 0, 0);
|
||||||
|
v.color = ubyte4(255, 255, 255, 255);
|
||||||
|
v.light = ubyte4(255, 255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
// plane
|
// plane
|
||||||
#ifdef GENERATE_WATER_PLANE
|
#ifdef GENERATE_WATER_PLANE
|
||||||
plane.vStart = vStartCommon;
|
plane.vStart = vStartCommon;
|
||||||
@@ -557,6 +605,7 @@ struct MeshBuilder {
|
|||||||
r.geometry[j].ranges[k].aIndex = rangeRoom.aIndex;
|
r.geometry[j].ranges[k].aIndex = rangeRoom.aIndex;
|
||||||
|
|
||||||
r.sprites.aIndex = rangeRoom.aIndex;
|
r.sprites.aIndex = rangeRoom.aIndex;
|
||||||
|
r.waterVolume.aIndex = rangeRoom.aIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshRange rangeModel;
|
MeshRange rangeModel;
|
||||||
@@ -642,7 +691,7 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
for (int i = 0; i < room.data.fCount; i++) {
|
for (int i = 0; i < room.data.fCount; i++) {
|
||||||
TR::Face &f = room.data.faces[i];
|
TR::Face &f = room.data.faces[i];
|
||||||
if (f.vertices[0] == 0xFFFF) continue;
|
if (f.flags.value == 0xFFFF) continue;
|
||||||
|
|
||||||
TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex;
|
TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex;
|
||||||
TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex;
|
TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex;
|
||||||
@@ -663,7 +712,8 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
if (isWaterSurface(yt, s.roomAbove, room.flags.water) ||
|
if (isWaterSurface(yt, s.roomAbove, room.flags.water) ||
|
||||||
isWaterSurface(yb, s.roomBelow, room.flags.water)) {
|
isWaterSurface(yb, s.roomBelow, room.flags.water)) {
|
||||||
f.vertices[0] = 0xFFFF; // mark as unused
|
f.flags.value = 0xFFFF; // mark as unused
|
||||||
|
|
||||||
room.waterLevel = a.y;
|
room.waterLevel = a.y;
|
||||||
if (f.vCount == 4) {
|
if (f.vCount == 4) {
|
||||||
iCount -= 6;
|
iCount -= 6;
|
||||||
@@ -672,10 +722,174 @@ struct MeshBuilder {
|
|||||||
iCount -= 3;
|
iCount -= 3;
|
||||||
vCount -= 3;
|
vCount -= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preserve indices & vertices for water volume
|
||||||
|
if (room.flags.water && Core::settings.detail.water > Core::Settings::MEDIUM) {
|
||||||
|
// water volume caps
|
||||||
|
iCount += (f.vCount == 4 ? 6 : 3) * 2;
|
||||||
|
vCount += f.vCount * 2;
|
||||||
|
// water volume bounds (reserved)
|
||||||
|
iCount += 6 * f.vCount;
|
||||||
|
vCount += 4 * f.vCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Index addUniqueVertex(Array<TR::Vertex> &vertices, TR::Vertex &v) {
|
||||||
|
for (int i = 0; i < vertices.count; i++) {
|
||||||
|
TR::Vertex &o = vertices[i];
|
||||||
|
if (o.x == v.x && o.y == v.y && o.z == v.z)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return vertices.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addUniqueEdge(Array<Edge> &edges, Index a, Index b) {
|
||||||
|
for (int i = 0; i < edges.count; i++) {
|
||||||
|
Edge &e = edges[i];
|
||||||
|
if ((e.a == a && e.b == b) || (e.a == b && e.b == a)) {
|
||||||
|
edges.remove(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
edges.push(Edge(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildWaterVolume(int roomIndex, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) {
|
||||||
|
TR::Room &room = level->rooms[roomIndex];
|
||||||
|
if (!room.flags.water) return;
|
||||||
|
MeshRange &range = rooms[roomIndex].waterVolume;
|
||||||
|
|
||||||
|
Array<Edge> wEdges(128);
|
||||||
|
Array<Index> wIndices(128);
|
||||||
|
Array<TR::Vertex> wVertices(128);
|
||||||
|
|
||||||
|
for (int i = 0; i < room.data.fCount; i++) {
|
||||||
|
TR::Face &f = room.data.faces[i];
|
||||||
|
if (f.flags.value != 0xFFFF) continue;
|
||||||
|
|
||||||
|
Index idx[4];
|
||||||
|
|
||||||
|
idx[0] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[0]].vertex);
|
||||||
|
idx[1] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[1]].vertex);
|
||||||
|
idx[2] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[2]].vertex);
|
||||||
|
|
||||||
|
if (f.vCount > 3) {
|
||||||
|
idx[3] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[3]].vertex);
|
||||||
|
|
||||||
|
wIndices.push(idx[0]);
|
||||||
|
wIndices.push(idx[1]);
|
||||||
|
wIndices.push(idx[3]);
|
||||||
|
|
||||||
|
wIndices.push(idx[3]);
|
||||||
|
wIndices.push(idx[1]);
|
||||||
|
wIndices.push(idx[2]);
|
||||||
|
|
||||||
|
addUniqueEdge(wEdges, idx[0], idx[1]);
|
||||||
|
addUniqueEdge(wEdges, idx[1], idx[2]);
|
||||||
|
addUniqueEdge(wEdges, idx[2], idx[3]);
|
||||||
|
addUniqueEdge(wEdges, idx[3], idx[0]);
|
||||||
|
} else {
|
||||||
|
wIndices.push(idx[0]);
|
||||||
|
wIndices.push(idx[1]);
|
||||||
|
wIndices.push(idx[2]);
|
||||||
|
|
||||||
|
addUniqueEdge(wEdges, idx[0], idx[1]);
|
||||||
|
addUniqueEdge(wEdges, idx[1], idx[2]);
|
||||||
|
addUniqueEdge(wEdges, idx[2], idx[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wEdges.count) return;
|
||||||
|
|
||||||
|
Array<short3> wOffsets(wVertices.count);
|
||||||
|
|
||||||
|
for (int i = 0; i < wVertices.count; i++)
|
||||||
|
wOffsets.push(short3(0, WATER_VOLUME_OFFSET, 0));
|
||||||
|
|
||||||
|
for (int i = 0; i < wEdges.count; i++) {
|
||||||
|
Edge &e = wEdges[i];
|
||||||
|
TR::Vertex &a = wVertices[e.a];
|
||||||
|
TR::Vertex &b = wVertices[e.b];
|
||||||
|
int16 dx = a.z - b.z;
|
||||||
|
int16 dz = b.x - a.x;
|
||||||
|
|
||||||
|
short3 &ao = wOffsets[e.a];
|
||||||
|
ao.x = clamp(ao.x + dx, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
|
||||||
|
ao.z = clamp(ao.z + dz, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
|
||||||
|
|
||||||
|
short3 &bo = wOffsets[e.b];
|
||||||
|
bo.x = clamp(bo.x + dx, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
|
||||||
|
bo.z = clamp(bo.z + dz, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
range.vStart = vStart;
|
||||||
|
range.iCount = wIndices.count * 2 + wEdges.count * 6;
|
||||||
|
range.iStart = iCount;
|
||||||
|
|
||||||
|
for (int i = 0; i < wIndices.count; i += 3) {
|
||||||
|
indices[iCount++] = vCount + wIndices[i + 2];
|
||||||
|
indices[iCount++] = vCount + wIndices[i + 1];
|
||||||
|
indices[iCount++] = vCount + wIndices[i + 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < wIndices.count; i++)
|
||||||
|
indices[iCount++] = vCount + wIndices[i] + wVertices.count;
|
||||||
|
|
||||||
|
for (int i = 0; i < wEdges.count; i++) {
|
||||||
|
Index a = wEdges[i].a;
|
||||||
|
Index b = wEdges[i].b;
|
||||||
|
|
||||||
|
indices[iCount++] = vCount + a;
|
||||||
|
indices[iCount++] = vCount + b;
|
||||||
|
indices[iCount++] = vCount + a + wVertices.count;
|
||||||
|
|
||||||
|
indices[iCount++] = vCount + b;
|
||||||
|
indices[iCount++] = vCount + b + wVertices.count;
|
||||||
|
indices[iCount++] = vCount + a + wVertices.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < wVertices.count; i++) {
|
||||||
|
TR::Vertex &v = wVertices[i];
|
||||||
|
short3 &o = wOffsets[i];
|
||||||
|
|
||||||
|
v.x += o.x;
|
||||||
|
v.y += o.y;
|
||||||
|
v.z += o.z;
|
||||||
|
|
||||||
|
vertices[vCount++].coord = short4(v.x, v.y, v.z, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < wVertices.count; i++) {
|
||||||
|
TR::Vertex &v = wVertices[i];
|
||||||
|
|
||||||
|
v.y += WATER_VOLUME_HEIGHT - WATER_VOLUME_OFFSET - WATER_VOLUME_OFFSET;
|
||||||
|
|
||||||
|
const vec3 sectorOffsets[] = {
|
||||||
|
vec3(-8, 0, -8),
|
||||||
|
vec3( 8, 0, -8),
|
||||||
|
vec3( 8, 0, 8),
|
||||||
|
vec3(-8, 0, 8),
|
||||||
|
};
|
||||||
|
|
||||||
|
int16 floor = 32000;
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
vec3 pos = room.getOffset() + vec3(v.x, v.y, v.z) + sectorOffsets[j];
|
||||||
|
int16 rIndex = roomIndex;
|
||||||
|
TR::Room::Sector *sector = level->getSector(rIndex, pos);
|
||||||
|
if (sector->floor == TR::NO_FLOOR || !level->rooms[rIndex].flags.water) continue;
|
||||||
|
floor = min(floor, int16(level->getFloor(sector, pos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
floor -= WATER_VOLUME_OFFSET * 3;
|
||||||
|
|
||||||
|
v.y = min(v.y, floor);
|
||||||
|
|
||||||
|
vertices[vCount++].coord = short4(v.x, v.y, v.z, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline int getBlendMask(int texAttribute) {
|
inline int getBlendMask(int texAttribute) {
|
||||||
ASSERT(texAttribute < 3);
|
ASSERT(texAttribute < 3);
|
||||||
return 1 << texAttribute;
|
return 1 << texAttribute;
|
||||||
@@ -691,7 +905,7 @@ struct MeshBuilder {
|
|||||||
TR::Face &f = d.faces[j];
|
TR::Face &f = d.faces[j];
|
||||||
TR::ObjectTexture &t = level.objectTextures[f.flags.texture];
|
TR::ObjectTexture &t = level.objectTextures[f.flags.texture];
|
||||||
|
|
||||||
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
if (f.flags.value == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
||||||
|
|
||||||
CHECK_ROOM_NORMAL(f);
|
CHECK_ROOM_NORMAL(f);
|
||||||
|
|
||||||
@@ -725,7 +939,7 @@ struct MeshBuilder {
|
|||||||
TR::Face &f = d.faces[j];
|
TR::Face &f = d.faces[j];
|
||||||
TR::ObjectTexture &t = level.objectTextures[f.flags.texture];
|
TR::ObjectTexture &t = level.objectTextures[f.flags.texture];
|
||||||
|
|
||||||
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
if (f.flags.value == 0xFFFF) continue; // skip if marks as unused (removing water planes)
|
||||||
|
|
||||||
if (!(blendMask & getBlendMask(t.attribute)))
|
if (!(blendMask & getBlendMask(t.attribute)))
|
||||||
continue;
|
continue;
|
||||||
@@ -1220,6 +1434,16 @@ struct MeshBuilder {
|
|||||||
void renderPlane() {
|
void renderPlane() {
|
||||||
mesh->render(plane);
|
mesh->render(plane);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderBox() {
|
||||||
|
mesh->render(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderWaterVolume(int roomIndex) {
|
||||||
|
MeshRange &range = rooms[roomIndex].waterVolume;
|
||||||
|
if (range.iCount)
|
||||||
|
mesh->render(range);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -7,10 +7,10 @@ struct Shader : GAPI::Shader {
|
|||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
/* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4,
|
/* shader */ SPRITE = 0, FLASH, ROOM, ENTITY, MIRROR,
|
||||||
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE = 1, FILTER_GRAYSCALE = 2, FILTER_BLUR = 3, FILTER_EQUIRECTANGULAR = 4,
|
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE, FILTER_GRAYSCALE, FILTER_BLUR, FILTER_EQUIRECTANGULAR,
|
||||||
/* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4,
|
/* water */ WATER_DROP = 0, WATER_SIMULATE, WATER_CAUSTICS, WATER_RAYS, WATER_MASK, WATER_COMPOSE,
|
||||||
MAX = 5
|
MAX = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
Shader(Core::Pass pass, Type type, int *def, int defCount) : GAPI::Shader() {
|
Shader(Core::Pass pass, Type type, int *def, int defCount) : GAPI::Shader() {
|
||||||
|
@@ -66,6 +66,13 @@ uniform sampler2D sNormal;
|
|||||||
#else
|
#else
|
||||||
gl_Position = vec4(coord.xyz, 1.0);
|
gl_Position = vec4(coord.xyz, 1.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WATER_RAYS
|
||||||
|
vCoord = aCoord.xyz + uParam.xyz;
|
||||||
|
vec4 cp = uViewProj * vec4(vCoord, 1.0);
|
||||||
|
vProjCoord = cp;
|
||||||
|
gl_Position = cp;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
vViewVec = uViewPos.xyz - vCoord.xyz;
|
vViewVec = uViewPos.xyz - vCoord.xyz;
|
||||||
vLightVec = uLightPos.xyz - vCoord.xyz;
|
vLightVec = uLightPos.xyz - vCoord.xyz;
|
||||||
@@ -81,7 +88,7 @@ uniform sampler2D sNormal;
|
|||||||
|
|
||||||
float calcFresnel(float VoH, float f0) {
|
float calcFresnel(float VoH, float f0) {
|
||||||
float f = pow(1.0 - VoH, 5.0);
|
float f = pow(1.0 - VoH, 5.0);
|
||||||
return f + f0 * (1.0f - f);
|
return f + f0 * (1.0 - f);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 applyFog(vec3 color, vec3 fogColor, float factor) {
|
vec3 applyFog(vec3 color, vec3 fogColor, float factor) {
|
||||||
@@ -130,7 +137,7 @@ uniform sampler2D sNormal;
|
|||||||
return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.0005;
|
return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.0005;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 calc() {
|
vec4 simulate() {
|
||||||
vec2 tc = vTexCoord;
|
vec2 tc = vTexCoord;
|
||||||
|
|
||||||
if (texture2D(sMask, tc).x < 0.5)
|
if (texture2D(sMask, tc).x < 0.5)
|
||||||
@@ -156,6 +163,7 @@ uniform sampler2D sNormal;
|
|||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WATER_CAUSTICS
|
#ifdef WATER_CAUSTICS
|
||||||
vec4 caustics() {
|
vec4 caustics() {
|
||||||
float rOldArea = length(dFdx(vOldPos)) * length(dFdy(vOldPos));
|
float rOldArea = length(dFdx(vOldPos)) * length(dFdy(vOldPos));
|
||||||
@@ -166,6 +174,45 @@ uniform sampler2D sNormal;
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WATER_RAYS
|
||||||
|
|
||||||
|
float boxIntersect(vec3 rayPos, vec3 rayDir, vec3 center, vec3 hsize) {
|
||||||
|
center -= rayPos;
|
||||||
|
vec3 bMin = (center - hsize) / rayDir;
|
||||||
|
vec3 bMax = (center + hsize) / rayDir;
|
||||||
|
vec3 m = min(bMin, bMax);
|
||||||
|
return max(0.0, max(m.x, max(m.y, m.z)));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 rays() {
|
||||||
|
#define RAY_STEPS 16.0
|
||||||
|
|
||||||
|
vec3 viewVec = normalize(vViewVec);
|
||||||
|
|
||||||
|
float t = boxIntersect(uViewPos.xyz, -viewVec, uPosScale[0].xyz, uPosScale[1].xyz);
|
||||||
|
|
||||||
|
vec3 p0 = uViewPos.xyz - viewVec * t;
|
||||||
|
vec3 p1 = vCoord.xyz;
|
||||||
|
|
||||||
|
float dither = texture2D(sMask, gl_FragCoord.xy * (1.0 / 8.0)).x;
|
||||||
|
vec3 step = (p1 - p0) / RAY_STEPS;
|
||||||
|
vec3 pos = p0 + step * dither;
|
||||||
|
|
||||||
|
float sum = 0.0;
|
||||||
|
for (float i = 0.0; i < RAY_STEPS; i++) {
|
||||||
|
vec3 wpos = (pos - uPosScale[0].xyz) / uPosScale[1].xyz;
|
||||||
|
vec2 tc = wpos.xz * 0.5 + 0.5;
|
||||||
|
float light = texture2D(sReflect, tc).x;
|
||||||
|
sum += light * (1.0 - (clamp(wpos.y, -1.0, 1.0) * 0.5 + 0.5));
|
||||||
|
pos += step;
|
||||||
|
}
|
||||||
|
sum /= RAY_STEPS;
|
||||||
|
sum *= uParam.w;
|
||||||
|
|
||||||
|
return vec4(UNDERWATER_COLOR * sum, 1.0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
vec4 mask() {
|
vec4 mask() {
|
||||||
return vec4(0.0);
|
return vec4(0.0);
|
||||||
}
|
}
|
||||||
@@ -205,14 +252,18 @@ uniform sampler2D sNormal;
|
|||||||
return drop();
|
return drop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WATER_STEP
|
#ifdef WATER_SIMULATE
|
||||||
return calc();
|
return simulate();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WATER_CAUSTICS
|
#ifdef WATER_CAUSTICS
|
||||||
return caustics();
|
return caustics();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WATER_RAYS
|
||||||
|
return rays();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WATER_MASK
|
#ifdef WATER_MASK
|
||||||
return mask();
|
return mask();
|
||||||
#endif
|
#endif
|
||||||
|
54
src/utils.h
54
src/utils.h
@@ -1594,4 +1594,58 @@ namespace String {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Array {
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
T *items;
|
||||||
|
|
||||||
|
Array(int capacity = 32) : capacity(capacity), count(0), items(NULL) {}
|
||||||
|
|
||||||
|
~Array() {
|
||||||
|
free(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
int push(const T &item) {
|
||||||
|
if (!items)
|
||||||
|
items = (T*)malloc(capacity * sizeof(T));
|
||||||
|
|
||||||
|
if (count == capacity) {
|
||||||
|
capacity += capacity + capacity / 2;
|
||||||
|
if (items)
|
||||||
|
items = (T*)realloc(items, capacity * sizeof(T));
|
||||||
|
else
|
||||||
|
items = (T*)malloc(capacity * sizeof(T));
|
||||||
|
}
|
||||||
|
items[count] = item;
|
||||||
|
return count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pop() {
|
||||||
|
ASSERT(count > 0);
|
||||||
|
return --count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeFast(int index) {
|
||||||
|
(*this)[index] = (*this)[--count];
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(int index) {
|
||||||
|
count--;
|
||||||
|
ASSERT(count >= 0);
|
||||||
|
for (int i = index; i < count; i++)
|
||||||
|
items[i] = items[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[] (int index) {
|
||||||
|
ASSERT(index >= 0 && index < count);
|
||||||
|
return items[index];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user