jedi-academy/codemp/RMG/RM_Terrain.cpp

518 lines
13 KiB
C++

//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "../qcommon/cm_local.h"
#include "../cgame/tr_types.h"
#include "RM_Headers.h"
//#include "../qcommon/q_imath.h"
#pragma optimize("", off)
void R_LoadDataImage ( const char *name, byte **pic, int *width, int *height);
void R_InvertImage ( byte *data, int width, int height, int depth);
void R_Resample ( byte *source, int swidth, int sheight, byte *dest, int dwidth, int dheight, int components);
void RE_GetModelBounds (refEntity_t *refEnt, vec3_t bounds1, vec3_t bounds2);
static CRMLandScape *rm_landscape;
static CCMLandScape *origin_land;
CRMLandScape::CRMLandScape(void)
{
common = NULL;
mDensityMap = NULL;
}
CRMLandScape::~CRMLandScape(void)
{
if(mDensityMap)
{
Z_Free(mDensityMap);
mDensityMap = NULL;
}
}
void CCGHeightDetails::AddModel(const CRandomModel *hd)
{
if(mNumModels < MAX_RANDOM_MODELS)
{
mTotalFrequency += hd->GetFrequency();
mModels[mNumModels++] = *hd;
}
}
void CRMLandScape::AddModel(const int height, int maxheight, const CRandomModel *hd)
{
int i;
if(maxheight > HEIGHT_RESOLUTION)
{
maxheight = HEIGHT_RESOLUTION;
}
for(i = height; hd->GetModel() && (i < maxheight); i++)
{
mHeightDetails[i].AddModel(hd);
}
}
void CRMLandScape::LoadMiscentDef(const char *td)
{
char miscentDef[MAX_QPATH];
CGenericParser2 parse;
CGPGroup *basegroup, *classes, *items, *model;
CGPValue *pair;
Com_sprintf(miscentDef, MAX_QPATH, "ext_data/RMG/%s.miscents", Info_ValueForKey(td, "miscentDef"));
Com_DPrintf("CG_Terrain: Loading and parsing miscentDef %s.....\n", Info_ValueForKey(td, "miscentDef"));
if(!Com_ParseTextFile(miscentDef, parse))
{
Com_sprintf(miscentDef, MAX_QPATH, "ext_data/arioche/%s.miscents", Info_ValueForKey(td, "miscentDef"));
if(!Com_ParseTextFile(miscentDef, parse))
{
Com_Printf("Could not open %s\n", miscentDef);
return;
}
}
// The whole file....
basegroup = parse.GetBaseParseGroup();
// The root { } struct
classes = basegroup->GetSubGroups();
while(classes)
{
items = classes->GetSubGroups();
while(items)
{
if(!Q_stricmp(items->GetName(), "miscent"))
{
int height, maxheight;
// Height must exist - the rest are optional
height = atol(items->FindPairValue("height", "0"));
maxheight = atol(items->FindPairValue("maxheight", "255"));
model = items->GetSubGroups();
while(model)
{
if(!Q_stricmp(model->GetName(), "model"))
{
CRandomModel hd;
// Set defaults
hd.SetModel("");
hd.SetFrequency(1.0f);
hd.SetMinScale(1.0f);
hd.SetMaxScale(1.0f);
pair = model->GetPairs();
while(pair)
{
if(!Q_stricmp(pair->GetName(), "name"))
{
hd.SetModel(pair->GetTopValue());
}
else if(!Q_stricmp(pair->GetName(), "frequency"))
{
hd.SetFrequency((float)atof(pair->GetTopValue()));
}
else if(!Q_stricmp(pair->GetName(), "minscale"))
{
hd.SetMinScale((float)atof(pair->GetTopValue()));
}
else if(!Q_stricmp(pair->GetName(), "maxscale"))
{
hd.SetMaxScale((float)atof(pair->GetTopValue()));
}
pair = (CGPValue *)pair->GetNext();
}
AddModel(height, maxheight, &hd);
}
model = (CGPGroup *)model->GetNext();
}
}
items = (CGPGroup *)items->GetNext();
}
classes = (CGPGroup *)classes->GetNext();
}
Com_ParseTextFileDestroy(parse);
}
void CG_Decrease(byte *work, float lerp, int *info)
{
int val;
val = *work - origin_land->irand(2, 5);
*work = (byte)Com_Clampi(1, 255, val);
}
void CRMLandScape::CreateRandomDensityMap(byte *density, int width, int height, int seed)
{
// int i, border, inc;
int x, y, count;
// byte *work, *work2;
CArea *area;
vec3_t derxelSize, pos;
ivec3_t dmappos;
byte *hm_map = common->GetHeightMap();
int hm_width = common->GetRealWidth();
int hm_height = common->GetRealHeight();
int xpos, ypos, dx, dy;
byte *densityPos = density;
bool foundUneven;
// Init to linear spread
memset(density, 0, width * height);
/* // Make more prevalent towards the edges
border = Com_Clamp(6, 12, (width + height) >> 4);
for(i = 0; i < border; i++)
{
inc = (border - i + 1) * 9;
// Top line
work = density + i + (i * width);
for(x = i; x < width - i; x++, work++)
{
*work += (byte)common->irand(inc >> 1, inc);
}
// Left and right edges
work = density + i + ((i + 1) * width);
work2 = density + (width - i) + ((i + 1) * width);
for(y = i + 1; y < height - i - 2; y++, work += width, work2 += width)
{
*work += (byte)common->irand(inc >> 1, inc);
*work2 += (byte)common->irand(inc >> 1, inc);
}
// Bottom line
work = density + i + ((height - i - 1) * width);
for(x = i; x < width - i; x++, work++)
{
*work += (byte)common->irand(inc >> 1, inc);
}
}
*/
count = 0;
for(y=0;y<height;y++)
{
for(x=0;x<width;x++,densityPos++)
{
xpos = (x * hm_width / width);
ypos = (y * hm_height / height);
ypos = hm_height - ypos - 1;
if (hm_map[ypos*hm_width + xpos] < 150)
{
continue;
}
foundUneven = false;
for(dx=-4;(dx<=4 && !foundUneven);dx++)
{
for(dy=-4;(dy<=4 && !foundUneven);dy++)
{
if (dx == 0 && dy == 0)
{
continue;
}
if ((xpos+dx) >= 0 && (xpos+dx) < hm_width && (ypos+dy) >= 0 && (ypos+dy) < hm_height)
{
if (hm_map[(ypos+dy)*hm_width + (xpos+dx)] < 190)
{
*densityPos = 205;
count++;
foundUneven = true;
}
}
}
}
}
}
/* FILE *FH;
FH = fopen("c:\o.raw", "wb");
fwrite(hm_map, 1, common->GetRealWidth() * common->GetRealHeight(), FH);
fclose(FH);
FH = fopen("c:\d.raw", "wb");
fwrite(density, 1, width*height, FH);
fclose(FH);
*/
// Reduce severely for any settlements/buildings/objectives
VectorScale(common->GetSize(), 1.0f / width, derxelSize);
origin_land = common;
area = common->GetFirstArea();
while(area)
{
// Skip group types since they encompass to much open area
if ( area->GetType ( ) == AT_GROUP )
{
area = common->GetNextArea();
continue;
}
VectorSubtract(area->GetPosition(), common->GetMins(), pos);
VectorInverseScaleVector(pos, derxelSize, dmappos);
// Damn upside down gensurf
dmappos[1] = height - dmappos[1];
count = ceilf(area->GetRadius() / derxelSize[1]);
while(count > 0)
{
CM_CircularIterate(density, width, height, dmappos[0], dmappos[1], 0, count, NULL, CG_Decrease);
count--;
}
area = common->GetNextArea();
}
}
void CRMLandScape::LoadDensityMap(const char *td)
{
char densityMap[MAX_QPATH];
byte *imageData;
int iWidth, iHeight, seed;
char *ptr;
// Fill in with default values
mDensityMap = (byte *)Z_Malloc(common->GetBlockCount(), TAG_TERRAIN);
memset(mDensityMap, 128, common->GetBlockCount());
// Load in density map (if any)
Com_sprintf(densityMap, MAX_QPATH, "%s", Info_ValueForKey(td, "densityMap"));
if(strlen(densityMap))
{
Com_DPrintf("CG_Terrain: Loading density map %s.....\n", densityMap);
#ifdef DEDICATED
imageData = NULL;
#else
R_LoadDataImage(densityMap, &imageData, &iWidth, &iHeight);
if(imageData)
{
if(strstr(densityMap, "density_"))
{
seed = strtoul(Info_ValueForKey(td, "seed"),&ptr,10);
CreateRandomDensityMap(imageData, iWidth, iHeight, seed);
}
R_Resample(imageData, iWidth, iHeight, mDensityMap, common->GetBlockWidth(), common->GetBlockHeight(), 1);
R_InvertImage(mDensityMap, common->GetBlockWidth(), common->GetBlockHeight(), 1);
Z_Free(imageData);
}
#endif
}
}
CRandomModel *CCGHeightDetails::GetRandomModel(CCMLandScape *land)
{
int seek, i;
seek = land->irand(0, mTotalFrequency);
for(i = 0; i < mNumModels; i++)
{
seek -= mModels[i].GetFrequency();
if(seek <= 0)
{
return(mModels + i);
}
}
assert(0);
return(NULL);
}
#ifndef DEDICATED
void CRMLandScape::Sprinkle(CCMPatch *patch, CCGHeightDetails *hd, int level)
{
int i, count, px, py;
float density;
vec3_t origin, scale, angles, bounds[2];
refEntity_t refEnt;
CRandomModel *rm;
CArea area;
// int areaTypes[] = { AT_BSP, AT_OBJECTIVE };
TCGMiscEnt *data = (TCGMiscEnt *)cl.mSharedMemory;
TCGTrace *td = (TCGTrace *)cl.mSharedMemory;
// memset(&refEnt, 0, sizeof(refEntity_t));
px = patch->GetHeightMapX() / common->GetTerxels();
py = patch->GetHeightMapY() / common->GetTerxels();
// Get a number -5.3f to 5.3f
density = (mDensityMap[px + (common->GetBlockWidth() * py)] - 128) / 24.0f;
// ..and multiply that into the count
count = Round(common->GetPatchScalarSize() * hd->GetAverageFrequency() * Q_powf(2.0f, density) * 0.001);
for(i = 0; i < count; i++)
{
if(!common->irand(0, 10))
{
vec3_t temp;
float average;
rm = hd->GetRandomModel(common);
refEnt.hModel = re.RegisterModel(rm->GetModelName());
refEnt.frame = 0;
RE_GetModelBounds(&refEnt, bounds[0], bounds[1]);
// Calculate the scale using some magic to help ensure that the
// scales are never too different from eachother. Otherwise you
// could get an entity that is really small on one axis but huge
// on another.
temp[0] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
temp[1] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
temp[2] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
// Average of the three random numbers and divide that by two
average = ( ( temp[0] + temp[1] + temp[2] ) / 3) / 2;
// Add in half of the other two numbers and then subtract half the average to prevent.
// any number from going beyond the range. If all three numbers were the same then
// they would remain unchanged after this calculation.
scale[0] = temp[0] + (temp[1]+temp[2]) / 2 - average;
scale[1] = temp[1] + (temp[0]+temp[2]) / 2 - average;
scale[2] = temp[2] + (temp[0]+temp[1]) / 2 - average;
angles[0] = 0.0f;
angles[1] = common->flrand(-M_PI, M_PI);
angles[2] = 0.0f;
VectorCopy(patch->GetMins(), origin);
origin[0] += common->flrand(0.0f, common->GetPatchWidth());
origin[1] += common->flrand(0.0f, common->GetPatchHeight());
// Get above world height
float slope = common->GetWorldHeight(origin, bounds, true);
if (slope > 1.33)
{ // spot has too steep of a slope
continue;
}
if(origin[2] < common->GetWaterHeight())
{
continue;
}
// very that we aren't dropped too low
if (origin[2] < common->CalcWorldHeight(level))
{
continue;
}
// Hack-ariffic, don't allow them to drop below the big player clip brush.
if (origin[2] < 1280 )
{
continue;
}
// FIXME: shouldn't be using a hard-coded 1280 number, only allow to spawn if inside player clip brush?
// if( !(CONTENTS_PLAYERCLIP & VM_Call( cgvm, CG_POINT_CONTENTS )) )
// {
// continue;
// }
// Simple radius check for buildings
/* area.Init(origin, VectorLength(bounds[0]));
if(common->AreaCollision(&area, areaTypes, sizeof(areaTypes) / sizeof(int)))
{
continue;
}*/
// Make sure there is no architecture around - doesn't work for ents though =(
memset(td, sizeof(*td), 0);
VectorCopy(origin, td->mStart);
VectorCopy(bounds[0], td->mMins);
VectorCopy(bounds[1], td->mMaxs);
VectorCopy(origin, td->mEnd);
td->mSkipNumber = -1;
td->mMask = MASK_PLAYERSOLID;
VM_Call( cgvm, CG_TRACE );
if(td->mResult.surfaceFlags & SURF_NOMISCENTS)
{
continue;
}
if(td->mResult.startsolid)
{
// continue;
}
// Get minimum height of area
common->GetWorldHeight(origin, bounds, false);
// Account for relative origin
origin[2] -= bounds[0][2] * scale[2];
origin[2] -= common->flrand(2.0, (bounds[1][2] - bounds[0][2]) / 4);
// Spawn the client model
strcpy(data->mModel, rm->GetModelName());
VectorCopy(origin, data->mOrigin);
VectorCopy(angles, data->mAngles);
VectorCopy(scale, data->mScale);
VM_Call( cgvm, CG_MISC_ENT);
mModelCount++;
}
}
}
#endif // !DEDICATED
void CRMLandScape::SpawnPatchModels(CCMPatch *patch)
{
#ifndef DEDICATED
int i;
CCGHeightDetails *hd;
// Rand_Init(10);
for(i = 0; i < 4; i++)
{
hd = mHeightDetails + patch->GetHeight(i);
if(hd->GetNumModels())
{
Sprinkle(patch, hd, patch->GetHeight(i));
}
}
#endif // !DEDICATED
}
void SpawnPatchModelsWrapper(CCMPatch *patch, void *userdata)
{
CRMLandScape *landscape = (CRMLandScape *)userdata;
landscape->SpawnPatchModels(patch);
}
void RM_CreateRandomModels(int terrainId, const char *terrainInfo)
{
CRMLandScape *landscape;
landscape = rm_landscape = new CRMLandScape;
landscape->SetCommon(cmg.landScape);
Com_DPrintf("CG_Terrain: Creating random models.....\n");
landscape->LoadMiscentDef(terrainInfo);
landscape->LoadDensityMap(terrainInfo);
landscape->ClearModelCount();
CM_TerrainPatchIterate(landscape->GetCommon(), SpawnPatchModelsWrapper, landscape);
Com_DPrintf(".....%d random client models spawned\n", landscape->GetModelCount());
}
void RM_InitTerrain(void)
{
rm_landscape = NULL;
}
void RM_ShutdownTerrain(void)
{
CRMLandScape *landscape;
landscape = rm_landscape;
if(landscape)
{
delete landscape;
rm_landscape = NULL;
}
}
// end
#pragma optimize("", on)