From 1a8e61e05eaf164f577180b71adf96fae347985e Mon Sep 17 00:00:00 2001 From: XProger Date: Fri, 27 Jan 2017 02:56:24 +0300 Subject: [PATCH] #23 water shader, fixed simulation step, caustics --- src/camera.h | 6 +- src/core.h | 2 + src/debug.h | 11 +- src/filter.glsl | 5 + src/format.h | 20 ++-- src/gui.glsl | 5 + src/level.h | 219 +++++++++++++++++++++----------------- src/mesh.h | 110 ++++++++++++++++++- src/platform/win/main.cpp | 3 + src/shader.glsl | 9 +- src/shader.h | 10 +- src/utils.h | 11 ++ src/water.glsl | 180 +++++++++++++++++++++++++++++++ 13 files changed, 462 insertions(+), 129 deletions(-) create mode 100644 src/water.glsl diff --git a/src/camera.h b/src/camera.h index a79d282..c126702 100644 --- a/src/camera.h +++ b/src/camera.h @@ -89,11 +89,11 @@ struct Camera : Controller { } else #endif { - if (Input::down[ikMouseR]) { - vec2 delta = Input::mouse.pos - Input::mouse.start.R; + if (Input::down[ikMouseL]) { + vec2 delta = Input::mouse.pos - Input::mouse.start.L; angleAdv.x -= delta.y * 0.01f; angleAdv.y += delta.x * 0.01f; - Input::mouse.start.R = Input::mouse.pos; + Input::mouse.start.L = Input::mouse.pos; } angleAdv.x -= Input::joy.R.y * 2.0f * Core::deltaTime; diff --git a/src/core.h b/src/core.h index c6ad76a..4afd3a3 100644 --- a/src/core.h +++ b/src/core.h @@ -341,6 +341,7 @@ namespace Core { if (!target) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glColorMask(true, true, true, true); + setViewport(0, 0, Core::width, Core::height); return; } @@ -362,6 +363,7 @@ namespace Core { if (depth) glColorMask(false, false, false, false); + setViewport(0, 0, target->width, target->height); } void resetStates() { diff --git a/src/debug.h b/src/debug.h index 492bad8..fe87eaf 100644 --- a/src/debug.h +++ b/src/debug.h @@ -411,7 +411,6 @@ namespace Debug { Box box = controller->animation.getBoundingBox(vec3(0.0f), 0); Debug::Draw::box(matrix, box.min, box.max, vec4(1.0)); -/* for (int j = 0; j < level.modelsCount; j++) { TR::Model &m = level.models[j]; @@ -424,7 +423,7 @@ namespace Debug { int fSize = sizeof(TR::AnimFrame) + m.mCount * sizeof(uint16) * 2; TR::Animation *anim = controller->animation; - TR::AnimFrame *frame = (TR::AnimFrame*)&level.frameData[(anim->frameOffset + (controller ? int((controller->animTime * 30.0f / anim->frameRate)) * fSize : 0) >> 1)]; + TR::AnimFrame *frame = (TR::AnimFrame*)&level.frameData[(anim->frameOffset + (controller ? int((controller->animation.time * 30.0f / anim->frameRate)) * fSize : 0) >> 1)]; //mat4 m; //m.identity(); @@ -461,12 +460,13 @@ namespace Debug { joint = joint * rot; int offset = level.meshOffsets[m.mStart + k]; - TR::Mesh *mesh = (TR::Mesh*)&level.meshData[offset / 2]; - Debug::Draw::sphere(matrix * joint * mesh->center, mesh->collider.radius, mesh->collider.info ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f)); + TR::Mesh *mesh = (TR::Mesh*)&level.meshes[offset]; + if (!mesh->flags) continue; + Debug::Draw::sphere(matrix * joint * mesh->center, mesh->radius, vec4(0, 1, 1, 0.5f)); { //if (e.id != 0) { char buf[255]; - sprintf(buf, "(%d) radius %d info %d flags %d", e.id, (int)mesh->collider.radius, (int)mesh->collider.info, (int)mesh->collider.flags); + sprintf(buf, "(%d) radius %d flags %d", (int)e.type, (int)mesh->radius, (int)mesh->flags); Debug::Draw::text(matrix * joint * mesh->center, vec4(0.5, 1, 0.5, 1), buf); } @@ -477,7 +477,6 @@ namespace Debug { } } -*/ } } diff --git a/src/filter.glsl b/src/filter.glsl index 0de2d39..ec40d3e 100644 --- a/src/filter.glsl +++ b/src/filter.glsl @@ -1,4 +1,9 @@ R"====( +#ifdef GL_ES + precision highp int; + precision highp float; +#endif + varying vec2 vTexCoord; #define FILTER_DOWNSAMPLE 0 diff --git a/src/format.h b/src/format.h index 93a8cb0..eececfe 100644 --- a/src/format.h +++ b/src/format.h @@ -320,10 +320,10 @@ namespace TR { uint16 boxIndex:15, end:1; }; - struct Collider { - uint16 radius:10, info:6; - uint16 flags:16; - }; + //struct Collider { + // uint16 radius:10, info:6; + // uint16 flags:16; + //}; // internal mesh structure struct Mesh { @@ -333,8 +333,9 @@ namespace TR { short4 normal; }; - Collider collider; TR::Vertex center; + uint16 radius; + uint16 flags; int16 vCount; int16 rCount; int16 tCount; @@ -669,7 +670,7 @@ namespace TR { uint16 volume; uint16 chance; // If !=0 and ((rand()&0x7fff) > Chance), this sound is not played union { - struct { uint16 replay:2, count:6, unknown:5, pitch:1, gain:1, :1; }; + struct { uint16 mode:2, count:4, unused:6, fixed:1, pitch:1, gain:1, :1; }; uint16 value; } flags; }; @@ -872,7 +873,6 @@ namespace TR { } else if (version == VER_TR1_PC) { // tiles stream.read(tiles8, stream.read(tilesCount)); - stream.read(unused); } if (!version /*PSX cutscene*/ || version == VER_TR1_PSX) { @@ -880,8 +880,9 @@ namespace TR { // tiles stream.read(tiles4, tilesCount = 13); stream.read(cluts, 512); - stream.seek(0x4000 + 4); + stream.seek(0x4000); } + stream.read(unused); // rooms rooms = new Room[stream.read(roomsCount)]; @@ -1116,7 +1117,8 @@ namespace TR { mesh.offset = stream.pos - start; stream.read(mesh.center); - stream.read(mesh.collider); + stream.read(mesh.radius); + stream.read(mesh.flags); stream.read(mesh.vCount); switch (version) { diff --git a/src/gui.glsl b/src/gui.glsl index 295a9bf..1bdcd79 100644 --- a/src/gui.glsl +++ b/src/gui.glsl @@ -1,4 +1,9 @@ R"====( +#ifdef GL_ES + precision highp int; + precision highp float; +#endif + varying vec2 vTexCoord; #ifdef VERTEX diff --git a/src/level.h b/src/level.h index 502a155..b93c283 100644 --- a/src/level.h +++ b/src/level.h @@ -47,6 +47,7 @@ struct Level { Texture *waterRefract; Texture *waterReflect; Texture *waterCaustics; + float waterTimer; float time; float clipHeight; @@ -130,7 +131,6 @@ struct Level { int size = 64 >> (i << 1); Core::active.shader->setParam(uParam, vec4(float(size << 2), 0.0f, 0.0f, 0.0f)); - Core::setViewport(0, 0, size, size); for (int j = 0; j < 6; j++) { Texture *src = textures[j * 4 + i - 1]; @@ -364,15 +364,23 @@ struct Level { delete camera; } + #define WATER_SIMULATE_TIMESTEP (1.0f / 40.0f) + void initTextures() { shadow = new Texture(1024, 1024, Texture::SHADOW, false); water[0] = new Texture(256, 256, Texture::RGBA_HALF, false); water[1] = new Texture(256, 256, Texture::RGBA_HALF, false); - waterRefract = new Texture(1024, 1024, Texture::RGBA, false); - waterReflect = new Texture(1024, 1024, Texture::RGBA, false); - waterCaustics = new Texture(1024, 1024, Texture::RGBA, false); + waterRefract = NULL; + waterReflect = new Texture( 512, 512, Texture::RGBA, false); + waterCaustics = new Texture( 256, 256, Texture::RGBA, false); + + waterTimer = WATER_SIMULATE_TIMESTEP; + + Core::setTarget(waterRefract); // clear refract mask (frame borders) + Core::clear(vec4(0.0f)); + Core::setTarget(NULL); if (!level.tilesCount) { atlas = NULL; @@ -415,6 +423,12 @@ struct Level { level.tiles = NULL; } + void checkRefractTexture() { + if (waterRefract && Core::width <= waterRefract->width && Core::height <= waterRefract->height) return; + delete waterRefract; + waterRefract = new Texture(nextPow2(Core::width), nextPow2(Core::height), Texture::RGBA, false); + } + void initShaders() { char def[255], ext[255]; @@ -721,6 +735,7 @@ struct Level { void update() { time += Core::deltaTime; + waterTimer += Core::deltaTime; for (int i = 0; i < level.entitiesCount; i++) if (level.entities[i].type != TR::Entity::NONE) { @@ -861,10 +876,10 @@ struct Level { Core::setBlending(bmAlpha); Texture *target = targets[0]->cube ? targets[0] : targets[i * stride]; Core::setTarget(target, i); - Core::setViewport(0, 0, target->width, target->height); Core::clear(vec4(0, 0, 0, 1)); renderScene(roomIndex); } + Core::setTarget(NULL); } void renderShadows(int roomIndex) { @@ -874,7 +889,6 @@ struct Level { setPassShader(Core::passShadow); Core::setBlending(bmNone); Core::setTarget(shadow); - Core::setViewport(0, 0, shadow->width, shadow->height); Core::clear(vec4(1.0)); Core::setCulling(cfBack); renderScene(roomIndex); @@ -891,72 +905,40 @@ struct Level { shadow->bind(sShadow); renderScene(roomIndex); } - - void renderWater() { + void waterSimulate() { + if (waterTimer < WATER_SIMULATE_TIMESTEP) return; + Core::active.shader->setParam(uParam, vec4(1.0f / water[0]->width, 1.0f / water[0]->height, 0.0f, 0.0f)); + Core::active.shader->setParam(uType, Shader::WATER_STEP); + + while (waterTimer >= WATER_SIMULATE_TIMESTEP) { + // water step + water[0]->bind(sDiffuse); + Core::setTarget(water[1]); + mesh->renderQuad(); + swap(water[0], water[1]); + waterTimer -= WATER_SIMULATE_TIMESTEP; + } + + // calc normals + //water[0]->bind(sDiffuse); + //Core::setTarget(water[1]); + //Core::active.shader->setParam(uType, Shader::WATER_NORMAL); + //mesh->renderQuad(); + //swap(water[0], water[1]); + + // calc caustics + Core::active.shader->setParam(uScale, 1.0f / PLANE_DETAIL); + water[0]->bind(sNormal); + waterCaustics->unbind(sReflect); + Core::setTarget(waterCaustics); + Core::active.shader->setParam(uType, Shader::WATER_CAUSTICS); + mesh->renderPlane(); + Core::active.shader->setParam(uScale, 1.0f); } - void render() { - clipHeight = 1000000.0f; - clipSign = 1.0f; - Core::resetStates(); - - ambientCache->precessQueue(); - renderShadows(lara->getRoomIndex()); - Core::setViewport(0, 0, Core::width, Core::height); - renderCompose(camera->getRoomIndex()); - - - Core::setViewport(0, 0, waterRefract->width, waterRefract->height); - - bool underwater = level.rooms[camera->getRoomIndex()].flags.water; - - Core::setTarget(waterRefract); - renderCompose(camera->getRoomIndex()); - - setPassShader(Core::passWater); - atlas->bind(sNormal); - atlas->bind(sReflect); - - Core::active.shader->setParam(uType, Shader::WATER_MASK); - Core::active.shader->setParam(uViewProj, Core::mViewProj); - Core::setBlending(bmNone); - Core::setCulling(cfNone); - glColorMask(false, false, false, true); - mesh->renderQuad(); - glColorMask(true, true, true, true); - - Core::setTarget(waterReflect); - vec3 p = vec3(40448, 3584, 60928); - vec3 n = vec3(0, 1, 0); - - vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p)); - - camera->reflectPlane = &reflectPlane; - Core::setCulling(cfBack); - clipSign = underwater ? -1.0f : 1.0f; - clipHeight = 3584.0 * clipSign; - renderCompose(underwater ? 13 : lara->getRoomIndex()); - clipHeight = 1000000.0f; - clipSign = 1.0f; - Core::setCulling(cfFront); - camera->reflectPlane = NULL; - - Core::setTarget(NULL); - Core::setViewport(0, 0, Core::width, Core::height); - - camera->setup(true); - -// Core::mViewInv = camera->mViewInv; -// Core::mView = Core::mViewInv.inverse(); - - - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - Core::setViewport(0, 0, water[0]->width, water[0]->height); - setPassShader(Core::passWater); - + void waterDrop() { static vec3 lastPos = vec3(0.0); vec3 head = lara->animation.getJoints(lara->getMatrix(), 14).pos; bool flag = (head - lastPos).length() > 16.0f; @@ -981,8 +963,6 @@ struct Level { strength = 0.05f; } - - water[0]->bind(sDiffuse); Core::setTarget(water[1]); Core::active.shader->setParam(uType, Shader::WATER_DROP); @@ -991,38 +971,63 @@ struct Level { swap(water[0], water[1]); Input::down[ikU] = false; } + } + void renderWater() { + bool underwater = level.rooms[camera->getRoomIndex()].flags.water; - Core::active.shader->setParam(uParam, vec4(1.0f / water[0]->width, 1.0f / water[0]->height, Core::deltaTime * 4.0f, 0.0f)); + // mask underwater geometry by zero alpha + setPassShader(Core::passWater); + atlas->bind(sNormal); + atlas->bind(sReflect); - water[0]->bind(sDiffuse); - Core::setTarget(water[1]); - Core::active.shader->setParam(uType, Shader::WATER_STEP); + Core::active.shader->setParam(uType, Shader::WATER_MASK); + Core::active.shader->setParam(uViewProj, Core::mViewProj); + Core::active.shader->setParam(uScale, 1.0f); + Core::setBlending(bmNone); + Core::setCulling(cfNone); + glDepthMask(false); + glColorMask(false, false, false, true); mesh->renderQuad(); - swap(water[0], water[1]); + glColorMask(true, true, true, true); + glDepthMask(true); - water[0]->bind(sDiffuse); - Core::setTarget(water[1]); - Core::active.shader->setParam(uType, Shader::WATER_NORMAL); - mesh->renderQuad(); - swap(water[0], water[1]); + // get refraction texture + checkRefractTexture(); + waterRefract->bind(sDiffuse); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Core::width, Core::height); - Core::setViewport(0, 0, waterCaustics->width, waterCaustics->height); + // render mirror reflection + Core::setTarget(waterReflect); + vec3 p = vec3(40448, 3584, 60928); + vec3 n = vec3(0, 1, 0); - water[0]->bind(sDiffuse); - Core::setTarget(waterCaustics); - Core::active.shader->setParam(uType, Shader::WATER_CAUSTICS); - mesh->renderQuad(); - swap(water[0], water[1]); + vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p)); + + camera->reflectPlane = &reflectPlane; + Core::setCulling(cfBack); + clipSign = underwater ? -1.0f : 1.0f; + clipHeight = 3584.0 * clipSign; + renderCompose(underwater ? 13 : lara->getRoomIndex()); + clipHeight = 1000000.0f; + clipSign = 1.0f; + Core::setCulling(cfFront); + camera->reflectPlane = NULL; + // simulate water + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + + setPassShader(Core::passWater); + waterDrop(); + waterSimulate(); Core::setTarget(NULL); - Core::setViewport(0, 0, Core::width, Core::height); - glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); + // render water plane int dx, dz; TR::Room::Sector &s = level.getSector(lara->getRoomIndex(), int(lara->pos.x), int(lara->pos.z), dx, dz); if (s.roomAbove != 0xFF && level.rooms[s.roomAbove].lightsCount) { @@ -1032,11 +1037,14 @@ struct Level { Core::lightColor[0] = vec4(lum, lum, lum, float(light.radius) * float(light.radius)); } - Core::active.shader->setParam(uType, 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(uLightColor, Core::lightColor[0], 1); + Core::active.shader->setParam(uType, 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(uLightColor, Core::lightColor[0], 1); + Core::active.shader->setParam(uParam, vec4(float(Core::width) / waterRefract->width, float(Core::height) / waterRefract->height, 0.075f, 0.02f)); +// Core::active.shader->setParam(uParam, vec4(1.0f, 1.0f, 0.075f, 0.02f)); + Core::active.shader->setParam(uScale, 1.0f); waterRefract->bind(sDiffuse); waterReflect->bind(sReflect); @@ -1044,9 +1052,25 @@ struct Level { Core::setCulling(cfNone); mesh->renderQuad(); Core::setCulling(cfFront); + } + + void render() { + clipHeight = 1000000.0f; + clipSign = 1.0f; + Core::resetStates(); + + ambientCache->precessQueue(); + renderShadows(lara->getRoomIndex()); + Core::setViewport(0, 0, Core::width, Core::height); + renderCompose(camera->getRoomIndex()); + + renderWater(); + +// Core::mViewInv = camera->mViewInv; +// Core::mView = Core::mViewInv.inverse(); #ifdef _DEBUG - + camera->setup(true); static int modelIndex = 0; static bool lastStateK = false; static int lastEntity = -1; @@ -1072,7 +1096,7 @@ struct Level { // renderModel(level.models[modelIndex], level.entities[4]); Debug::begin(); - + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); @@ -1081,7 +1105,6 @@ struct Level { glLoadIdentity(); glOrtho(0, Core::width, 0, Core::height, 0, 1); - waterCaustics->bind(sDiffuse); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); @@ -1108,7 +1131,7 @@ struct Level { // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level, lara->getRoomIndex()); // Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); - // Debug::Level::portals(level); + Debug::Level::portals(level); // Debug::Level::meshes(level); // Debug::Level::entities(level); /* @@ -1206,7 +1229,7 @@ struct Level { */ - // Debug::Level::info(level, lara->getEntity(), lara->animation); + Debug::Level::info(level, lara->getEntity(), lara->animation); Debug::end(); diff --git a/src/mesh.h b/src/mesh.h index d08b50c..547ff53 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -39,6 +39,8 @@ struct MeshRange { } }; +#define PLANE_DETAIL 100 + struct Mesh { GLuint ID[2]; GLuint *VAO; @@ -142,6 +144,7 @@ struct MeshBuilder { MeshRange shadowBlob; MeshRange bar; MeshRange quad; + MeshRange plane; vec2 *animTexRanges; vec2 *animTexOffsets; @@ -180,6 +183,9 @@ struct MeshBuilder { range.geometry.iStart = iCount; iCount += d.rCount * 6 + d.tCount * 3; vCount += d.rCount * 4 + d.tCount * 3; + + if (roomCheckWaterPortal(r)) + roomRemoveWaterSurfaces(r, iCount, vCount); for (int j = 0; j < r.meshesCount; j++) { TR::Room::Mesh &m = r.meshes[j]; @@ -236,25 +242,33 @@ struct MeshBuilder { shadowBlob.vStart = vCount; shadowBlob.iStart = iCount; shadowBlob.iCount = 8 * 3; - aCount++; iCount += shadowBlob.iCount; vCount += 8; + aCount++; // bar (health, oxygen) bar.vStart = vCount; bar.iStart = iCount; bar.iCount = 2 * 3; - aCount++; iCount += bar.iCount; vCount += 4; + aCount++; // quad (post effect filter) quad.vStart = vCount; quad.iStart = iCount; quad.iCount = 2 * 3; - aCount++; iCount += quad.iCount; vCount += 4; + aCount++; + + // detailed plane + plane.vStart = vCount; + plane.iStart = iCount; + plane.iCount = PLANE_DETAIL * 2 * PLANE_DETAIL * 2 * (2 * 3); + iCount += plane.iCount; + vCount += (PLANE_DETAIL * 2 + 1) * (PLANE_DETAIL * 2 + 1); + aCount++; // make meshes buffer (single vertex buffer object for all geometry & sprites on level) Index *indices = new Index[iCount]; @@ -272,6 +286,8 @@ struct MeshBuilder { TR::Rectangle &f = d.rectangles[j]; TR::ObjectTexture &t = level.objectTextures[f.texture]; + if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes) + addQuad(indices, iCount, vCount, vStart, vertices, &t); TR::Vertex n; @@ -290,6 +306,8 @@ struct MeshBuilder { TR::Triangle &f = d.triangles[j]; TR::ObjectTexture &t = level.objectTextures[f.texture]; + if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes) + addTriangle(indices, iCount, vCount, vStart, vertices, &t); TR::Vertex n; @@ -405,6 +423,22 @@ struct MeshBuilder { } vCount += 4; + // plane + for (int16 j = -PLANE_DETAIL; j <= PLANE_DETAIL; j++) + for (int16 i = -PLANE_DETAIL; i <= PLANE_DETAIL; i++) { + vertices[vCount++].coord = { i, j, 0, 0 }; + if (j < PLANE_DETAIL && i < PLANE_DETAIL) { + int idx = (j + PLANE_DETAIL) * (PLANE_DETAIL * 2 + 1) + i + PLANE_DETAIL; + indices[iCount + 0] = idx + PLANE_DETAIL * 2 + 1; + indices[iCount + 1] = idx + 1; + indices[iCount + 2] = idx; + indices[iCount + 3] = idx + PLANE_DETAIL * 2 + 2; + indices[iCount + 4] = idx + 1; + indices[iCount + 5] = idx + PLANE_DETAIL * 2 + 1; + iCount += 6; + } + } + // compile buffer and ranges mesh = new Mesh(indices, iCount, vertices, vCount, aCount); delete[] indices; @@ -428,6 +462,7 @@ struct MeshBuilder { mesh->initRange(shadowBlob); mesh->initRange(bar); mesh->initRange(quad); + mesh->initRange(plane); } ~MeshBuilder() { @@ -461,6 +496,70 @@ struct MeshBuilder { return res; } + bool roomCheckWaterPortal(TR::Room room) { + for (int i = 0; i < room.portalsCount; i++) + if (room.flags.water ^ level->rooms[room.portals[i].roomIndex].flags.water) + return true; + return false; + } + + void roomRemoveWaterSurfaces(TR::Room room, int &iCount, int &vCount) { + // remove animated water polygons from room geometry + for (int j = 0; j < room.portalsCount; j++) { + TR::Room::Portal &p = room.portals[j]; + if (!(room.flags.water ^ level->rooms[p.roomIndex].flags.water)) // check if portal is not on water surface + continue; + + TR::Vertex pMin, pMax; + pMin.x = min(min(min(p.vertices[0].x, p.vertices[1].x), p.vertices[2].x), p.vertices[3].x); + pMin.z = min(min(min(p.vertices[0].z, p.vertices[1].z), p.vertices[2].z), p.vertices[3].z); + pMax.x = max(max(max(p.vertices[0].x, p.vertices[1].x), p.vertices[2].x), p.vertices[3].x); + pMax.z = max(max(max(p.vertices[0].z, p.vertices[1].z), p.vertices[2].z), p.vertices[3].z); + pMin.y = pMax.y = p.vertices[0].y; + + for (int i = 0; i < room.data.rCount; i++) { + TR::Rectangle &f = room.data.rectangles[i]; + TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; + TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; + TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; + TR::Vertex &d = room.data.vertices[f.vertices[3]].vertex; + + if (abs(a.y - pMin.y) > 1 || a.y != b.y || a.y != c.y || a.y != d.y) // skip non-horizontal or not in portal plane primitive + continue; + + int cx = (int(a.x) + int(b.x) + int(c.x) + int(d.x)) / 4; + int cz = (int(a.z) + int(b.z) + int(c.z) + int(d.z)) / 4; + + if (c.x < pMin.x || c.x > pMax.x || c.z < pMin.z || c.z > pMax.z) // check for portal borders + continue; + + f.vertices[0] = 0xFFFF; // mark it as unused + iCount -= 3 * 2; + vCount -= 4; + } + + for (int i = 0; i < room.data.tCount; i++) { + TR::Triangle &f = room.data.triangles[i]; + TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; + TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; + TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; + + if (abs(a.y - pMin.y) > 1 || a.y != b.y || a.y != c.y) // skip non-horizontal or not in portal plane primitive + continue; + + int cx = (int(a.x) + int(b.x) + int(c.x)) / 3; + int cz = (int(a.z) + int(b.z) + int(c.z)) / 3; + + if (c.x < pMin.x || c.x > pMax.x || c.z < pMin.z || c.z > pMax.z) // check for portal borders + continue; + + f.vertices[0] = 0xFFFF; // mark it as unused + iCount -= 3; + vCount -= 3; + } + } + } + void buildMesh(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 }; @@ -503,7 +602,6 @@ struct MeshBuilder { } } - vec2 getTexCoord(const TR::ObjectTexture &tex) { int tile = tex.tile.index; int tx = (tile % 4) * 256; @@ -682,6 +780,10 @@ struct MeshBuilder { mesh->render(quad); } + void renderPlane() { + mesh->render(plane); + } + void renderBar(const vec2 &size, float value) { /* float w = size.y / 9.0f; diff --git a/src/platform/win/main.cpp b/src/platform/win/main.cpp index a7e17a3..251b5a0 100644 --- a/src/platform/win/main.cpp +++ b/src/platform/win/main.cpp @@ -330,6 +330,9 @@ int main(int argc, char** argv) { Core::stats.tris = 0; Game::render(); SwapBuffers(hDC); + #ifdef _DEBUG + Sleep(20); + #endif if (fpsTime < getTime()) { LOG("FPS: %d DIP: %d TRI: %d\n", fps, Core::stats.dips, Core::stats.tris); diff --git a/src/shader.glsl b/src/shader.glsl index f3194e4..b7b80dc 100644 --- a/src/shader.glsl +++ b/src/shader.glsl @@ -1,4 +1,9 @@ R"====( +#ifdef GL_ES + precision highp int; + precision highp float; +#endif + varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords #ifndef PASS_SHADOW @@ -78,10 +83,6 @@ uniform int uType; vTexCoord.xy = (aTexCoord.xy + offset) * TEXCOORD_SCALE; // first frame + offset * isAnimated vNormal = vec4(mulQuat(rBasisRot, aNormal.xyz), aNormal.w); - - if (aTexCoord.z > 0.0) - coord.w = -1.0; - } else { coord.xyz += uViewInv[0].xyz * aTexCoord.z - uViewInv[1].xyz * aTexCoord.w; vTexCoord.xy = aTexCoord.xy * TEXCOORD_SCALE; diff --git a/src/shader.h b/src/shader.h index cbb9b92..5a864a7 100644 --- a/src/shader.h +++ b/src/shader.h @@ -5,11 +5,11 @@ enum AttribType { aCoord, aTexCoord, aNormal, aColor, aMAX }; enum SamplerType { sDiffuse, sNormal, sReflect, sShadow, sEnvironment, sMAX }; -enum UniformType { uType, uCaustics, uParam, uViewProj, uViewInv, uBasis, uLightProj, uColor, uAmbient, uViewPos, uLightsCount, uLightPos, uLightColor, uAnimTexRanges, uAnimTexOffsets, uRoomSize, uMAX }; +enum UniformType { uType, uCaustics, uParam, uViewProj, uViewInv, uBasis, uLightProj, uColor, uAmbient, uViewPos, uLightsCount, uLightPos, uLightColor, uAnimTexRanges, uAnimTexOffsets, uRoomSize, uScale, uMAX }; const char *AttribName[aMAX] = { "aCoord", "aTexCoord", "aNormal", "aColor" }; const char *SamplerName[sMAX] = { "sDiffuse", "sNormal", "sReflect", "sShadow", "sEnvironment" }; -const char *UniformName[uMAX] = { "uType", "uCaustics", "uParam", "uViewProj", "uViewInv", "uBasis", "uLightProj", "uColor", "uAmbient", "uViewPos", "uLightsCount", "uLightPos", "uLightColor", "uAnimTexRanges", "uAnimTexOffsets", "uRoomSize" }; +const char *UniformName[uMAX] = { "uType", "uCaustics", "uParam", "uViewProj", "uViewInv", "uBasis", "uLightProj", "uColor", "uAmbient", "uViewPos", "uLightsCount", "uLightPos", "uLightColor", "uAnimTexRanges", "uAnimTexOffsets", "uRoomSize", "uScale" }; struct Shader { GLuint ID; @@ -18,14 +18,14 @@ struct Shader { enum : GLint { /* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4, /* filter */ FILTER_DOWNSAMPLE = 0, - /* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_NORMAL = 2, WATER_CAUSTICS = 3, WATER_MASK = 4, WATER_COMPOSE = 5, + /* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4, }; Shader(const char *text, const char *defines = "") { #ifdef MOBILE - #define GLSL_DEFINE "precision highp int;\nprecision highp float;\n" + #define GLSL_DEFINE "" #else - #define GLSL_DEFINE "#version 120\n" + #define GLSL_DEFINE "#version 120\n" #endif const int type[2] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }; diff --git a/src/utils.h b/src/utils.h index db7fc63..bc76b35 100644 --- a/src/utils.h +++ b/src/utils.h @@ -95,6 +95,17 @@ float decrease(float delta, float &value, float &speed) { return 0.0f; } +int nextPow2(uint32 x) { + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + return x; +} + struct vec2 { float x, y; vec2() {} diff --git a/src/water.glsl b/src/water.glsl new file mode 100644 index 0000000..e60bc41 --- /dev/null +++ b/src/water.glsl @@ -0,0 +1,180 @@ +R"====( +#ifdef GL_ES + #ifdef FRAGMENT + #extension GL_OES_standard_derivatives : enable + #endif + precision highp int; + precision highp float; +#endif + +varying vec3 vCoord; +varying vec2 vTexCoord; +varying vec4 vProjCoord; +varying vec3 vRefPos1; +varying vec3 vRefPos2; + +#define WATER_DROP 0 +#define WATER_STEP 1 +#define WATER_CAUSTICS 2 +#define WATER_MASK 3 +#define WATER_COMPOSE 4 + +uniform int uType; +uniform vec3 uViewPos; +uniform mat4 uViewProj; +uniform float uScale; + +uniform sampler2D sNormal; + +#ifdef VERTEX + #define ETA_AIR 1.000 + #define ETA_WATER 1.333 + + attribute vec4 aCoord; + + void main() { + vec4 rCoord = aCoord * uScale; + + if (uType >= WATER_MASK) { + // hardcoded pool geometry + vec2 p = rCoord.xy * 2560.0; + vCoord = vec3(p.x + 40448.0, 3584.0, p.y + 60928.0); + vec4 cp = uViewProj * vec4(vCoord, 1.0); + + vProjCoord = cp; + gl_Position = cp; + } else { + vProjCoord = vec4(0.0); + if (uType == WATER_CAUSTICS) { + vec4 info = texture2D(sNormal, rCoord.xy * 0.5 + 0.5); + info.ba *= 0.5; + vec3 normal = vec3(info.b, -sqrt(1.0 - dot(info.ba, info.ba)), info.a); + + vec3 light = vec3(0.0, -1.0, 0.0); + vec3 refractedLight = refract(-light, vec3(0.0, 1.0, 0.0), ETA_AIR / ETA_WATER); + vec3 ray = refract(-light, normal, ETA_AIR / ETA_WATER); + vRefPos1 = rCoord.xzy + vec3(0.0, 1.0, 0.0); + vRefPos2 = rCoord.xzy + vec3(0.0, info.r, 0.0) + ray / ray.y; + + gl_Position = vec4((vRefPos2.xz + 0.0 * refractedLight.xz / refractedLight.y), 0.0, 1.0); + } else { + vRefPos1 = vRefPos2 = vec3(0.0); + vCoord = vec3(rCoord.xy, 0.0); + + gl_Position = vec4(rCoord.xyz, 1.0); + } + } + + vTexCoord = rCoord.xy * 0.5 + 0.5; + } +#else + uniform sampler2D sDiffuse; + uniform sampler2D sReflect; + + uniform vec4 uParam; // texture size + + uniform vec3 uLightPos; + uniform vec4 uLightColor; + + #define PI 3.141592653589793 + + vec3 cubeProj(vec3 ray, vec3 cubePos, vec3 cubeMin, vec3 cubeMax) { + vec3 i1 = (cubeMax - vCoord) / ray; + vec3 i2 = (cubeMin - vCoord) / ray; + vec3 i0 = max(i1, i2); + float dist = min(min(i0.x, i0.y), i0.z); + return vCoord + ray * dist - cubePos; + } + + float calcFresnel(float NdotL, float fbias, float fpow) { + float f = (1.0 - abs(NdotL)); + return clamp(fbias + (1.0 - fbias) * pow(f, fpow), 0.0, 1.0); + } + + vec4 drop() { + vec4 value = texture2D(sDiffuse, vTexCoord); + float drop = max(0.0, 1.0 - length(uParam.xy - vTexCoord) / uParam.z); + drop = 0.5 - cos(drop * PI) * 0.5; + value.r += drop * uParam.w; + return value; + } + + vec4 step() { + vec2 dx = vec2(uParam.x, 0.0); + vec2 dy = vec2(0.0, uParam.y); + + vec4 v = texture2D(sDiffuse, vTexCoord); + vec4 f = vec4(texture2D(sDiffuse, vTexCoord + dx).r, texture2D(sDiffuse, vTexCoord + dy).r, + texture2D(sDiffuse, vTexCoord - dx).r, texture2D(sDiffuse, vTexCoord - dy).r); + + float average = dot(f, vec4(0.25)); + + // normal + f.xy -= v.r; + vec3 nx = vec3(uParam.x, f.x, 0.0); + vec3 ny = vec3(0.0, f.y, uParam.y); + v.ba = normalize(cross(ny, nx)).xz; + + // velocity + v.g += (average - v.r) * 2.0; + v.g *= 0.995; + + // amplitude + v.r += v.g * 0.4; + + return v; + } + + vec4 caustics() { + vec4 v = texture2D(sNormal, vTexCoord); + v.ba *= 0.5; + vec3 normal = vec3(v.b, sqrt(1.0 - dot(v.ba, v.ba)), v.a); + + float area1 = length(dFdx(vRefPos1)) * length(dFdy(vRefPos1)); + float area2 = length(dFdx(vRefPos2)) * length(dFdy(vRefPos2)); + + return vec4(vec3(area1 / area2 * 0.2), 1.0); + } + + vec4 mask() { + return vec4(0.0); + } + + vec4 compose() { + vec2 tc = vProjCoord.xy / vProjCoord.w * 0.5 + 0.5; + + vec4 value = texture2D(sNormal, vTexCoord); + + value.ba *= 0.5; + vec3 normal = normalize(vec3(value.b, -sqrt(1.0 - dot(value.ba, value.ba)), value.a)); + vec2 dudv = (uViewProj * vec4(normal.x, 0.0, normal.z, 0.0)).xy; + + vec3 viewVec = normalize(uViewPos - vCoord); + vec3 rv = reflect(-viewVec, normal); + vec3 lv = normalize(uLightPos - vCoord.xyz); + + float spec = pow(max(0.0, dot(rv, -lv)), 64.0) * 0.5; + + vec4 refrA = texture2D(sDiffuse, uParam.xy * clamp(tc + dudv * uParam.z, 0.0, 0.999) ); + vec4 refrB = texture2D(sDiffuse, uParam.xy * (tc) ); + vec4 refr = vec4(mix(refrA.xyz, refrB.xyz, refrA.w), 1.0); + vec4 refl = texture2D(sReflect, tc + dudv * uParam.w); + + float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0); + return mix(refr, refl, fresnel) + spec; + } + + vec4 pass() { + if (uType == WATER_DROP) return drop(); + if (uType == WATER_STEP) return step(); + if (uType == WATER_CAUSTICS) return caustics(); + if (uType == WATER_MASK) return mask(); + if (uType == WATER_COMPOSE) return compose(); + return vec4(1.0, 0.0, 0.0, 1.0); + } + + void main() { + gl_FragColor = pass(); + } +#endif +)====" \ No newline at end of file