From 5d390a7f7dd02b822a9b2d493979ac902a1d94cc Mon Sep 17 00:00:00 2001 From: XProger Date: Sun, 4 Sep 2016 00:05:53 +0300 Subject: [PATCH] #3 ground & underwater movement #4 ceiling collisions --- bin/OpenLara.exe | Bin 35840 -> 36864 bytes src/camera.h | 18 +- src/controller.h | 794 +++++++++++++++++++++++++++-------------------- src/debug.h | 227 +++++++++++++- src/format.h | 26 +- src/level.h | 256 ++------------- src/utils.h | 27 +- 7 files changed, 752 insertions(+), 596 deletions(-) diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 09304781f2f36c6f38c9e2b7924cc0807bdd654b..3feb5374f73d176a9974681690201d8e8d493ab9 100644 GIT binary patch delta 18901 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 a;}ARd!caJbEy`lE@4s!!Ny9T4(f=2Up|gkp diff --git a/src/camera.h b/src/camera.h index ec07618..8cced19 100644 --- a/src/camera.h +++ b/src/camera.h @@ -122,7 +122,7 @@ struct Camera { } *frustum; float fov, znear, zfar; - vec3 pos, angle, offset; + vec3 pos, angle, offset, deltaPos, deltaAngle, targetDeltaPos, targetAngle; Camera() : frustum(new Frustum()) {} @@ -141,12 +141,14 @@ struct Camera { if (Input::down[ikA]) v = v - dir.cross(vec3(0, 1, 0)); pos = pos + v.normal() * (Core::deltaTime * 2048.0f); #endif + deltaPos = deltaPos.lerp(targetDeltaPos, Core::deltaTime * 10.0f); + angle = angle.lerp(targetAngle, Core::deltaTime); if (Input::down[ikMouseL]) { vec2 delta = Input::mouse.pos - Input::mouse.start.L; - angle.x -= delta.y * 0.01f; - angle.y -= delta.x * 0.01f; - angle.x = min(max(angle.x, -PI * 0.5f + EPS), PI * 0.5f - EPS); + deltaAngle.x -= delta.y * 0.01f; + deltaAngle.y -= delta.x * 0.01f; + deltaAngle.x = min(max(deltaAngle.x + angle.x, -PI * 0.5f + EPS), PI * 0.5f - EPS) - angle.x; Input::mouse.start.L = Input::mouse.pos; } } @@ -154,10 +156,10 @@ struct Camera { void setup() { Core::mView.identity(); Core::mView.translate(vec3(-offset.x, -offset.y, -offset.z)); - Core::mView.rotateZ(-angle.z); - Core::mView.rotateX(-angle.x); - Core::mView.rotateY(-angle.y); - Core::mView.translate(vec3(-pos.x, -pos.y, -pos.z)); + Core::mView.rotateZ(-(angle.z + deltaAngle.z)); + Core::mView.rotateX(-(angle.x + deltaAngle.x)); + Core::mView.rotateY(-(angle.y + deltaAngle.y)); + Core::mView.translate(deltaPos - pos); Core::mView.scale(vec3(-1, -1, 1)); Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar); diff --git a/src/controller.h b/src/controller.h index ac37b67..8e99926 100644 --- a/src/controller.h +++ b/src/controller.h @@ -6,310 +6,27 @@ #define GRAVITY 7.0f struct Controller { - TR::Level *level; - int entity; + TR::Level *level; + int entity; - TR::Animation *anim; - float fTime; + float fTime; + int lastFrame; - vec3 pos, velocity; - float angle; + vec3 pos, velocity; + vec3 angle; - int state; // target state - int lastFrame; + int health; - int sc; - bool lState; - bool onGround; + float turnTime; - Controller(TR::Level *level, int entity) : level(level), entity(entity), pos(0.0f), velocity(0.0f), angle(0.0f), fTime(0.0f) { - anim = &level->anims[getModel().animation]; - lastFrame = 0; + bool onGround; + bool inWater; - TR::Entity &e = level->entities[entity]; - pos = vec3((float)e.x, (float)e.y, (float)e.z); - angle = e.rotation / 16384.0f * PI * 0.5f; - - sc = 0; - lState = false; - - state = TR::STATE_STOP; - } - - void update() { - float rot = 0.0f; - - enum { LEFT = 1, RIGHT = 2, FORTH = 4, BACK = 8, - JUMP = 16, WALK = 32, ACTION = 64, WEAPON = 128, ROLL = 256, - GROUND = 512, WATER = 1024, DEATH = 2048, - PULL = 4096, PICKUP = 8192, SWITCH_ON = 16 * 1024, SWITCH_OFF = 32 * 1024, KEY = 64 * 1024, PUZZLE = 128 * 1024, HANG = 256 * 1024, FALL = 512 * 1024, COMPRESS = 1024 * 1024}; - int mask = 0; - - if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH; - if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK; - if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT; - if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT; - if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP; - if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK; - if (Input::down[ikE] || /*Input::down[ikMouseL] ||*/ Input::down[ikJoyA]) mask |= ACTION; - if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON; - if (onGround) mask |= GROUND; - if (getRoom().flags & 1) mask |= WATER; - if (velocity.y > 2048) mask |= FALL; - if (anim->state == TR::STATE_COMPRESS) mask |= COMPRESS; - - int origMask = mask; - if (origMask & (FORTH | BACK)) - mask &= ~(LEFT | RIGHT); - - int stateMask[TR::STATE_MAX]; - for (int i = 0; i < TR::STATE_MAX; i++) - stateMask[i] = -1; - - stateMask[TR::STATE_WALK] = GROUND | FORTH | WALK; - stateMask[TR::STATE_RUN] = GROUND | FORTH; - stateMask[TR::STATE_STOP] = GROUND; - stateMask[TR::STATE_FORWARD_JUMP] = GROUND | JUMP | FORTH; -// stateMask[TR::STATE_FAST_TURN] = 0; - stateMask[TR::STATE_FAST_BACK] = GROUND | BACK; - stateMask[TR::STATE_TURN_RIGHT] = GROUND | RIGHT; - stateMask[TR::STATE_TURN_LEFT] = GROUND | LEFT; - stateMask[TR::STATE_DEATH] = DEATH; - stateMask[TR::STATE_FAST_FALL] = FALL; - stateMask[TR::STATE_HANG] = HANG | ACTION; - stateMask[TR::STATE_REACH] = ACTION; -// stateMask[TR::STATE_SPLAT] -// stateMask[TR::STATE_TREAD] -// stateMask[TR::STATE_FAST_TURN_14] - stateMask[TR::STATE_COMPRESS] = GROUND | JUMP; - stateMask[TR::STATE_BACK] = GROUND | WALK | BACK; - stateMask[TR::STATE_SWIM] = WATER | JUMP; -// stateMask[TR::STATE_GLIDE] -// stateMask[TR::STATE_NULL_19] -// stateMask[TR::STATE_FAST_TURN_20] - stateMask[TR::STATE_FAST_TURN_20] = GROUND | LEFT | RIGHT; - stateMask[TR::STATE_STEP_RIGHT] = GROUND | WALK | RIGHT; - stateMask[TR::STATE_STEP_LEFT] = GROUND | WALK | LEFT; - stateMask[TR::STATE_ROLL] = GROUND | ROLL; -// stateMask[TR::STATE_SLIDE] - stateMask[TR::STATE_BACK_JUMP] = GROUND | COMPRESS | BACK; - stateMask[TR::STATE_RIGHT_JUMP] = GROUND | COMPRESS | RIGHT; - stateMask[TR::STATE_LEFT_JUMP] = GROUND | COMPRESS | LEFT; - stateMask[TR::STATE_UP_JUMP] = GROUND | COMPRESS; - - stateMask[TR::STATE_DIVE] = WATER; - - stateMask[TR::STATE_PUSH_PULL_READY] = GROUND | ACTION | PULL; - stateMask[TR::STATE_PICK_UP] = GROUND | ACTION | PICKUP; - stateMask[TR::STATE_SWITCH_ON] = GROUND | ACTION | SWITCH_ON; - stateMask[TR::STATE_SWITCH_OFF] = GROUND | ACTION | SWITCH_OFF; - stateMask[TR::STATE_USE_KEY] = GROUND | ACTION | KEY; - stateMask[TR::STATE_USE_PUZZLE] = GROUND | ACTION | PUZZLE; - - stateMask[TR::STATE_GLIDE] = WATER | JUMP; - stateMask[TR::STATE_SWAN_DIVE] = JUMP | WALK | FORTH; - stateMask[TR::STATE_TREAD] = WATER | GROUND; - - stateMask[TR::STATE_UNDERWATER_DEATH] = WATER | DEATH; - - - - - fTime += Core::deltaTime; - int fCount = anim->frameEnd - anim->frameStart + 1; - int fIndex = int(fTime * 30.0f); - - state = -1; - int maxMask = 0; - if (stateMask[anim->state] != mask) - for (int i = 0; i < anim->scCount; i++) { - TR::AnimState &sc = level->states[anim->scOffset + i]; - if (sc.state >= TR::STATE_MAX || stateMask[sc.state] == -1) - LOG("unknown state %d\n", sc.state); - else - if (stateMask[sc.state] > maxMask && ((stateMask[sc.state] & mask) == stateMask[sc.state])) { - maxMask = stateMask[sc.state]; - state = anim->scOffset + i; - } - } - - if (state > -1 && anim->state != level->states[state].state) { - TR::AnimState &sc = level->states[state]; - for (int j = 0; j < sc.rangesCount; j++) { - TR::AnimRange &range = level->ranges[sc.rangesOffset + j]; - if ( anim->frameStart + fIndex >= range.low && anim->frameStart + fIndex <= range.high) { - int st = anim->state; - anim = &level->anims[range.nextAnimation]; - fIndex = range.nextFrame - anim->frameStart; - fCount = anim->frameEnd - anim->frameStart + 1; - fTime = fIndex / 30.0f; - break; - } - } - } - -#ifdef _DEBUG - if (Input::down[ikEnter]) { - if (!lState) { - lState = true; - // state = TR::STATE_ROLL; - // fTime = 0; - - // sc = (sc + 1) % level->statesCount; - // anim = &level->anims[146];//level->ranges[ level->states[sc].rangesOffset ].nextAnimation ]; - // fTime = 0; - // state = level->states[sc].state; - - LOG("state: %d\n", anim->state); - for (int i = 0; i < anim->scCount; i++) { - auto &sc = level->states[anim->scOffset + i]; - LOG("-> %d : ", (int)sc.state); - for (int j = 0; j < sc.rangesCount; j++) { - TR::AnimRange &range = level->ranges[sc.rangesOffset + j]; - LOG("%d ", range.nextAnimation); - //range. - } - LOG("\n"); - } - - } - } else - lState = false; -#endif - if (anim->state == TR::STATE_RUN || - anim->state == TR::STATE_FAST_BACK || - anim->state == TR::STATE_WALK || - anim->state == TR::STATE_BACK || - anim->state == TR::STATE_TURN_LEFT || - anim->state == TR::STATE_TURN_RIGHT) { - - if (origMask & LEFT) angle -= Core::deltaTime * PI; - if (origMask & RIGHT) angle += Core::deltaTime * PI; - } - - float d = 0.0f; - switch (anim->state) { - case TR::STATE_BACK : - case TR::STATE_BACK_JUMP : - case TR::STATE_FAST_BACK : - d = PI; - break; - case TR::STATE_STEP_LEFT : - case TR::STATE_LEFT_JUMP : - d = -PI * 0.5f; - break; - case TR::STATE_STEP_RIGHT : - case TR::STATE_RIGHT_JUMP : - d = PI * 0.5f; - break; - } - d += angle; - - bool endFrame = fIndex >= fCount; - - int16 *ptr = &level->commands[anim->animCommand]; - - for (int i = 0; i < anim->acCount; i++) { - switch (*ptr++) { - case 0x01 : { // cmd position - int16 sx = *ptr++; - int16 sy = *ptr++; - int16 sz = *ptr++; - LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz); - break; - } - case 0x02 : { // cmd jump speed - int16 sy = *ptr++; - int16 sz = *ptr++; - if (endFrame) { - LOG("jump: %d %d\n", (int)sy, (int)sz); - velocity.x = sinf(d) * sz; - velocity.y = sy; - velocity.z = cosf(d) * sz; - onGround = false; - } - break; - } - case 0x03 : // empty hands - break; - case 0x04 : // kill - break; - case 0x05 : { // play sound - int frame = (*ptr++); - int id = (*ptr++) & 0x3FFF; - if (fIndex == frame - anim->frameStart && fIndex != lastFrame) { - auto a = level->soundsMap[id]; - auto b = level->soundsInfo[a].index; - auto c = level->soundOffsets[b]; - - void *p = &level->soundData[c]; - #ifdef WIN32 - PlaySound((LPSTR)p, NULL, SND_ASYNC | SND_MEMORY); - #endif - } - break; - } - case 0x06 : - if (fIndex != lastFrame && fIndex + anim->frameStart == ptr[0]) { - if (ptr[1] == 0) { - angle = angle + PI; - } - } - ptr += 2; - break; - } - } - - float dt = Core::deltaTime * 30.0f; - - if (onGround) { - float speed = anim->speed.toFloat() + anim->accel.toFloat() * (fTime * 30.0f); - velocity.x = sinf(d) * speed; - velocity.z = cosf(d) * speed; - } - - velocity.y += GRAVITY * dt; - - - if (endFrame) { - fIndex = anim->nextFrame; - int id = anim->nextAnimation; - anim = &level->anims[anim->nextAnimation]; - fIndex -= anim->frameStart; - fTime = fIndex / 30.0f; - fCount = anim->frameEnd - anim->frameStart + 1; - } - - move(velocity * dt); - collide(); - - lastFrame = fIndex; - } - - void move(const vec3 &offset) { - vec3 p = pos; - pos = pos + offset; - - updateEntity(); - - TR::Room &room = getRoom(); - TR::Entity &entity = getEntity(); - - int dx, dz; - TR::Room::Sector &s = getSector(dx, dz); - - int d = entity.y - s.floor * 256; - if (d >= 256 * 4) { - pos.x = p.x;//vec3(entity.x, entity.y, entity.z); - pos.z = p.z; - updateEntity(); - if (d >= 256 * 4) - anim = &level->anims[53]; // forward smash - else - anim = &level->anims[11]; // instant stand - state = anim->state; - fTime = 0; - } + Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), fTime(0.0f), lastFrame(0), health(100), turnTime(0.0f) { + TR::Entity &e = getEntity(); + pos = vec3((float)e.x, (float)e.y, (float)e.z); + angle = vec3(0.0f, e.rotation / 16384.0f * PI * 0.5f, 0.0f); + onGround = inWater = false; } void updateEntity() { @@ -317,7 +34,7 @@ struct Controller { e.x = int(pos.x); e.y = int(pos.y); e.z = int(pos.z); - e.rotation = int(angle / (PI * 0.5f) * 16384.0f); + e.rotation = int(angle.y / (PI * 0.5f) * 16384.0f); } bool insideRoom(const vec3 &pos, int room) { @@ -361,12 +78,418 @@ struct Controller { return room.sectors[sx * room.zSectors + sz]; } + bool changeState(int state) { + TR::Model &model = getModel(); + TR::Animation *anim = &level->anims[model.animation]; + + if (state == anim->state) + return true; + + int fIndex = int(fTime * 30.0f); + + bool exists = false; + + for (int i = 0; i < anim->scCount; i++) { + TR::AnimState &s = level->states[anim->scOffset + i]; + if (s.state == state) { + exists = true; + for (int j = 0; j < s.rangesCount; j++) { + TR::AnimRange &range = level->ranges[s.rangesOffset + j]; + if (anim->frameStart + fIndex >= range.low && anim->frameStart + fIndex <= range.high) { + model.animation = range.nextAnimation; + fTime = (range.nextFrame - level->anims[model.animation].frameStart) / 30.0f; + break; + } + } + } + } + + return exists; + } + + virtual void update() {} +}; + + +#define FAST_TURN_TIME 1.0f + +#define TURN_FAST PI +#define TURN_FAST_BACK PI * 3.0f / 4.0f +#define TURN_NORMAL PI / 2.0f +#define TURN_SLOW PI / 3.0f +#define TURN_TILT PI / 18.0f +#define TURN_WATER_FAST PI * 3.0f / 4.0f +#define TURN_WATER_SLOW PI * 2.0f / 3.0f +#define GLIDE_SPEED 50.0f + +struct Lara : Controller { + int sc; + bool lState; + + Lara(TR::Level *level, int entity) : Controller(level, entity) { + pos = vec3(70067, -256, 29104); + angle = vec3(0.0f, -0.68f, 0.0f); + getEntity().room = 15; + } + + virtual void update() { + TR::Model &model = getModel(); + TR::Animation *anim = &level->anims[model.animation]; + + float rot = 0.0f; + + enum { LEFT = 1 << 1, + RIGHT = 1 << 2, + FORTH = 1 << 3, + BACK = 1 << 4, + JUMP = 1 << 5, + WALK = 1 << 6, + ACTION = 1 << 7, + WEAPON = 1 << 8, + GROUND = 1 << 9, + WATER = 1 << 10, + DEATH = 1 << 11 }; + + int mask = 0; + + if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH; + if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK; + if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT; + if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT; + if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP; + if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK; + if (Input::down[ikE] || Input::down[ikMouseL] || Input::down[ikJoyA]) mask |= ACTION; + if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON; + if (health <= 0) mask |= DEATH; + if (onGround) mask |= GROUND; + if (inWater) mask |= WATER; + + int state = anim->state; + + if ((mask & (GROUND | WATER)) == (GROUND | WATER)) { // on water surface + angle.x = 0.0f; + + state = TR::STATE_SURF_TREAD; + + } else if (mask & GROUND) { + angle.x = 0.0f; + + if (state == TR::STATE_COMPRESS) { + switch (mask & (RIGHT | LEFT | FORTH | BACK)) { + case RIGHT : state = TR::STATE_RIGHT_JUMP; break; + case LEFT : state = TR::STATE_LEFT_JUMP; break; + case FORTH : state = TR::STATE_FORWARD_JUMP; break; + case BACK : state = TR::STATE_BACK_JUMP; break; + default : state = TR::STATE_UP_JUMP; break; + } + } else + if (mask & JUMP) { // jump button is pressed + if ((mask & FORTH) && state == TR::STATE_FORWARD_JUMP) + state = TR::STATE_RUN; + else + state = state == TR::STATE_RUN ? TR::STATE_FORWARD_JUMP : TR::STATE_COMPRESS; + } else + if (mask & WALK) { // walk button is pressed + if (mask & FORTH) + state = TR::STATE_WALK; + else if (mask & BACK) + state = TR::STATE_BACK; + else if (mask & LEFT) + state = TR::STATE_STEP_LEFT; + else if (mask & RIGHT) + state = TR::STATE_STEP_RIGHT; + else + state = TR::STATE_STOP; + } else { // only dpad buttons pressed + if (mask & FORTH) + state = TR::STATE_RUN; + else if (mask & BACK) + state = TR::STATE_FAST_BACK; + else if (mask & LEFT) + state = turnTime < FAST_TURN_TIME ? TR::STATE_TURN_LEFT : TR::STATE_FAST_TURN; + else if (mask & RIGHT) + state = turnTime < FAST_TURN_TIME ? TR::STATE_TURN_RIGHT : TR::STATE_FAST_TURN; + else + state = TR::STATE_STOP; + } + + } else if (mask & WATER) { // underwater + + if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_BACK_JUMP || state == TR::STATE_LEFT_JUMP || state == TR::STATE_RIGHT_JUMP || state == TR::STATE_FAST_FALL) { + model.animation = TR::ANIM_WATER_FALL; + fTime = 0.0f; + state = level->anims[model.animation].state; + } else + if (mask & JUMP) + state = TR::STATE_SWIM; + else + state = (state == TR::STATE_SWIM || velocity.y > GLIDE_SPEED) ? TR::STATE_GLIDE : TR::STATE_TREAD; + + } else { // in the air + angle.x = 0.0f; + + if (state == TR::STATE_FORWARD_JUMP) { + if (mask & ACTION) + state = TR::STATE_REACH; + else if ((mask & (FORTH | WALK)) == (FORTH | WALK)) + state = TR::STATE_SWAN_DIVE; + } + + // LOG("- speed: %f\n", velocity.length()); + + } + + // try to set new state + if (!changeState(state)) { + int stopState = TR::STATE_FAST_FALL; + + if ((mask & (GROUND | WATER)) == (GROUND | WATER)) + stopState = TR::STATE_SURF_TREAD; + else if (mask & WATER) + stopState = TR::STATE_TREAD; + else if (mask & GROUND) + stopState = TR::STATE_STOP; + + if (state != stopState) + changeState(stopState); + } + + anim = &level->anims[model.animation]; // get new animation and state (if it has been changed) + state = anim->state; + + fTime += Core::deltaTime; + int fCount = anim->frameEnd - anim->frameStart + 1; + int fIndex = int(fTime * 30.0f); + +#ifdef _DEBUG + // show state transitions for current animation + if (Input::down[ikEnter]) { + LOG("state: %d\n", anim->state); + for (int i = 0; i < anim->scCount; i++) { + auto &sc = level->states[anim->scOffset + i]; + LOG("-> %d : ", (int)sc.state); + for (int j = 0; j < sc.rangesCount; j++) { + TR::AnimRange &range = level->ranges[sc.rangesOffset + j]; + LOG("%d ", range.nextAnimation); + } + LOG("\n"); + } + } +#endif + + // calculate turn tilt + if (state == TR::STATE_RUN && (mask & (GROUND | WATER)) == GROUND && (mask & (LEFT | RIGHT))) { + if (mask & LEFT) angle.z -= Core::deltaTime * TURN_TILT; + if (mask & RIGHT) angle.z += Core::deltaTime * TURN_TILT; + angle.z = clamp(angle.z, -TURN_TILT, TURN_TILT); + } else + angle.z -= angle.z * min(Core::deltaTime * 8.0f, 1.0f); + + if (state == TR::STATE_TURN_LEFT || state == TR::STATE_TURN_RIGHT || state == TR::STATE_FAST_TURN) + turnTime += Core::deltaTime; + else + turnTime = 0.0f; + + // get turning angle + float w = 0.0f; + + if (state == TR::STATE_SWIM || state == TR::STATE_GLIDE) + w = TURN_WATER_FAST; + else if (state == TR::STATE_TREAD) + w = TURN_WATER_SLOW; + else if (state == TR::STATE_RUN || state == TR::STATE_FAST_TURN) + w = TURN_FAST; + else if (state == TR::STATE_FAST_BACK) + w = TURN_FAST_BACK; + else if (state == TR::STATE_TURN_LEFT || state == TR::STATE_TURN_RIGHT || state == TR::STATE_WALK) + w = TURN_NORMAL; + else if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_BACK) + w = TURN_SLOW; + + if (w != 0.0f) { + w *= Core::deltaTime; + // yaw + if (mask & LEFT) { angle.y -= w; velocity = velocity.rotateY(+w); } + if (mask & RIGHT) { angle.y += w; velocity = velocity.rotateY(-w); } + // pitch (underwater only) + if ( ((mask & (GROUND | WATER)) == WATER) && (mask & (FORTH | BACK)) ) { + angle.x += ((mask & FORTH) ? -w : w) * 0.5f; + angle.x = clamp(angle.x, -PI * 0.5f, PI * 0.5f); + } + } + + // get animation direction + float d = 0.0f; + switch (state) { + case TR::STATE_BACK : + case TR::STATE_BACK_JUMP : + case TR::STATE_FAST_BACK : + d = PI; + break; + case TR::STATE_STEP_LEFT : + case TR::STATE_LEFT_JUMP : + d = -PI * 0.5f; + break; + case TR::STATE_STEP_RIGHT : + case TR::STATE_RIGHT_JUMP : + d = PI * 0.5f; + break; + } + d += angle.y; + + bool endFrame = fIndex >= fCount; + + // calculate moving speed + float dt = Core::deltaTime * 30.0f; + + if (mask & (GROUND | WATER)) { + + if ((mask & (GROUND | WATER)) == (GROUND | WATER)) { // on water + + } else if (mask & WATER) { // underwater + + if (state == TR::STATE_SWIM) { + velocity = vec3(angle.x, angle.y) * 35.0f; + } else if (state == TR::STATE_GLIDE || state == TR::STATE_TREAD) + velocity = velocity - velocity * Core::deltaTime; + + // TODO: apply flow velocity + } else { // on ground + float speed = anim->speed + anim->accel * (fTime * 30.0f); + + velocity.x = sinf(d) * speed; + velocity.z = cosf(d) * speed; + velocity.y += GRAVITY * dt; + + } + } else + velocity.y += GRAVITY * dt; + + // apply animation commands + int16 *ptr = &level->commands[anim->animCommand]; + + for (int i = 0; i < anim->acCount; i++) { + switch (*ptr++) { + case 0x01 : { // cmd position + int16 sx = *ptr++; + int16 sy = *ptr++; + int16 sz = *ptr++; + LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz); + break; + } + case 0x02 : { // cmd jump speed + int16 sy = *ptr++; + int16 sz = *ptr++; + if (endFrame) { + LOG("jump: %d %d\n", (int)sy, (int)sz); + velocity.x = sinf(d) * sz; + velocity.y = sy; + velocity.z = cosf(d) * sz; + LOG("speed: %f\n", velocity.length()); + onGround = false; + } + break; + } + case 0x03 : // empty hands + break; + case 0x04 : // kill + break; + case 0x05 : { // play sound + int frame = (*ptr++); + int id = (*ptr++) & 0x3FFF; + if (fIndex == frame - anim->frameStart && fIndex != lastFrame) { + int16 a = level->soundsMap[id]; + TR::SoundInfo &b = level->soundsInfo[a]; + if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) { + uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)]; + LOG("count %d\n", int(((b.flags & 0xFF) >> 2))); + + void *p = &level->soundData[c]; + #ifdef WIN32 + PlaySound((LPSTR)p, NULL, SND_ASYNC | SND_MEMORY); + #endif + } + } + break; + } + case 0x06 : + if (fIndex != lastFrame && fIndex + anim->frameStart == ptr[0]) { + if (ptr[1] == 0) { + angle = angle + PI; + } + } + ptr += 2; + break; + default : + LOG("unknown animation command\n"); + } + } + + + + // check for next animation + if (endFrame) { + model.animation = anim->nextAnimation; + TR::Animation *nextAnim = &level->anims[anim->nextAnimation]; + fTime = (anim->nextFrame - nextAnim->frameStart) / 30.0f; + } + + move(velocity * dt); + collide(); + + lastFrame = fIndex; + } + + void move(const vec3 &offset) { + vec3 p = pos; + pos = pos + offset; + + updateEntity(); + + inWater = getRoom().flags & TR::ROOM_FLAG_WATER; + + TR::Room &room = getRoom(); + TR::Entity &entity = getEntity(); + + int dx, dz; + TR::Room::Sector &s = getSector(dx, dz); + + int d = entity.y - s.floor * 256; + if (d >= 256 * 4) { + LOG("wall %d\n", d); + pos = p; + updateEntity(); + + TR::Model &model = getModel(); + TR::Animation *anim = &level->anims[model.animation]; + + // smashes + if (onGround) { // onGround + if (d >= 256 * 4 && anim->state == TR::STATE_RUN) + model.animation = TR::ANIM_SMASH_RUN_LEFT; // TODO: RIGHT + else + model.animation = TR::ANIM_STAND; + velocity.x = velocity.z = 0.0f; + } else if (inWater) { // in water + // do nothing + //velocity.x = velocity.z = 0.0f; + } else { // in the air + model.animation = TR::ANIM_SMASH_JUMP; + velocity.x = -velocity.x * 0.5f; + velocity.z = -velocity.z * 0.5f; + velocity.y = 0.0f; + } + fTime = 0; + } + } + void collide() { int dx, dz; TR::Room::Sector &s = getSector(dx, dz); TR::Entity &entity = getEntity(); - float bottom = s.floor * 256.0f; + float floor = s.floor * 256.0f; + float ceiling = s.ceiling * 256.0f; float fx = dx / 1024.0f, fz = dz / 1024.0f; @@ -384,37 +507,29 @@ struct Controller { break; case 2 : case 3 : { - int8 sx = (int8)(*d & 0x00FF); - int8 sz = (int8)((*d & 0xFF00) >> 8); + int sx = (int8)(*d & 0x00FF); + int sz = (int8)((*d & 0xFF00) >> 8); if (func == 2) { if (sx > 0) - bottom += (int)sx * (1024 - dx) >> 2; + floor += sx * (1024 - dx) >> 2; else - bottom -= (int)sx * dx >> 2; + floor -= sx * dx >> 2; if (sz > 0) - bottom += (int)sz * (1024 - dz) >> 2; + floor += sz * (1024 - dz) >> 2; else - bottom -= (int)sz * dz >> 2; - } else { - /* - if (sx < 0) { - p[0].y += sx; - p[3].y += sx; - } else { - p[1].y -= sx; - p[2].y -= sx; - } + floor -= sz * dz >> 2; + } else { + if (sx < 0) + ceiling += sx * (1024 - dx) >> 2; + else + ceiling -= sx * dx >> 2; - if (sz > 0) { - p[0].y -= sz; - p[1].y -= sz; - } else { - p[3].y += sz; - p[2].y += sz; - } - */ + if (sz > 0) + ceiling -= sz * (1024 - dz) >> 2; + else + ceiling += sz * dz >> 2; } d++; break; @@ -445,19 +560,30 @@ struct Controller { } while (!(cmd & 0x8000)); - - onGround = pos.y > bottom; - if (onGround) { - onGround = true; - if (s.roomBelow != 255) { - entity.room = s.roomBelow; - onGround = false; - return; - } - pos.y = bottom; - velocity.y = 0.0f; + float hmin = 0.0f, hmax = -768.0f; + if (inWater) { + hmin = 256.0f + 128.0f; + hmax = -256.0f - 128.0f; } + onGround = (pos.y >= floor) && (s.roomBelow == 0xFF) && !(getRoom().flags & TR::ROOM_FLAG_WATER); + + if (pos.y + hmin >= floor) { + if (s.roomBelow == 0xFF) { + pos.y = floor - hmin; + velocity.y = 0.0f; + } else + entity.room = s.roomBelow; + } + + if (pos.y + hmax <= ceiling) { + if (s.roomAbove == 0xFF) { + pos.y = ceiling - hmax; + velocity.y = 0.0f; + } else + entity.room = s.roomAbove; + } + entity.y = (int)pos.y; } diff --git a/src/debug.h b/src/debug.h index d32fba1..6043c3c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -2,28 +2,29 @@ #define H_DEBUG #include "core.h" +#include "format.h" namespace Debug { + void begin() { + glMatrixMode(GL_PROJECTION); + glLoadMatrixf((GLfloat*)&Core::mProj); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glLoadMatrixf((GLfloat*)&Core::mView); + + glLineWidth(3); + glPointSize(32); + + glUseProgram(0); + } + + void end() { + // + } + namespace Draw { - void begin() { - glMatrixMode(GL_PROJECTION); - glLoadMatrixf((GLfloat*)&Core::mProj); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glLoadMatrixf((GLfloat*)&Core::mView); - - glLineWidth(3); - glPointSize(32); - - glUseProgram(0); - } - - void end() { - // - } - void box(const vec3 &min, const vec3 &max) { glBegin(GL_LINES); glVertex3f(min.x, min.y, min.z); @@ -108,6 +109,198 @@ namespace Debug { glEnd(); } } + + namespace Level { + + void debugFloor(const TR::Level &level, const vec3 &f, const vec3 &c, int floorIndex, bool current) { + vec3 vf[4] = { f, f + vec3(1024, 0, 0), f + vec3(1024, 0, 1024), f + vec3(0, 0, 1024) }; + vec3 vc[4] = { c, c + vec3(1024, 0, 0), c + vec3(1024, 0, 1024), c + vec3(0, 0, 1024) }; + + uint16 cmd, *d = &level.floors[floorIndex]; + + if (floorIndex) + do { + cmd = *d++; + int func = cmd & 0x00FF; // function + int sub = (cmd & 0x7F00) >> 8; // sub function + + if (func == 0x01) { // portal + d++; + // d += 2; + + } + + if ((func == 0x02 || func == 0x03) && sub == 0x00) { // floor & ceiling corners + int sx = 256 * int((int8)(*d & 0x00FF)); + int sz = 256 * int((int8)((*d & 0xFF00) >> 8)); + + auto &p = func == 0x02 ? vf : vc; + + if (func == 0x02) { + + // if (current) + // LOG("%d\n", sx); + + if (sx > 0) { + p[0].y += sx; + p[3].y += sx; + } else { + p[1].y -= sx; + p[2].y -= sx; + } + + if (sz > 0) { + p[0].y += sz; + p[1].y += sz; + } else { + p[3].y -= sz; + p[2].y -= sz; + } + + } else { + + if (sx < 0) { + p[0].y += sx; + p[3].y += sx; + } else { + p[1].y -= sx; + p[2].y -= sx; + } + + if (sz > 0) { + p[0].y -= sz; + p[1].y -= sz; + } else { + p[3].y += sz; + p[2].y += sz; + } + + } + d++; + } + + + if (func == 0x04) { + //*d++; // trigger setup + /* + if (sub == 0x00) LOG("trigger\n"); + if (sub == 0x01) LOG("pad\n"); + if (sub == 0x02) LOG("switch\n"); + if (sub == 0x03) LOG("key\n"); + if (sub == 0x04) LOG("pickup\n"); + if (sub == 0x05) LOG("heavy-trigger\n"); + if (sub == 0x06) LOG("anti-pad\n"); + if (sub == 0x07) LOG("combat\n"); + if (sub == 0x08) LOG("dummy\n"); + if (sub == 0x09) LOG("anti-trigger\n"); + */ + uint16 act; + do { + act = *d++; // trigger action + } while (!(act & 0x8000)); + + break; + } + + } while (!(cmd & 0x8000)); + + if (current) + glColor3f(1, 1, 1); + else + glColor3f(0, 1, 0); + + glBegin(GL_LINE_STRIP); + for (int i = 0; i < 5; i++) + glVertex3fv((GLfloat*)&vf[i % 4]); + glEnd(); + + glColor3f(1, 0, 0); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < 5; i++) + glVertex3fv((GLfloat*)&vc[i % 4]); + glEnd(); + } + + void debugSectors(const TR::Level &level, const vec3 &pos, int roomIndex) { + TR::Room &room = level.rooms[roomIndex]; + + vec3 p = (pos - vec3(room.info.x, 0, room.info.z)) / vec3(1024, 1, 1024); + + for (int z = 0; z < room.zSectors; z++) + for (int x = 0; x < room.xSectors; x++) { + auto &s = room.sectors[x * room.zSectors + z]; + vec3 f(x * 1024 + room.info.x, s.floor * 256, z * 1024 + room.info.z); + vec3 c(x * 1024 + room.info.x, s.ceiling * 256, z * 1024 + room.info.z); + + debugFloor(level, f, c, s.floorIndex, (int)p.x == x && (int)p.z == z); + } + } + + void rooms(const TR::Level &level, const vec3 &pos, int roomIndex) { + Core::setBlending(bmAdd); + glDepthMask(GL_FALSE); + + for (int i = 0; i < level.roomsCount; i++) { + TR::Room &r = level.rooms[i]; + vec3 p = vec3(r.info.x, r.info.yTop, r.info.z); + + if (i == roomIndex) { + //if (lara->insideRoom(Core::viewPos, i)) { + debugSectors(level, pos, i); + glColor3f(0, 1, 0); + } else + glColor3f(1, 1, 1); + + Debug::Draw::box(p, p + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024)); + } + + glDepthMask(GL_TRUE); + Core::setBlending(bmAlpha); + } + + void portals(const TR::Level &level) { + Core::setBlending(bmAdd); + glColor3f(0, 0.25f, 0.25f); + glDepthMask(GL_FALSE); + + glBegin(GL_QUADS); + for (int i = 0; i < level.roomsCount; i++) { + TR::Room &r = level.rooms[i]; + for (int j = 0; j < r.portalsCount; j++) { + TR::Room::Portal &p = r.portals[j]; + for (int k = 0; k < 4; k++) { + TR::Vertex &v = p.vertices[k]; + glVertex3f(v.x + r.info.x, v.y, v.z + r.info.z); + } + } + } + glEnd(); + + glDepthMask(GL_TRUE); + Core::setBlending(bmAlpha); + } + + void lights(const TR::Level &level) { + // int roomIndex = level.entities[lara->entity].room; + // int lightIndex = getLightIndex(lara->pos, roomIndex); + + glPointSize(8); + glBegin(GL_POINTS); + for (int i = 0; i < level.roomsCount; i++) + for (int j = 0; j < level.rooms[i].lightsCount; j++) { + TR::Room::Light &l = level.rooms[i].lights[j]; + float a = l.intensity / 8191.0f; + vec3 p = vec3(l.x, l.y, l.z); + vec4 color = vec4(a, a, a, 1); + Debug::Draw::point(p, color); + //if (i == roomIndex && j == lightIndex) + // color = vec4(0, 1, 0, 1); + Debug::Draw::sphere(p, l.attenuation, color); + } + glEnd(); + } + + } } #endif \ No newline at end of file diff --git a/src/format.h b/src/format.h index 3b40516..48506d1 100644 --- a/src/format.h +++ b/src/format.h @@ -6,7 +6,10 @@ #define TR1_DEMO namespace TR { - #define ROOM_FLAG_VISIBLE 0x8000 + enum : int32 { + ROOM_FLAG_WATER = 0x0001, + ROOM_FLAG_VISIBLE = 0x8000 + }; #define DATA_PORTAL 0x01 #define DATA_FLOOR 0x02 @@ -59,13 +62,22 @@ namespace TR { #define ENTITY_AMMO_SHOTGUN 89 #define ENTITY_AMMO_MAGNUM 90 + // http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left + enum LaraAnim : int32 { + ANIM_STAND = 11, + ANIM_SMASH_JUMP = 32, + ANIM_SMASH_RUN_LEFT = 53, + ANIM_SMASH_RUN_RIGHT = 54, + ANIM_WATER_FALL = 112, + }; - enum LaraState { + // http://www.tombraiderforums.com/showthread.php?t=211681 + enum LaraState : int32 { STATE_WALK, STATE_RUN, STATE_STOP, STATE_FORWARD_JUMP, - STATE_FAST_TURN, + STATE_4, STATE_FAST_BACK, STATE_TURN_RIGHT, STATE_TURN_LEFT, @@ -81,7 +93,7 @@ namespace TR { STATE_SWIM, STATE_GLIDE, STATE_NULL_19, - STATE_FAST_TURN_20, + STATE_FAST_TURN, STATE_STEP_RIGHT, STATE_STEP_LEFT, STATE_ROLL, @@ -143,7 +155,7 @@ namespace TR { struct fixed { uint16 L; int16 H; - float toFloat() { + operator float() const { return H + L / 65535.0f; } }; @@ -368,7 +380,7 @@ namespace TR { struct SpriteTexture { uint16 tile; uint8 u, v; - uint16 w, h; + uint16 w, h; // (ActualValue * 256) + 255 int16 l, t, r, b; }; @@ -417,7 +429,7 @@ namespace TR { }; struct SoundInfo { - uint16 index; // (index into soundsIndices) -- NOT USED IN TR4-5!!! + uint16 offset; uint16 volume; uint16 chance; // If !=0 and ((rand()&0x7fff) > Chance), this sound is not played uint16 flags; // Bits 0-1: Looped flag, bits 2-5: num samples, bits 6-7: UNUSED diff --git a/src/level.h b/src/level.h index 5b0ae84..4fd4591 100644 --- a/src/level.h +++ b/src/level.h @@ -55,14 +55,15 @@ struct Level { break; } - lara = new Controller(&level, entity); + lara = new Lara(&level, entity); - camera.fov = 75.0f; - camera.znear = 0.1f * 2048.0f; - camera.zfar = 1000.0f * 2048.0f; - camera.offset = vec3(0, 0, 768); - camera.pos = vec3(0.0f); - camera.angle = vec3(0, PI, 0); + camera.fov = 75.0f; + camera.znear = 0.1f * 2048.0f; + camera.zfar = 1000.0f * 2048.0f; + camera.offset = vec3(0, 0, 768); + camera.deltaPos = vec3(0.0f, 768.0f, 0.0f); + camera.deltaAngle = vec3(0.0f, PI, 0.0f); + camera.angle = vec3(0.0f); } ~Level() { @@ -488,8 +489,8 @@ struct Level { void renderRoom(int index) { TR::Room &room = level.rooms[index]; - if (room.flags & ROOM_FLAG_VISIBLE) return; // already rendered - room.flags |= ROOM_FLAG_VISIBLE; + if (room.flags & TR::ROOM_FLAG_VISIBLE) return; // already rendered + room.flags |= TR::ROOM_FLAG_VISIBLE; vec3 offset = vec3(room.info.x, 0.0f, room.info.z); @@ -614,16 +615,20 @@ struct Level { return ma.getRot().slerp(mb.getRot(), t).normal(); } - void renderModel(const TR::Model &model) { + void renderModel(const TR::Model &model, vec3 angle) { TR::Animation *anim = &level.anims[model.animation]; float fTime = time; if (model.id == ENTITY_LARA) { - anim = lara->anim; fTime = lara->fTime; + angle = lara->angle; } + if (angle.y != 0.0f) Core::mModel.rotateY(angle.y); + if (angle.x != 0.0f) Core::mModel.rotateX(angle.x); + if (angle.z != 0.0f) Core::mModel.rotateZ(angle.z); + float k = fTime * 30.0f / anim->frameRate; int fIndex = (int)k; int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1; @@ -737,7 +742,7 @@ struct Level { void renderEntity(const TR::Entity &entity) { // if (!(entity.flags & ENTITY_FLAG_VISIBLE)) // return; - if (!(level.rooms[entity.room].flags & ROOM_FLAG_VISIBLE)) // check for room visibility + if (!(level.rooms[entity.room].flags & TR::ROOM_FLAG_VISIBLE)) // check for room visibility return; mat4 m = Core::mModel; @@ -753,8 +758,7 @@ struct Level { for (int i = 0; i < level.modelsCount; i++) if (entity.id == level.models[i].id) { - Core::mModel.rotateY(entity.rotation / 16384.0f * PI * 0.5f); - renderModel(level.models[i]); + renderModel(level.models[i], vec3(0, entity.rotation / 16384.0f * PI * 0.5f, 0)); break; } /* @@ -767,210 +771,17 @@ struct Level { Core::mModel = m; } -#ifdef _DEBUG - void debugPortals() { - Core::setBlending(bmAdd); - glColor3f(0, 0.25f, 0.25f); - glDepthMask(GL_FALSE); - - glBegin(GL_QUADS); - for (int i = 0; i < level.roomsCount; i++) { - TR::Room &r = level.rooms[i]; - for (int j = 0; j < r.portalsCount; j++) { - TR::Room::Portal &p = r.portals[j]; - for (int k = 0; k < 4; k++) { - TR::Vertex &v = p.vertices[k]; - glVertex3f(v.x + r.info.x, v.y, v.z + r.info.z); - } - } - } - glEnd(); - - glDepthMask(GL_TRUE); - Core::setBlending(bmAlpha); - } - - void debugFloor(const vec3 &f, const vec3 &c, int floorIndex, bool current) { - vec3 vf[4] = { f, f + vec3(1024, 0, 0), f + vec3(1024, 0, 1024), f + vec3(0, 0, 1024) }; - vec3 vc[4] = { c, c + vec3(1024, 0, 0), c + vec3(1024, 0, 1024), c + vec3(0, 0, 1024) }; - - uint16 cmd, *d = &level.floors[floorIndex]; - - if (floorIndex) - do { - cmd = *d++; - int func = cmd & 0x00FF; // function - int sub = (cmd & 0x7F00) >> 8; // sub function - - if (func == 0x01) { // portal - d++; - // d += 2; - - } - - if ((func == 0x02 || func == 0x03) && sub == 0x00) { // floor & ceiling corners - int sx = 256 * int((int8)(*d & 0x00FF)); - int sz = 256 * int((int8)((*d & 0xFF00) >> 8)); - - auto &p = func == 0x02 ? vf : vc; - - if (func == 0x02) { - - // if (current) - // LOG("%d\n", sx); - - if (sx > 0) { - p[0].y += sx; - p[3].y += sx; - } else { - p[1].y -= sx; - p[2].y -= sx; - } - - if (sz > 0) { - p[0].y += sz; - p[1].y += sz; - } else { - p[3].y -= sz; - p[2].y -= sz; - } - - } else { - - if (sx < 0) { - p[0].y += sx; - p[3].y += sx; - } else { - p[1].y -= sx; - p[2].y -= sx; - } - - if (sz > 0) { - p[0].y -= sz; - p[1].y -= sz; - } else { - p[3].y += sz; - p[2].y += sz; - } - - } - d++; - } - - - if (func == 0x04) { - //*d++; // trigger setup - /* - if (sub == 0x00) LOG("trigger\n"); - if (sub == 0x01) LOG("pad\n"); - if (sub == 0x02) LOG("switch\n"); - if (sub == 0x03) LOG("key\n"); - if (sub == 0x04) LOG("pickup\n"); - if (sub == 0x05) LOG("heavy-trigger\n"); - if (sub == 0x06) LOG("anti-pad\n"); - if (sub == 0x07) LOG("combat\n"); - if (sub == 0x08) LOG("dummy\n"); - if (sub == 0x09) LOG("anti-trigger\n"); - */ - uint16 act; - do { - act = *d++; // trigger action - } while (!(act & 0x8000)); - - break; - } - - } while (!(cmd & 0x8000)); - - if (current) - glColor3f(1, 1, 1); - else - glColor3f(0, 1, 0); - - glBegin(GL_LINE_STRIP); - for (int i = 0; i < 5; i++) - glVertex3fv((GLfloat*)&vf[i % 4]); - glEnd(); - - glColor3f(1, 0, 0); - glBegin(GL_LINE_STRIP); - for (int i = 0; i < 5; i++) - glVertex3fv((GLfloat*)&vc[i % 4]); - glEnd(); - } - - void debugSectors(int index) { - TR::Room &room = level.rooms[index]; - - vec3 p = (lara->pos - vec3(room.info.x, 0, room.info.z)) / vec3(1024, 1, 1024); - - for (int z = 0; z < room.zSectors; z++) - for (int x = 0; x < room.xSectors; x++) { - auto &s = room.sectors[x * room.zSectors + z]; - vec3 f(x * 1024 + room.info.x, s.floor * 256, z * 1024 + room.info.z); - vec3 c(x * 1024 + room.info.x, s.ceiling * 256, z * 1024 + room.info.z); - - debugFloor(f, c, s.floorIndex, (int)p.x == x && (int)p.z == z); - } - } - - void debugRooms() { - Core::setBlending(bmAdd); - glDepthMask(GL_FALSE); - - for (int i = 0; i < level.roomsCount; i++) { - TR::Room &r = level.rooms[i]; - vec3 p = vec3(r.info.x, r.info.yTop, r.info.z); - - if (i == level.entities[lara->entity].room) { - //if (lara->insideRoom(Core::viewPos, i)) { - debugSectors(i); - glColor3f(0, 1, 0); - } else - glColor3f(1, 1, 1); - - Debug::Draw::box(p, p + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024)); - } - - glDepthMask(GL_TRUE); - Core::setBlending(bmAlpha); - } - - void debugMeshes() { - mat4 m = Core::mModel; - for (int i = 0; i < level.meshOffsetsCount; i++) { - renderMesh(i); - Core::mModel.translate(vec3(-128, 0, 0)); - } - Core::mModel = m; - } - - void debugLights() { - int roomIndex = level.entities[lara->entity].room; - int lightIndex = getLightIndex(lara->pos, roomIndex); - - glPointSize(8); - glBegin(GL_POINTS); - for (int i = 0; i < level.roomsCount; i++) - for (int j = 0; j < level.rooms[i].lightsCount; j++) { - TR::Room::Light &l = level.rooms[i].lights[j]; - float a = l.intensity / 8191.0f; - vec3 p = vec3(l.x, l.y, l.z); - vec4 color = vec4(a, a, a, 1); - Debug::Draw::point(p, color); - if (i == roomIndex && j == lightIndex) - color = vec4(0, 1, 0, 1); - Debug::Draw::sphere(p, l.attenuation, color); - } - glEnd(); - } -#endif - float tickTextureAnimation = 0.0f; void update() { time += Core::deltaTime; lara->update(); + + #ifndef FREE_CAMERA + camera.pos = vec3(-lara->pos.x, -lara->pos.y, lara->pos.z); + #endif + camera.targetDeltaPos = lara->inWater ? vec3(0.0f, -256.0f, 0.0f) : vec3(0.0f, -768.0f, 0.0f); + camera.targetAngle = vec3(lara->angle.x, -lara->angle.y, 0.0f); //-lara->angle.z); camera.update(); /* @@ -1003,9 +814,7 @@ struct Level { void render() { // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - #ifndef FREE_CAMERA - camera.pos = vec3(-lara->pos.x, -lara->pos.y + 768, lara->pos.z); - #endif + camera.setup();; atlas->bind(0); @@ -1025,8 +834,9 @@ struct Level { Core::mModel.identity(); for (int i = 0; i < level.roomsCount; i++) - level.rooms[i].flags &= ~ROOM_FLAG_VISIBLE; // clear visible flag + level.rooms[i].flags &= ~TR::ROOM_FLAG_VISIBLE; // clear visible flag + // TODO: collision detection for camera renderRoom(getCameraRoomIndex()); renderRoom(lara->getEntity().room); @@ -1038,13 +848,11 @@ struct Level { renderEntity(level.entities[i]); #ifdef _DEBUG - // debugMeshes(); - - Debug::Draw::begin(); - // debugRooms(); - // debugLights(); - debugPortals(); - Debug::Draw::end(); + Debug::begin(); + Debug::Level::rooms(level, lara->pos, lara->getEntity().room); + Debug::Level::lights(level); + Debug::Level::portals(level); + Debug::end(); #endif } }; diff --git a/src/utils.h b/src/utils.h index ad23c97..fd37ba5 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,6 +1,7 @@ #ifndef H_UTILS #define H_UTILS +#include #include #include #include @@ -8,16 +9,19 @@ #ifdef _DEBUG #define debugBreak() _asm { int 3 } #define ASSERT(expr) if (expr) {} else { LOG("ASSERT %s in %s:%d\n", #expr, __FILE__, __LINE__); debugBreak(); } + + #ifndef ANDROID + #define LOG(...) printf(__VA_ARGS__) + #else + #include + #define LOG(...) __android_log_print(ANDROID_LOG_INFO,"X5",__VA_ARGS__) + #endif + #else #define ASSERT(expr) + #define LOG(...) ((void)0) #endif -#ifndef ANDROID - #define LOG(...) printf(__VA_ARGS__) -#else - #include - #define LOG(...) __android_log_print(ANDROID_LOG_INFO,"X5",__VA_ARGS__) -#endif #define PI 3.14159265358979323846f #define DEG2RAD (PI / 180.0f) @@ -57,6 +61,11 @@ inline const T& max(const T &a, const T &b) { return a > b ? a : b; } +template +inline const T& clamp(const T &x, const T &a, const T &b) { + return x < a ? a : (x > b ? b : x); +} + template inline const int sign(const T &x) { return x > 0 ? 1 : (x < 0 ? -1 : 0); @@ -84,6 +93,7 @@ struct vec3 { vec3(float s) : x(s), y(s), z(s) {} vec3(float x, float y, float z) : x(x), y(y), z(z) {} vec3(const vec2 &xy, float z = 0.0f) : x(xy.x), y(xy.y), z(z) {} + vec3(float lng, float lat) : x(sinf(lat) * cosf(lng)), y(-sinf(lng)), z(cosf(lat) * cosf(lng)) {} float& operator [] (int index) const { return ((float*)this)[index]; } @@ -102,6 +112,11 @@ struct vec3 { vec3 lerp(const vec3 &v, const float t) const { return *this + (v - *this) * t; } + + vec3 rotateY(float angle) const { + float s = sinf(angle), c = cosf(angle); + return vec3(x*c - z*s, y, x*s + z*c); + } }; struct vec4 {