From d32be2011fb14c332ae0af06667dbc54fdf68e11 Mon Sep 17 00:00:00 2001 From: MarkT Date: Thu, 3 Jun 2004 02:20:48 +0000 Subject: [PATCH] Pathfinding tweaks; coldet fixes. This was SVN commit r379. --- source/graphics/MapReader.cpp | 2 +- source/lib/timer.cpp | 2 +- source/renderer/Renderer.cpp | 3 +- source/sced/EditorData.cpp | 1314 +++++++++---------- source/simulation/BoundingObjects.cpp | 19 +- source/simulation/BoundingObjects.h | 10 +- source/simulation/Collision.cpp | 8 +- source/simulation/Collision.h | 2 +- source/simulation/Entity.cpp | 71 +- source/simulation/EntityHandles.h | 1 + source/simulation/EntityOrders.h | 6 +- source/simulation/EntityStateProcessing.cpp | 87 +- source/simulation/PathfindSparse.cpp | 104 +- source/simulation/PathfindSparse.h | 16 +- 14 files changed, 898 insertions(+), 747 deletions(-) diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 0bccc87a53..f51ef752e1 100755 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -160,7 +160,7 @@ void CMapReader::ApplyData(CFileUnpacker& unpacker) CVector3D orient = -((CMatrix3D*)m_Objects[i].m_Transform)->GetIn(); CVector3D position = ((CMatrix3D*)m_Objects[i].m_Transform)->GetTranslation(); - g_EntityManager.create( templateObject, position, atan2( orient.X, orient.Z ) ); + g_EntityManager.create( templateObject, position, atan2( -orient.X, -orient.Z ) ); } else { diff --git a/source/lib/timer.cpp b/source/lib/timer.cpp index a486dfc6f4..acbacb7f23 100755 --- a/source/lib/timer.cpp +++ b/source/lib/timer.cpp @@ -155,7 +155,7 @@ void calc_fps() // update fps counter if update threshold is exceeded const float avg_fps = fps_sum / H; const float d_avg = avg_fps-fps; - const float max_diff = fminf(5.f, 0.05f*fps); + const float max_diff = __min( 5.f, 0.05f * fps ); //fminf(5.f, 0.05f*fps); if((trend > 0 && (avg_fps > fps || d_avg < -4.f)) || // going up, or large drop (trend < 0 && (avg_fps < fps || d_avg > 4.f)) || // going down, or large raise diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index c215a46c94..adeccff606 100755 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -1,3 +1,4 @@ + /////////////////////////////////////////////////////////////////////////////// // // Name: Renderer.cpp @@ -1345,4 +1346,4 @@ void CRenderer::UpdateSubmittedObjectData() // (recursively) build transparent passes from model BuildTransparentPasses(m_Models[i]); } -} +} \ No newline at end of file diff --git a/source/sced/EditorData.cpp b/source/sced/EditorData.cpp index c8a7659744..fa6edae200 100755 --- a/source/sced/EditorData.cpp +++ b/source/sced/EditorData.cpp @@ -1,658 +1,658 @@ - - -#include "EditorData.h" -#include "UIGlobals.h" -#include "ToolManager.h" -#include "ObjectManager.h" -#include "UnitManager.h" -#include "TextureManager.h" -#include "Model.h" -#include "SkeletonAnimManager.h" - -#include "ogl.h" -#include "res/tex.h" -#include "time.h" - -#include "BaseEntityCollection.h" -#include "Entity.h" -#include "EntityHandles.h" -#include "EntityManager.h" - -const int NUM_ALPHA_MAPS = 14; -Handle AlphaMaps[NUM_ALPHA_MAPS]; - -CTerrain g_Terrain; -CLightEnv g_LightEnv; -CMiniMap g_MiniMap; -CEditorData g_EditorData; - -CEditorData::CEditorData() -{ - m_ModelMatrix.SetIdentity(); - m_ScenarioName="Scenario01"; -} - -void CEditorData::SetMode(EditMode mode) -{ - if (m_Mode==TEST_MODE && mode!=TEST_MODE) StopTestMode(); - m_Mode=mode; - if (m_Mode==TEST_MODE) StartTestMode(); -} - -bool CEditorData::InitScene() -{ - // setup default lighting environment - g_LightEnv.m_SunColor=RGBColor(1,1,1); - g_LightEnv.m_Rotation=DEGTORAD(270); - g_LightEnv.m_Elevation=DEGTORAD(45); - g_LightEnv.m_TerrainAmbientColor=RGBColor(0,0,0); - g_LightEnv.m_UnitsAmbientColor=RGBColor(0.4f,0.4f,0.4f); - g_Renderer.SetLightEnv(&g_LightEnv); - - // load the default - if (!LoadTerrain("terrain.raw")) return false; - - // get default texture to apply to terrain - CTextureEntry* texture=0; - for (uint i=0;im_MiniPatches[j][i].Tex1=texture ? texture->m_Handle : 0; - } - } - } - } - - // setup camera - InitCamera(); - - // build the terrain plane - float h=128*HEIGHT_SCALE; - u32 mapSize=g_Terrain.GetVerticesPerSide(); - CVector3D pt0(0,h,0),pt1(float(CELL_SIZE*mapSize),h,0),pt2(0,h,float(CELL_SIZE*mapSize)); - m_TerrainPlane.Set(pt0,pt1,pt2); - m_TerrainPlane.Normalize(); - - return true; -} - - -struct TGAHeader { - // header stuff - unsigned char iif_size; - unsigned char cmap_type; - unsigned char image_type; - unsigned char pad[5]; - - // origin : unused - unsigned short d_x_origin; - unsigned short d_y_origin; - - // dimensions - unsigned short width; - unsigned short height; - - // bits per pixel : 16, 24 or 32 - unsigned char bpp; - - // image descriptor : Bits 3-0: size of alpha channel - // Bit 4: must be 0 (reserved) - // Bit 5: should be 0 (origin) - // Bits 6-7: should be 0 (interleaving) - unsigned char image_descriptor; -}; - -static bool saveTGA(const char* filename,int width,int height,unsigned char* data) -{ - FILE* fp=fopen(filename,"wb"); - if (!fp) return false; - - // fill file header - TGAHeader header; - header.iif_size=0; - header.cmap_type=0; - header.image_type=2; - memset(header.pad,0,sizeof(header.pad)); - header.d_x_origin=0; - header.d_y_origin=0; - header.width=width; - header.height=height; - header.bpp=24; - header.image_descriptor=0; - - if (fwrite(&header,sizeof(TGAHeader),1,fp)!=1) { - fclose(fp); - return false; - } - - // write data - if (fwrite(data,width*height*3,1,fp)!=1) { - fclose(fp); - return false; - } - - // return success .. - fclose(fp); - return true; -} - -void CEditorData::LoadAlphaMaps() -{ - const char* fns[CRenderer::NumAlphaMaps] = { - "art/textures/terrain/alphamaps/special/blendcircle.png", - "art/textures/terrain/alphamaps/special/blendlshape.png", - "art/textures/terrain/alphamaps/special/blendedge.png", - "art/textures/terrain/alphamaps/special/blendedgecorner.png", - "art/textures/terrain/alphamaps/special/blendedgetwocorners.png", - "art/textures/terrain/alphamaps/special/blendfourcorners.png", - "art/textures/terrain/alphamaps/special/blendtwooppositecorners.png", - "art/textures/terrain/alphamaps/special/blendlshapecorner.png", - "art/textures/terrain/alphamaps/special/blendtwocorners.png", - "art/textures/terrain/alphamaps/special/blendcorner.png", - "art/textures/terrain/alphamaps/special/blendtwoedges.png", - "art/textures/terrain/alphamaps/special/blendthreecorners.png", - "art/textures/terrain/alphamaps/special/blendushape.png", - "art/textures/terrain/alphamaps/special/blendbad.png" - }; - - g_Renderer.LoadAlphaMaps(fns); -} - -void CEditorData::InitResources() -{ - g_TexMan.LoadTerrainTextures(); - LoadAlphaMaps(); - g_ObjMan.LoadObjects(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// InitSingletons: create and initialise required singletons -void CEditorData::InitSingletons() -{ - // create terrain related stuff - new CTextureManager; - - // create actor related stuff - new CSkeletonAnimManager; - new CObjectManager; - new CUnitManager; - - // create entity related stuff - new CBaseEntityCollection; - new CEntityManager; - g_EntityTemplateCollection.loadTemplates(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// Init: perform one time initialisation of the editor -bool CEditorData::Init() -{ - // create and initialise singletons - InitSingletons(); - - // load default textures - InitResources(); - - // create the scene - terrain, camera, light environment etc - if (!InitScene()) return false; - - // set up an empty minimap - g_MiniMap.Initialise(); - - // set up the info box - m_InfoBox.Initialise(); - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// Terminate: close down the editor (destroy singletons in reverse order to construction) -void CEditorData::Terminate() -{ - // destroy entity related stuff - delete CEntityManager::GetSingletonPtr(); - delete CBaseEntityCollection::GetSingletonPtr(); - - // destroy actor related stuff - delete CUnitManager::GetSingletonPtr(); - delete CObjectManager::GetSingletonPtr(); - delete CSkeletonAnimManager::GetSingletonPtr(); - - // destroy terrain related stuff - delete CTextureManager::GetSingletonPtr(); -} - -void CEditorData::InitCamera() -{ - g_NaviCam.GetCamera().SetProjection(1.0f,10000.0f,DEGTORAD(90)); - g_NaviCam.GetCamera().m_Orientation.SetIdentity(); -#ifdef TOPDOWNVIEW - g_NaviCam.GetCamera().m_Orientation.RotateX(DEGTORAD(90)); - g_NaviCam.GetCamera().m_Orientation.Translate(CELL_SIZE*250*0.5, 80, CELL_SIZE*250*0.5); -#else - g_NaviCam.GetCamera().m_Orientation.RotateX(DEGTORAD(40)); - g_NaviCam.GetCamera().m_Orientation.RotateY(DEGTORAD(-45)); - g_NaviCam.GetCamera().m_Orientation.Translate(600, 200, 125); -#endif - - OnCameraChanged(); -} - -void CEditorData::OnCameraChanged() -{ - int width=g_Renderer.GetWidth(); - int height=g_Renderer.GetHeight(); - - // resize viewport - SViewPort viewport; - viewport.m_X=0; - viewport.m_Y=0; - viewport.m_Width=width; - viewport.m_Height=height; - g_NaviCam.GetCamera().SetViewPort(&viewport); - - // rebuild object camera - m_ObjectCamera.SetViewPort(&viewport); - m_ObjectCamera.SetProjection(1.0f,10000.0f,DEGTORAD(90)); - - - // recalculate projection matrix - g_NaviCam.GetCamera().SetProjection(1.0f,10000.0f,DEGTORAD(20)); - - // update viewing frustum - g_NaviCam.GetCamera().UpdateFrustum(); - - // calculate intersection of camera stabbing lines with terrain plane - - // get points of back plane of frustum in camera space - float aspect=height>0 ? float(width)/float(height) : 1.0f; - float zfar=g_NaviCam.GetCamera().GetFarPlane(); - CVector3D cPts[4]; - float x=zfar*float(tan(g_NaviCam.GetCamera().GetFOV()*aspect*0.5)); - float y=zfar*float(tan(g_NaviCam.GetCamera().GetFOV()*0.5)); - cPts[0].X=-x; - cPts[0].Y=-y; - cPts[0].Z=zfar; - cPts[1].X=x; - cPts[1].Y=-y; - cPts[1].Z=zfar; - cPts[2].X=x; - cPts[2].Y=y; - cPts[2].Z=zfar; - cPts[3].X=-x; - cPts[3].Y=y; - cPts[3].Z=zfar; - - // transform to world space - CVector3D wPts[4]; - for (int i=0;i<4;i++) { - wPts[i]=g_NaviCam.GetCamera().m_Orientation.Transform(cPts[i]); - } - - // now intersect a ray from the camera through each point - CVector3D rayOrigin=g_NaviCam.GetCamera().m_Orientation.GetTranslation(); - CVector3D rayDir=g_NaviCam.GetCamera().m_Orientation.GetIn(); - - CVector3D hitPt[4]; - for (i=0;i<4;i++) { - CVector3D rayDir=wPts[i]-rayOrigin; - rayDir.Normalize(); - - // get intersection point - m_TerrainPlane.FindRayIntersection(rayOrigin,rayDir,&hitPt[i]); - } - - for (i=0;i<4;i++) { - // convert to minimap space - float px=hitPt[i].X; - float pz=hitPt[i].Z; - g_MiniMap.m_ViewRect[i][0]=(197*px/float(CELL_SIZE*g_Terrain.GetVerticesPerSide())); - g_MiniMap.m_ViewRect[i][1]=197*pz/float(CELL_SIZE*g_Terrain.GetVerticesPerSide()); - } -} - -void CEditorData::RenderTerrain() -{ - CFrustum frustum=g_NaviCam.GetCamera().GetFustum(); - u32 patchesPerSide=g_Terrain.GetPatchesPerSide(); - for (uint j=0; jGetBounds())) { - g_Renderer.Submit(patch); - } - } - } -} - -void CEditorData::OnScreenShot(const char* filename) -{ - g_Renderer.SetClearColor(0); - g_Renderer.BeginFrame(); - g_Renderer.SetCamera(g_NaviCam.GetCamera()); - - RenderWorld(); - - g_Renderer.EndFrame(); - - int width=g_Renderer.GetWidth(); - int height=g_Renderer.GetHeight(); - unsigned char* data=new unsigned char[width*height*3]; - - glReadBuffer(GL_BACK); - glReadPixels(0,0,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,data); - - saveTGA(filename,width,height,data); - - delete[] data; -} - -////////////////////////////////////////////////////////////////////////////////////////////////// -// SubmitModelRecursive: recurse down given model, submitting it and all it's descendents to the -// renderer -void SubmitModelRecursive(CModel* model) -{ - g_Renderer.Submit(model); - - const std::vector& props=model->GetProps(); - for (uint i=0;i& units=g_UnitMan.GetUnits(); - for (i=0;iGetModel()); - } - - u32 patchesPerSide=g_Terrain.GetPatchesPerSide(); - for (j=0; j& units=g_UnitMan.GetUnits(); - uint i; - for (i=0;iGetModel()->GetBounds())) { - SubmitModelRecursive(units[i]->GetModel()); - } - } -} - -void CEditorData::RenderWorld() -{ - // render terrain - RenderTerrain(); - - // render all the units - RenderModels(); - - // flush prior to rendering overlays etc - g_Renderer.FlushFrame(); -} - -void CEditorData::RenderObEdGrid() -{ - int i; - const int numSteps=32; - const CVector3D start(-numSteps*CELL_SIZE/2,0,-numSteps*CELL_SIZE/2); - const CVector3D end(numSteps*CELL_SIZE/2,0,numSteps*CELL_SIZE/2); - - glDisable(GL_TEXTURE_2D); - - glDepthMask(0); - glColor4f(0.5f,0.5f,0.5f,0.35f); - glLineWidth(1.0f); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - - glBegin(GL_LINES); - for (i=0;i<=numSteps;i++) { - if (i%8==0) continue; - CVector3D v0(start.X+(i*(end.X-start.X))/float(numSteps),start.Y,start.Z); - glVertex3fv(&v0.X); - - CVector3D v1(v0.X,end.Y,end.Z); - glVertex3fv(&v1.X); - } - - for (i=0;i<=numSteps;i++) { - if (i%8==0) continue; - CVector3D v0(start.X,start.Y,start.Z+(i*(end.Z-start.Z))/float(numSteps)); - glVertex3fv(&v0.X); - - CVector3D v1(end.X,end.Y,v0.Z); - glVertex3fv(&v1.X); - } - glEnd(); - - glDisable(GL_BLEND); - - glColor3f(0,0,0.5); - glLineWidth(2.0f); - - glBegin(GL_LINES); - for (i=0;i<=numSteps;i+=8) { - CVector3D v0(start.X+(i*(end.X-start.X))/float(numSteps),start.Y,start.Z); - glVertex3fv(&v0.X); - - CVector3D v1(v0.X,end.Y,end.Z); - glVertex3fv(&v1.X); - } - - for (i=0;i<=numSteps;i+=8) { - CVector3D v0(start.X,start.Y,start.Z+(i*(end.Z-start.Z))/float(numSteps)); - glVertex3fv(&v0.X); - - CVector3D v1(end.X,end.Y,v0.Z); - glVertex3fv(&v1.X); - } - glEnd(); - glDepthMask(1); -} - -void CEditorData::OnDraw() -{ - if (m_Mode==SCENARIO_EDIT || m_Mode==TEST_MODE) { - g_Renderer.SetClearColor(0); - g_Renderer.BeginFrame(); - - // setup camera - g_Renderer.SetCamera(g_NaviCam.GetCamera()); - - // render base terrain plus models - RenderWorld(); - - if (m_Mode!=TEST_MODE) { - // render the active tool - g_ToolMan.OnDraw(); - } - - // flush prior to rendering overlays .. - g_Renderer.FlushFrame(); - - // .. and here's the overlays - g_MiniMap.Render(); - m_InfoBox.Render(); - } else { - g_Renderer.SetClearColor(0x00453015); - g_Renderer.BeginFrame(); - - CObjectEntry* selobject=g_ObjMan.GetSelectedObject(); - if (selobject && selobject->m_Model) { - // setup camera such that object is in the centre of the viewport - m_ModelMatrix.SetIdentity(); - selobject->m_Model->SetTransform(m_ModelMatrix); - - const CBound& bound=selobject->m_Model->GetBounds(); - CVector3D pt((bound[0].X+bound[1].X)*0.5f,(bound[0].Y+bound[1].Y)*0.5f,bound[0].Z); - - float hfov=tan(DEGTORAD(45)); - float vfov=hfov/g_Renderer.GetAspect(); - float zx=(bound[1].X-bound[0].X)*0.5f/hfov; - float zy=(bound[1].Y-bound[0].Y)*0.5f/vfov; - float z=zx>zy ? zx : zy; - z=(z+1)*2; - - m_ObjectCamera.m_Orientation.SetIdentity(); - m_ObjectCamera.m_Orientation.Translate(pt.X,pt.Y,-z); - - g_Renderer.SetCamera(m_ObjectCamera); - - RenderObEdGrid(); - - g_Renderer.Submit(selobject->m_Model); - } - - // flush prior to rendering overlays .. - g_Renderer.FlushFrame(); - - // .. and here's the overlays - m_InfoBox.Render(); - } - - g_Renderer.EndFrame(); - - // notify info box frame is complete; gives it chance to accumulate stats, check - // fps, etc - m_InfoBox.OnFrameComplete(); -} - - -bool CEditorData::LoadTerrain(const char* filename) -{ - Handle h = tex_load(filename); - if(h<=0) { - char buf[1024]; - sprintf(buf,"Failed to load \"%s\"",filename); - ErrorBox(buf); - return false; - } else { - - int width=0; - int height=0; - int bpp=0; - void *ptr=0; - - int i=tex_info(h, &width, &height, NULL, &bpp, &ptr); - if (i) - { - printf("tex_info error: %d\n",i); - fflush(stdout); - } - - // rescale the texture to fit to the nearest of the 4 possible map sizes - u32 mapsize=9; // assume smallest map - if (width>11*16+1) mapsize=11; - if (width>13*16+1) mapsize=13; - if (width>17*16+1) mapsize=17; - - u32 targetsize=mapsize*16+1; - unsigned char* data=new unsigned char[targetsize*targetsize*bpp/8]; - u32 fmt=(bpp==8) ? GL_RED : ((bpp==24) ? GL_RGB : GL_RGBA); - gluScaleImage(fmt,width,height,GL_UNSIGNED_BYTE,ptr, - targetsize,targetsize,GL_UNSIGNED_BYTE,data); - - // build 16 bit heightmap from red channel of texture - u16* heightmap=new u16[targetsize*targetsize]; - int stride=bpp/8; - u16* hmptr=heightmap; - - // get src of copy - const u8* dataptr = (bpp==8) ? data : ((bpp==24) ? data+2 : data+3); - - // build heightmap - for (uint j=0;j& units=g_UnitMan.GetUnits(); - for (uint i=0;iGetModel()->Update(time); - } - if (m_Mode==TEST_MODE) { - g_EntityManager.updateAll( time ); - } - } else { - CObjectEntry* selobject=g_ObjMan.GetSelectedObject(); - if (selobject && selobject->m_Model) { - selobject->m_Model->Update(time); - } - } -} - -void CEditorData::StartTestMode() -{ - // initialise entities - g_EntityManager.dispatchAll( &CMessage( CMessage::EMSG_INIT ) ); -} - -void CEditorData::StopTestMode() -{ - // make all units idle again - const std::vector& units=g_UnitMan.GetUnits(); - for (uint i=0;iGetObject()->m_IdleAnim) { - units[i]->GetModel()->SetAnimation(units[i]->GetObject()->m_IdleAnim); - } - } + + +#include "EditorData.h" +#include "UIGlobals.h" +#include "ToolManager.h" +#include "ObjectManager.h" +#include "UnitManager.h" +#include "TextureManager.h" +#include "Model.h" +#include "SkeletonAnimManager.h" + +#include "ogl.h" +#include "res/tex.h" +#include "time.h" + +#include "BaseEntityCollection.h" +#include "Entity.h" +#include "EntityHandles.h" +#include "EntityManager.h" + +const int NUM_ALPHA_MAPS = 14; +Handle AlphaMaps[NUM_ALPHA_MAPS]; + +CTerrain g_Terrain; +CLightEnv g_LightEnv; +CMiniMap g_MiniMap; +CEditorData g_EditorData; + +CEditorData::CEditorData() +{ + m_ModelMatrix.SetIdentity(); + m_ScenarioName="Scenario01"; +} + +void CEditorData::SetMode(EditMode mode) +{ + if (m_Mode==TEST_MODE && mode!=TEST_MODE) StopTestMode(); + m_Mode=mode; + if (m_Mode==TEST_MODE) StartTestMode(); +} + +bool CEditorData::InitScene() +{ + // setup default lighting environment + g_LightEnv.m_SunColor=RGBColor(1,1,1); + g_LightEnv.m_Rotation=DEGTORAD(270); + g_LightEnv.m_Elevation=DEGTORAD(45); + g_LightEnv.m_TerrainAmbientColor=RGBColor(0,0,0); + g_LightEnv.m_UnitsAmbientColor=RGBColor(0.4f,0.4f,0.4f); + g_Renderer.SetLightEnv(&g_LightEnv); + + // load the default + if (!LoadTerrain("terrain.raw")) return false; + + // get default texture to apply to terrain + CTextureEntry* texture=0; + for (uint i=0;im_MiniPatches[j][i].Tex1=texture ? texture->m_Handle : 0; + } + } + } + } + + // setup camera + InitCamera(); + + // build the terrain plane + float h=128*HEIGHT_SCALE; + u32 mapSize=g_Terrain.GetVerticesPerSide(); + CVector3D pt0(0,h,0),pt1(float(CELL_SIZE*mapSize),h,0),pt2(0,h,float(CELL_SIZE*mapSize)); + m_TerrainPlane.Set(pt0,pt1,pt2); + m_TerrainPlane.Normalize(); + + return true; +} + + +struct TGAHeader { + // header stuff + unsigned char iif_size; + unsigned char cmap_type; + unsigned char image_type; + unsigned char pad[5]; + + // origin : unused + unsigned short d_x_origin; + unsigned short d_y_origin; + + // dimensions + unsigned short width; + unsigned short height; + + // bits per pixel : 16, 24 or 32 + unsigned char bpp; + + // image descriptor : Bits 3-0: size of alpha channel + // Bit 4: must be 0 (reserved) + // Bit 5: should be 0 (origin) + // Bits 6-7: should be 0 (interleaving) + unsigned char image_descriptor; +}; + +static bool saveTGA(const char* filename,int width,int height,unsigned char* data) +{ + FILE* fp=fopen(filename,"wb"); + if (!fp) return false; + + // fill file header + TGAHeader header; + header.iif_size=0; + header.cmap_type=0; + header.image_type=2; + memset(header.pad,0,sizeof(header.pad)); + header.d_x_origin=0; + header.d_y_origin=0; + header.width=width; + header.height=height; + header.bpp=24; + header.image_descriptor=0; + + if (fwrite(&header,sizeof(TGAHeader),1,fp)!=1) { + fclose(fp); + return false; + } + + // write data + if (fwrite(data,width*height*3,1,fp)!=1) { + fclose(fp); + return false; + } + + // return success .. + fclose(fp); + return true; +} + +void CEditorData::LoadAlphaMaps() +{ + const char* fns[CRenderer::NumAlphaMaps] = { + "art/textures/terrain/alphamaps/special/blendcircle.png", + "art/textures/terrain/alphamaps/special/blendlshape.png", + "art/textures/terrain/alphamaps/special/blendedge.png", + "art/textures/terrain/alphamaps/special/blendedgecorner.png", + "art/textures/terrain/alphamaps/special/blendedgetwocorners.png", + "art/textures/terrain/alphamaps/special/blendfourcorners.png", + "art/textures/terrain/alphamaps/special/blendtwooppositecorners.png", + "art/textures/terrain/alphamaps/special/blendlshapecorner.png", + "art/textures/terrain/alphamaps/special/blendtwocorners.png", + "art/textures/terrain/alphamaps/special/blendcorner.png", + "art/textures/terrain/alphamaps/special/blendtwoedges.png", + "art/textures/terrain/alphamaps/special/blendthreecorners.png", + "art/textures/terrain/alphamaps/special/blendushape.png", + "art/textures/terrain/alphamaps/special/blendbad.png" + }; + + g_Renderer.LoadAlphaMaps(fns); +} + +void CEditorData::InitResources() +{ + g_TexMan.LoadTerrainTextures(); + LoadAlphaMaps(); + g_ObjMan.LoadObjects(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// InitSingletons: create and initialise required singletons +void CEditorData::InitSingletons() +{ + // create terrain related stuff + new CTextureManager; + + // create actor related stuff + new CSkeletonAnimManager; + new CObjectManager; + new CUnitManager; + + // create entity related stuff + new CBaseEntityCollection; + new CEntityManager; + g_EntityTemplateCollection.loadTemplates(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Init: perform one time initialisation of the editor +bool CEditorData::Init() +{ + // create and initialise singletons + InitSingletons(); + + // load default textures + InitResources(); + + // create the scene - terrain, camera, light environment etc + if (!InitScene()) return false; + + // set up an empty minimap + g_MiniMap.Initialise(); + + // set up the info box + m_InfoBox.Initialise(); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Terminate: close down the editor (destroy singletons in reverse order to construction) +void CEditorData::Terminate() +{ + // destroy entity related stuff + delete CEntityManager::GetSingletonPtr(); + delete CBaseEntityCollection::GetSingletonPtr(); + + // destroy actor related stuff + delete CUnitManager::GetSingletonPtr(); + delete CObjectManager::GetSingletonPtr(); + delete CSkeletonAnimManager::GetSingletonPtr(); + + // destroy terrain related stuff + delete CTextureManager::GetSingletonPtr(); +} + +void CEditorData::InitCamera() +{ + g_NaviCam.GetCamera().SetProjection(1.0f,10000.0f,DEGTORAD(90)); + g_NaviCam.GetCamera().m_Orientation.SetIdentity(); +#ifdef TOPDOWNVIEW + g_NaviCam.GetCamera().m_Orientation.RotateX(DEGTORAD(90)); + g_NaviCam.GetCamera().m_Orientation.Translate(CELL_SIZE*250*0.5, 80, CELL_SIZE*250*0.5); +#else + g_NaviCam.GetCamera().m_Orientation.RotateX(DEGTORAD(40)); + g_NaviCam.GetCamera().m_Orientation.RotateY(DEGTORAD(-45)); + g_NaviCam.GetCamera().m_Orientation.Translate(600, 200, 125); +#endif + + OnCameraChanged(); +} + +void CEditorData::OnCameraChanged() +{ + int width=g_Renderer.GetWidth(); + int height=g_Renderer.GetHeight(); + + // resize viewport + SViewPort viewport; + viewport.m_X=0; + viewport.m_Y=0; + viewport.m_Width=width; + viewport.m_Height=height; + g_NaviCam.GetCamera().SetViewPort(&viewport); + + // rebuild object camera + m_ObjectCamera.SetViewPort(&viewport); + m_ObjectCamera.SetProjection(1.0f,10000.0f,DEGTORAD(90)); + + + // recalculate projection matrix + g_NaviCam.GetCamera().SetProjection(1.0f,10000.0f,DEGTORAD(20)); + + // update viewing frustum + g_NaviCam.GetCamera().UpdateFrustum(); + + // calculate intersection of camera stabbing lines with terrain plane + + // get points of back plane of frustum in camera space + float aspect=height>0 ? float(width)/float(height) : 1.0f; + float zfar=g_NaviCam.GetCamera().GetFarPlane(); + CVector3D cPts[4]; + float x=zfar*float(tan(g_NaviCam.GetCamera().GetFOV()*aspect*0.5)); + float y=zfar*float(tan(g_NaviCam.GetCamera().GetFOV()*0.5)); + cPts[0].X=-x; + cPts[0].Y=-y; + cPts[0].Z=zfar; + cPts[1].X=x; + cPts[1].Y=-y; + cPts[1].Z=zfar; + cPts[2].X=x; + cPts[2].Y=y; + cPts[2].Z=zfar; + cPts[3].X=-x; + cPts[3].Y=y; + cPts[3].Z=zfar; + + // transform to world space + CVector3D wPts[4]; + for (int i=0;i<4;i++) { + wPts[i]=g_NaviCam.GetCamera().m_Orientation.Transform(cPts[i]); + } + + // now intersect a ray from the camera through each point + CVector3D rayOrigin=g_NaviCam.GetCamera().m_Orientation.GetTranslation(); + CVector3D rayDir=g_NaviCam.GetCamera().m_Orientation.GetIn(); + + CVector3D hitPt[4]; + for (i=0;i<4;i++) { + CVector3D rayDir=wPts[i]-rayOrigin; + rayDir.Normalize(); + + // get intersection point + m_TerrainPlane.FindRayIntersection(rayOrigin,rayDir,&hitPt[i]); + } + + for (i=0;i<4;i++) { + // convert to minimap space + float px=hitPt[i].X; + float pz=hitPt[i].Z; + g_MiniMap.m_ViewRect[i][0]=(197*px/float(CELL_SIZE*g_Terrain.GetVerticesPerSide())); + g_MiniMap.m_ViewRect[i][1]=197*pz/float(CELL_SIZE*g_Terrain.GetVerticesPerSide()); + } +} + +void CEditorData::RenderTerrain() +{ + CFrustum frustum=g_NaviCam.GetCamera().GetFustum(); + u32 patchesPerSide=g_Terrain.GetPatchesPerSide(); + for (uint j=0; jGetBounds())) { + g_Renderer.Submit(patch); + } + } + } +} + +void CEditorData::OnScreenShot(const char* filename) +{ + g_Renderer.SetClearColor(0); + g_Renderer.BeginFrame(); + g_Renderer.SetCamera(g_NaviCam.GetCamera()); + + RenderWorld(); + + g_Renderer.EndFrame(); + + int width=g_Renderer.GetWidth(); + int height=g_Renderer.GetHeight(); + unsigned char* data=new unsigned char[width*height*3]; + + glReadBuffer(GL_BACK); + glReadPixels(0,0,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,data); + + saveTGA(filename,width,height,data); + + delete[] data; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// SubmitModelRecursive: recurse down given model, submitting it and all it's descendents to the +// renderer +void SubmitModelRecursive(CModel* model) +{ + g_Renderer.Submit(model); + + const std::vector& props=model->GetProps(); + for (uint i=0;i& units=g_UnitMan.GetUnits(); + for (i=0;iGetModel()); + } + + u32 patchesPerSide=g_Terrain.GetPatchesPerSide(); + for (j=0; j& units=g_UnitMan.GetUnits(); + uint i; + for (i=0;iGetModel()->GetBounds())) { + SubmitModelRecursive(units[i]->GetModel()); + } + } +} + +void CEditorData::RenderWorld() +{ + // render terrain + RenderTerrain(); + + // render all the units + RenderModels(); + + // flush prior to rendering overlays etc + g_Renderer.FlushFrame(); +} + +void CEditorData::RenderObEdGrid() +{ + int i; + const int numSteps=32; + const CVector3D start(-numSteps*CELL_SIZE/2,0,-numSteps*CELL_SIZE/2); + const CVector3D end(numSteps*CELL_SIZE/2,0,numSteps*CELL_SIZE/2); + + glDisable(GL_TEXTURE_2D); + + glDepthMask(0); + glColor4f(0.5f,0.5f,0.5f,0.35f); + glLineWidth(1.0f); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_LINES); + for (i=0;i<=numSteps;i++) { + if (i%8==0) continue; + CVector3D v0(start.X+(i*(end.X-start.X))/float(numSteps),start.Y,start.Z); + glVertex3fv(&v0.X); + + CVector3D v1(v0.X,end.Y,end.Z); + glVertex3fv(&v1.X); + } + + for (i=0;i<=numSteps;i++) { + if (i%8==0) continue; + CVector3D v0(start.X,start.Y,start.Z+(i*(end.Z-start.Z))/float(numSteps)); + glVertex3fv(&v0.X); + + CVector3D v1(end.X,end.Y,v0.Z); + glVertex3fv(&v1.X); + } + glEnd(); + + glDisable(GL_BLEND); + + glColor3f(0,0,0.5); + glLineWidth(2.0f); + + glBegin(GL_LINES); + for (i=0;i<=numSteps;i+=8) { + CVector3D v0(start.X+(i*(end.X-start.X))/float(numSteps),start.Y,start.Z); + glVertex3fv(&v0.X); + + CVector3D v1(v0.X,end.Y,end.Z); + glVertex3fv(&v1.X); + } + + for (i=0;i<=numSteps;i+=8) { + CVector3D v0(start.X,start.Y,start.Z+(i*(end.Z-start.Z))/float(numSteps)); + glVertex3fv(&v0.X); + + CVector3D v1(end.X,end.Y,v0.Z); + glVertex3fv(&v1.X); + } + glEnd(); + glDepthMask(1); +} + +void CEditorData::OnDraw() +{ + if (m_Mode==SCENARIO_EDIT || m_Mode==TEST_MODE) { + g_Renderer.SetClearColor(0); + g_Renderer.BeginFrame(); + + // setup camera + g_Renderer.SetCamera(g_NaviCam.GetCamera()); + + // render base terrain plus models + RenderWorld(); + + if (m_Mode!=TEST_MODE) { + // render the active tool + g_ToolMan.OnDraw(); + } + + // flush prior to rendering overlays .. + g_Renderer.FlushFrame(); + + // .. and here's the overlays + g_MiniMap.Render(); + m_InfoBox.Render(); + } else { + g_Renderer.SetClearColor(0x00453015); + g_Renderer.BeginFrame(); + + CObjectEntry* selobject=g_ObjMan.GetSelectedObject(); + if (selobject && selobject->m_Model) { + // setup camera such that object is in the centre of the viewport + m_ModelMatrix.SetIdentity(); + selobject->m_Model->SetTransform(m_ModelMatrix); + + const CBound& bound=selobject->m_Model->GetBounds(); + CVector3D pt((bound[0].X+bound[1].X)*0.5f,(bound[0].Y+bound[1].Y)*0.5f,bound[0].Z); + + float hfov=tan(DEGTORAD(45)); + float vfov=hfov/g_Renderer.GetAspect(); + float zx=(bound[1].X-bound[0].X)*0.5f/hfov; + float zy=(bound[1].Y-bound[0].Y)*0.5f/vfov; + float z=zx>zy ? zx : zy; + z=(z+1)*2; + + m_ObjectCamera.m_Orientation.SetIdentity(); + m_ObjectCamera.m_Orientation.Translate(pt.X,pt.Y,-z); + + g_Renderer.SetCamera(m_ObjectCamera); + + RenderObEdGrid(); + + g_Renderer.Submit(selobject->m_Model); + } + + // flush prior to rendering overlays .. + g_Renderer.FlushFrame(); + + // .. and here's the overlays + m_InfoBox.Render(); + } + + g_Renderer.EndFrame(); + + // notify info box frame is complete; gives it chance to accumulate stats, check + // fps, etc + m_InfoBox.OnFrameComplete(); +} + + +bool CEditorData::LoadTerrain(const char* filename) +{ + Handle h = tex_load(filename); + if(h<=0) { + char buf[1024]; + sprintf(buf,"Failed to load \"%s\"",filename); + ErrorBox(buf); + return false; + } else { + + int width=0; + int height=0; + int bpp=0; + void *ptr=0; + + int i=tex_info(h, &width, &height, NULL, &bpp, &ptr); + if (i) + { + printf("tex_info error: %d\n",i); + fflush(stdout); + } + + // rescale the texture to fit to the nearest of the 4 possible map sizes + u32 mapsize=9; // assume smallest map + if (width>11*16+1) mapsize=11; + if (width>13*16+1) mapsize=13; + if (width>17*16+1) mapsize=17; + + u32 targetsize=mapsize*16+1; + unsigned char* data=new unsigned char[targetsize*targetsize*bpp/8]; + u32 fmt=(bpp==8) ? GL_RED : ((bpp==24) ? GL_RGB : GL_RGBA); + gluScaleImage(fmt,width,height,GL_UNSIGNED_BYTE,ptr, + targetsize,targetsize,GL_UNSIGNED_BYTE,data); + + // build 16 bit heightmap from red channel of texture + u16* heightmap=new u16[targetsize*targetsize]; + int stride=bpp/8; + u16* hmptr=heightmap; + + // get src of copy + const u8* dataptr = (bpp==8) ? data : ((bpp==24) ? data+2 : data+3); + + // build heightmap + for (uint j=0;j& units=g_UnitMan.GetUnits(); + for (uint i=0;iGetModel()->Update(time); + } + if (m_Mode==TEST_MODE) { + g_EntityManager.updateAll( time ); + } + } else { + CObjectEntry* selobject=g_ObjMan.GetSelectedObject(); + if (selobject && selobject->m_Model) { + selobject->m_Model->Update(time); + } + } +} + +void CEditorData::StartTestMode() +{ + // initialise entities + g_EntityManager.dispatchAll( &CMessage( CMessage::EMSG_INIT ) ); +} + +void CEditorData::StopTestMode() +{ + // make all units idle again + const std::vector& units=g_UnitMan.GetUnits(); + for (uint i=0;iGetObject()->m_IdleAnim) { + units[i]->GetModel()->SetAnimation(units[i]->GetObject()->m_IdleAnim); + } + } } \ No newline at end of file diff --git a/source/simulation/BoundingObjects.cpp b/source/simulation/BoundingObjects.cpp index e21dacf4c2..f3234f4400 100755 --- a/source/simulation/BoundingObjects.cpp +++ b/source/simulation/BoundingObjects.cpp @@ -46,7 +46,7 @@ CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy ) void CBoundingObject::setPosition( float x, float y ) { m_pos.x = x; m_pos.y = y; - m_pos += m_offset; + m_pos -= m_offset; } void CBoundingCircle::setRadius( float radius ) @@ -99,6 +99,23 @@ CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, CBoundingBox* setOrientation( u ); } +CBoundingBox::CBoundingBox( float x, float y, float orientation, float width, float height ) +{ + m_type = BOUND_OABB; + setPosition( x, y ); + setDimensions( width, height ); + setOrientation( orientation ); +} + +CBoundingBox::CBoundingBox( float x, float y, float orientation, CBoundingBox* copy ) +{ + m_type = BOUND_OABB; + m_offset = copy->m_offset; + setPosition( x, y ); + setDimensions( copy->getWidth(), copy->getHeight() ); + setOrientation( orientation ); +} + void CBoundingBox::setDimensions( float width, float height ) { m_w = width / 2.0f; diff --git a/source/simulation/BoundingObjects.h b/source/simulation/BoundingObjects.h index 0717855726..0a08ebd8b3 100755 --- a/source/simulation/BoundingObjects.h +++ b/source/simulation/BoundingObjects.h @@ -56,15 +56,9 @@ public: CVector2D m_v; // Unit vector along the direction of this box's width. float m_h; // Half this box's height. float m_w; // Half this box's width. - CBoundingBox( float x, float y, float orientation, float width, float height ) - { - CBoundingBox( x, y, CVector2D( sin( orientation ), cos( orientation ) ), width, height ); - } + CBoundingBox( float x, float y, float orientation, float width, float height ); CBoundingBox( float x, float y, const CVector2D& orientation, float width, float height ); - CBoundingBox( float x, float y, float orientation, CBoundingBox* copy ) - { - CBoundingBox( x, y, CVector2D( sin( orientation ), cos( orientation ) ), copy ); - } + CBoundingBox( float x, float y, float orientation, CBoundingBox* copy ); CBoundingBox( float x, float y, const CVector2D& orientation, CBoundingBox* copy ); void setDimensions( float width, float height ); void setOrientation( float orientation ); diff --git a/source/simulation/Collision.cpp b/source/simulation/Collision.cpp index 7f62b2e686..c981ec1a5e 100755 --- a/source/simulation/Collision.cpp +++ b/source/simulation/Collision.cpp @@ -54,7 +54,7 @@ HEntity getCollisionObject( CEntity* entity, float x, float y ) return( _e ); } -bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, rayIntersectionResults* results ) +bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results ) { std::vector* entities = g_EntityManager.getActive(); std::vector::iterator it; @@ -69,13 +69,17 @@ bool getRayIntersection( const CVector2D& source, const CVector2D& forward, cons for( it = entities->begin(); it != entities->end(); it++ ) { assert( (*it)->m_bounds ); + if( (*it)->m_bounds == destinationCollisionObject ) continue; + // HACK: + if( (*it)->m_bounds->m_type == CBoundingObject::BOUND_OABB ) continue; if( (*it)->m_speed ) continue; CBoundingObject* obj = (*it)->m_bounds; delta = obj->m_pos - source; closestApproach = delta.dot( right ); dist = delta.dot( forward ); + float collisionRadius = maxDistance + obj->m_radius; - if( ( fabs( closestApproach ) < maxDistance + obj->m_radius ) && ( dist > -maxDistance ) && ( dist < length + maxDistance ) ) + if( ( fabs( closestApproach ) < collisionRadius ) && ( dist > collisionRadius * 0.0f ) && ( dist < length - collisionRadius * 0.0f ) ) { if( dist < results->distance ) { diff --git a/source/simulation/Collision.h b/source/simulation/Collision.h index 5a9b4b6637..ca2cfdd951 100755 --- a/source/simulation/Collision.h +++ b/source/simulation/Collision.h @@ -30,6 +30,6 @@ struct rayIntersectionResults HEntity getCollisionObject( CEntity* entity ); HEntity getCollisionObject( CEntity* entity, float x, float y ); CBoundingObject* getContainingObject( const CVector2D& point ); -bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, rayIntersectionResults* results ); +bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results ); #endif diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 7697da1fac..1ca9e8761d 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -17,6 +17,10 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) m_base = base; m_actor = new CUnit(m_base->m_actorObject,m_base->m_actorObject->m_Model->Clone()); + + // HACK: Debugging + // assert( m_base->m_name != CStr( "Waypoint" ) ); + // Register the actor with the renderer. g_UnitMan.AddUnit( m_actor ); @@ -33,13 +37,13 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE ) { - m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle ); + m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle ); } else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB ) { m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box ); } - + snapToGround(); updateActorTransforms(); @@ -141,6 +145,7 @@ void CEntity::update( float timestep ) { case CEntityOrder::ORDER_GOTO_NOPATHING: case CEntityOrder::ORDER_GOTO_COLLISION: + case CEntityOrder::ORDER_GOTO_SMOOTHED: if( processGotoNoPathing( current, timestep ) ) break; return; case CEntityOrder::ORDER_GOTO: @@ -200,21 +205,59 @@ void CEntity::pushOrder( CEntityOrder& order ) void CEntity::render() { // Rich! Help! ;) - // We can loose this later on, I just need a way to see collision boxes temporarily + + // HACK: As in this entire function is a... if( !m_orderQueue.empty() ) { - glShadeModel( GL_FLAT ); - glBegin( GL_LINE_STRIP ); - std::deque::iterator it; + CBoundingObject* destinationCollisionObject; + float x0, y0, x, y; - glVertex3f( m_position.X, m_position.Y + 0.25f /* 20.0f */, m_position.Z ); + x = m_orderQueue.front().m_data[0].location.x; + y = m_orderQueue.front().m_data[0].location.y; for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ ) { - float x = it->m_data[0].location.x; - float y = it->m_data[0].location.y; + if( it->m_type == CEntityOrder::ORDER_PATROL ) + break; + x = it->m_data[0].location.x; + y = it->m_data[0].location.y; + } + destinationCollisionObject = getContainingObject( CVector2D( x, y ) ); + + glShadeModel( GL_FLAT ); + glBegin( GL_LINE_STRIP ); + + + + glVertex3f( m_position.X, m_position.Y + 0.25f, m_position.Z ); + + + x = m_position.X; + y = m_position.Z; + + for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ ) + { + x0 = x; y0 = y; + x = it->m_data[0].location.x; + y = it->m_data[0].location.y; + rayIntersectionResults r; + CVector2D fwd( x - x0, y - y0 ); + float l = fwd.length(); + fwd = fwd.normalize(); + CVector2D rgt = fwd.beta(); + if( getRayIntersection( CVector2D( x0, y0 ), fwd, rgt, l, m_bounds->m_radius, destinationCollisionObject, &r ) ) + { + glEnd(); + glBegin( GL_LINES ); + glColor3f( 1.0f, 0.0f, 0.0f ); + glVertex3f( x0 + fwd.x * r.distance, getExactGroundLevel( x0 + fwd.x * r.distance, y0 + fwd.y * r.distance ) + 0.25f, y0 + fwd.y * r.distance ); + glVertex3f( r.position.x, getExactGroundLevel( r.position.x, r.position.y ) + 0.25f, r.position.y ); + glEnd(); + glBegin( GL_LINE_STRIP ); + glVertex3f( x0, getExactGroundLevel( x0, y0 ), y0 ); + } switch( it->m_type ) { case CEntityOrder::ORDER_GOTO: @@ -222,11 +265,15 @@ void CEntity::render() case CEntityOrder::ORDER_GOTO_COLLISION: glColor3f( 1.0f, 0.5f, 0.5f ); break; case CEntityOrder::ORDER_GOTO_NOPATHING: + case CEntityOrder::ORDER_GOTO_SMOOTHED: glColor3f( 0.5f, 0.5f, 0.5f ); break; + case CEntityOrder::ORDER_PATROL: + glColor3f( 0.0f, 1.0f, 0.0f ); break; default: - glColor3f( 1.0f, 1.0f, 1.0f ); break; + continue; } - glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f /* 20.0f */, y ); + + glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f, y ); } glEnd(); @@ -237,7 +284,7 @@ void CEntity::render() if( getCollisionObject( this ) ) glColor3f( 0.5f, 0.5f, 1.0f ); - m_bounds->render( m_position.Y + 0.25f /* 20.0f */ ); + m_bounds->render( getExactGroundLevel( m_position.X, m_position.Z ) + 0.25f ); //m_position.Y + 0.25f ); } diff --git a/source/simulation/EntityHandles.h b/source/simulation/EntityHandles.h index 783de7bb99..091af56cd7 100755 --- a/source/simulation/EntityHandles.h +++ b/source/simulation/EntityHandles.h @@ -39,6 +39,7 @@ class HEntity { friend class CEntityManager; u16 m_handle; +private: void addRef(); void decRef(); HEntity( u16 index ); diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index af920f5f6a..30a854a8ee 100755 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -6,8 +6,11 @@ // // Usage: All orders at this point use the location component of the union. // Orders are: ORDER_GOTO_NOPATHING: Attempts to reach the given destination via a line-of-sight -// system. Do not create an order of this type directly; it is +// ORDER_GOTO_SMOOTED: system. Do not create an order of these types directly; it is // used to return a path of line segments from the pathfinder. +// _SMOOTHED flags to the entity state-control that it's OK to +// smooth the corner between segments. _NOPATHING just does +// zero-radius turns. // ORDER_GOTO_COLLISION: When the coldet system is trying to get us out of a collision, // it generates these intermediate waypoints. We don't really have // any reason to go to this specific point, so if a better way @@ -45,6 +48,7 @@ public: enum { ORDER_GOTO_NOPATHING, + ORDER_GOTO_SMOOTHED, ORDER_GOTO_COLLISION, ORDER_GOTO, ORDER_PATROL diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index 9b9ad3380d..397e570759 100755 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -14,13 +14,71 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep ) float len = delta.length(); - if( len < 0.1f ) + // Curve smoothing. + // Here there be trig. + + if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED ) { - m_orderQueue.pop_front(); - return( false ); + // We can only really attempt to smooth paths the pathfinder + // has flagged for us. If the turning-radius calculations are + // applied to other types of waypoint, wierdness happens. + // Things like an entity trying to walk to a point inside + // his turning radius (which he can't do directly, so he'll + // orbit the point indefinately), or just massive deviations + // making the paths we calculate useless. + // It's also painful trying to watch two entities resolve their + // collision when they're both bound by turning constraints. + m_ahead = delta / len; + m_orientation = atan2( m_ahead.x, m_ahead.y ); + } + else + { + m_targetorientation = atan2( delta.x, delta.y ); + + float deltatheta = m_targetorientation - m_orientation; + while( deltatheta > PI ) deltatheta -= 2 * PI; + while( deltatheta < -PI ) deltatheta += 2 * PI; + + if( fabs( deltatheta ) > 0.01f ) + { + float maxTurningSpeed = ( m_speed / m_turningRadius ) * timestep; + if( deltatheta > 0 ) + { + m_orientation += MIN( deltatheta, maxTurningSpeed ); + } + else + m_orientation += MAX( deltatheta, -maxTurningSpeed ); + + m_ahead.x = sin( m_orientation ); + m_ahead.y = cos( m_orientation ); + } + else + { + m_ahead = delta / len; + m_orientation = atan2( m_ahead.x, m_ahead.y ); + } } - m_ahead = delta / len; + if( len < 0.1f ) + { + if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION ) + { + // Repath. + CVector2D destination; + while( !m_orderQueue.empty() && + ( ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_COLLISION ) + || ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_NOPATHING ) + || ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_SMOOTHED ) ) ) + { + destination = m_orderQueue.front().m_data[0].location; + m_orderQueue.pop_front(); + } + g_Pathfinder.requestPath( me, destination ); + } + else + m_orderQueue.pop_front(); + return( false ); + } if( m_bounds->m_type == CBoundingObject::BOUND_OABB ) ((CBoundingBox*)m_bounds)->setOrientation( m_ahead ); @@ -78,7 +136,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep ) avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION; CVector2D right; right.x = m_ahead.y; right.y = -m_ahead.x; - CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius * 2.5f ); + CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f ); avoidance.m_data[0].location = avoidancePosition; if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION ) m_orderQueue.pop_front(); @@ -91,12 +149,27 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep ) { // A circle. // TODO: Implement this properly. - // Try turning right. + // Work out if our path goes to the left or to the right + // of this obstacle. Go that way. + // Weight a little to the right, too (helps unit-unit collisions) + CEntityOrder avoidance; avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION; CVector2D right; right.x = m_ahead.y; right.y = -m_ahead.x; - CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius * 2.5f ); + CVector2D avoidancePosition; + + if( ( collide->m_bounds->m_pos - m_bounds->m_pos ).dot( right ) < 1 ) + { + // Turn right. + avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f ); + } + else + { + // Turn left. + avoidancePosition = collide->m_bounds->m_pos - right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f ); + } + avoidance.m_data[0].location = avoidancePosition; if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION ) m_orderQueue.pop_front(); diff --git a/source/simulation/PathfindSparse.cpp b/source/simulation/PathfindSparse.cpp index 39ecaf7614..8b4b849d15 100755 --- a/source/simulation/PathfindSparse.cpp +++ b/source/simulation/PathfindSparse.cpp @@ -1,7 +1,5 @@ #include "PathfindSparse.h" -#define NODESMOOTH_STEPS 4 - sparsePathTree::sparsePathTree( const CVector2D& _from, const CVector2D& _to, HEntity _entity, CBoundingObject* _destinationCollisionObject ) { from = _from; to = _to; @@ -13,6 +11,7 @@ sparsePathTree::sparsePathTree( const CVector2D& _from, const CVector2D& _to, HE rightPre = NULL; rightPost = NULL; type = SPF_OPEN_UNVISITED; leftImpossible = false; rightImpossible = false; + nextSubtree = 0; } sparsePathTree::~sparsePathTree() @@ -31,46 +30,58 @@ bool sparsePathTree::slice() CVector2D forward = to - from; float len = forward.length(); - forward /= len; - CVector2D right = CVector2D( forward.y, -forward.x ); - // Hit nothing or hit destination; that's OK. - if( !getRayIntersection( from, forward, right, len, entity->m_bounds->m_radius * 1.1f, &r ) || ( r.boundingObject == destinationCollisionObject ) ) + assert( len != 0.0f ); + + forward /= len; + CVector2D v_right = CVector2D( forward.y, -forward.x ); + + if( !getRayIntersection( from, forward, v_right, len, entity->m_bounds->m_radius * 1.1f, destinationCollisionObject, &r ) ) { type = SPF_CLOSED_DIRECT; return( true ); } - float turningRadius = ( entity->m_bounds->m_radius + r.boundingObject->m_radius ) * 1.1f; + float turningRadius = ( entity->m_bounds->m_radius + r.boundingObject->m_radius ) * 1.1f; if( turningRadius < entity->m_turningRadius ) turningRadius = entity->m_turningRadius; // Too close, an impossible turn - if( r.distance < turningRadius || - r.distance > ( len - turningRadius ) ) + if( r.distance < turningRadius ) { - type = SPF_IMPOSSIBLE; - return( true ); + // Too close to make a proper turn; try dodging immediately a long way to the left or right. + left = from - v_right * r.boundingObject->m_radius * 2.5f; + right = from + v_right * r.boundingObject->m_radius * 2.5f; } + else if( r.distance > ( len - turningRadius ) ) + { + // Again, too close to avoid it properly. Try approaching the goal from the left or right. + left = to - v_right * r.boundingObject->m_radius * 2.5f; + right = to + v_right * r.boundingObject->m_radius * 2.5f; + } + else + { + // Dodge to the left or right of the obstacle. + // A distance of offsetDistance is sufficient to guarantee we'll make the turn. - CVector2D delta = r.position - from; - float length = delta.length(); + CVector2D delta = r.position - from; + float length = delta.length(); - float offsetDistance = ( turningRadius * length / sqrt( length * length - turningRadius * turningRadius ) ); - + float offsetDistance = ( turningRadius * length / sqrt( length * length - turningRadius * turningRadius ) ); + left = r.position - v_right * offsetDistance; + right = r.position + v_right * offsetDistance; + } favourLeft = false; if( r.closestApproach < 0 ) favourLeft = true; - + // First we path to the left... - - left = r.position - right * offsetDistance; + leftPre = new sparsePathTree( from, left, entity, destinationCollisionObject ); leftPost = new sparsePathTree( left, to, entity, destinationCollisionObject ); // Then we path to the right... - right = r.position + right * offsetDistance; rightPre = new sparsePathTree( from, right, entity, destinationCollisionObject ); rightPost = new sparsePathTree( right, to, entity, destinationCollisionObject ); @@ -87,24 +98,18 @@ bool sparsePathTree::slice() else /* type == SPF_OPEN_PROCESSING */ { bool done = false; - if( !leftImpossible ) + while( !done ) { - if( !done && ( leftPre->type & SPF_OPEN ) ) - done |= leftPre->slice(); - if( !done && ( leftPost->type & SPF_OPEN ) ) - done |= leftPost->slice(); - if( ( leftPre->type == SPF_IMPOSSIBLE ) || ( leftPost->type == SPF_IMPOSSIBLE ) ) - leftImpossible = true; - } - if( !rightImpossible && !done ) - { - if( !done && ( rightPre->type & SPF_OPEN ) ) - done |= rightPre->slice(); - if( !done && ( rightPost->type & SPF_OPEN ) ) - done |= rightPost->slice(); - if( ( rightPre->type == SPF_IMPOSSIBLE ) || ( rightPost->type == SPF_IMPOSSIBLE ) ) - rightImpossible = true; + if( subtrees[nextSubtree]->type & SPF_OPEN ) + if( subtrees[nextSubtree]->slice() ) + done = true; + nextSubtree++; + nextSubtree %= 4; } + if( ( leftPre->type == SPF_IMPOSSIBLE ) || ( leftPost->type == SPF_IMPOSSIBLE ) ) + leftImpossible = true; + if( ( rightPre->type == SPF_IMPOSSIBLE ) || ( rightPost->type == SPF_IMPOSSIBLE ) ) + rightImpossible = true; if( leftImpossible && rightImpossible ) { type = SPF_IMPOSSIBLE; @@ -149,14 +154,11 @@ void sparsePathTree::pushResults( std::vector& nodelist ) void nodeSmooth( HEntity entity, std::vector& nodelist ) { - // All your CPU are belong to us. - // But Jan wanted it ;) - std::vector::iterator it; CVector2D next = nodelist.front(); CEntityOrder node; - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; + node.m_type = CEntityOrder::ORDER_GOTO_SMOOTHED; node.m_data[0].location = next; entity->m_orderQueue.push_front( node ); @@ -173,30 +175,30 @@ void nodeSmooth( HEntity entity, std::vector& nodelist ) CVector2D ubar = u.beta(); CVector2D vbar = v.beta(); float alpha = entity->m_turningRadius * ( ubar - vbar ).length() / ( u + v ).length(); - u *= alpha; - v *= alpha; - - for( int t = NODESMOOTH_STEPS; t >= 0; t-- ) - { - float lambda = t / (float)NODESMOOTH_STEPS; - CVector2D arcpoint = current + v * lambda * lambda - u * ( 1 - lambda ) * ( 1 - lambda ); - node.m_data[0].location = arcpoint; - entity->m_orderQueue.push_front( node ); - } - + node.m_data[0].location = current - u * alpha; + entity->m_orderQueue.push_front( node ); next = current; } + + // If we try to apply turning constraints to getting onto this path, there's a reasonable + // risk the entity will deviate so far from the first path segment that the path becomes + // unwalkable for it. + entity->m_orderQueue.front().m_type = CEntityOrder::ORDER_GOTO_NOPATHING; } void pathSparse( HEntity entity, CVector2D destination ) { std::vector pathnodes; - sparsePathTree sparseEngine( CVector2D( entity->m_position.X, entity->m_position.Z ), destination, entity, getContainingObject( destination ) ); + CVector2D source( entity->m_position.X, entity->m_position.Z ); + sparsePathTree sparseEngine( source, destination, entity, getContainingObject( destination ) ); while( sparseEngine.type & sparsePathTree::SPF_OPEN ) sparseEngine.slice(); + assert( sparseEngine.type & sparsePathTree::SPF_SOLVED ); // Shouldn't be any impossible cases yet. + if( sparseEngine.type & sparsePathTree::SPF_SOLVED ) { sparseEngine.pushResults( pathnodes ); + pathnodes.push_back( source ); nodeSmooth( entity, pathnodes ); } else diff --git a/source/simulation/PathfindSparse.h b/source/simulation/PathfindSparse.h index 25fbd7e03b..d56e324a4b 100755 --- a/source/simulation/PathfindSparse.h +++ b/source/simulation/PathfindSparse.h @@ -47,10 +47,18 @@ struct sparsePathTree bool rightImpossible; CVector2D right; bool favourLeft; - sparsePathTree* leftPre; - sparsePathTree* leftPost; - sparsePathTree* rightPre; - sparsePathTree* rightPost; + union + { + struct + { + sparsePathTree* leftPre; + sparsePathTree* leftPost; + sparsePathTree* rightPre; + sparsePathTree* rightPost; + }; + sparsePathTree* subtrees[4]; + }; + unsigned short nextSubtree; sparsePathTree( const CVector2D& from, const CVector2D& to, HEntity entity, CBoundingObject* destinationCollisionObject ); ~sparsePathTree(); bool slice();