diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 0930478..3feb537 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ 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 {