Initial commit.
This commit is contained in:
517
codemp/rmg/rm_terrain.cpp
Normal file
517
codemp/rmg/rm_terrain.cpp
Normal file
@@ -0,0 +1,517 @@
|
||||
//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(!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(!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(!stricmp(pair->GetName(), "name"))
|
||||
{
|
||||
hd.SetModel(pair->GetTopValue());
|
||||
}
|
||||
else if(!stricmp(pair->GetName(), "frequency"))
|
||||
{
|
||||
hd.SetFrequency((float)atof(pair->GetTopValue()));
|
||||
}
|
||||
else if(!stricmp(pair->GetName(), "minscale"))
|
||||
{
|
||||
hd.SetMinScale((float)atof(pair->GetTopValue()));
|
||||
}
|
||||
else if(!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() * 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)
|
||||
Reference in New Issue
Block a user