0ad/source/tools/sced/EditorData.cpp

622 lines
15 KiB
C++

#include "precompiled.h"
#include "MathUtil.h"
#include "EditorData.h"
#include "ui/UIGlobals.h"
#include "ToolManager.h"
#include "ObjectManager.h"
#include "UnitManager.h"
#include "TextureManager.h"
#include "TextureEntry.h"
#include "Model.h"
#include "SkeletonAnimManager.h"
#include "Unit.h"
#include "ogl.h"
#include "lib/res/graphics/tex.h"
#include "time.h"
#include "BaseEntityCollection.h"
#include "Entity.h"
#include "EntityHandles.h"
#include "EntityManager.h"
#include "ConfigDB.h"
#include "Scheduler.h"
#include "Loader.h"
#include "XML/XML.h"
#include "Game.h"
#include "GameAttributes.h"
const int NUM_ALPHA_MAPS = 14;
Handle AlphaMaps[NUM_ALPHA_MAPS];
extern 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("temp/terrain.png")) return false;
// get default texture to apply to terrain
CTextureEntry* texture=g_TexMan.FindTexture("aaa.dds");
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
// cover entire terrain with default texture
u32 patchesPerSide=terrain->GetPatchesPerSide();
for (uint pj=0; pj<patchesPerSide; pj++) {
for (uint pi=0; pi<patchesPerSide; pi++) {
CPatch* patch=terrain->GetPatch(pi,pj);
for (int j=0;j<PATCH_SIZE;j++) {
for (int i=0;i<PATCH_SIZE;i++) {
patch->m_MiniPatches[j][i].Tex1=texture ? texture->GetHandle() : 0;
}
}
}
}
// setup camera
InitCamera();
// build the terrain plane
float h=128*HEIGHT_SCALE;
u32 mapSize=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;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Init: perform one time initialisation of the editor
bool CEditorData::Init()
{
// Set attributes for the game
g_GameAttributes.m_MapFile = L""; // start without a map
for (int i=1; i<8; ++i)
g_GameAttributes.GetSlot(i)->AssignLocal();
// Set up the actual game
g_Game = new CGame();
PSRETURN ret = g_Game->StartGame(&g_GameAttributes);
if (ret != PSRETURN_OK)
{
// Failed to start the game
delete g_Game;
g_Game = NULL;
return false;
}
LDR_NonprogressiveLoad();
// 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()
{
}
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 (int i=0;i<4;i++) {
CVector3D rayDir=wPts[i]-rayOrigin;
rayDir.Normalize();
// get intersection point
m_TerrainPlane.FindRayIntersection(rayOrigin,rayDir,&hitPt[i]);
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
for (int 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*terrain->GetVerticesPerSide()));
g_MiniMap.m_ViewRect[i][1]=197*pz/float(CELL_SIZE*terrain->GetVerticesPerSide());
}
}
void CEditorData::RenderTerrain()
{
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CFrustum frustum=g_NaviCam.GetCamera().GetFrustum();
u32 patchesPerSide= g_Game->GetWorld()->GetTerrain()->GetPatchesPerSide();
for (uint j=0; j<patchesPerSide; j++) {
for (uint i=0; i<patchesPerSide; i++) {
CPatch* patch=terrain->GetPatch(i,j);
if (frustum.IsBoxVisible (CVector3D(0,0,0),patch->GetBounds())) {
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 its descendants to the
// renderer
void SubmitModelRecursive(CModel* model)
{
g_Renderer.Submit(model);
const std::vector<CModel::Prop>& props=model->GetProps();
for (uint i=0;i<props.size();i++) {
SubmitModelRecursive(props[i].m_Model);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
// RenderNoCull: render absolutely everything to a blank frame to force renderer
// to load required assets
void CEditorData::RenderNoCull()
{
g_Renderer.BeginFrame();
g_Renderer.SetCamera(g_NaviCam.GetCamera());
uint i,j;
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
for (i=0;i<units.size();++i) {
SubmitModelRecursive(units[i]->GetModel());
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
u32 patchesPerSide=terrain->GetPatchesPerSide();
for (j=0; j<patchesPerSide; j++) {
for (i=0; i<patchesPerSide; i++) {
CPatch* patch=terrain->GetPatch(i,j);
g_Renderer.Submit(patch);
}
}
g_Renderer.FlushFrame();
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
g_Renderer.EndFrame();
}
void CEditorData::RenderModels()
{
CFrustum frustum=g_NaviCam.GetCamera().GetFrustum();
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
uint i;
for (i=0;i<units.size();++i) {
if (frustum.IsBoxVisible (CVector3D(0,0,0),units[i]->GetModel()->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(0x00000000);
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();
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
for (size_t i=0;i<units.size();++i)
if (units[i]->GetEntity())
units[i]->GetEntity()->renderSelectionOutline();
} 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)
{
Tex t;
if(tex_load(filename, &t) < 0)
{
char buf[1024];
sprintf(buf,"Failed to load \"%s\"",filename);
ErrorBox(buf);
return false;
}
uint width=t.w, height=t.h, bpp=t.bpp;
void *ptr=tex_get_data(&t);
// rescale the texture to fit to the nearest of the 4 possible map sizes
u32 mapsize=9; // assume smallest map
if (width>11*PATCH_SIZE+1) mapsize=11;
if (width>13*PATCH_SIZE+1) mapsize=13;
if (width>17*PATCH_SIZE+1) mapsize=17;
u32 targetsize=mapsize*PATCH_SIZE+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<targetsize;++j) {
for (uint i=0;i<targetsize;++i) {
*hmptr=(*dataptr) << 8;
hmptr++;
dataptr+=stride;
}
}
// rebuild terrain
g_Game->GetWorld()->GetTerrain()->Resize(mapsize);
g_Game->GetWorld()->GetTerrain()->SetHeightMap(heightmap);
// clean up
delete[] data;
delete[] heightmap;
(void)tex_free(&t);
// re-initialise minimap - terrain size may have changed
g_MiniMap.Initialise();
return true;
}
// UpdateWorld: update time dependent data in the world to account for changes over
// the given time (in s)
void CEditorData::UpdateWorld(float time)
{
if (m_Mode==SCENARIO_EDIT || m_Mode==TEST_MODE) {
g_EntityManager.interpolateAll(0.f);
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
for (uint i=0;i<units.size();++i) {
units[i]->GetModel()->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.InitializeAll();
}
void CEditorData::StopTestMode()
{
// make all units idle again
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
for (uint i=0;i<units.size();++i) {
units[i]->SetRandomAnimation("idle");
}
}