MegaGlest/source/glest_game/game/game_camera.cpp

583 lines
18 KiB
C++

// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "game_camera.h"
#include <cstdlib>
#include "config.h"
#include "game_constants.h"
#include "xml_parser.h"
#include "conversion.h"
#include "platform_common.h"
#include "leak_dumper.h"
#include "randomgen.h"
using namespace Shared::Graphics;
using Shared::Xml::XmlNode;
using namespace Shared::Util;
namespace Glest { namespace Game {
// =====================================================
// class GameCamera
// =====================================================
//static std::map<float, std::map<float, std::map<Vec3f, Quad2i> > > cacheVisibleQuad;
// ================== PUBLIC =====================
const float GameCamera::startingVAng= -60.f;
const float GameCamera::startingHAng= 0.f;
const float GameCamera::vTransitionMult= 0.125f;
const float GameCamera::hTransitionMult= 0.125f;
const float GameCamera::defaultHeight= 20.f;
const float GameCamera::centerOffsetZ= 8.0f;
const float GameCamera::shakeDist= 50.f;
// ================= Constructor =================
GameCamera::GameCamera() : pos(0.f, defaultHeight, 0.f),
destPos(0.f, defaultHeight, 0.f), destAng(startingVAng, startingHAng) {
//Config &config = Config::getInstance();
calculatedDefault=defaultHeight;
state= sGame;
cacheVisibleQuad.clear();
//MaxVisibleQuadItemCache = config.getInt("MaxVisibleQuadItemCache",intToStr(-1).c_str());
MaxVisibleQuadItemCache = -1;
//if(Config::getInstance().getBool("DisableCaching","false") == true) {
// MaxVisibleQuadItemCache = 0;
//}
//config
speed= Config::getInstance().getFloat("CameraMoveSpeed","15") / GameConstants::cameraFps;
clampBounds= !Config::getInstance().getBool("PhotoMode");
clampDisable = false;
vAng= startingVAng;
hAng= startingHAng;
rotate=0;
move= Vec3f(0.f);
shakeDecrement=0.f;
currentShakeIntensity=0;
shakeOffset= Vec2f(0.f);
//maxRenderDistance = Config::getInstance().getFloat("RenderDistanceMax","64");
maxHeight = Config::getInstance().getFloat("CameraMaxDistance","20");
minHeight = Config::getInstance().getFloat("CameraMinDistance","7");
//maxCameraDist = maxHeight;
//minCameraDist = minHeight;
minVAng = -Config::getInstance().getFloat("CameraMaxYaw","77.5");
maxVAng = -Config::getInstance().getFloat("CameraMinYaw","20");
fov = Config::getInstance().getFloat("CameraFov","45");
lastHAng=0;
lastVAng=0;
limitX=0;
limitY=0;
}
GameCamera::~GameCamera() {
cacheVisibleQuad.clear();
}
std::string GameCamera::getCameraMovementKey() const {
char szBuf[8096]="";
snprintf(szBuf,8096,"%s_%f_%f_%f_%s,%f",pos.getString().c_str(),hAng,vAng,rotate,move.getString().c_str(),fov);
return szBuf;
}
void GameCamera::setMaxHeight(float value) {
if(value < 0) {
maxHeight = Config::getInstance().getFloat("CameraMaxDistance","20");
}
else {
maxHeight = value;
}
}
void GameCamera::setCalculatedDefault(float calculatedDefault){
this->calculatedDefault= calculatedDefault;
if(maxHeight>0 && maxHeight<calculatedDefault){
setMaxHeight(calculatedDefault);
}
resetPosition();
}
void GameCamera::init(int limitX, int limitY){
this->limitX= limitX;
this->limitY= limitY;
}
// ==================== Misc =====================
void GameCamera::setPos(Vec2f pos){
this->pos= Vec3f(pos.x, this->pos.y, pos.y);
clampPosXZ(0.0f, (float)limitX, 0.0f, (float)limitY);
destPos.x = pos.x;
destPos.z = pos.y;
}
void GameCamera::setPos(Vec3f pos){
this->pos= pos;
//clampPosXZ(0.0f, (float)limitX, 0.0f, (float)limitY);
destPos.x = pos.x;
destPos.y = pos.y;
destPos.z = pos.z;
}
void GameCamera::shake(int shakeDuration, int shakeStartIntensity , bool cameraDistanceAffected, Vec3f unitVector) {
float currentDurationLeft=0;
float incomingShakeIntensity=((float) shakeStartIntensity)/1000;
// calculate the shake duration which is left
if(this->currentShakeIntensity > 0.f && this->shakeDecrement != 0.f){
currentDurationLeft=this->currentShakeIntensity / this->shakeDecrement;
}
// reduce new shake effect camera distance related
if (cameraDistanceAffected) {
incomingShakeIntensity=incomingShakeIntensity*(1.f-unitVector.dist(getPos())/GameCamera::shakeDist);
}
// add camera shake effect to current one ( if exsists ).
if(this->currentShakeIntensity>0){
this->currentShakeIntensity = this->currentShakeIntensity+incomingShakeIntensity ;
}
else {
this->currentShakeIntensity = incomingShakeIntensity ;
}
// use bigger shakeDuration to calculate new shakeDecrement
if(currentDurationLeft < shakeDuration){
this->shakeDecrement = currentShakeIntensity / ((float) shakeDuration);
}
else if(currentDurationLeft!=0.0f)
{
this->shakeDecrement = currentShakeIntensity / ((float) currentDurationLeft);
}
}
void GameCamera::shakeCamera(){
//RandomGen random;
if(currentShakeIntensity > 0.f) {
// pos.x += (((float) (rand() % 50)) / 50.f - 0.5f) * currentShakeIntensity;
// pos.z += (((float) (rand() % 50)) / 50.f - 0.5f) * currentShakeIntensity;
shakeOffset.x = (((float) (rand() % 50)) / 50.f - 0.5f) * currentShakeIntensity;
shakeOffset.y = (((float) (rand() % 50)) / 50.f - 0.5f) * currentShakeIntensity;
currentShakeIntensity -= shakeDecrement;
}
}
void GameCamera::update(){
//move XZ
if(move.z){
moveForwardH(speed * move.z, 0.9f);
}
if(move.x){
moveSideH(speed * move.x, 0.9f);
}
//free state
if(state==sFree ){
if(std::fabs(rotate) == 1){
rotateHV(speed*5*rotate, 0);
}
if(move.y>0){
moveUp(speed * move.y);
if(clampDisable == false && clampBounds && pos.y<maxHeight){
rotateHV(0.f, -speed * 1.7f * move.y);
}
}
if(move.y<0){
moveUp(speed * move.y);
if(clampDisable == false && clampBounds && pos.y>minHeight){
rotateHV(0.f, -speed * 1.7f * move.y);
}
}
}
//game state
if(abs(destAng.x - vAng) > 0.01f) {
vAng+= (destAng.x - vAng) * hTransitionMult;
}
if(abs(destAng.y - hAng) > 0.01f) {
if(abs(destAng.y - hAng) > 180) {
if(destAng.y > hAng) {
hAng+= (destAng.y - hAng - 360) * vTransitionMult;
} else {
hAng+= (destAng.y - hAng + 360) * vTransitionMult;
}
} else {
hAng+= (destAng.y - hAng) * vTransitionMult;
}
}
if(abs(destPos.x - pos.x) > 0.01f) {
pos.x += (destPos.x - pos.x) / 32.0f;
}
if(abs(destPos.y - pos.y) > 0.01f) {
pos.y += (destPos.y - pos.y) / 32.0f;
}
if(abs(destPos.z - pos.z) > 0.01f) {
pos.z += (destPos.z - pos.z) / 32.0f;
}
clampAng();
shakeCamera();
if(clampDisable == false && clampBounds){
clampPosXYZ(0.0f, (float)limitX, minHeight, maxHeight, 0.0f, (float)limitY);
}
}
Quad2i GameCamera::computeVisibleQuad() {
//printf("\n@@@ hAng [%f] vAng [%f] fov [%f]\n",hAng,vAng,fov);
if(MaxVisibleQuadItemCache != 0) {
std::map<float, std::map<float, std::map<Vec3f, Quad2i> > >::const_iterator iterFind = cacheVisibleQuad.find(fov);
if(iterFind != cacheVisibleQuad.end()) {
std::map<float, std::map<Vec3f, Quad2i> >::const_iterator iterFind2 = iterFind->second.find(hAng);
if(iterFind2 != iterFind->second.end()) {
std::map<Vec3f, Quad2i>::const_iterator iterFind3 = iterFind2->second.find(pos);
if(iterFind3 != iterFind2->second.end()) {
return iterFind3->second;
}
}
}
}
float nearDist = 15.f;
float dist = pos.y > nearDist ? pos.y * 1.2f : nearDist;
float farDist = 90.f * (pos.y > nearDist ? pos.y / 15.f : 1.f);
const float viewDegree = 180.f;
Vec2f v(std::sin(degToRad(viewDegree - hAng)), std::cos(degToRad(viewDegree - hAng)));
Vec2f v1(std::sin(degToRad(viewDegree - hAng - fov)), std::cos(degToRad(viewDegree - hAng - fov)));
Vec2f v2(std::sin(degToRad(viewDegree - hAng + fov)), std::cos(degToRad(viewDegree - hAng + fov)));
v.normalize();
v1.normalize();
v2.normalize();
Vec2f p = Vec2f(pos.x, pos.z) - v * dist;
Vec2i p1(static_cast<int>(p.x + v1.x * nearDist), static_cast<int>(p.y + v1.y * nearDist));
Vec2i p2(static_cast<int>(p.x + v1.x * farDist), static_cast<int>(p.y + v1.y * farDist));
Vec2i p3(static_cast<int>(p.x + v2.x * nearDist), static_cast<int>(p.y + v2.y * nearDist));
Vec2i p4(static_cast<int>(p.x + v2.x * farDist), static_cast<int>(p.y + v2.y * farDist));
const bool debug = false;
Quad2i result;
if (hAng >= 135 && hAng <= 225) {
if(debug) printf("Line %d hAng [%f] fov [%f]\n",__LINE__,hAng,fov);
result = Quad2i(p1, p2, p3, p4);
if(MaxVisibleQuadItemCache != 0 &&
(MaxVisibleQuadItemCache < 0 || (int)cacheVisibleQuad[fov][hAng].size() <= MaxVisibleQuadItemCache)) {
cacheVisibleQuad[fov][hAng][pos] = result;
}
}
else if (hAng >= 45 && hAng <= 135) {
if(debug) printf("Line %d hAng [%f] fov [%f]\n",__LINE__,hAng,fov);
result = Quad2i(p3, p1, p4, p2);
if(MaxVisibleQuadItemCache != 0 &&
(MaxVisibleQuadItemCache < 0 || (int)cacheVisibleQuad[fov][hAng].size() <= MaxVisibleQuadItemCache)) {
cacheVisibleQuad[fov][hAng][pos] = result;
}
}
else if (hAng >= 225 && hAng <= 315) {
if(debug) printf("Line %d hAng [%f] fov [%f]\n",__LINE__,hAng,fov);
result = Quad2i(p2, p4, p1, p3);
if(MaxVisibleQuadItemCache != 0 &&
(MaxVisibleQuadItemCache < 0 || (int)cacheVisibleQuad[fov][hAng].size() <= MaxVisibleQuadItemCache)) {
cacheVisibleQuad[fov][hAng][pos] = result;
}
}
else {
if(debug) printf("Line %d hAng [%f] fov [%f]\n",__LINE__,hAng,fov);
result = Quad2i(p4, p3, p2, p1);
if(MaxVisibleQuadItemCache != 0 &&
(MaxVisibleQuadItemCache < 0 || (int)cacheVisibleQuad[fov][hAng].size() <= MaxVisibleQuadItemCache)) {
cacheVisibleQuad[fov][hAng][pos] = Quad2i(p4, p3, p2, p1);
}
}
return result;
}
void GameCamera::setState(State s){
if(s==sGame){
state = sGame;
setClampDisabled(false);
resetPosition();
}
else if(s==sUnit){
state = sUnit;
setClampDisabled(true);
}
else if(s==sFree){
state = sFree;
setClampDisabled(false);
resetPosition();
}
else {
abort();//"unknown camera state"
}
}
void GameCamera::resetPosition(){
destAng.x = startingVAng;
destAng.y = startingHAng;
destPos.y = calculatedDefault;
}
void GameCamera::centerXZ(float x, float z){
destPos.x = pos.x= x;
destPos.z = pos.z= z+centerOffsetZ;
}
//void GameCamera::transitionXYZ(float x, float y, float z) {
// destPos.x += x;
// destPos.y += y;
// destPos.z += z;
// clampPosXYZ(0.0f, (float)limitX, minHeight, maxHeight, 0.0f, (float)limitY);
//}
void GameCamera::transitionVH(float v, float h) {
destAng.x -= v;
//destPos.y -= v * destPos.y / 100.f;
destAng.y -= h;
clampAng();
}
void GameCamera::rotateToVH(float v, float h) {
destAng.x = v;
destAng.y = h;
clampAng();
}
void GameCamera::zoom(float dist) {
float flatDist = dist * std::cos(degToRad(vAng));
Vec3f offset(flatDist * std::sin(degToRad(hAng)), dist * std::sin(degToRad(vAng)), flatDist * -std::cos(degToRad(hAng)));
destPos += offset;
}
void GameCamera::load(const XmlNode *node) {
//destPos = node->getChildVec3fValue("pos");
//destAng = node->getChildVec2fValue("angle");
}
void GameCamera::save(XmlNode *node) const {
//node->addChild("pos", pos);
//node->addChild("angle", Vec2f(vAng, hAng));
}
// ==================== PRIVATE ====================
void GameCamera::clampPosXZ(float x1, float x2, float z1, float z2){
if(clampDisable == true) {
return;
}
if(pos.x < x1) pos.x = x1;
if(destPos.x < x1) destPos.x = x1;
if(pos.z < z1) pos.z = z1;
if(destPos.z < z1) destPos.z = z1;
if(pos.x > x2) pos.x = x2;
if(destPos.x > x2) destPos.x = x2;
if(pos.z > z2) pos.z = z2;
if(destPos.z > z2) destPos.z = z2;
}
void GameCamera::clampPosXYZ(float x1, float x2, float y1, float y2, float z1, float z2){
if(clampDisable == true) {
return;
}
if(pos.x < x1) pos.x = x1;
if(destPos.x < x1) destPos.x = x1;
if(pos.y < y1) pos.y = y1;
if(destPos.y < y1) destPos.y = y1;
if(pos.z < z1) pos.z = z1;
if(destPos.z < z1) destPos.z = z1;
if(pos.x > x2) pos.x = x2;
if(destPos.x > x2) destPos.x = x2;
if(pos.y > y2) pos.y = y2;
if(destPos.y > y2) destPos.y = y2;
if(pos.z > z2) pos.z = z2;
if(destPos.z > z2) destPos.z = z2;
}
void GameCamera::rotateHV(float h, float v){
destAng.x = vAng += v;
destAng.y = hAng += h;
clampAng();
}
void GameCamera::clampAng() {
if(clampDisable == true && state != sUnit ) {
return;
}
if(vAng > maxVAng) vAng = maxVAng;
if(destAng.x > maxVAng) destAng.x = maxVAng;
if(vAng < minVAng) vAng = minVAng;
if(destAng.x < minVAng) destAng.x = minVAng;
if(hAng > 360.f) hAng -= 360.f;
if(destAng.y > 360.f) destAng.y -= 360.f;
if(hAng < 0.f) hAng += 360.f;
if(destAng.y < 0.f) destAng.y = 360.f;
}
//move camera forwad but never change heightFactor
void GameCamera::moveForwardH(float d, float response) {
Vec3f offset(std::sin(degToRad(hAng)) * d, 0.f, -std::cos(degToRad(hAng)) * d);
destPos += offset;
pos.x += offset.x * response;
pos.z += offset.z * response;
}
//move camera to a side but never change heightFactor
void GameCamera::moveSideH(float d, float response){
Vec3f offset(std::sin(degToRad(hAng+90)) * d, 0.f, -std::cos(degToRad(hAng+90)) * d);
destPos += offset;
pos.x += (destPos.x - pos.x) * response;
pos.z += (destPos.z - pos.z) * response;
}
void GameCamera::moveUp(float d){
// pos.y+= d;
destPos.y += d;
}
void GameCamera::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *gamecameraNode = rootNode->addChild("GameCamera");
// Vec3f pos;
gamecameraNode->addAttribute("pos",pos.getString(), mapTagReplacements);
// Vec3f destPos;
gamecameraNode->addAttribute("destPos",destPos.getString(), mapTagReplacements);
//
// float hAng; //YZ plane positive -Z axis
gamecameraNode->addAttribute("hAng",floatToStr(hAng,6), mapTagReplacements);
// float vAng; //XZ plane positive +Z axis
gamecameraNode->addAttribute("vAng",floatToStr(vAng,6), mapTagReplacements);
// float lastHAng;
gamecameraNode->addAttribute("lastHAng",floatToStr(lastHAng,6), mapTagReplacements);
// float lastVAng;
gamecameraNode->addAttribute("lastVAng",floatToStr(lastVAng,6), mapTagReplacements);
// Vec2f destAng;
gamecameraNode->addAttribute("destAng",destAng.getString(), mapTagReplacements);
// float rotate;
gamecameraNode->addAttribute("rotate",floatToStr(rotate,6), mapTagReplacements);
// Vec3f move;
gamecameraNode->addAttribute("move",move.getString(), mapTagReplacements);
// State state;
gamecameraNode->addAttribute("state",intToStr(state), mapTagReplacements);
// int limitX;
gamecameraNode->addAttribute("limitX",intToStr(limitX), mapTagReplacements);
// int limitY;
gamecameraNode->addAttribute("limitY",intToStr(limitY), mapTagReplacements);
// //config
// float speed;
gamecameraNode->addAttribute("speed",floatToStr(speed,6), mapTagReplacements);
// bool clampBounds;
gamecameraNode->addAttribute("clampBounds",intToStr(clampBounds), mapTagReplacements);
// //float maxRenderDistance;
// float maxHeight;
gamecameraNode->addAttribute("maxHeight",floatToStr(maxHeight,6), mapTagReplacements);
// float minHeight;
gamecameraNode->addAttribute("minHeight",floatToStr(minHeight,6), mapTagReplacements);
// //float maxCameraDist;
// //float minCameraDist;
// float minVAng;
gamecameraNode->addAttribute("minVAng",floatToStr(minVAng,6), mapTagReplacements);
// float maxVAng;
gamecameraNode->addAttribute("maxVAng",floatToStr(maxVAng,6), mapTagReplacements);
// float fov;
gamecameraNode->addAttribute("fov",floatToStr(fov,6), mapTagReplacements);
// float calculatedDefault;
gamecameraNode->addAttribute("calculatedDefault",floatToStr(calculatedDefault,6), mapTagReplacements);
// std::map<float, std::map<float, std::map<Vec3f, Quad2i> > > cacheVisibleQuad;
// int MaxVisibleQuadItemCache;
gamecameraNode->addAttribute("MaxVisibleQuadItemCache",intToStr(MaxVisibleQuadItemCache), mapTagReplacements);
}
void GameCamera::loadGame(const XmlNode *rootNode) {
const XmlNode *gamecameraNode = rootNode->getChild("GameCamera");
//firstTime = timeflowNode->getAttribute("firstTime")->getFloatValue();
// Vec3f pos;
pos = Vec3f::strToVec3(gamecameraNode->getAttribute("pos")->getValue());
// Vec3f destPos;
destPos = Vec3f::strToVec3(gamecameraNode->getAttribute("destPos")->getValue());
//
// float hAng; //YZ plane positive -Z axis
hAng = gamecameraNode->getAttribute("hAng")->getFloatValue();
// float vAng; //XZ plane positive +Z axis
vAng = gamecameraNode->getAttribute("vAng")->getFloatValue();
// float lastHAng;
lastHAng = gamecameraNode->getAttribute("lastHAng")->getFloatValue();
// float lastVAng;
lastVAng = gamecameraNode->getAttribute("lastVAng")->getFloatValue();
// Vec2f destAng;
destAng = Vec2f::strToVec2(gamecameraNode->getAttribute("destAng")->getValue());
// float rotate;
rotate = gamecameraNode->getAttribute("rotate")->getFloatValue();
// Vec3f move;
move = Vec3f::strToVec3(gamecameraNode->getAttribute("move")->getValue());
// State state;
state = static_cast<State>(gamecameraNode->getAttribute("state")->getIntValue());
// int limitX;
limitX = gamecameraNode->getAttribute("limitX")->getIntValue();
// int limitY;
limitY = gamecameraNode->getAttribute("limitY")->getIntValue();
// //config
// float speed;
speed = gamecameraNode->getAttribute("speed")->getFloatValue();
// bool clampBounds;
clampBounds = gamecameraNode->getAttribute("clampBounds")->getIntValue() != 0;
// //float maxRenderDistance;
// float maxHeight;
maxHeight = gamecameraNode->getAttribute("maxHeight")->getFloatValue();
// float minHeight;
minHeight = gamecameraNode->getAttribute("minHeight")->getFloatValue();
// //float maxCameraDist;
// //float minCameraDist;
// float minVAng;
minVAng = gamecameraNode->getAttribute("minVAng")->getFloatValue();
// float maxVAng;
maxVAng = gamecameraNode->getAttribute("maxVAng")->getFloatValue();
// float fov;
fov = gamecameraNode->getAttribute("fov")->getFloatValue();
// float calculatedDefault;
calculatedDefault = gamecameraNode->getAttribute("calculatedDefault")->getFloatValue();
// std::map<float, std::map<float, std::map<Vec3f, Quad2i> > > cacheVisibleQuad;
// int MaxVisibleQuadItemCache;
MaxVisibleQuadItemCache = gamecameraNode->getAttribute("MaxVisibleQuadItemCache")->getIntValue();
}
}}//end namespace