From f8bd8789a7fe699de5401c999a190c2207a4bfad Mon Sep 17 00:00:00 2001 From: XProger Date: Thu, 16 Mar 2017 18:21:51 +0300 Subject: [PATCH] #15 core stats refactoring; gamepad analog fix for web; shadow matrix crop (test) --- src/camera.h | 6 +- src/controller.h | 4 +- src/core.h | 33 +++++-- src/game.h | 3 +- src/lara.h | 197 +++++++++++++++++++------------------- src/level.h | 67 ++++++++++--- src/platform/nix/main.cpp | 42 ++++---- src/platform/web/main.cpp | 23 ++--- src/platform/win/main.cpp | 23 ++--- src/shader.glsl | 34 +++---- src/utils.h | 78 ++++++++++++++- 11 files changed, 320 insertions(+), 190 deletions(-) diff --git a/src/camera.h b/src/camera.h index 675c3d4..f9261b1 100644 --- a/src/camera.h +++ b/src/camera.h @@ -254,6 +254,10 @@ struct Camera : Controller { updateListener(); } + mat4 getProjMatrix() { + return mat4(fov, (float)Core::width / (float)Core::height, znear, zfar); + } + virtual void setup(bool calcMatrices) { if (calcMatrices) { if (reflectPlane) { @@ -263,7 +267,7 @@ struct Camera : Controller { Core::mViewInv = mViewInv; Core::mView = Core::mViewInv.inverse(); - Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar); + Core::mProj = getProjMatrix(); // TODO: camera shake // TODO: temporal anti-aliasing diff --git a/src/controller.h b/src/controller.h index 7983bec..df61906 100644 --- a/src/controller.h +++ b/src/controller.h @@ -541,7 +541,7 @@ struct Controller { entity.flags.rendered = true; - if (Core::frameIndex != frameIndex) + if (Core::stats.frame != frameIndex) animation.getJoints(matrix, -1, true, joints); if (layers) { @@ -563,7 +563,7 @@ struct Controller { mesh->renderModel(entity.modelIndex - 1); } - frameIndex = Core::frameIndex; + frameIndex = Core::stats.frame; // blob shadow // TODO: fake AO if (!Core::settings.shadows && Core::pass == Core::passCompose && TR::castShadow(entity.type)) { diff --git a/src/core.h b/src/core.h index 74e8a21..00d0506 100644 --- a/src/core.h +++ b/src/core.h @@ -186,9 +186,10 @@ struct Texture; enum CullMode { cfNone, cfBack, cfFront }; enum BlendMode { bmNone, bmAlpha, bmAdd, bmMultiply, bmScreen }; +extern int getTime(); + namespace Core { int width, height; - int frameIndex; float deltaTime; mat4 mView, mProj, mViewProj, mViewInv, mLightProj; Basis basis; @@ -221,9 +222,24 @@ namespace Core { CullMode cullMode; } active; - struct { - int dips; - int tris; + struct Stats { + int dips, tris, frame, fps, fpsTime; + + Stats() : frame(0), fps(0), fpsTime(0) {} + + void start() { + dips = tris = 0; + } + + void stop() { + if (fpsTime < getTime()) { + LOG("FPS: %d DIP: %d TRI: %d\n", fps, dips, tris); + fps = frame; + frame = 0; + fpsTime = getTime() + 1000; + } else + frame++; + } } stats; struct { @@ -360,8 +376,6 @@ namespace Core { for (int i = 0; i < MAX_LIGHTS; i++) lightColor[i] = vec4(0, 0, 0, 1); - - frameIndex = 0; uint32 data = 0x00000000; blackTex = new Texture(1, 1, Texture::RGBA, false, &data, false); @@ -532,13 +546,18 @@ namespace Core { glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height); } - void resetStates() { + void beginFrame() { memset(&active, 0, sizeof(active)); setDepthTest(true); active.blendMode = bmAlpha; active.cullMode = cfNone; setCulling(cfFront); setBlending(bmNone); + Core::stats.start(); + } + + void endFrame() { + Core::stats.stop(); } } diff --git a/src/game.h b/src/game.h index 3f709fe..6f83de0 100644 --- a/src/game.h +++ b/src/game.h @@ -60,8 +60,9 @@ namespace Game { } void render() { + Core::beginFrame(); level->render(); - Core::frameIndex++; + Core::endFrame(); } } diff --git a/src/lara.h b/src/lara.h index 50a9ce9..baca5a5 100644 --- a/src/lara.h +++ b/src/lara.h @@ -54,7 +54,7 @@ struct Lara : Character { ANIM_STAND_RIGHT = 3, ANIM_STAND = 11, - + ANIM_CLIMB_JUMP = 26, ANIM_HANG_FALL = 28, @@ -159,7 +159,7 @@ struct Lara : Character { STATE_HANDSTAND, STATE_WATER_OUT, STATE_MAX }; - + enum : int { BODY_HIP = 0x0001, BODY_LEG_L1 = 0x0002, @@ -209,7 +209,7 @@ struct Lara : Character { } arms[2]; ActionCommand actionList[MAX_TRIGGER_ACTIONS]; - + Inventory inventory; int lastPickUp; int viewTarget; @@ -272,10 +272,10 @@ struct Lara : Character { float DAMPING = 1.5f; if (lara->getRoom().flags.water) { - ACCEL *= -0.5f; + ACCEL *= -0.5f; DAMPING = 4.0f; } - + DAMPING = 1.0f / (1.0f + DAMPING * TIMESTEP); // Pade approximation for (int i = 1; i < jointsCount; i++) { @@ -340,7 +340,7 @@ struct Lara : Character { a.pos -= dir; b.pos += dir; } else - b.pos += dir * (d * 1.0f); + b.pos += dir * (d * 1.0f); } } @@ -349,7 +349,7 @@ struct Lara : Character { integrate(); // Verlet integration step collide(); // check collision with Lara's mesh for (int i = 0; i < jointsCount; i++) // solve connections (springs) - solve(); + solve(); vec3 headDir = getBasis().rot * vec3(0.0f, 0.0f, -1.0f); @@ -357,13 +357,13 @@ struct Lara : Character { vec3 d = (joints[i + 1].pos - joints[i].pos).normal(); vec3 r = d.cross(headDir).normal(); vec3 u = d.cross(r).normal(); - + mat4 m; m.up = vec4(u, 0.0f); m.dir = vec4(d, 0.0f); m.right = vec4(r, 0.0f); m.offset = vec4(0.0f, 0.0f, 0.0f, 1.0f); - + basis[i].identity(); basis[i].translate(joints[i].pos); basis[i].rotate(m.getRot()); @@ -374,11 +374,11 @@ struct Lara : Character { Core::active.shader->setParam(uBasis, basis[0], jointsCount); mesh->renderModel(lara->level->extra.braid); } - + } *braid; Lara(IGame *game, int entity, bool home) : Character(game, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1), braid(NULL) { - + if (getEntity().type == TR::Entity::LARA) { if (getRoom().flags.water) animation.setAnim(ANIM_UNDERWATER); @@ -408,7 +408,7 @@ struct Lara : Character { if (level->extra.braid > -1) braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f)); - #ifdef _DEBUG + #ifdef _DEBUG //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, true); // gym (pool) //reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge) @@ -443,7 +443,7 @@ struct Lara : Character { } updateEntity(); } - + void wpnSet(Weapon::Type wType) { wpnCurrent = wType; wpnState = Weapon::IS_FIRING; @@ -463,9 +463,9 @@ struct Lara : Character { arm.anim = wAnim; - if (wAnimDir > 0.0f) + if (wAnimDir > 0.0f) arm.animation.time = wAnimTime; - else + else if (wAnimDir < 0.0f) arm.animation.time = arm.animation.timeMax + wAnimTime; arm.animation.updateInfo(); @@ -490,7 +490,7 @@ struct Lara : Character { int mask = 0; switch (wpnCurrent) { case Weapon::EMPTY : break; - case Weapon::PISTOLS : + case Weapon::PISTOLS : case Weapon::MAGNUMS : case Weapon::UZIS : switch (wState) { @@ -518,7 +518,7 @@ struct Lara : Character { // 2 - shotgun (hands, chest) // 3 - angry (head) - // swap weapon parts + // swap weapon parts if (wpnCurrent != Weapon::SHOTGUN) { meshSwap(1, level->extra.weapons[wpnCurrent], mask); // have a shotgun in inventory place it on the back if another weapon is in use @@ -529,7 +529,7 @@ struct Lara : Character { // mesh swap to angry Lara's head while firing (from uzis model) meshSwap(3, level->extra.weapons[Weapon::UZIS], (wState == Weapon::IS_FIRING) ? BODY_HEAD : 0); - + wpnState = wState; } @@ -538,7 +538,7 @@ struct Lara : Character { } bool canLookAt() { - return (stand == STAND_GROUND || stand == STAND_SLIDE) + return (stand == STAND_GROUND || stand == STAND_SLIDE) && state != STATE_REACH && state != STATE_PUSH_BLOCK && state != STATE_PULL_BLOCK @@ -551,7 +551,7 @@ struct Lara : Character { && emptyHands() && animation.index != ANIM_CLIMB_3 && animation.index != ANIM_CLIMB_2 - && state != STATE_DEATH + && state != STATE_DEATH && state != STATE_HANG && state != STATE_REACH && state != STATE_TREAD @@ -595,7 +595,7 @@ struct Lara : Character { wpnSetAnim(arms[0], wpnState, Weapon::Anim::PREPARE, 0.0f, 1.0f); wpnSetAnim(arms[1], wpnState, Weapon::Anim::PREPARE, 0.0f, 1.0f); } else - wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, 1.0f); + wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, 1.0f); } } @@ -621,7 +621,7 @@ struct Lara : Character { case Weapon::Anim::PREPARE : ASSERT(false); break; // rifle has no prepare animation case Weapon::Anim::UNHOLSTER : return 1; case Weapon::Anim::HOLSTER : return 3; - case Weapon::Anim::HOLD : + case Weapon::Anim::HOLD : case Weapon::Anim::AIM : return 0; case Weapon::Anim::FIRE : return 2; default : ; @@ -631,7 +631,7 @@ struct Lara : Character { case Weapon::Anim::PREPARE : return 1; case Weapon::Anim::UNHOLSTER : return 2; case Weapon::Anim::HOLSTER : ASSERT(false); break; // pistols has no holster animation (it's reversed unholster) - case Weapon::Anim::HOLD : + case Weapon::Anim::HOLD : case Weapon::Anim::AIM : return 0; case Weapon::Anim::FIRE : return 3; default : ; @@ -663,7 +663,7 @@ struct Lara : Character { wpnSetAnim(arms[i], Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, target == -1); } // shotgun reload sound - if (wpnCurrent == Weapon::SHOTGUN) { + if (wpnCurrent == Weapon::SHOTGUN) { if (anim.frameIndex == 10) playSound(TR::SND_SHOTGUN_RELOAD, pos, Sound::Flags::PAN); } @@ -681,12 +681,12 @@ struct Lara : Character { void doShot(bool rightHand, bool leftHand) { int count = wpnCurrent == Weapon::SHOTGUN ? 6 : 2; - + float nearDist = 32.0f * 1024.0f; vec3 nearPos; bool hasShot = false; - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { int armIndex; if (wpnCurrent == Weapon::SHOTGUN) { if (!rightHand) continue; @@ -696,7 +696,7 @@ struct Lara : Character { armIndex = i; } Arm *arm = &arms[armIndex]; - + arm->shotTimer = 0.0f; hasShot = true; @@ -786,12 +786,12 @@ struct Lara : Character { float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, 0.0f, 1.0f); Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, sqrtf(intensity)); } - + if (isRifle) animateShotgun(); else animatePistols(); - + wpnFire(); // make a shot } } @@ -800,7 +800,7 @@ struct Lara : Character { for (int i = 0; i < 2; i++) { Arm &arm = arms[i]; - if (!arm.animation.isEnded) continue; + if (!arm.animation.isEnded) continue; if (arm.animation.dir >= 0.0f) switch (arm.anim) { @@ -815,7 +815,7 @@ struct Lara : Character { break; default : ; }; - + if (arm.animation.dir < 0.0f) switch (arm.anim) { case Weapon::Anim::PREPARE : wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 1.0f, false); break; @@ -842,7 +842,7 @@ struct Lara : Character { break; default : ; } - } else + } else if (arm.animation.frameIndex != arm.animation.framePrev) { float delta = arm.animation.time / arm.animation.timeMax; switch (arm.anim) { @@ -857,11 +857,11 @@ struct Lara : Character { } void updateOverrides() { - // head & chest + // head & chest animation.overrideMask |= BODY_CHEST | BODY_HEAD; animation.overrides[ 7] = animation.getJointRot( 7); - animation.overrides[14] = animation.getJointRot(14); + animation.overrides[14] = animation.getJointRot(14); /* TODO: shotgun full body animation if (wpnCurrent == Weapon::SHOTGUN) { @@ -889,14 +889,14 @@ struct Lara : Character { lookAt(viewTarget); - + if (wpnCurrent == Weapon::SHOTGUN) aimShotgun(); else aimPistols(); } - void lookAt(int target) { // TODO: character lookAt + void lookAt(int target) { // TODO: character lookAt float speed = 8.0f * Core::deltaTime; quat rot; @@ -904,7 +904,7 @@ struct Lara : Character { // chest if (can && aim(target, 7, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.9f, PI * 0.9f), rot)) rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed); - else + else rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed); animation.overrides[7] = rotChest * animation.overrides[7]; @@ -1010,7 +1010,7 @@ struct Lara : Character { } else return false; } - + virtual void cmdEmpty() { wpnHide(); } @@ -1034,7 +1034,7 @@ struct Lara : Character { switch (fx) { case TR::EFFECT_FLIP_MAP : break; // TODO case TR::EFFECT_LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break; - case TR::EFFECT_DRAW_RIGHTGUN : + case TR::EFFECT_DRAW_RIGHTGUN : case TR::EFFECT_DRAW_LEFTGUN : drawGun(fx == TR::EFFECT_DRAW_RIGHTGUN); break; default : LOG("unknown effect command %d (anim %d)\n", fx, animation.index); } @@ -1094,7 +1094,7 @@ struct Lara : Character { pos -= getDir() * 256.0f; pos.y -= 256; } - updateEntity(); + updateEntity(); return true; } } @@ -1109,8 +1109,8 @@ struct Lara : Character { bool useItem(TR::Entity::Type item, TR::Entity::Type slot) { if (item == TR::Entity::NONE) { switch (slot) { - case TR::Entity::HOLE_KEY : item = TR::Entity::KEY_1; break; // TODO: 1-4 - case TR::Entity::HOLE_PUZZLE : item = TR::Entity::PUZZLE_1; break; + case TR::Entity::HOLE_KEY : item = TR::Entity::KEY_1; break; // TODO: 1-4 + case TR::Entity::HOLE_PUZZLE : item = TR::Entity::PUZZLE_1; break; default : return false; } } @@ -1269,13 +1269,13 @@ struct Lara : Character { if (stand == STAND_SLIDE || (stand == STAND_AIR && velocity.y > 0) || stand == STAND_GROUND) { if (e.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) { if (stand == STAND_AIR) - playSound(TR::SND_LANDING, pos, Sound::Flags::PAN); + playSound(TR::SND_LANDING, pos, Sound::Flags::PAN); pos.y = float(info.floor); updateEntity(); if (stand == STAND_GROUND || stand == STAND_AIR) slideStart(); - + return STAND_SLIDE; } } @@ -1314,7 +1314,7 @@ struct Lara : Character { vec3 p = vec3(pos.x, bounds.min.y, pos.z); Collision c = Collision(level, getRoomIndex(), p, getDir() * 32.0f, vec3(0.0f), LARA_RADIUS, angleExt, 0, 0, 0, 0); - + if (c.side != Collision::FRONT) return state; @@ -1343,7 +1343,7 @@ struct Lara : Character { if (input & ACTION) return STATE_REACH; if ((input & (FORTH | WALK)) == (FORTH | WALK)) return STATE_SWAN_DIVE; } - } else + } else if (state != STATE_SWAN_DIVE && state != STATE_REACH && state != STATE_FALL && state != STATE_UP_JUMP && state != STATE_BACK_JUMP && state != STATE_LEFT_JUMP && state != STATE_RIGHT_JUMP) return animation.setAnim(ANIM_FALL); @@ -1373,7 +1373,7 @@ struct Lara : Character { for (int i = 0; i < level->entitiesCount; i++) { TR::Entity &e = level->entities[i]; - + int q = entityQuadrant(e); int dx = abs(int(pos.x) - e.x); int dz = abs(int(pos.z) - e.z); @@ -1394,10 +1394,10 @@ struct Lara : Character { if ((input & ACTION) && emptyHands() && doPickUp()) return STATE_PICK_UP; - if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands() && collision.side == Collision::FRONT) { // TODO: get rid of animation.index + if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands() && collision.side == Collision::FRONT) { // TODO: get rid of animation.index int floor = collision.info[Collision::FRONT].floor; int h = (int)pos.y - floor; - + int aIndex = animation.index; if (h <= 2 * 256 + 128) { aIndex = ANIM_CLIMB_2; @@ -1406,7 +1406,7 @@ struct Lara : Character { aIndex = ANIM_CLIMB_3; pos.y = floor + 768.0f; } else if (h <= 7 * 256 + 128) - aIndex = ANIM_CLIMB_JUMP; + aIndex = ANIM_CLIMB_JUMP; if (aIndex != animation.index) { alignToWall(-LARA_RADIUS); @@ -1429,29 +1429,29 @@ struct Lara : Character { } // jump button is pressed - if (input & JUMP) { + if (input & JUMP) { if ((input & FORTH) && state == STATE_FORWARD_JUMP) return STATE_RUN; if (state == STATE_RUN) return STATE_FORWARD_JUMP; if (animation.index == ANIM_SLIDE_BACK) // TODO: animation index? %) - return STATE_SLIDE_BACK; + return STATE_SLIDE_BACK; return STATE_COMPRESS; } - + // walk button is pressed - if (input & WALK) { + if (input & WALK) { if (input & FORTH) return STATE_WALK; if (input & BACK) return STATE_BACK; if (input & LEFT) return STATE_STEP_LEFT; if (input & RIGHT) return STATE_STEP_RIGHT; return STATE_STOP; - } + } if ((input & ACTION) && emptyHands()) { if (state == STATE_PUSH_PULL_READY && (input & (FORTH | BACK))) { int pushState = (input & FORTH) ? STATE_PUSH_BLOCK : STATE_PULL_BLOCK; - Block *block = getBlock(); + Block *block = getBlock(); if (animation.canSetState(pushState) && block->doMove((input & FORTH) != 0)) { alignToWall(-LARA_RADIUS); return pushState; @@ -1519,7 +1519,7 @@ struct Lara : Character { vec3 p = pos + getDir() * (LARA_RADIUS + 2.0f); level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info); if (info.floor - info.ceiling >= LARA_HEIGHT) - return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP; + return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP; } return STATE_HANG; } @@ -1535,16 +1535,16 @@ struct Lara : Character { angle.x = -45.0f * DEG2RAD; return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation } - + if (state == STATE_SWAN_DIVE) { angle.x = -PI * 0.5f; game->waterDrop(pos, 128.0f, 0.2f); Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z); return STATE_DIVE; } - + if (input & JUMP) return STATE_SWIM; - + if (state == STATE_GLIDE && speed < LARA_SWIM_SPEED * 2.0f / 3.0f) return STATE_TREAD; @@ -1552,7 +1552,7 @@ struct Lara : Character { } virtual int getStateOnwater() { - angle.x = 0.0f; + angle.x = 0.0f; if (state == STATE_WATER_OUT) return state; @@ -1571,7 +1571,7 @@ struct Lara : Character { } if (input & FORTH) { - if (input & JUMP) { + if (input & JUMP) { angle.x = -PI * 0.25f; game->waterDrop(pos, 256.0f, 0.2f); return animation.setAnim(ANIM_TO_UNDERWATER); @@ -1584,11 +1584,11 @@ struct Lara : Character { return STATE_SURF_SWIM; } - + if (input & BACK) return STATE_SURF_BACK; if (input & WALK) { if (input & LEFT) return STATE_SURF_LEFT; - if (input & RIGHT) return STATE_SURF_RIGHT; + if (input & RIGHT) return STATE_SURF_RIGHT; } return STATE_SURF_TREAD; } @@ -1636,32 +1636,33 @@ struct Lara : Character { if (Input::down[ikE] || Input::down[ikCtrl] || Input::down[ikJoyA]) input |= ACTION; if (Input::down[ikQ] || Input::down[ikAlt] || Input::down[ikJoyY]) input |= WEAPON; - // analog control - rotFactor = vec2(1.0f); + // analog control + rotFactor = vec2(1.0f); - if (Input::down[ikJoyL]) input = FORTH | BACK; + if (Input::down[ikJoyL]) input = FORTH | BACK; - if ((state == STATE_STOP || stand == STATE_SURF_TREAD) && fabsf(Input::joy.L.x) < 0.5f && fabsf(Input::joy.L.y) < 0.5f) - return input; + if ((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(Input::joy.L.x) < 0.5f && fabsf(Input::joy.L.y) < 0.5f) + return input; bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK; - if (!moving) - if (fabsf(Input::joy.L.x) < fabsf(Input::joy.L.y)) - Input::joy.L.x = 0.0f; - else - Input::joy.L.y = 0.0f; + if (!moving) { + if (fabsf(Input::joy.L.x) < fabsf(Input::joy.L.y)) + Input::joy.L.x = 0.0f; + else + Input::joy.L.y = 0.0f; + } - if (Input::joy.L.x != 0.0f) { - input |= (Input::joy.L.x < 0.0f) ? LEFT : RIGHT; - if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER) - rotFactor.y = min(fabsf(Input::joy.L.x) / 0.75f, 1.0f); - } + if (Input::joy.L.x != 0.0f) { + input |= (Input::joy.L.x < 0.0f) ? LEFT : RIGHT; + if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER) + rotFactor.y = min(fabsf(Input::joy.L.x) / 0.75f, 1.0f); + } - if (Input::joy.L.y != 0.0f) { - input |= (Input::joy.L.y < 0.0f) ? FORTH : BACK; - if (stand == STAND_UNDERWATER) - rotFactor.x = min(fabsf(Input::joy.L.y) / 0.75f, 1.0f); + if (Input::joy.L.y != 0.0f) { + input |= (Input::joy.L.y < 0.0f) ? FORTH : BACK; + if (stand == STAND_UNDERWATER) + rotFactor.x = min(fabsf(Input::joy.L.y) / 0.75f, 1.0f); } return input; @@ -1672,7 +1673,7 @@ struct Lara : Character { case STATE_PICK_UP : { TR::Entity &item = level->entities[lastPickUp]; if (!item.flags.invisible) { - int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER; + int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER; if (animation.isFrameActive(pickupFrame)) { item.flags.invisible = true; inventory.add(item.type, 1); @@ -1716,10 +1717,10 @@ struct Lara : Character { else if (state == STATE_FAST_BACK) w *= TURN_FAST_BACK; else if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK) - w *= TURN_NORMAL; + w *= TURN_NORMAL; else if (state == STATE_FORWARD_JUMP || state == STATE_BACK) w *= TURN_SLOW; - else + else w = 0.0f; if (w != 0.0f) @@ -1736,7 +1737,7 @@ struct Lara : Character { case STATE_BACK_JUMP : case STATE_FAST_BACK : case STATE_SLIDE_BACK : - case STATE_ROLL_1 : + case STATE_ROLL_1 : angleExt += PI; break; case STATE_LEFT_JUMP : @@ -1766,7 +1767,7 @@ struct Lara : Character { case STATE_SURF_SWIM : case STATE_SURF_BACK : case STATE_SURF_LEFT : - case STATE_SURF_RIGHT : + case STATE_SURF_RIGHT : speed = min(speed + 30.0f * LARA_WATER_ACCEL * Core::deltaTime, LARA_SURF_SPEED); break; default : @@ -1817,7 +1818,7 @@ struct Lara : Character { if (velocity.length() >= 1.0f) move(); - + if (getEntity().type != TR::Entity::LARA) { TR::Entity &e = getEntity(); vec3 &p = getPos(); @@ -1827,9 +1828,9 @@ struct Lara : Character { checkRoom(); updateEntity(); } - + if (braid) - braid->update(); + braid->update(); } virtual vec3& getPos() { @@ -1839,7 +1840,7 @@ struct Lara : Character { void move() { //TR::Entity &e = getEntity(); //TR::Level::FloorInfo info; - + //float f, c; //bool canPassGap = true; /* @@ -1899,7 +1900,7 @@ struct Lara : Character { } if (stand == STAND_HANG && collision.side != Collision::FRONT) { - offset.x = offset.z = 0.0f; + offset.x = offset.z = 0.0f; minHeight = LARA_HANG_OFFSET; maxDescent = 0xFFFFFF; maxAscent = -LARA_HANG_OFFSET; @@ -1932,10 +1933,10 @@ struct Lara : Character { } } } - + // check entities in the room if (canPassGap) - for (int i = 0; i < level->entitiesCount; i++) + for (int i = 0; i < level->entitiesCount; i++) if (i != entity && level->entities[i].room == e.room && level->entities[i].controller) { Box mBox = ((Controller*)level->entities[i].controller)->getBoundingBox(); if (eBox.intersect(mBox)) { @@ -1951,7 +1952,7 @@ struct Lara : Character { if (state == STATE_WALK) rightStart = 13; if (state == STATE_BACK) rightStart = 28; bool isLeftFoot = animation.frameIndex < rightStart || animation.frameIndex > (rightStart + animation.framesCount / 2); - + if (stand == STAND_UNDERWATER) { if (collision.side == Collision::TOP) @@ -1963,7 +1964,7 @@ struct Lara : Character { if (stand == STAND_AIR && collision.side == Collision::TOP && velocity.y < 0.0f) velocity.y = 30.0f; - if (collision.side == Collision::FRONT) { + if (collision.side == Collision::FRONT) { int floor = collision.info[Collision::FRONT].floor; // hit the wall @@ -1997,7 +1998,7 @@ struct Lara : Character { pos.y = opos.y; break; default : ;// no smash animation - } + } } else { if (stand == STAND_GROUND) { int floor = collision.info[Collision::NONE].floor; diff --git a/src/level.h b/src/level.h index cf0ead9..23641a1 100644 --- a/src/level.h +++ b/src/level.h @@ -330,7 +330,7 @@ struct Level : IGame { } void initReflections() { - Core::resetStates(); + Core::beginFrame(); for (int i = 0; i < level.entitiesBaseCount; i++) { TR::Entity &e = level.entities[i]; if (e.type == TR::Entity::CRYSTAL) { @@ -338,6 +338,7 @@ struct Level : IGame { renderEnvironment(c->getRoomIndex(), c->pos - vec3(0, 512, 0), &c->environment); } } + Core::endFrame(); } void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) { @@ -523,7 +524,7 @@ struct Level : IGame { vec3 pos = controller->getPos(); if (Core::settings.ambient) { AmbientCache::Cube cube; - if (Core::frameIndex != controller->frameIndex) { + if (Core::stats.frame != controller->frameIndex) { ambientCache->getAmbient(entity.room, pos, cube); if (cube.status == AmbientCache::Cube::READY) memcpy(controller->ambient, cube.colors, sizeof(cube.colors)); // store last calculated ambient into controller @@ -642,6 +643,29 @@ struct Level : IGame { Core::mProj = mat4(90, 1.0f, camera->znear, camera->zfar); } + + mat4 calcCromMatrix(const mat4 &lightViewProj, const Box *boxes, int count) { + mat4 cameraViewProjInv = (mat4(camera->fov, float(Core::width) / Core::height, 512.0f, 4096.0f) * camera->mViewInv.inverse()).inverse(); + Box frustumBox = Box(vec3(-1.0f), vec3(1.0f)) * cameraViewProjInv; + + Box casterBox(vec3(+INF), vec3(-INF)); + + for (int i = 0; i < count; i++) + casterBox += boxes[i] * lightViewProj; + + casterBox -= frustumBox * lightViewProj; + + vec3 scale = vec3(2.0f, 2.0f, 1.0f) / casterBox.size(); + vec3 center = casterBox.center(); + vec3 offset = vec3(center.x, center.y, casterBox.min.z) * scale; + + return mat4(scale.x, 0.0f, 0.0f, 0.0f, + 0.0f, scale.y, 0.0f, 0.0f, + 0.0f, 0.0f, scale.z, 0.0f, + -offset.x, -offset.y, -offset.z, 1.0f); + } + + bool setupLightCamera() { vec3 pos = lara->getPos(); @@ -654,12 +678,32 @@ struct Level : IGame { vec3 shadowLightPos = vec3(float(light.x), float(light.y), float(light.z)); Core::mViewInv = mat4(shadowLightPos, pos - vec3(0, 256, 0), vec3(0, -1, 0)); Core::mView = Core::mViewInv.inverse(); - Core::mProj = mat4(120, 1.0f, camera->znear, camera->zfar); + Core::mProj = mat4(120.0f, 1.0f, camera->znear, camera->zfar); mat4 bias; bias.identity(); bias.e03 = bias.e13 = bias.e23 = bias.e00 = bias.e11 = bias.e22 = 0.5f; - Core::mLightProj = bias * Core::mProj * Core::mView; + /* + Box boxes[32]; + int bCount = 0; + + float rq = light.radius * light.radius; + for (int i = 0; i < level.entitiesCount; i++) { + TR::Entity &e = level.entities[i]; + Controller *controller = (Controller*)e.controller; + if (controller && TR::castShadow(e.type) && rq > (shadowLightPos - controller->pos).length2()) + boxes[bCount++] = controller->getBoundingBox(); + } + */ + /* + vec3 shadowBox(1024.0f, 0.0f, 1024.0f); + boxes[0] = lara->getBoundingBox(); + boxes[0] += Box(lara->pos - shadowBox, lara->pos + shadowBox); + bCount++; + Core::mProj = calcCromMatrix(Core::mProj * Core::mView, &boxes[0], bCount) * Core::mProj; + */ + + Core::mLightProj = bias * (Core::mProj * Core::mView); return true; } @@ -672,7 +716,8 @@ struct Level : IGame { bool colorShadow = shadow->format == Texture::Format::RGBA ? true : false; if (colorShadow) Core::setClearColor(vec4(1.0f, 1.0f, 1.0f, 1.0f)); - Core::setTarget(shadow, true); + Core::setTarget(shadow); + Core::clear(true, true); Core::setCulling(cfBack); renderScene(roomIndex); Core::invalidateTarget(!colorShadow, colorShadow); @@ -686,7 +731,6 @@ struct Level : IGame { params->clipHeight = NO_CLIP_PLANE; params->clipSign = 1.0f; params->waterHeight = params->clipHeight; - Core::resetStates(); if (ambientCache) ambientCache->precessQueue(); @@ -695,7 +739,8 @@ struct Level : IGame { if (shadow) renderShadows(lara->getRoomIndex()); - Core::setTarget(NULL, true); + Core::setTarget(NULL); + Core::clear(true, true); Core::setViewport(0, 0, Core::width, Core::height); if (waterCache) @@ -789,10 +834,10 @@ struct Level : IGame { glLoadIdentity(); glOrtho(0, Core::width, 0, Core::height, 0, 1); - if (waterCache->count) - waterCache->refract->bind(sDiffuse); - else - atlas->bind(sDiffuse); +// if (waterCache->count) +// waterCache->refract->bind(sDiffuse); +// else + shadow->bind(sDiffuse); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); diff --git a/src/platform/nix/main.cpp b/src/platform/nix/main.cpp index 03577ef..10d1cb5 100644 --- a/src/platform/nix/main.cpp +++ b/src/platform/nix/main.cpp @@ -33,7 +33,7 @@ void sndInit() { .rate = 44100, .channels = 2 }; - + static const pa_buffer_attr attr = { .maxlength = SND_DATA_SIZE * 2, .tlength = 0xFFFFFFFF, @@ -44,20 +44,20 @@ void sndInit() { pthread_mutex_init(&sndMutex, NULL); - int error; + int error; if (!(sndOut = pa_simple_new(NULL, WND_TITLE, PA_STREAM_PLAYBACK, NULL, "game", &spec, NULL, &attr, &error))) { LOG("pa_simple_new() failed: %s\n", pa_strerror(error)); sndData = NULL; return; } - - sndData = new Sound::Frame[SND_DATA_SIZE / SND_FRAME_SIZE]; - pthread_create(&sndThread, NULL, sndFill, NULL); + + sndData = new Sound::Frame[SND_DATA_SIZE / SND_FRAME_SIZE]; + pthread_create(&sndThread, NULL, sndFill, NULL); } void sndFree() { if (sndOut) { - pthread_cancel(sndThread); + pthread_cancel(sndThread); pthread_mutex_lock(&sndMutex); // pa_simple_flush(sndOut, NULL); // pa_simple_free(sndOut); @@ -102,7 +102,7 @@ void WndProc(const XEvent &e) { Core::width = e.xconfigure.width; Core::height = e.xconfigure.height; break; - case KeyPress : + case KeyPress : case KeyRelease : if (e.type == KeyPress && (e.xkey.state & Mod1Mask) && e.xkey.keycode == 36) { // TODO: windowed <-> fullscreen switch @@ -120,10 +120,15 @@ void WndProc(const XEvent &e) { case MotionNotify : Input::setPos(ikMouseL, vec2((float)e.xmotion.x, (float)e.xmotion.y)); break; - } + } } +char Stream::cacheDir[255]; +char Stream::contentDir[255]; + int main() { + Stream::contentDir[0] = Stream::cacheDir[0] = 0; + static int XGLAttr[] = { GLX_RGBA, GLX_DOUBLEBUFFER, @@ -142,8 +147,8 @@ int main() { ButtonMotionMask | PointerMotionMask; Window wnd = XCreateWindow(dpy, RootWindow(dpy, vis->screen), - 0, 0, 1280, 720, 0, - vis->depth, InputOutput, vis->visual, + 0, 0, 1280, 720, 0, + vis->depth, InputOutput, vis->visual, CWColormap | CWBorderPixel | CWEventMask, &attr); XStoreName(dpy, wnd, WND_TITLE); @@ -156,9 +161,9 @@ int main() { sndInit(); Game::init(); - - int lastTime = getTime(), fpsTime = lastTime + 1000, fps = 0; - + + int lastTime = getTime(); + while (1) { if (XPending(dpy)) { XEvent event; @@ -181,20 +186,11 @@ int main() { pthread_mutex_unlock(&sndMutex); lastTime = time; - Core::stats.dips = 0; - Core::stats.tris = 0; Game::render(); glXSwapBuffers(dpy, wnd); - - if (fpsTime < getTime()) { - LOG("FPS: %d DIP: %d TRI: %d\n", fps, Core::stats.dips, Core::stats.tris); - fps = 0; - fpsTime = getTime() + 1000; - } else - fps++; } }; - + sndFree(); Game::free(); diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index a924b53..08cf0e5 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -3,7 +3,7 @@ #include "game.h" -int lastTime, fpsTime, fps; +int lastTime; EGLDisplay display; EGLSurface surface; EGLContext context; @@ -23,7 +23,7 @@ extern "C" { } InputKey joyToInputKey(int code) { - static const int codes[] = { 0, 1, 2, 3, 4, 5, 10, 11, 8, 9, 6, 7 }; + static const int codes[] = { 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 6, 7 }; for (int i = 0; i < sizeof(codes) / sizeof(codes[0]); i++) if (codes[i] == code) @@ -63,9 +63,13 @@ void joyUpdate() { int count = emscripten_get_num_gamepads(); if (count <= 0) return; - + EmscriptenGamepadEvent state; - if (emscripten_get_gamepad_status(0, &state) != EMSCRIPTEN_RESULT_SUCCESS) + for (int i = 0; i < count; i++) + if (emscripten_get_gamepad_status(i, &state) == EMSCRIPTEN_RESULT_SUCCESS && state.numButtons >= 12) + break; + + if (state.numButtons < 12) return; for (int i = 0; i < max(state.numButtons, 12); i++) { @@ -101,17 +105,8 @@ void main_loop() { } lastTime = time; - Core::stats.dips = 0; - Core::stats.tris = 0; Game::render(); eglSwapBuffers(display, surface); - - if (fpsTime < getTime()) { - LOG("FPS: %d DIP: %d TRI: %d\n", fps, Core::stats.dips, Core::stats.tris); - fps = 0; - fpsTime = getTime() + 1000; - } else - fps++; } bool initGL() { @@ -291,8 +286,6 @@ int main() { resize(); lastTime = getTime(); - fpsTime = lastTime + 1000; - fps = 0; emscripten_set_main_loop(main_loop, 0, true); diff --git a/src/platform/win/main.cpp b/src/platform/win/main.cpp index 532a500..2097115 100644 --- a/src/platform/win/main.cpp +++ b/src/platform/win/main.cpp @@ -13,15 +13,15 @@ #include "game.h" -DWORD getTime() { +int getTime() { #ifdef DEBUG LARGE_INTEGER Freq, Count; QueryPerformanceFrequency(&Freq); QueryPerformanceCounter(&Count); - return (DWORD)(Count.QuadPart * 1000L / Freq.QuadPart); + return int(Count.QuadPart * 1000L / Freq.QuadPart); #else timeBeginPeriod(0); - return timeGetTime(); + return int(timeGetTime()); #endif } @@ -232,8 +232,8 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara break; case WM_KEYDOWN : case WM_KEYUP : - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: + case WM_SYSKEYDOWN : + case WM_SYSKEYUP : if (msg == WM_SYSKEYDOWN && wParam == VK_RETURN) { // switch to fullscreen or window static WINDOWPLACEMENT pLast; DWORD style = GetWindowLong(hWnd, GWL_STYLE); @@ -262,6 +262,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_MBUTTONDOWN : case WM_MBUTTONUP : case WM_MBUTTONDBLCLK : { + if ((GetMessageExtraInfo() & 0xFFFFFF00) == 0xFF515700) break; InputKey key = mouseToInputKey(msg); Input::setPos(key, vec2((float)(short)LOWORD(lParam), (float)(short)HIWORD(lParam))); bool down = msg != WM_LBUTTONUP && msg != WM_RBUTTONUP && msg != WM_MBUTTONUP; @@ -273,6 +274,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara break; } case WM_MOUSEMOVE : + if ((GetMessageExtraInfo() & 0xFFFFFF00) == 0xFF515700) break; Input::setPos(ikMouseL, vec2((float)(short)LOWORD(lParam), (float)(short)HIWORD(lParam))); break; // joystick @@ -355,7 +357,7 @@ int main(int argc, char** argv) { SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&WndProc); ShowWindow(hWnd, SW_SHOWDEFAULT); - DWORD lastTime = getTime(), fpsTime = lastTime + 1000, fps = 0; + DWORD lastTime = getTime(); MSG msg; do { @@ -379,20 +381,11 @@ int main(int argc, char** argv) { LeaveCriticalSection(&sndCS); lastTime = time; - Core::stats.dips = 0; - 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); - fps = 0; - fpsTime = getTime() + 1000; - } else - fps++; } } while (msg.message != WM_QUIT); diff --git a/src/shader.glsl b/src/shader.glsl index 53991cd..44b4315 100644 --- a/src/shader.glsl +++ b/src/shader.glsl @@ -204,26 +204,28 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords } #endif + #define SHADOW_TEXEL (1.5 / 1024.0) + float getShadow(vec4 lightProj) { vec3 p = lightProj.xyz / lightProj.w; float rShadow = 0.0; - rShadow += SHADOW(p + (vec3(-0.94201624, -0.39906216, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.94558609, -0.76890725, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.09418410, -0.92938870, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.34495938, 0.29387760, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.91588581, 0.45771432, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.81544232, -0.87912464, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.38277543, 0.27676845, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.97484398, 0.75648379, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.44323325, -0.97511554, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.53742981, -0.47373420, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.26496911, -0.41893023, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.79197514, 0.19090188, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.24188840, 0.99706507, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3(-0.81409955, 0.91437590, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.19984126, 0.78641367, 0.0) * (1.5 / 1024.0))); - rShadow += SHADOW(p + (vec3( 0.14383161, -0.14100790, 0.0) * (1.5 / 1024.0))); + rShadow += SHADOW(p + (vec3(-0.94201624, -0.39906216, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.94558609, -0.76890725, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.09418410, -0.92938870, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.34495938, 0.29387760, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.91588581, 0.45771432, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.81544232, -0.87912464, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.38277543, 0.27676845, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.97484398, 0.75648379, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.44323325, -0.97511554, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.53742981, -0.47373420, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.26496911, -0.41893023, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.79197514, 0.19090188, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.24188840, 0.99706507, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3(-0.81409955, 0.91437590, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.19984126, 0.78641367, 0.0) * SHADOW_TEXEL)); + rShadow += SHADOW(p + (vec3( 0.14383161, -0.14100790, 0.0) * SHADOW_TEXEL)); rShadow /= 16.0; diff --git a/src/utils.h b/src/utils.h index 0172135..18d4fb9 100644 --- a/src/utils.h +++ b/src/utils.h @@ -143,16 +143,20 @@ struct vec2 { vec2& operator += (const vec2 &v) { x += v.x; y += v.y; return *this; } vec2& operator -= (const vec2 &v) { x -= v.x; y -= v.y; return *this; } vec2& operator *= (const vec2 &v) { x *= v.x; y *= v.y; return *this; } + vec2& operator /= (const vec2 &v) { x /= v.x; y /= v.y; return *this; } vec2& operator += (float s) { x += s; y += s; return *this; } vec2& operator -= (float s) { x -= s; y -= s; return *this; } vec2& operator *= (float s) { x *= s; y *= s; return *this; } + vec2& operator /= (float s) { x /= s; y /= s; return *this; } vec2 operator + (const vec2 &v) const { return vec2(x + v.x, y + v.y); } vec2 operator - (const vec2 &v) const { return vec2(x - v.x, y - v.y); } vec2 operator * (const vec2 &v) const { return vec2(x * v.x, y * v.y); } + vec2 operator / (const vec2 &v) const { return vec2(x / v.x, y / v.y); } vec2 operator + (float s) const { return vec2(x + s, y + s ); } vec2 operator - (float s) const { return vec2(x - s, y - s ); } vec2 operator * (float s) const { return vec2(x * s, y * s ); } + vec2 operator / (float s) const { return vec2(x / s, y / s ); } float dot(const vec2 &v) const { return x * v.x + y * v.y; } float cross(const vec2 &v) const { return x * v.y - y * v.x; } @@ -164,7 +168,11 @@ struct vec2 { }; struct vec3 { - float x, y, z; + union { + struct { vec2 xy; }; + struct { float x, y, z; }; + }; + vec3() {} vec3(float s) : x(s), y(s), z(s) {} vec3(float x, float y, float z) : x(x), y(y), z(z) {} @@ -182,16 +190,20 @@ struct vec3 { vec3& operator += (const vec3 &v) { x += v.x; y += v.y; z += v.z; return *this; } vec3& operator -= (const vec3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } vec3& operator *= (const vec3 &v) { x *= v.x; y *= v.y; z *= v.z; return *this; } + vec3& operator /= (const vec3 &v) { x /= v.x; y /= v.y; z /= v.z; return *this; } vec3& operator += (float s) { x += s; y += s; z += s; return *this; } vec3& operator -= (float s) { x -= s; y -= s; z -= s; return *this; } vec3& operator *= (float s) { x *= s; y *= s; z *= s; return *this; } + vec3& operator /= (float s) { x /= s; y /= s; z /= s; return *this; } vec3 operator + (const vec3 &v) const { return vec3(x + v.x, y + v.y, z + v.z); } vec3 operator - (const vec3 &v) const { return vec3(x - v.x, y - v.y, z - v.z); } vec3 operator * (const vec3 &v) const { return vec3(x * v.x, y * v.y, z * v.z); } + vec3 operator / (const vec3 &v) const { return vec3(x / v.x, y / v.y, z / v.z); } vec3 operator + (float s) const { return vec3(x + s, y + s, z + s); } vec3 operator - (float s) const { return vec3(x - s, y - s, z - s); } vec3 operator * (float s) const { return vec3(x * s, y * s, z * s); } + vec3 operator / (float s) const { return vec3(x / s, y / s, z / s); } float dot(const vec3 &v) const { return x * v.x + y * v.y + z * v.z; } vec3 cross(const vec3 &v) const { return vec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); } @@ -219,6 +231,7 @@ struct vec3 { struct vec4 { union { + struct { vec2 xy; }; struct { vec3 xyz; }; struct { float x, y, z, w; }; }; @@ -356,6 +369,15 @@ struct mat4 { mat4() {} + mat4(float e00, float e10, float e20, float e30, + float e01, float e11, float e21, float e31, + float e02, float e12, float e22, float e32, + float e03, float e13, float e23, float e33) : + e00(e00), e10(e10), e20(e20), e30(e30), + e01(e01), e11(e11), e21(e21), e31(e31), + e02(e02), e12(e12), e22(e22), e32(e32), + e03(e03), e13(e13), e23(e23), e33(e33) {} + mat4(const quat &rot, const vec3 &pos) { setRot(rot); setPos(pos); @@ -683,6 +705,60 @@ struct Box { Box() {} Box(const vec3 &min, const vec3 &max) : min(min), max(max) {} + vec3 operator [] (int index) const { + ASSERT(index >= 0 && index <= 7); + switch (index) { + case 0 : return min; + case 1 : return max; + case 2 : return vec3(min.x, max.y, max.z); + case 3 : return vec3(max.x, min.y, max.z); + case 4 : return vec3(min.x, min.y, max.z); + case 5 : return vec3(max.x, max.y, min.z); + case 6 : return vec3(min.x, max.y, min.z); + case 7 : return vec3(max.x, min.y, min.z); + } + return min; + } + + Box& operator += (const Box &box) { + min.x = ::min(min.x, box.min.x); + min.y = ::min(min.y, box.min.y); + min.z = ::min(min.z, box.min.z); + max.x = ::max(max.x, box.max.x); + max.y = ::max(max.y, box.max.y); + max.z = ::max(max.z, box.max.z); + return *this; + } + + Box& operator += (const vec3 &v) { + min.x = ::min(min.x, v.x); + min.y = ::min(min.y, v.y); + min.z = ::min(min.z, v.z); + max.x = ::max(max.x, v.x); + max.y = ::max(max.y, v.y); + max.z = ::max(max.z, v.z); + return *this; + } + + Box& operator -= (const Box &box) { + min.x = ::max(min.x, box.min.x); + min.y = ::max(min.y, box.min.y); + min.z = ::max(min.z, box.min.z); + max.x = ::min(max.x, box.max.x); + max.y = ::min(max.y, box.max.y); + max.z = ::min(max.z, box.max.z); + return *this; + } + + Box operator * (const mat4 &m) const { + Box res(vec3(+INF), vec3(-INF)); + for (int i = 0; i < 8; i++) { + vec4 v = m * vec4((*this)[i], 1.0f); + res += v.xyz /= v.w; + } + return res; + } + vec3 center() const { return (min + max) * 0.5f; }