mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-14 17:14:29 +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) {
|
||||
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_RAYS, fx, RS_COLOR_WRITE | RS_DEPTH_TEST);
|
||||
compile(Core::passWater, Shader::WATER_CAUSTICS, fx, RS_COLOR_WRITE);
|
||||
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);
|
||||
|
||||
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(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
|
||||
}
|
||||
|
||||
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() {
|
||||
if (!visible) return;
|
||||
PROFILE_MARKER("WATER_RENDER_MASK");
|
||||
PROFILE_MARKER("WATER_MASK");
|
||||
// mask underwater geometry by zero alpha
|
||||
game->setShader(Core::passWater, Shader::WATER_MASK);
|
||||
Core::active.shader->setParam(uTexParam, vec4(1.0f));
|
||||
@@ -891,9 +930,9 @@ struct WaterCache {
|
||||
camera->setup(true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
void compose() {
|
||||
if (!visible) return;
|
||||
PROFILE_MARKER("WATER_RENDER");
|
||||
PROFILE_MARKER("WATER_COMPOSE");
|
||||
for (int i = 0; i < count; i++) {
|
||||
Item &item = items[i];
|
||||
if (!item.visible) continue;
|
||||
|
@@ -523,7 +523,7 @@ struct Camera : ICamera {
|
||||
smooth = false;
|
||||
|
||||
fov = firstPerson ? 90.0f : 65.0f;
|
||||
znear = firstPerson ? 8.0f : 32.0f;
|
||||
znear = firstPerson ? 16.0f : 32.0f;
|
||||
zfar = 45.0f * 1024.0f;
|
||||
|
||||
#ifdef _OS_PSP
|
||||
|
41
src/core.h
41
src/core.h
@@ -352,6 +352,7 @@ enum RenderState {
|
||||
|
||||
// Texture image format
|
||||
enum TexFormat {
|
||||
FMT_LUMINANCE,
|
||||
FMT_RGBA,
|
||||
FMT_RGB16,
|
||||
FMT_RGBA16,
|
||||
@@ -364,12 +365,13 @@ enum TexFormat {
|
||||
|
||||
// Texture options
|
||||
enum TexOption {
|
||||
OPT_CUBEMAP = 1,
|
||||
OPT_MIPMAPS = 2,
|
||||
OPT_NEAREST = 4,
|
||||
OPT_TARGET = 8,
|
||||
OPT_VERTEX = 16,
|
||||
OPT_PROXY = 32,
|
||||
OPT_REPEAT = 1,
|
||||
OPT_CUBEMAP = 2,
|
||||
OPT_MIPMAPS = 4,
|
||||
OPT_NEAREST = 8,
|
||||
OPT_TARGET = 16,
|
||||
OPT_VERTEX = 32,
|
||||
OPT_PROXY = 64,
|
||||
};
|
||||
|
||||
// Pipeline State Object
|
||||
@@ -384,6 +386,13 @@ struct PSO {
|
||||
|
||||
typedef uint16 Index;
|
||||
|
||||
struct Edge {
|
||||
Index a, b;
|
||||
|
||||
Edge() {}
|
||||
Edge(Index a, Index b) : a(a), b(b) {}
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
short4 coord; // xyz - position, w - joint index (for entities only)
|
||||
short4 normal; // xyz - vertex normal, w - unused
|
||||
@@ -448,8 +457,9 @@ struct MeshRange {
|
||||
E( TYPE_MIRROR ) \
|
||||
/* water sub-passes */ \
|
||||
E( WATER_DROP ) \
|
||||
E( WATER_STEP ) \
|
||||
E( WATER_SIMULATE ) \
|
||||
E( WATER_CAUSTICS ) \
|
||||
E( WATER_RAYS ) \
|
||||
E( WATER_MASK ) \
|
||||
E( WATER_COMPOSE ) \
|
||||
/* filter types */ \
|
||||
@@ -509,7 +519,7 @@ namespace Core {
|
||||
vec4 fogParams;
|
||||
vec4 contacts[MAX_CONTACTS];
|
||||
|
||||
Texture *whiteTex, *whiteCube, *blackTex;
|
||||
Texture *whiteTex, *whiteCube, *blackTex, *ditherTex;
|
||||
|
||||
enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass;
|
||||
|
||||
@@ -639,7 +649,19 @@ namespace Core {
|
||||
data = 0;
|
||||
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.detail.setFilter (Core::Settings::HIGH);
|
||||
@@ -737,6 +759,7 @@ namespace Core {
|
||||
delete whiteTex;
|
||||
delete whiteCube;
|
||||
delete blackTex;
|
||||
delete ditherTex;
|
||||
|
||||
GAPI::deinit();
|
||||
|
||||
|
@@ -553,8 +553,8 @@ namespace GAPI {
|
||||
}
|
||||
|
||||
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_T, 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, (opt & OPT_REPEAT) ? GL_REPEAT : (border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE));
|
||||
if (border) {
|
||||
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
|
||||
@@ -566,7 +566,8 @@ namespace GAPI {
|
||||
static const struct FormatDesc {
|
||||
GLuint ifmt, fmt;
|
||||
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_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 }, // RGB16
|
||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, // RGBA16
|
||||
|
@@ -2031,10 +2031,14 @@ struct Level : IGame {
|
||||
Core::setBlendMode(bmNone);
|
||||
if (water && waterCache && waterCache->visible) {
|
||||
Core::Pass pass = Core::pass;
|
||||
if (!camera->isUnderwater())
|
||||
waterCache->renderRays();
|
||||
waterCache->renderMask();
|
||||
waterCache->copyScreenToRefract();
|
||||
setMainLight(player);
|
||||
waterCache->render();
|
||||
waterCache->compose();
|
||||
if (camera->isUnderwater())
|
||||
waterCache->renderRays();
|
||||
|
||||
Core::pass = pass;
|
||||
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 MAX_ROOM_DYN_FACES 512
|
||||
|
||||
#define WATER_VOLUME_HEIGHT (768 * 2)
|
||||
#define WATER_VOLUME_OFFSET 4
|
||||
|
||||
struct Mesh : GAPI::Mesh {
|
||||
int aIndex;
|
||||
|
||||
@@ -139,6 +142,7 @@ struct MeshBuilder {
|
||||
Geometry geometry[3]; // opaque, double-side alpha, additive
|
||||
Dynamic dynamic[3]; // lists of dynamic polygons (with animated textures) like lava, waterfalls etc.
|
||||
MeshRange sprites;
|
||||
MeshRange waterVolume;
|
||||
int split;
|
||||
} *rooms;
|
||||
|
||||
@@ -154,7 +158,7 @@ struct MeshBuilder {
|
||||
|
||||
// procedured
|
||||
MeshRange shadowBlob;
|
||||
MeshRange quad, circle;
|
||||
MeshRange quad, circle, box;
|
||||
MeshRange plane;
|
||||
|
||||
int transparent;
|
||||
@@ -263,6 +267,28 @@ struct MeshBuilder {
|
||||
iCount += CIRCLE_SEGS * 3;
|
||||
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
|
||||
#ifdef GENERATE_WATER_PLANE
|
||||
iCount += SQR(PLANE_DETAIL * 2) * 6;
|
||||
@@ -289,6 +315,10 @@ struct MeshBuilder {
|
||||
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
|
||||
int blendMask = getBlendMask(transp);
|
||||
|
||||
@@ -470,12 +500,11 @@ struct MeshBuilder {
|
||||
vertices[vCount + 3].texCoord = short4( 0, 0, 0, 0 );
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Vertex &v = vertices[vCount + i];
|
||||
Vertex &v = vertices[vCount++];
|
||||
v.normal = short4( 0, 0, 0, 0 );
|
||||
v.color = ubyte4( 255, 255, 255, 255 );
|
||||
v.light = ubyte4( 255, 255, 255, 255 );
|
||||
}
|
||||
vCount += 4;
|
||||
|
||||
// circle
|
||||
circle.vStart = vStartCommon;
|
||||
@@ -503,6 +532,25 @@ struct MeshBuilder {
|
||||
vertices[vCount + CIRCLE_SEGS].coord = short4( 0, 0, 0, 0 );
|
||||
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
|
||||
#ifdef GENERATE_WATER_PLANE
|
||||
plane.vStart = vStartCommon;
|
||||
@@ -557,6 +605,7 @@ struct MeshBuilder {
|
||||
r.geometry[j].ranges[k].aIndex = rangeRoom.aIndex;
|
||||
|
||||
r.sprites.aIndex = rangeRoom.aIndex;
|
||||
r.waterVolume.aIndex = rangeRoom.aIndex;
|
||||
}
|
||||
|
||||
MeshRange rangeModel;
|
||||
@@ -642,7 +691,7 @@ struct MeshBuilder {
|
||||
|
||||
for (int i = 0; i < room.data.fCount; 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 &b = room.data.vertices[f.vertices[1]].vertex;
|
||||
@@ -663,7 +712,8 @@ struct MeshBuilder {
|
||||
|
||||
if (isWaterSurface(yt, s.roomAbove, 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;
|
||||
if (f.vCount == 4) {
|
||||
iCount -= 6;
|
||||
@@ -672,10 +722,174 @@ struct MeshBuilder {
|
||||
iCount -= 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) {
|
||||
ASSERT(texAttribute < 3);
|
||||
return 1 << texAttribute;
|
||||
@@ -691,7 +905,7 @@ struct MeshBuilder {
|
||||
TR::Face &f = d.faces[j];
|
||||
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);
|
||||
|
||||
@@ -725,7 +939,7 @@ struct MeshBuilder {
|
||||
TR::Face &f = d.faces[j];
|
||||
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)))
|
||||
continue;
|
||||
@@ -1220,6 +1434,16 @@ struct MeshBuilder {
|
||||
void renderPlane() {
|
||||
mesh->render(plane);
|
||||
}
|
||||
|
||||
void renderBox() {
|
||||
mesh->render(box);
|
||||
}
|
||||
|
||||
void renderWaterVolume(int roomIndex) {
|
||||
MeshRange &range = rooms[roomIndex].waterVolume;
|
||||
if (range.iCount)
|
||||
mesh->render(range);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -7,10 +7,10 @@ struct Shader : GAPI::Shader {
|
||||
|
||||
enum Type {
|
||||
DEFAULT = 0,
|
||||
/* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4,
|
||||
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE = 1, FILTER_GRAYSCALE = 2, FILTER_BLUR = 3, FILTER_EQUIRECTANGULAR = 4,
|
||||
/* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4,
|
||||
MAX = 5
|
||||
/* shader */ SPRITE = 0, FLASH, ROOM, ENTITY, MIRROR,
|
||||
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE, FILTER_GRAYSCALE, FILTER_BLUR, FILTER_EQUIRECTANGULAR,
|
||||
/* water */ WATER_DROP = 0, WATER_SIMULATE, WATER_CAUSTICS, WATER_RAYS, WATER_MASK, WATER_COMPOSE,
|
||||
MAX = 6
|
||||
};
|
||||
|
||||
Shader(Core::Pass pass, Type type, int *def, int defCount) : GAPI::Shader() {
|
||||
|
@@ -66,6 +66,13 @@ uniform sampler2D sNormal;
|
||||
#else
|
||||
gl_Position = vec4(coord.xyz, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef WATER_RAYS
|
||||
vCoord = aCoord.xyz + uParam.xyz;
|
||||
vec4 cp = uViewProj * vec4(vCoord, 1.0);
|
||||
vProjCoord = cp;
|
||||
gl_Position = cp;
|
||||
#endif
|
||||
#endif
|
||||
vViewVec = uViewPos.xyz - vCoord.xyz;
|
||||
vLightVec = uLightPos.xyz - vCoord.xyz;
|
||||
@@ -81,7 +88,7 @@ uniform sampler2D sNormal;
|
||||
|
||||
float calcFresnel(float VoH, float f0) {
|
||||
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) {
|
||||
@@ -130,7 +137,7 @@ uniform sampler2D sNormal;
|
||||
return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.0005;
|
||||
}
|
||||
|
||||
vec4 calc() {
|
||||
vec4 simulate() {
|
||||
vec2 tc = vTexCoord;
|
||||
|
||||
if (texture2D(sMask, tc).x < 0.5)
|
||||
@@ -156,6 +163,7 @@ uniform sampler2D sNormal;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
#ifdef WATER_CAUSTICS
|
||||
vec4 caustics() {
|
||||
float rOldArea = length(dFdx(vOldPos)) * length(dFdy(vOldPos));
|
||||
@@ -166,6 +174,45 @@ uniform sampler2D sNormal;
|
||||
}
|
||||
#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() {
|
||||
return vec4(0.0);
|
||||
}
|
||||
@@ -205,14 +252,18 @@ uniform sampler2D sNormal;
|
||||
return drop();
|
||||
#endif
|
||||
|
||||
#ifdef WATER_STEP
|
||||
return calc();
|
||||
#ifdef WATER_SIMULATE
|
||||
return simulate();
|
||||
#endif
|
||||
|
||||
#ifdef WATER_CAUSTICS
|
||||
return caustics();
|
||||
#endif
|
||||
|
||||
#ifdef WATER_RAYS
|
||||
return rays();
|
||||
#endif
|
||||
|
||||
#ifdef WATER_MASK
|
||||
return mask();
|
||||
#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
|
||||
|
Reference in New Issue
Block a user