www.pudn.com > RCApp-src.zip > Entity.cpp
/* RedEye Project (http://members.ozemail.com.au/~ndmcevoy/) Copyright (C) 2003 Nick McEvoy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ----------------------------------------------------------------- Commented for use with Doxygen (http://www.doxygen.org) ----------------------------------------------------------------- */ /*! \file Entity.cpp * \brief Game entity. * * This file contains the game entity. */ // GLUT includes #include// ODE includes #include "ode/common.h" #include "ode/contact.h" #include "ode/objects.h" #include "ode/geom.h" #include "ode/rotation.h" // RedEye includes #include "Entity.h" #include "Engine.h" #include "MaterialManager.h" #include "ModelManager.h" #include "Collision.h" #include "RCDefs.h" static sgVec4 black = { 0.0f, 0.0f, 0.0f, 1.0f } ; static sgVec4 white = { 1.0f, 1.0f, 1.0f, 1.0f } ; static sgVec4 translucent_white = { 1.0f, 1.0f, 1.0f, 0.8f } ; void reUpdateValue(float& fValue, bool bIncFactor1, bool bDecFactor2, float fIncFactor1, float fDecFactor2, float fDampFactor, float fMaxValue, float fMinValue, float fDeltaSec); //********************************************************************** // // reShadow // //********************************************************************** reShadow::reShadow(float x1, float x2, float y1, float y2) { setName("Shadow"); sgVec4* scolors = new sgVec4[1]; sgVec3* scoords = new sgVec3[4]; sgVec2* tcoords = new sgVec2[4]; sgVec3* snorms = new sgVec3[1]; sgSetVec4(scolors[0], 0.0f, 0.0f, 0.0f, 1.0f); sgSetVec3(snorms [0], 0.0f, 0.0f, 1.0f); sgSetVec3(scoords[0], x1, y1, 0.01); sgSetVec3(scoords[1], x2, y1, 0.01); sgSetVec3(scoords[2], x1, y2, 0.01); sgSetVec3(scoords[3], x2, y2, 0.01); sgSetVec2(tcoords[0], 0.0, 0.0); sgSetVec2(tcoords[1], 1.0, 0.0); sgSetVec2(tcoords[2], 0.0, 1.0); sgSetVec2(tcoords[3], 1.0, 1.0); clrTraversalMaskBits(SSGTRAV_ISECT|SSGTRAV_HOT); ssgVTable* gs = new ssgVTable( GL_TRIANGLE_STRIP, 4, scoords, 1, snorms, 4, tcoords, 1, scolors); gs->clrTraversalMaskBits(SSGTRAV_ISECT|SSGTRAV_HOT); ssgSimpleState* pFuzzyState = reGetMaterialManager()->GetSimpleState("textures/fuzzy.inta"); gs->setState(pFuzzyState); mpTransform = new ssgTransform; addKid(mpTransform); mpTransform->addKid(gs); } void reShadow::Update(float x, float y) { sgCoord Position; sgSetCoord(&Position, x, y, 0, 0, 0, 0); ssgHit* pHit; sgVec3 vNormal; float fHOT = reGetHeightAndNormal(reGetWorld(), Position.xyz, vNormal, &pHit); Position.xyz[SG_Z] = fHOT; pr_from_normal(Position.hpr, vNormal); mpTransform->setTransform(&Position); } //********************************************************************** // // reGameEntity // //********************************************************************** reGameEntity::reGameEntity() { mpTransform = NULL; sgZeroVec3(mVDampFactor); sgZeroVec3(mAVDampFactor); mpShadow = NULL; mODEBody = NULL; mODEGeom = NULL; } reGameEntity::~reGameEntity() { if (mODEGeom) { dGeomDestroy(mODEGeom); mODEGeom = NULL; } if (mODEBody) { dBodyDestroy(mODEBody); mODEBody = NULL; } if (mpShadow) { reGetWorld()->removeKid(mpShadow); ssgDeRefDelete(mpShadow); } } reGameEntity* reGameEntity::Create(const char* psFile) { char sFilepath[RE_STRING_MAX]; sprintf(sFilepath, "%s/%s", reGetGameEngine()->GetLevelPath(), psFile); TiXmlDocument Config; if (!Config.LoadFile(sFilepath)) { ulSetError(UL_WARNING, "reGameEntity::Create() Could not read %s.", sFilepath); return NULL; } TiXmlNode* pNode = Config.FirstChild("Entity"); return Create(pNode); } reGameEntity* reGameEntity::Create(TiXmlNode* pNode) { reGameEntity* pEntity = NULL; const char* psType = reGetXMLAttribute(pNode, "type", ""); const char* psSubType = reGetXMLAttribute(pNode, "sub_type", ""); if (strcmp(psType, "rePlatformEntity") == 0) { if (strcmp(psSubType, "reBoatEntity") == 0) pEntity = new reBoatEntity(); else if (strcmp(psSubType, "reBillyCartEntity") == 0) pEntity = new reBillyCartEntity(); else if (strcmp(psSubType, "reSpaceShipEntity") == 0) pEntity = new reSpaceShipEntity(); } else if (strcmp(psType, "reBuildingEntity") == 0) { } else if (strcmp(psType, "reEquipmentEntity") == 0) { } else if (strcmp(psType, "reSceneryEntity") == 0) { if (strcmp(psSubType, "reTerrainEntity") == 0) pEntity = new reTerrainEntity(); else if (strcmp(psSubType, "reOceanEntity") == 0) pEntity = new reOceanEntity(); else if (strcmp(psSubType, "reSkyEntity") == 0) pEntity = new reSkyEntity(); } else if (strcmp(psType, "reParticleEntity") == 0) { if (strcmp(psSubType, "reParticleEntity") == 0) pEntity = new reParticleEntity(); } if (pEntity) { pEntity->Init(pNode); pEntity->ref(); } else { ulSetError(UL_WARNING, "reGameEntity::Create() Invalid platform type %s sub_type %s.", psType, psSubType); } return pEntity; } bool reGameEntity::Init(TiXmlNode* pConfig) { mpTransform = new ssgTransform; addKid(mpTransform); if (pConfig) { // Name //setName("todo"); // Geometry TiXmlNode* pGeometry = pConfig->FirstChild("Geometry"); const char* psGeometry = reGetXMLAttribute(pGeometry, "type", ""); if (strcmp("box", psGeometry)==0) { sgVec4 Geom = {0}; // 0=Width,1=Length,2=Height,3=Mass reGetXMLChildAttribVec4(pGeometry, "Param", Geom); dMass m; mODEBody = dBodyCreate(reGetODEWorldId()); dBodySetPosition(mODEBody, 0, 0, 0); dMassSetBox(&m, 1, Geom[0], Geom[1], Geom[2]); dMassAdjust(&m, Geom[3]); dBodySetMass(mODEBody, &m); mODEGeom = dCreateBox(0,Geom[0], Geom[1], Geom[2]); dGeomSetBody(mODEGeom, mODEBody); } else if (strcmp("sphere", psGeometry)==0) { sgVec2 Geom = {0}; // 0=Radius,1=Mass reGetXMLChildAttribVec2(pGeometry, "Param", Geom); dMass m; mODEBody = dBodyCreate(reGetODEWorldId()); dBodySetPosition(mODEBody, 0, 0, 0); dMassSetSphere(&m, 1, Geom[0]); dMassAdjust(&m, Geom[1]); dBodySetMass(mODEBody, &m); mODEGeom = dCreateSphere(0, Geom[0]); dGeomSetBody(mODEGeom, mODEBody); } else // none { mODEBody = NULL; mODEGeom = NULL; } // Shadow TiXmlNode* pShadow = pConfig->FirstChild("Shadow"); bool bShadowOn = reGetXMLAttribute(pShadow, "enabled", false); if (bShadowOn) { sgVec4 Shadow = {0}; reGetXMLChildAttribVec4(pShadow, "Param", Shadow); mpShadow = new reShadow( Shadow[0], Shadow[1], Shadow[2], Shadow[3]); mpShadow->ref(); reGetWorld()->addKid(mpShadow); } reGetXMLChildAttribVec3(pConfig, "VelocityDampFactor", mVDampFactor); reGetXMLChildAttribVec3(pConfig, "AngularVelocityDampFactor", mAVDampFactor); } return true; } void reGameEntity::Update(float fDeltaSec) { if (mODEBody) { // Get position sgCoord p; GetPosition(&p); // Setup rotation matricies // - R1 useful for conversion between body-to-world coords // - R2 useful for conversion between world-to-body coords sgMat4 R1, R2; sgMakeRotMat4(R1, p.hpr); sgCopyMat4(R2, R1); sgInvertMat4(R2); // Apply a damping force (based on current velocity) // - convert velocity from world-to-body coordinates // - calculate damping in the xz direction (assume body facing +y direction) // - convert damping force from body-to-world coordinates // - apply damping sgVec3 BodyVelocity, DampingForce; const dReal* pV = dBodyGetLinearVel(mODEBody); sgSetVec3(BodyVelocity, pV[0], pV[1], pV[2]); sgXformVec3(BodyVelocity, R2); sgSetVec3(DampingForce, -BodyVelocity[0]*fabs(BodyVelocity[0])*mVDampFactor[0], -BodyVelocity[1]*fabs(BodyVelocity[1])*mVDampFactor[1], -BodyVelocity[2]*fabs(BodyVelocity[2])*mVDampFactor[2]); sgXformVec3(DampingForce, R1); dBodyAddForce(mODEBody, DampingForce[0], DampingForce[1], DampingForce[2]); //printf("BodyVelocity <%0.2f:%0.2f:%0.2f>\n", BodyVelocity[0], BodyVelocity[1], BodyVelocity[2]); //printf("DampingForce <%0.2f:%0.2f:%0.2f>\n", DampingForce[0], DampingForce[1], DampingForce[2]); // Apply a damping torque (based on current angular velocity) // - convert angular velocity from world-to-body coordinates // - calculate damping torque // - convert damping torque from body-to-world coordinates // - apply damping torque sgVec3 BodyAngularVelocity, DampingTorque; const dReal* pAV = dBodyGetAngularVel(mODEBody); sgSetVec3(BodyAngularVelocity, pAV[0], pAV[1], pAV[2]); sgXformVec3(BodyAngularVelocity, R2); sgSetVec3(DampingTorque, -BodyAngularVelocity[0]*fabs(BodyAngularVelocity[0])*mAVDampFactor[0], -BodyAngularVelocity[1]*fabs(BodyAngularVelocity[1])*mAVDampFactor[1], -BodyAngularVelocity[2]*fabs(BodyAngularVelocity[2])*mAVDampFactor[2]); sgXformVec3(DampingTorque, R1); dBodyAddTorque(mODEBody, DampingTorque[0], DampingTorque[1], DampingTorque[2]); //printf("BodyAngularVelocity <%0.2f:%0.2f:%0.2f>\n", BodyAngularVelocity[0], BodyAngularVelocity[1], BodyAngularVelocity[2]); //printf("DampingTorque <%0.2f:%0.2f:%0.2f>\n", DampingTorque[0], DampingTorque[1], DampingTorque[2]); // Get the ODE position and set entity position reGetBodyTransform(mpTransform, mODEBody); } // Update shadow if (mpShadow) { sgCoord Pos; GetPosition(&Pos); mpShadow->Update(Pos.xyz[SG_X], Pos.xyz[SG_Y]); } } void reGameEntity::SetPosition(sgCoord* p) { if (mODEBody) { /*dBodySetPosition(mODEBody, p->xyz[SG_X], p->xyz[SG_Y], p->xyz[SG_Z]); dMatrix3 ROT; dRFromEulerAngles(ROT, p->hpr[0]*SG_DEGREES_TO_RADIANS, p->hpr[1]*SG_DEGREES_TO_RADIANS, p->hpr[2]*SG_DEGREES_TO_RADIANS); // todo: set rotation //dBodySetRotation(mODEBody, ROT);*/ dBodySetPosition(mODEBody, p->xyz[SG_X], p->xyz[SG_Y], p->xyz[SG_Z]); } else { mpTransform->setTransform(p); } } void reGameEntity::GetPosition(sgCoord* p) { sgMat4 Xform; mpTransform->getTransform(Xform); sgSetCoord(p, Xform); } void reGameEntity::GetTransform(sgMat4 Xform) { mpTransform->getTransform(Xform); } void reGameEntity::GetVelocity(sgVec3& Velocity) { reGetBodyVelocity(Velocity, mODEBody); } int reGameEntity::GetHits(ssgHit** pHitList) { sgMat4 InvMat; sgMakeIdentMat4(InvMat); // GetBSphere() results are slightly larger and off center than expected // ... is this a PLIB bug ? ... sigh ... no time to investigate now. :-( sgCoord Pos; sgSphere Sphere; GetPosition(&Pos); Sphere.setCenter(Pos.xyz[SG_X], Pos.xyz[SG_Y], Pos.xyz[SG_Z]); Sphere.setRadius(getBSphere()->getRadius()); // Using our bounding sphere ... check for hits int iNumHits = ssgIsect(reGetWorld(), &Sphere, InvMat, pHitList); return iNumHits; } //********************************************************************** // // rePlatformEntity // //********************************************************************** rePlatformEntity::rePlatformEntity() { // meEntityType = ePlatform; mfPower = 0.0f; sgZeroVec3(mDirChange); mfMaxFuel = 0; mfBurnRate = 0; sgZeroVec2(mMaxThrust); sgZeroVec3(mMaxAngularThrust); sgZeroVec3(mPowerFactor); sgZeroVec3(mPowerDampFactor); sgZeroVec3(mAngularPowerFactor); sgZeroVec3(mAngularDampFactor); mbForward = false; mbReverse = false; mbBreaking = false; mbHeadingLeft = false; mbHeadingRight = false; mbPitchUp = false; mbPitchDown = false; mbRollLeft = false; mbRollRight = false; } rePlatformEntity::~rePlatformEntity() { } bool rePlatformEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reGameEntity::Init(pConfig)) return false; if (pConfig) { TiXmlNode* pFuel = pConfig->FirstChild("Fuel"); mfMaxFuel = reGetXMLAttribute(pFuel, "max", 0); mfBurnRate = reGetXMLAttribute(pFuel, "burn_rate", 0); reGetXMLChildAttribVec3(pConfig, "MaxThrust", mMaxThrust); reGetXMLChildAttribVec3(pConfig, "MaxAngularThrust", mMaxAngularThrust); reGetXMLChildAttribVec3(pConfig, "PowerFactor", mPowerFactor); reGetXMLChildAttribVec3(pConfig, "PowerDampingFactor", mPowerDampFactor); reGetXMLChildAttribVec3(pConfig, "AngularPowerFactor", mAngularPowerFactor); reGetXMLChildAttribVec3(pConfig, "AngularDampingFactor", mAngularDampFactor); } return true; } //********************************************************************** // // reLandCraftEntity // //********************************************************************** bool reLandCraftEntity::Init(TiXmlNode* pConfig) { // Call base class if (!rePlatformEntity::Init(pConfig)) return false; // Read config return true; } //********************************************************************** // // reWaterCraftEntity // //********************************************************************** bool reWaterCraftEntity::Init(TiXmlNode* pConfig) { // Call base class if (!rePlatformEntity::Init(pConfig)) return false; // Read config return true; } //********************************************************************** // // reAirCraftEntity // //********************************************************************** bool reAirCraftEntity::Init(TiXmlNode* pConfig) { // Call base class if (!rePlatformEntity::Init(pConfig)) return false; // Read config return true; } //********************************************************************** // // reSpaceCraftEntity // //********************************************************************** bool reSpaceCraftEntity::Init(TiXmlNode* pConfig) { // Call base class if (!rePlatformEntity::Init(pConfig)) return false; // Read config return true; } //********************************************************************** // // reWeaponEntity // //********************************************************************** bool reWeaponEntity::Init(TiXmlNode* pConfig) { // Call base class if (!rePlatformEntity::Init(pConfig)) return false; // Read config return true; } //********************************************************************** // // reSceneryEntity // //********************************************************************** bool reSceneryEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reGameEntity::Init(pConfig)) return false; // Read config return true; } //********************************************************************** // // reParticleSystem // //********************************************************************** static void ParticleCreateCallback(ssgaParticleSystem* ps, int, ssgaParticle* p) { reParticleSystem* reps = (reParticleSystem*)ps; // Set colour, position, velocity, gravity & time to live //float c = ((float)(rand()%100)/100.0f) * (256.0f-163.0f)/255.0f; //sgSetVec4(p->col, 96.0f/255.0f+c, 147.0f/255.0f+c, 163.0f/255.0f+c, 0.5); sgSetVec4(p->col, reGenerateRandom(reps->mColMin[0], reps->mColMax[0]), reGenerateRandom(reps->mColMin[1], reps->mColMax[1]), reGenerateRandom(reps->mColMin[2], reps->mColMax[2]), 0.5); sgSetVec3(p->pos, reGenerateRandom(reps->mPosMin[0], reps->mPosMax[0]), reGenerateRandom(reps->mPosMin[1], reps->mPosMax[1]), reGenerateRandom(reps->mPosMin[2], reps->mPosMax[2])); sgSetVec3(p->vel, reGenerateRandom(reps->mVelMin[0], reps->mVelMax[0]), reGenerateRandom(reps->mVelMin[1], reps->mVelMax[1]), reGenerateRandom(reps->mVelMin[2], reps->mVelMax[2])); sgSetVec3(p->acc, reGenerateRandom(reps->mAccelMin[0], reps->mAccelMax[0]), reGenerateRandom(reps->mAccelMin[1], reps->mAccelMax[1]), reGenerateRandom(reps->mAccelMin[2], reps->mAccelMax[2])); p->time_to_live = reGenerateRandom(reps->mfLiveTimeMin, reps->mfLiveTimeMax); } reParticleSystem::reParticleSystem(TiXmlNode* pConfig) : ssgaParticleSystem(reGetXMLAttribute(pConfig, "num", 0), // Max Number of particles, reGetXMLAttribute(pConfig, "initial_num", 0), // Number to launch initially reGetXMLAttribute(pConfig, "create_rate", 0.0f), // Max number to create per sec reGetXMLAttribute(pConfig, "turn_to_face", true), // Turn to face? reGetXMLAttribute(pConfig, "size", 0.0f), // Size of particles reGetXMLAttribute(pConfig, "bsphere_size", 0.0f), // Size of bounding sphere ParticleCreateCallback, NULL, NULL) { sgZeroVec3(mColMin); sgZeroVec3(mColMax); sgZeroVec3(mPosMin); sgZeroVec3(mPosMax); sgZeroVec3(mVelMin); sgZeroVec3(mVelMax); sgZeroVec3(mAccelMin); sgZeroVec3(mAccelMax); mfLiveTimeMin = 0; mfLiveTimeMax = 0; TiXmlNode* pCol = pConfig->FirstChild("Colour"); reGetXMLChildAttribVec3(pCol, "Min", mColMin); reGetXMLChildAttribVec3(pCol, "Max", mColMax); TiXmlNode* pPos = pConfig->FirstChild("Position"); reGetXMLChildAttribVec3(pPos, "Min", mPosMin); reGetXMLChildAttribVec3(pPos, "Max", mPosMax); TiXmlNode* pVel = pConfig->FirstChild("Velocity"); reGetXMLChildAttribVec3(pVel, "Min", mVelMin); reGetXMLChildAttribVec3(pVel, "Max", mVelMax); TiXmlNode* pAccel = pConfig->FirstChild("Acceleration"); reGetXMLChildAttribVec3(pAccel, "Min", mAccelMin); reGetXMLChildAttribVec3(pAccel, "Max", mAccelMax); TiXmlNode* pLive = pConfig->FirstChild("TimeToLive"); mfLiveTimeMin = reGetXMLChildValue(pLive, "Min", 0.0f); mfLiveTimeMax = reGetXMLChildValue(pLive, "Max", 0.0f); } //********************************************************************** // // reParticleEntity // //********************************************************************** reParticleEntity::reParticleEntity() { mpParticle = NULL; } reParticleEntity::~reParticleEntity() { ssgDeRefDelete(mpParticle); } bool reParticleEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reGameEntity::Init(pConfig)) return false; TiXmlNode* pNode = pConfig->FirstChild("reParticleEntity"); if (!pNode) return false; // todo: remove hardcoded values & read SimpleState from config TiXmlNode* pState = pNode->FirstChild("SimpleState"); ssgSimpleState* splash_state = new ssgSimpleState () ; splash_state -> setTexture ( "textures/Droplet.rgb"); splash_state -> setTranslucent () ; splash_state -> enable ( GL_TEXTURE_2D); splash_state -> setShadeModel ( GL_SMOOTH); splash_state -> enable ( GL_CULL_FACE); splash_state -> enable ( GL_BLEND); splash_state -> enable ( GL_LIGHTING); splash_state -> setColourMaterial ( GL_EMISSION); splash_state -> setMaterial ( GL_AMBIENT, 0, 0, 0, 1); splash_state -> setMaterial ( GL_DIFFUSE, 0, 0, 0, 1); splash_state -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1); splash_state -> setShininess ( 0); /* */ mpParticle = new reParticleSystem(pNode->FirstChild("Particle")); mpParticle->ref(); mpParticle->setState(splash_state); mpTransform->addKid(mpParticle); clrTraversalMaskBits(SSGTRAV_ISECT|SSGTRAV_HOT); return true; } void reParticleEntity::Update(float fDeltaSec) { // Call base class reGameEntity::Update(fDeltaSec); // Update particle mpParticle->update(fDeltaSec); } void reParticleEntity::SetCreationRate(float fRate) { mpParticle->setCreationRate(fRate); } //********************************************************************** // // reTreeEntity // //********************************************************************** reTreeEntity::reTreeEntity() { } bool reTreeEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reSceneryEntity::Init(pConfig)) return false; // todo: remove hardcoded values & read from config // Get model ssgEntity* pTreeObj = reGetModelManager()->GetModel("tree"); mpTransform->addKid(pTreeObj); return true; } void reTreeEntity::Update(float fDeltaSec) { // Call base class reSceneryEntity::Update(fDeltaSec); // Update tree // todo } //********************************************************************** // // reTerrainEntity // //********************************************************************** reTerrainEntity::reTerrainEntity() : mEntityList(false) { } reTerrainEntity::~reTerrainEntity() { LISTPOSITION pos = mEntityList.GetHeadPosition(); while (pos) { reGameEntity* pGameEntity = mEntityList.GetNext(pos); mpTransform->removeKid(pGameEntity); ssgDeRefDelete(pGameEntity); } mEntityList.RemoveAll(); } bool reTerrainEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reSceneryEntity::Init(pConfig)) return false; const char* psModelName = reGetXMLChildValue(pConfig, "ModelName", ""); TiXmlNode* pTrees = pConfig->FirstChild("Trees"); bool bTreesOn = reGetXMLAttribute(pTrees, "enabled", false); const char* psTreesFile = reGetXMLAttribute(pTrees, "file", ""); TiXmlNode* pOcean = pConfig->FirstChild("Ocean"); bool bOceanOn = reGetXMLAttribute(pOcean, "enabled", false); const char* psOceanFile = reGetXMLAttribute(pOcean, "file", ""); // Get model ssgEntity* pTerrainObj = reGetModelManager()->GetModel(psModelName); mpTransform->addKid(pTerrainObj); /*// Create trees if (iTreesCount > 0) { // todo: use range selector for lod FILE* pFile = reOpenLevelFile(sTreesFile); float fTRadius = getBSphere()->getRadius(); sgCoord TreePos; for (int i = 0; i < iTreesCount; i++) { // todo: fix this cause this is never going to happen cause // the terrain is not yet added to the model !!! float fX, fY; float fZ = DEEPEST_HELL; fX = reGenerateRandom(-fTRadius, fTRadius); fY = reGenerateRandom(-fTRadius, fTRadius); sgSetCoord(&TreePos, fX, fY, HIGHEST_HEAVEN, 0.0f, 0.0f, 0.0f); fZ = reGetHeight(reGetWorld(), TreePos.xyz); // Only add trees above zero elevation if (fZ > 0) { sgSetCoord(&TreePos, fX, fY, fZ, 0.0f, 0.0f, 0.0f); reTreeEntity* pTree = new reTreeEntity; pTree->Init(pFile); pTree->ref(); pTree->SetPosition(&TreePos); mpTransform->addKid(pTree); mEntityList.AddTail(pTree); } } }*/ // Create ocean if (bOceanOn) { reOceanEntity* pOcean = (reOceanEntity*)reGameEntity::Create(psOceanFile); mpTransform->addKid(pOcean); mEntityList.AddTail(pOcean); } return true; } void reTerrainEntity::Update(float fDeltaSec) { // Call base class reSceneryEntity::Update(fDeltaSec); // Update terrain features LISTPOSITION pos = mEntityList.GetHeadPosition(); while (pos) { reGameEntity* pGameEntity = mEntityList.GetNext(pos); pGameEntity->Update(fDeltaSec); } } //********************************************************************** // // reOceanEntity // //********************************************************************** static float reOceanDepth(float x, float y) { // todo: calc depth based on terrain ... too cpu expensive !!! return 0.5f; } reOceanEntity::reOceanEntity() { mfTime = 0; mpOcean = NULL; } bool reOceanEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reSceneryEntity::Init(pConfig)) return false; sgVec3 Center = {0}; sgVec4 Colour = {0}; TiXmlNode* pNode = pConfig->FirstChild("reOceanEntity"); reGetXMLChildAttribVec4(pNode, "OceanColor", Colour); TiXmlNode* pOcean = pNode->FirstChild("Ocean"); const char* psOceanTex = reGetXMLAttribute(pOcean, "tex_path", ""); if (strlen(psOceanTex) == 0) psOceanTex = NULL; ssgSimpleState* pSeaState = reGetMaterialManager()->GetSimpleState((char*)psOceanTex); mpOcean = new ssgaWaveSystem(reGetXMLAttribute(pOcean, "triangles", 1000)); mpOcean->setColour (Colour); mpOcean->setSize (reGetXMLAttribute(pOcean, "size", 1000)); mpOcean->setTexScale (reGetXMLAttribute(pOcean, "tex_u", 1), reGetXMLAttribute(pOcean, "tex_v", 1)); mpOcean->setCenter (Center); // todo: calc depth based on terrain ... too cpu expensive !!! //mpOcean->setDepthCallback (reOceanDepth); mpOcean->setKidState (pSeaState); mpOcean->setWindSpeed (reGetXMLAttribute(pOcean, "wind_speed", 0)); int i = 0; TiXmlNode* pTrains = pNode->FirstChild("WaveTrains"); TiXmlNode* pTrain = pTrains->FirstChild("WaveTrain"); TiXmlNode* pElement = pTrain->ToElement(); while (pElement && i < SSGA_MAX_WAVETRAIN) { ssgaWaveTrain* pWaveTrain = new ssgaWaveTrain; pWaveTrain->setSpeed (reGetXMLAttribute(pElement, "speed", 0.0f)); pWaveTrain->setLength (reGetXMLAttribute(pElement, "length", 0.0f)); pWaveTrain->setLambda (reGetXMLAttribute(pElement, "lambda", 0.0f)); pWaveTrain->setHeading (reGetXMLAttribute(pElement, "heading", 0.0f)); pWaveTrain->setWaveHeight (reGetXMLAttribute(pElement, "height", 0.0f)); mWaveTrainList.AddTail(pWaveTrain); mpOcean->setWaveTrain (i, pWaveTrain); pElement = pElement->NextSiblingElement(); i++; } mpTransform->addKid(mpOcean); return true; } void reOceanEntity::Update(float fDeltaSec) { // Call base class reSceneryEntity::Update(fDeltaSec); // Update ocean mfTime += fDeltaSec; mpOcean->updateAnimation(mfTime); } //********************************************************************** // // reSkyEntity // //********************************************************************** reSkyEntity::reSkyEntity() : mpSky(NULL), mpSunRef(NULL) { } reSkyEntity::~reSkyEntity() { mpSunRef = NULL; if (mpSky) { delete mpSky; mpSky = NULL; } } bool reSkyEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reSceneryEntity::Init(pConfig)) return false; TiXmlNode* pNode = pConfig->FirstChild("reSkyEntity"); /* textures/Droplet.rgb true true GL_EMISSION 0 */ TiXmlNode* pDome = pNode->FirstChild("Dome"); mpSky = new ssgaSky; mpSky->build(reGetXMLAttribute(pDome, "h_radius", 80000), reGetXMLAttribute(pDome, "v_radius", 80000), 0, // todo: nplanets NULL, 0, // todo: nstars NULL); TiXmlNode* pBodies = pNode->FirstChild("Bodies"); TiXmlNode* pBody = pBodies->FirstChild("Body"); TiXmlNode* pElement = pBody->ToElement(); while (pElement) { const char* psBodyTex = reGetXMLAttribute(pElement, "body_tex_path", ""); if (strlen(psBodyTex) == 0) psBodyTex = NULL; const char* psHaloTex = reGetXMLAttribute(pElement, "halo_tex_path", ""); if (strlen(psHaloTex) == 0) psHaloTex = NULL; ssgaCelestialBody* cb = mpSky->addBody( psBodyTex, psHaloTex, reGetXMLAttribute(pElement, "size", 5000), reGetXMLAttribute(pElement, "distance", 80000), reGetXMLAttribute(pElement, "is_sun", false)); if (reGetXMLAttribute(pElement, "is_sun", false)) mpSunRef = cb; cb->setRightAscension(reGetXMLAttribute(pElement, "right_ascension", 0)*SGD_DEGREES_TO_RADIANS); cb->setDeclination(reGetXMLAttribute(pElement, "declination", 0)*SGD_DEGREES_TO_RADIANS); pElement = pElement->NextSiblingElement(); } TiXmlNode* pClouds = pNode->FirstChild("Clouds"); TiXmlNode* pCloud = pClouds->FirstChild("Cloud"); pElement = pCloud->ToElement(); while (pElement) { const char* psCloudTex = reGetXMLAttribute(pElement, "cloud_tex_path", ""); if (strlen(psCloudTex) == 0) psCloudTex = NULL; ssgaCloudLayer* cl = mpSky->addCloud( psCloudTex, reGetXMLAttribute(pElement, "span", 80000), reGetXMLAttribute(pElement, "elevation", 2000), reGetXMLAttribute(pElement, "thickness", 100), reGetXMLAttribute(pElement, "transition", 100)); cl->setSpeed(reGetXMLAttribute(pElement, "speed", 0)); cl->setDirection(reGetXMLAttribute(pElement, "direction", 0)); pElement = pElement->NextSiblingElement(); } return true; } // todo: tidy-up static sgVec4 base_sky_color = { 0.39, 0.5, 0.74, 1.0 } ; static sgVec4 base_fog_color = { 0.84, 0.87, 1.0, 1.0 } ; static sgVec4 base_ambient = { 0.6, 0.6, 0.6, 1.0 } ; static sgVec4 base_diffuse = { 1.0, 1.0, 1.0, 1.0 } ; static sgVec4 base_specular = { 1.0, 1.0, 1.0, 1.0 } ; static sgVec4 sky_color ; static sgVec4 fog_color ; static sgVec4 cloud_color ; static sgVec4 scene_ambient ; static sgVec4 scene_diffuse ; static sgVec4 scene_specular ; void reSkyEntity::Update(float fDeltaSec) { // Call base class reSceneryEntity::Update(fDeltaSec); if (!mpSky) return; sgCoord CameraPos = reGetGameEngine()->GetCamera()->GetCameraPos(); mpSky->repositionFlat(CameraPos.xyz, 0, fDeltaSec); mpSky->modifyVisibility(CameraPos.xyz[SG_Z], fDeltaSec); double sun_angle = 0; double sky_brightness = 1; double scene_brightness = 1; if (mpSunRef) { sun_angle = mpSunRef->getAngle(); sky_brightness = (1.0 + cos(sun_angle))/2.0; // 0.0 - 1.0 scene_brightness = pow(sky_brightness,0.5); } /* set sky color */ sky_color[0] = base_sky_color[0] * sky_brightness; sky_color[1] = base_sky_color[1] * sky_brightness; sky_color[2] = base_sky_color[2] * sky_brightness; sky_color[3] = base_sky_color[3]; /* set cloud and fog color */ cloud_color[0] = fog_color[0] = base_fog_color[0] * sky_brightness; cloud_color[1] = fog_color[1] = base_fog_color[1] * sky_brightness; cloud_color[2] = fog_color[2] = base_fog_color[2] * sky_brightness; cloud_color[3] = fog_color[3] = base_fog_color[3]; /* repaint the sky */ mpSky->repaint(sky_color, fog_color, cloud_color, sun_angle, 0, NULL, 0, NULL); if (mpSunRef) { /* set light source */ sgCoord sunpos; mpSunRef->getPosition(&sunpos); ssgGetLight(0)->setPosition(sunpos.xyz); scene_ambient[0] = base_ambient[0] * scene_brightness; scene_ambient[1] = base_ambient[1] * scene_brightness; scene_ambient[2] = base_ambient[2] * scene_brightness; scene_ambient[3] = 1.0; scene_diffuse[0] = base_diffuse[0] * scene_brightness; scene_diffuse[1] = base_diffuse[1] * scene_brightness; scene_diffuse[2] = base_diffuse[2] * scene_brightness; scene_diffuse[3] = 1.0; scene_specular[0] = base_specular[0] * scene_brightness; scene_specular[1] = base_specular[1] * scene_brightness; scene_specular[2] = base_specular[2] * scene_brightness; scene_specular[3] = 1.0; // GL_LIGHT_MODEL_AMBIENT has a default non-zero value so if // we only update GL_AMBIENT for our lights we will never get // a completely dark scene. So, we set GL_LIGHT_MODEL_AMBIENT // explicitely to black. glLightModelfv(GL_LIGHT_MODEL_AMBIENT, black); ssgGetLight(0)->setColour(GL_AMBIENT, scene_ambient); ssgGetLight(0)->setColour(GL_DIFFUSE, scene_diffuse); ssgGetLight(0)->setColour(GL_SPECULAR, scene_specular); } } void reSkyEntity::PreDraw() { // todo: Adjust fog based on visibility (when in clouds) //static const double m_log01 = -log( 0.01 ); //static const double sqrt_m_log01 = sqrt( m_log01 ); //GLfloat fog_exp2_density = sqrt_m_log01 / sky->getVisibility(); //glEnable( GL_FOG ); //glFogf ( GL_FOG_DENSITY, fog_exp2_density); //glFogi ( GL_FOG_MODE, GL_EXP2 ); //glFogfv ( GL_FOG_COLOR, fog_color ); if (mpSky) { // We need a white diffuse light for the phase of the moon ssgGetLight(0)->setColour(GL_DIFFUSE, white); mpSky->preDraw(); // Return to the desired diffuse color ssgGetLight(0)->setColour(GL_DIFFUSE, scene_diffuse); } } void reSkyEntity::PostDraw() { if (mpSky) { sgCoord CameraPos = reGetGameEngine()->GetCamera()->GetCameraPos(); mpSky->postDraw(CameraPos.xyz[SG_Z]); // altitude } } //********************************************************************** // // reBillyCartEntity // //********************************************************************** reBillyCartEntity::reBillyCartEntity() { sgZeroVec3(mWOffset); sgZeroVec3(mTOffset); mODEGeomGroupEx = NULL; } reBillyCartEntity::~reBillyCartEntity() { int i; for (i = 0; i <= 4; i++) { dJointDestroy(mODEJointEx[i]); } for (i = 0; i <= 4; i++) { dGeomDestroy(mODEGeomEx[i]); dBodyDestroy(mODEBodyEx[i]); } dGeomDestroy(mODEGeomGroupEx); } bool reBillyCartEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reLandCraftEntity::Init(pConfig)) return false; /*// Read file char sLabel[RE_STRING_MAX]; char sChassisModel[RE_STRING_MAX]; char sWheelModel[RE_STRING_MAX]; float fWRadius, fWMass, fTRadius, fTMass; fscanf(pConfig, "%s\n", sLabel); fscanf(pConfig, "ChassisModelName=%[^\n]\n", sChassisModel); fscanf(pConfig, "WheelModelName=%[^\n]\n", sWheelModel); fscanf(pConfig, "WheelGeomParams=%f,%f\n", &fWRadius, &fWMass); fscanf(pConfig, "WheelFROffset=%f,%f,%f\n", &mWOffset[0], &mWOffset[1], &mWOffset[2]); fscanf(pConfig, "TuxGeomParams=%f,%f\n", &fTRadius, &fTMass); fscanf(pConfig, "TuxOffset=%f,%f,%f\n", &mTOffset[0], &mTOffset[1], &mTOffset[2]); // Get models ssgEntity* pBodyObj = reGetModelManager()->GetModel(sChassisModel); ssgEntity* pWheelObj = reGetModelManager()->GetModel(sWheelModel); mpTransform->addKid(pBodyObj); int i; dMass m; for (i = 0; i <= 3; i++) { mpWheelTransform[i] = new ssgTransform; mpWheelTransform[i]->addKid(pWheelObj); addKid(mpWheelTransform[i]); } // Create wheel body & geom for (i = 0; i <= 3; i++) { mODEBodyEx[i] = dBodyCreate(reGetODEWorldId()); dQuaternion q; dQFromAxisAndAngle(q, 1, 0, 0, M_PI*0.5); dBodySetQuaternion(mODEBodyEx[i], q); dMassSetSphere(&m, 1, fWRadius); dMassAdjust(&m, fWMass); dBodySetMass(mODEBodyEx[i], &m); mODEGeomEx[i] = dCreateSphere(0, fWRadius); dGeomSetBody(mODEGeomEx[i], mODEBodyEx[i]); // Attempt to stop wheels freaking out dBodySetFiniteRotationMode(mODEBodyEx[i], 1); } dBodySetPosition(mODEBodyEx[0], mWOffset[0], mWOffset[1], mWOffset[2]); dBodySetPosition(mODEBodyEx[1], -mWOffset[0], mWOffset[1], mWOffset[2]); dBodySetPosition(mODEBodyEx[2], mWOffset[0], -mWOffset[1], mWOffset[2]); dBodySetPosition(mODEBodyEx[3], -mWOffset[0], -mWOffset[1], mWOffset[2]); // Create tux body & geom mODEBodyEx[4] = dBodyCreate(reGetODEWorldId()); dBodySetPosition(mODEBodyEx[4], mTOffset[0], mTOffset[1], mTOffset[2]); dMassSetSphere(&m, 1, fTRadius); dMassAdjust(&m, fTMass); dBodySetMass(mODEBodyEx[4], &m); mODEGeomEx[4] = dCreateSphere(0, fTRadius); dGeomSetBody(mODEGeomEx[4], mODEBodyEx[4]); // Create front and back wheel hinges for (i = 0; i <= 3; i++) { mODEJointEx[i] = dJointCreateHinge2(reGetODEWorldId(),0); dJointAttach(mODEJointEx[i], mODEBody, mODEBodyEx[i]); const dReal *a = dBodyGetPosition(mODEBodyEx[i]); dJointSetHinge2Anchor(mODEJointEx[i], a[0], a[1], a[2]); dJointSetHinge2Axis1(mODEJointEx[i], 0, 0, 1); dJointSetHinge2Axis2(mODEJointEx[i], 1, 0, 0); // Set joint suspension //dJointSetHinge2Param(mODEJointEx[i], dParamSuspensionERP, 0.4f); //dJointSetHinge2Param(mODEJointEx[i], dParamSuspensionCFM, 0.8f); dJointSetHinge2Param(mODEJointEx[i], dParamSuspensionERP, 0.8f); dJointSetHinge2Param(mODEJointEx[i], dParamSuspensionCFM, 0.2f); // Lock back 'wheels' along the steering axis if (i >= 2) { // Set stops to make sure 'wheels' always stay in alignment dJointSetHinge2Param(mODEJointEx[i], dParamLoStop, 0); dJointSetHinge2Param(mODEJointEx[i], dParamHiStop, 0); } } // Create tux joint mODEJointEx[4] = dJointCreateFixed(reGetODEWorldId(), 0); dJointAttach(mODEJointEx[4], mODEBody, mODEBodyEx[4]); dJointSetFixed(mODEJointEx[4]); // Create geometry group and add it to the space // todo: mODEGeom should really be added to the group too !!! // need to fix the way body & body extensions are created // this should all be in reGameEntity mODEGeomGroupEx = dCreateGeomGroup(reGetODESpaceId()); for (i = 0; i <= 4; i++) { dGeomGroupAdd(mODEGeomGroupEx, mODEGeomEx[i]); }*/ return true; } void reBillyCartEntity::Update(float fDeltaSec) { // Call base class reLandCraftEntity::Update(fDeltaSec); // Get position sgCoord p; GetPosition(&p); // Setup rotation matricies // - R1 useful for conversion between body-to-world coords // - R2 useful for conversion between world-to-body coords sgMat4 R1, R2; sgMakeRotMat4(R1, p.hpr); sgCopyMat4(R2, R1); sgInvertMat4(R2); // Update power change reUpdateValue(mfPower, mbForward, mbReverse, mPowerFactor[0], mPowerFactor[1], mPowerDampFactor[0], 1.0f, -1.0f, fDeltaSec); // Update heading change reUpdateValue(mDirChange[0], mbHeadingLeft, mbHeadingRight, mAngularPowerFactor[0], mAngularPowerFactor[0], mAngularDampFactor[0], 1.0f, -1.0f, fDeltaSec); float speed = -mfPower*100.0f; float steer = -mDirChange[0]; // Breaking /*if (mfPower < 0) { for (i = 0; i <= 1; i++) { dJointSetHinge2Param(mODEJointEx[i], dParamVel2, 0); dJointSetHinge2Param(mODEJointEx[i], dParamFMax2, -mfPower*0.9); } } else { for (i = 0; i <= 1; i++) { dJointSetHinge2Param(mODEJointEx[0], dParamFMax2, 0); } }*/ // Motor int i; for (i = 0; i <= 1; i++) { dJointSetHinge2Param(mODEJointEx[i], dParamVel2, -speed); dJointSetHinge2Param(mODEJointEx[i], dParamFMax2, 0.1); } // Steering dReal v[2]; for (i = 0; i <= 1; i++) { v[i] = dJointGetHinge2Angle1(mODEJointEx[i]); if (v[i] > 0.1) v[i] = 0.1; if (v[i] < -0.1) v[i] = -0.1; } dReal vd = fabs(v[0] - v[1]); //printf("b4 %.3f %.3f %.3f\n", v[0], v[1], vd); if (v[0] > v[1]) { v[0] -= vd/4; v[1] += vd/4; } else if (v[0] < v[1]) { v[0] += vd/4; v[1] -= vd/4; } //vd = fabs(v[0] - v[1]); //printf("af %.3f %.3f %.3f\n", v[0], v[1], vd); for (i = 0; i <= 1; i++) { v[i] = steer - v[i]; v[i] *= 10; dJointSetHinge2Param(mODEJointEx[i], dParamVel, v[i]); dJointSetHinge2Param(mODEJointEx[i], dParamFMax, 0.2); dJointSetHinge2Param(mODEJointEx[i], dParamLoStop, -0.75); dJointSetHinge2Param(mODEJointEx[i], dParamHiStop, 0.75); dJointSetHinge2Param(mODEJointEx[i], dParamFudgeFactor, 0.1); } /*for (i = 0; i <= 1; i++) { dReal v = steer - dJointGetHinge2Angle1(mODEJointEx[i]); if (v > 0.1) v = 0.1; if (v < -0.1) v = -0.1; v *= 10.0; dJointSetHinge2Param(mODEJointEx[i], dParamVel, v); dJointSetHinge2Param(mODEJointEx[i], dParamFMax, 0.2); dJointSetHinge2Param(mODEJointEx[i], dParamLoStop, -0.75); dJointSetHinge2Param(mODEJointEx[i], dParamHiStop, 0.75); dJointSetHinge2Param(mODEJointEx[i], dParamFudgeFactor, 0.1); }*/ for (i = 2; i <= 3; i++) { // Attempt to stop wheels freaking out dBodySetFiniteRotationMode(mODEBodyEx[i], 1); dBodySetFiniteRotationAxis(mODEBodyEx[i], 0, 0, 0); } // Get the ODE wheel position and set wheel transforms for (i = 0; i <= 3; i++) { reGetBodyTransform(mpWheelTransform[i], mODEBodyEx[i]); } } void reBillyCartEntity::SpaceCollide() { ssgHit* pHitList; int iNumHits = GetHits(&pHitList); // Wheel collision check for (int i = 0; i <= 3; i++) { reTriangleSphereContact(iNumHits, pHitList, mODEBodyEx[i], mODEGeomEx[i]); } // Tux collision check reTriangleSphereContact(iNumHits, pHitList, mODEBodyEx[4], mODEGeomEx[4]); // Chassis collision check reTriangleBoxContact( iNumHits, pHitList, mODEBody, mODEGeom); } void reBillyCartEntity::Keyboard(unsigned char key, int x, int y, bool bKeyDown) { // Call base class // todo: maybe!? // none ... yet } void reBillyCartEntity::KeyboardSpecial(int key, int x, int y, bool bKeyDown) { // Call base class // todo: maybe!? switch (key) { case GLUT_KEY_UP: // forward if (!glutGetModifiers()) SetForward(bKeyDown); else SetForward(false); break; case GLUT_KEY_DOWN: // reverse if (!glutGetModifiers()) SetReverse(bKeyDown); else SetReverse(false); break; case GLUT_KEY_LEFT: // heading left if (!glutGetModifiers()) SetHeadingLeft(bKeyDown); else SetHeadingLeft(false); break; case GLUT_KEY_RIGHT: // heading right if (!glutGetModifiers()) SetHeadingRight(bKeyDown); else SetHeadingRight(false); break; default: break; } } void reBillyCartEntity::SetPosition(sgCoord* p) { // Call base class reLandCraftEntity::SetPosition(p); dBodySetPosition(mODEBodyEx[0], p->xyz[SG_X]+mWOffset[0], p->xyz[SG_Y]+mWOffset[1], p->xyz[SG_Z]+mWOffset[2]); dBodySetPosition(mODEBodyEx[1], p->xyz[SG_X]-mWOffset[0], p->xyz[SG_Y]+mWOffset[1], p->xyz[SG_Z]+mWOffset[2]); dBodySetPosition(mODEBodyEx[2], p->xyz[SG_X]+mWOffset[0], p->xyz[SG_Y]-mWOffset[1], p->xyz[SG_Z]+mWOffset[2]); dBodySetPosition(mODEBodyEx[3], p->xyz[SG_X]-mWOffset[0], p->xyz[SG_Y]-mWOffset[1], p->xyz[SG_Z]+mWOffset[2]); dBodySetPosition(mODEBodyEx[4], p->xyz[SG_X]+mTOffset[0], p->xyz[SG_Y]+mTOffset[1], p->xyz[SG_Z]+mTOffset[2]); }; //********************************************************************** // // reBoatEntity // //********************************************************************** reBoatEntity::reBoatEntity() { mpSpray = NULL; } reBoatEntity::~reBoatEntity() { int i; if (mpSpray) { reGetWorld()->removeKid(mpSpray); ssgDeRefDelete(mpSpray); } for (i = 0; i <= 1; i++) { dJointDestroy(mODEJointEx[i]); } for (i = 0; i <= 1; i++) { dGeomDestroy(mODEGeomEx[i]); dBodyDestroy(mODEBodyEx[i]); } dGeomDestroy(mODEGeomGroupEx); } bool reBoatEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reWaterCraftEntity::Init(pConfig)) return false; const char* psModelName = reGetXMLChildValue(pConfig, "ModelName", ""); TiXmlNode* pParticle = pConfig->FirstChild("Particle"); bool bParticle = reGetXMLAttribute(pParticle, "enabled", false); const char* psParticleFile = reGetXMLAttribute(pParticle, "file", ""); // Get model ssgEntity* pBodyObj = reGetModelManager()->GetModel(psModelName); mpTransform->addKid(pBodyObj); // Create boat spray if (bParticle) { mpSpray = (reParticleEntity*)reGameEntity::Create(psParticleFile); reGetWorld()->addKid(mpSpray); } int i; dMass m; // Create front/back end sphere for (i = 0; i <= 1; i++) { mODEBodyEx[i] = dBodyCreate(reGetODEWorldId()); dMassSetSphere(&m, 1, 0.15); dMassAdjust(&m, 0.1); dBodySetMass(mODEBodyEx[i], &m); mODEGeomEx[i] = dCreateSphere(0, 0.15); dGeomSetBody(mODEGeomEx[i], mODEBodyEx[i]); } dBodySetPosition(mODEBodyEx[0], 0, 0.5, 0); dBodySetPosition(mODEBodyEx[1], 0, -0.5, 0); // Create joint for (i = 0; i <= 1; i++) { mODEJointEx[i] = dJointCreateFixed(reGetODEWorldId(), 0); dJointAttach(mODEJointEx[i], mODEBody, mODEBodyEx[i]); dJointSetFixed(mODEJointEx[i]); } // Create geometry group and add it to the space // todo: mODEGeom should really be added to the group too !!! // need to fix the way body & body extensions are created // this should all be in reGameEntity mODEGeomGroupEx = dCreateGeomGroup(reGetODESpaceId()); for (i = 0; i <= 1; i++) { dGeomGroupAdd(mODEGeomGroupEx, mODEGeomEx[i]); } return true; } void reBoatEntity::Update(float fDeltaSec) { // Call base class reWaterCraftEntity::Update(fDeltaSec); // Get position sgCoord p; GetPosition(&p); float fAltitude = reGetAltitude(reGetWorld(), p.xyz); // Setup rotation matricies // - R1 useful for conversion between body-to-world coords // - R2 useful for conversion between world-to-body coords sgMat4 R1, R2; sgMakeRotMat4(R1, p.hpr); sgCopyMat4(R2, R1); sgInvertMat4(R2); // Update power change reUpdateValue(mfPower, mbForward, mbReverse, mPowerFactor[0], mPowerFactor[1], mPowerDampFactor[0], 1.0f, -1.0f, fDeltaSec); // Update heading change reUpdateValue(mDirChange[0], mbHeadingLeft, mbHeadingRight, mAngularPowerFactor[0], mAngularPowerFactor[0], mAngularDampFactor[0], 1.0f, -1.0f, fDeltaSec); // Update pitch change reUpdateValue(mDirChange[1], mbPitchUp, mbPitchDown, mAngularPowerFactor[1], mAngularPowerFactor[1], mAngularDampFactor[1], 1.0f, -1.0f, fDeltaSec); // Update roll change reUpdateValue(mDirChange[2], mbRollRight, mbRollLeft, mAngularPowerFactor[2], mAngularPowerFactor[2], mAngularDampFactor[2], 1.0f, -1.0f, fDeltaSec); // Check if touching surface // todo: we actually need to know what surface we are touching float fPower; sgVec3 Torque; if (fAltitude <= 0.2f) { // Power on, pitch off, roll on, heading on fPower = mfPower; sgSetVec3(Torque, 0, mDirChange[2]*mMaxAngularThrust[2], mDirChange[0]*mMaxAngularThrust[0]); } else // not touching surface { // Power off, pitch on, roll on, heading off fPower = 0; sgSetVec3(Torque, mDirChange[1]*mMaxAngularThrust[1], mDirChange[2]*mMaxAngularThrust[2], 0); } // Apply a force to move the entity // - calculate force (assume entity is facing in the +y direction) // - transform force from body-to-world coordinates // - apply force sgVec3 Force; if (fPower > 0.0f) sgSetVec3(Force, 0.0f, fPower*mMaxThrust[0], 0.0f); else if (fPower < 0.0f) sgSetVec3(Force, 0.0f, fPower*mMaxThrust[1], 0.0f); else sgZeroVec3(Force); sgXformVec3(Force, R1); dBodyAddForce(mODEBody, Force[SG_X], Force[SG_Y], Force[SG_Z]); //printf("Force <%0.2f>\n", sgLengthVec3(Force)); // Apply a torque to rotate the entity // - calculate torque (already done above) // - transform torque from body-to-world coordinates // - apply torque sgXformVec3(Torque, R1); dBodyAddTorque(mODEBody, Torque[0], Torque[1], Torque[2]); //printf("Torque <%0.2f:%0.2f:%0.2f>\n", Torque[0], Torque[1], Torque[2]); // Update spray GetPosition(&p); if (mpSpray) { mpSpray->SetPosition(&p); //if (fPower == 0 || fAltitude > 0.2) // mpSpray->SetCreationRate(0); //else mpSpray->SetCreationRate(fPower*100.0f); mpSpray->Update(fDeltaSec); } } void reBoatEntity::SpaceCollide() { ssgHit* pHitList; int iNumHits = GetHits(&pHitList); reTriangleBoxContact(iNumHits, pHitList, mODEBody, mODEGeom); // Front/back end collision check for (int i = 0; i <= 1; i++) { reTriangleSphereContact(iNumHits, pHitList, mODEBodyEx[i], mODEGeomEx[i]); } } void reBoatEntity::Keyboard(unsigned char key, int x, int y, bool bKeyDown) { // Call base class // todo: maybe!? switch (key) { case ',': // roll left case '<': SetRollLeft(bKeyDown); break; case '.': // roll right case '>': SetRollRight(bKeyDown); break; default: break; } } void reBoatEntity::KeyboardSpecial(int key, int x, int y, bool bKeyDown) { // Call base class // todo: maybe!? switch (key) { case GLUT_KEY_UP: // forward & pitch down if (!glutGetModifiers()) { SetForward(bKeyDown); SetPitchDown(bKeyDown); } else { SetForward(false); SetPitchDown(false); } break; case GLUT_KEY_DOWN: // reverse & pitch up if (!glutGetModifiers()) { SetReverse(bKeyDown); SetPitchUp(bKeyDown); } else { SetReverse(false); SetPitchUp(false); } break; case GLUT_KEY_LEFT: // heading left if (!glutGetModifiers()) SetHeadingLeft(bKeyDown); else SetHeadingLeft(false); break; case GLUT_KEY_RIGHT: // heading right if (!glutGetModifiers()) SetHeadingRight(bKeyDown); else SetHeadingRight(false); break; default: break; } } void reBoatEntity::SetPosition(sgCoord* p) { // Call base class reWaterCraftEntity::SetPosition(p); dBodySetPosition(mODEBodyEx[0], p->xyz[SG_X], p->xyz[SG_Y]+0.5, p->xyz[SG_Z]); dBodySetPosition(mODEBodyEx[1], p->xyz[SG_X], p->xyz[SG_Y]-0.5, p->xyz[SG_Z]); }; //********************************************************************** // // reSpaceShipEntity // //********************************************************************** reSpaceShipEntity::reSpaceShipEntity() { } reSpaceShipEntity::~reSpaceShipEntity() { } bool reSpaceShipEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reSpaceCraftEntity::Init(pConfig)) return false; const char* psModelName = reGetXMLChildValue(pConfig, "ModelName", ""); // Get model ssgEntity* pBodyObj = reGetModelManager()->GetModel(psModelName); mpTransform->addKid(pBodyObj); return true; } void reSpaceShipEntity::Update(float fDeltaSec) { // Call base class reSpaceCraftEntity::Update(fDeltaSec); // Get position sgCoord p; GetPosition(&p); // Setup rotation matricies // - R1 useful for conversion between body-to-world coords // - R2 useful for conversion between world-to-body coords sgMat4 R1, R2; sgMakeRotMat4(R1, p.hpr); sgCopyMat4(R2, R1); sgInvertMat4(R2); // Update power change reUpdateValue(mfPower, mbForward, mbReverse, mPowerFactor[0], mPowerFactor[1], mPowerDampFactor[0], 1.0f, -1.0f, fDeltaSec); // Update heading change reUpdateValue(mDirChange[0], mbHeadingLeft, mbHeadingRight, mAngularPowerFactor[0], mAngularPowerFactor[0], mAngularDampFactor[0], 1.0f, -1.0f, fDeltaSec); // Update pitch change reUpdateValue(mDirChange[1], mbPitchUp, mbPitchDown, mAngularPowerFactor[1], mAngularPowerFactor[1], mAngularDampFactor[1], 1.0f, -1.0f, fDeltaSec); // Update roll change reUpdateValue(mDirChange[2], mbRollRight, mbRollLeft, mAngularPowerFactor[2], mAngularPowerFactor[2], mAngularDampFactor[2], 1.0f, -1.0f, fDeltaSec); // Apply a force to move the entity // - calculate force (assume entity is facing in the +y direction) // - transform force from body-to-world coordinates // - apply force sgVec3 Force; if (mfPower > 0.0f) sgSetVec3(Force, 0.0f, mfPower*mMaxThrust[0], 0.0f); else if (mfPower < 0.0f) sgSetVec3(Force, 0.0f, mfPower*mMaxThrust[1], 0.0f); else sgZeroVec3(Force); sgXformVec3(Force, R1); dBodyAddForce(mODEBody, Force[SG_X], Force[SG_Y], Force[SG_Z]); //printf("Force <%0.2f>\n", sgLengthVec3(Force)); // Apply a torque to rotate the entity // - calculate torque // - transform torque from body-to-world coordinates // - apply torque // Power on, pitch off, roll on, heading on sgVec3 Torque; sgSetVec3(Torque, mDirChange[1]*mMaxAngularThrust[1], mDirChange[2]*mMaxAngularThrust[2], mDirChange[0]*mMaxAngularThrust[0]); sgXformVec3(Torque, R1); dBodyAddTorque(mODEBody, Torque[0], Torque[1], Torque[2]); //printf("Torque <%0.2f:%0.2f:%0.2f>\n", Torque[0], Torque[1], Torque[2]); } void reSpaceShipEntity::SpaceCollide() { ssgHit* pHitList; int iNumHits = GetHits(&pHitList); reTriangleBoxContact(iNumHits, pHitList, mODEBody, mODEGeom); //reTriangleSphereContact(iNumHits, pHitList, mODEBody2, mODEGeom2); } void reSpaceShipEntity::Keyboard(unsigned char key, int x, int y, bool bKeyDown) { // Call base class // todo: maybe!? switch (key) { case 'a': // forward case 'A': SetForward(bKeyDown); break; case 'z': // reverse case 'Z': SetReverse(bKeyDown); break; case ',': // heading left case '<': SetHeadingLeft(bKeyDown); break; case '.': // heading right case '>': SetHeadingRight(bKeyDown); break; default: break; } } void reSpaceShipEntity::KeyboardSpecial(int key, int x, int y, bool bKeyDown) { // Call base class // todo: maybe!? switch (key) { case GLUT_KEY_UP: // pitch down if (!glutGetModifiers()) SetPitchDown(bKeyDown); else SetPitchDown(false); break; case GLUT_KEY_DOWN: // pitch up if (!glutGetModifiers()) SetPitchUp(bKeyDown); else SetPitchUp(false); break; case GLUT_KEY_LEFT: // roll left if (!glutGetModifiers()) SetRollLeft(bKeyDown); else SetRollLeft(false); break; case GLUT_KEY_RIGHT: // roll right if (!glutGetModifiers()) SetRollRight(bKeyDown); else SetRollRight(false); break; default: break; } } //********************************************************************** // // rePlasmaBoltEntity // //********************************************************************** rePlasmaBoltEntity::rePlasmaBoltEntity() { } rePlasmaBoltEntity::~rePlasmaBoltEntity() { } bool rePlasmaBoltEntity::Init(TiXmlNode* pConfig) { // Call base class if (!reWeaponEntity::Init(pConfig)) return false; const char* psModelName = reGetXMLChildValue(pConfig, "ModelName", ""); // Get model ssgEntity* pBodyObj = reGetModelManager()->GetModel(psModelName); mpTransform->addKid(pBodyObj); return true; } void rePlasmaBoltEntity::Update(float fDeltaSec) { // Call base class reWeaponEntity::Update(fDeltaSec); // Update plasma bolt // todo } void rePlasmaBoltEntity::SpaceCollide() { ssgHit* pHitList; int iNumHits = GetHits(&pHitList); reTriangleBoxContact(iNumHits, pHitList, mODEBody, mODEGeom); } //********************************************************************** // // reUpdateValue // //********************************************************************** void reUpdateValue(float& fValue, bool bIncFactor1, bool bDecFactor2, float fIncFactor1, float fDecFactor2, float fDampFactor, float fMaxValue, float fMinValue, float fDeltaSec) { if (bIncFactor1) fValue += fIncFactor1*fDeltaSec; if (bDecFactor2) fValue -= fDecFactor2*fDeltaSec; if (!bIncFactor1 && !bDecFactor2 && fDampFactor != 0.0f) { if (fValue > 0.0f) { fValue -= fDampFactor*fDeltaSec; if (fValue < 0.0f) fValue = 0.0f; } else if (fValue < 0.0f) { fValue += fDampFactor*fDeltaSec; if (fValue > 0.0f) fValue = 0.0f; } } if (fValue > fMaxValue) { fValue = fMaxValue; } else if (fValue < fMinValue) { fValue = fMinValue; } }