Entity.cpp 9.37 KB
Newer Older
1 2 3 4 5 6 7 8
#include "StdInc.h"
#include "Entity.h"

#include "Game.h"

namespace krt
{

9
Entity::Entity(Game* ourGame)
10
{
11
	this->ourGame = ourGame;
12

13
	LIST_INSERT(ourGame->activeEntities.root, this->gameNode);
14

15
	this->modelID = -1;
16

17
	this->interiorId = 0;
18

19 20
	this->matrix.setIdentity();
	this->hasLocalMatrix = false;
21

22 23 24 25 26
	// Initialize the flags.
	this->isUnderwater            = false;
	this->isTunnelObject          = false;
	this->isTunnelTransition      = false;
	this->isUnimportantToStreamer = false;
27

28 29 30
	// Initialize LOD things.
	this->higherQualityEntity = NULL;
	this->lowerQualityEntity  = NULL;
31

32
	this->rwObject = NULL;
33

34 35 36
	this->onWorld = NULL; // we are not part of a world.

	this->cachedBoundSphereRadius = 400.0f; // TODO: cache the real bounding sphere radius in some file so we dont need the model
37 38 39

	this->lodChildrenCount = 0;
	this->lodChildrenDrawn = 0;
40 41
}

42
Entity::~Entity(void)
43
{
44 45
	// Make sure we released our RW object.
	this->DeleteRWObject();
46

47 48 49 50
	// Make sure we are not references anywhere anymore.
	{
		this->RemoveEntityFromWorldSectors();
	}
51

52 53
	// Remove us from any world.
	this->LinkToWorld(NULL);
54

55 56 57 58 59 60
	// Check whether we are linked as LOD or have a LOD.
	// Unlink those relationships.
	{
		if (Entity* highLOD = this->higherQualityEntity)
		{
			highLOD->lowerQualityEntity = NULL;
61

62 63
			this->higherQualityEntity = NULL;
		}
64

65 66 67
		if (Entity* lowLOD = this->lowerQualityEntity)
		{
			lowLOD->higherQualityEntity = NULL;
68

69 70 71
			this->lowerQualityEntity = NULL;
		}
	}
72

73 74
	// Remove us from the game.
	LIST_REMOVE(this->gameNode);
75 76
}

77
void Entity::SetModelIndex(streaming::ident_t modelID)
78
{
79 80 81
	// Make sure we have no RW object when switching models
	// This is required because models are not entirely thread-safe if not following certain rules.
	assert(this->rwObject == NULL);
82

83
	this->modelID = modelID;
84 85
}

86
static rw::Frame* RwObjectGetFrame(rw::Object* rwObj)
87
{
88
	rw::uint8 objType = rwObj->type;
89

90 91 92
	if (objType == rw::Atomic::ID)
	{
		rw::Atomic* atomic = (rw::Atomic*)rwObj;
93

94 95 96 97 98
		return atomic->getFrame();
	}
	else if (objType == rw::Clump::ID)
	{
		rw::Clump* clump = (rw::Clump*)rwObj;
99

100 101
		return clump->getFrame();
	}
102

103
	return NULL;
104 105
}

106
bool Entity::CreateRWObject(void)
107
{
108
	streaming::ident_t modelID = this->modelID;
109

110 111
	if (modelID == -1)
		return false;
112

113 114 115
	// If we already have an atomic, we refuse to update.
	if (this->rwObject)
		return false;
116

117 118
	// Fetch a model from the model manager.
	Game* game = theGame;
119

120
	ModelManager::ModelResource* modelEntry = game->GetModelManager().GetModelByID(modelID);
121

122 123
	if (!modelEntry)
		return false;
124

125
	rw::Object* rwobj = modelEntry->CloneModel();
126

127 128
	if (!rwobj)
		return false;
129

130 131 132 133
	// Make sure our RW object has a frame.
	// This is because we will render it.
	{
		rw::uint8 objType = rwobj->type;
134

135 136 137
		if (objType == rw::Atomic::ID)
		{
			rw::Atomic* atomic = (rw::Atomic*)rwobj;
138

139 140 141
			if (atomic->getFrame() == NULL)
			{
				rw::Frame* parentFrame = rw::Frame::create();
142

143 144 145 146 147 148
				atomic->setFrame(parentFrame);
			}
		}
		else if (objType == rw::Clump::ID)
		{
			rw::Clump* clump = (rw::Clump*)rwobj;
149

150 151 152
			if (clump->getFrame() == NULL)
			{
				rw::Frame* parentFrame = rw::Frame::create();
153

154 155 156 157
				clump->setFrame(parentFrame);
			}
		}
	}
158

159
	this->rwObject = rwobj;
160

161 162 163 164 165
	// If we have a local matrix, then set it into our frame.
	{
		if (this->hasLocalMatrix)
		{
			rw::Frame* parentFrame = RwObjectGetFrame(rwobj);
166

167 168 169 170
			if (parentFrame)
			{
				parentFrame->matrix = this->matrix;
				parentFrame->updateObjects();
171

172 173 174 175
				this->hasLocalMatrix = false;
			}
		}
	}
176

177
	return true;
178 179
}

180
void Entity::DeleteRWObject(void)
181
{
182
	rw::Object* rwobj = this->rwObject;
183

184 185
	if (!rwobj)
		return;
186

187
	Game* game = theGame;
188

189
	ModelManager::ModelResource* modelEntry = game->GetModelManager().GetModelByID(this->modelID);
190

191 192
	if (!modelEntry)
		return;
193

194 195 196
	// Store our matrix.
	{
		rw::Frame* parentFrame = RwObjectGetFrame(rwobj);
197

198 199 200
		if (parentFrame)
		{
			this->matrix = parentFrame->matrix;
201

202 203 204
			this->hasLocalMatrix = true;
		}
	}
205

206
	modelEntry->ReleaseModel(rwobj);
207

208
	this->rwObject = NULL;
209 210
}

211
void Entity::SetModelling(const rw::Matrix& mat)
212
{
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
	rw::Object* rwobj = this->rwObject;

	if (rwobj == NULL)
	{
		this->matrix = mat;

		this->hasLocalMatrix = true;
	}
	else
	{
		rw::Frame* parentFrame = RwObjectGetFrame(rwobj);

		assert(parentFrame != NULL);

		if (parentFrame)
		{
			parentFrame->matrix = mat;
			parentFrame->updateObjects(); // that's kinda how RW works; you say that you updated the struct.
		}
	}
233 234
}

235
const rw::Matrix& Entity::GetModelling(void) const
236
{
237
	rw::Object* rwobj = this->rwObject;
238

239 240 241
	if (rwobj)
	{
		rw::Frame* parentFrame = RwObjectGetFrame(rwobj);
242

243 244 245 246 247
		if (parentFrame)
		{
			return parentFrame->matrix;
		}
	}
248

249
	return this->matrix;
250 251
}

252
const rw::Matrix& Entity::GetMatrix(void) const
253
{
254
	rw::Object* rwobj = this->rwObject;
255

256 257 258
	if (rwobj)
	{
		rw::Frame* parentFrame = RwObjectGetFrame(rwobj);
259

260 261 262 263 264
		if (parentFrame)
		{
			return *parentFrame->getLTM();
		}
	}
265

266
	return this->matrix;
267 268
}

269
static bool RpClumpCalculateBoundingSphere(rw::Clump* clump, rw::Sphere& sphereOut)
270
{
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
	rw::Sphere tmpSphere;
	bool hasSphere = false;

	rw::clumpForAllAtomics(clump,
	    [&](rw::Atomic* atomic) {
		    rw::Sphere* atomicSphere = atomic->getWorldBoundingSphere();

		    if (atomicSphere)
		    {
			    if (!hasSphere)
			    {
				    tmpSphere = *atomicSphere;

				    hasSphere = true;
			    }
			    else
			    {
				    // Create a new sphere that encloses both spheres.
				    rw::V3d vecHalfDist = rw::scale(rw::sub(atomicSphere->center, tmpSphere.center), 0.5f);

				    // Adjust the radius.
				    float vecHalfDistScalar = rw::length(vecHalfDist);

				    tmpSphere.center = rw::add(tmpSphere.center, vecHalfDist);
				    tmpSphere.radius = std::max(tmpSphere.radius, atomicSphere->radius) + vecHalfDistScalar;
			    }
		    }
		});

	if (!hasSphere)
		return false;

	sphereOut = tmpSphere;
	return true;
305 306
}

307
bool Entity::GetWorldBoundingSphere(rw::Sphere& sphereOut) const
308
{
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	// prefer a collision model
	auto colModel = GetModelInfo()->GetCollisionModel();

	if (colModel)
	{
		sphereOut.center = rw::add(colModel->boundingSphere.center, this->GetMatrix().pos);
		sphereOut.radius = colModel->boundingSphere.radius;

		// if the radius is empty, don't do anything
		if (sphereOut.radius == 0.0f)
		{
			return false;
		}

		return true;
	}

	// try RW objects
327 328 329 330 331 332
	rw::Object* rwObj = this->rwObject;

	if (rwObj == NULL)
	{
		// Even if we have no model we need a bounding sphere.
		const rw::Matrix& curMatrix = this->GetMatrix();
333

334 335
		sphereOut.center = curMatrix.pos;
		sphereOut.radius = this->cachedBoundSphereRadius;
336

337 338
		return true;
	}
339

340
	bool hasSphere = false;
341

342 343 344
	if (rwObj->type == rw::Atomic::ID)
	{
		rw::Atomic* atomic = (rw::Atomic*)rwObj;
345

346
		rw::Sphere* atomicSphere = atomic->getWorldBoundingSphere();
347

348 349 350
		if (atomicSphere)
		{
			sphereOut = *atomicSphere;
351

352 353 354 355 356 357
			hasSphere = true;
		}
	}
	else if (rwObj->type == rw::Clump::ID)
	{
		rw::Clump* clump = (rw::Clump*)rwObj;
358

359 360
		hasSphere = RpClumpCalculateBoundingSphere(clump, sphereOut);
	}
361

362 363 364 365 366
	if (hasSphere)
	{
		// Store the last calculated bounding sphere radius for later.
		this->cachedBoundSphereRadius = sphereOut.radius;
	}
367

368
	return hasSphere;
369 370
}

371
void Entity::LinkToWorld(World* theWorld)
372
{
373 374 375
	if (World* prevWorld = this->onWorld)
	{
		LIST_REMOVE(this->worldNode);
376

377 378
		this->onWorld = NULL;
	}
379

380 381 382
	if (theWorld)
	{
		LIST_INSERT(theWorld->entityList.root, worldNode);
383

384 385
		this->onWorld = theWorld;
	}
386 387
}

388
World* Entity::GetWorld(void)
389
{
390
	return onWorld;
391 392
}

393
void Entity::SetLODEntity(Entity* lodInst)
394
{
395 396 397
	if (Entity* prevLOD = this->lowerQualityEntity)
	{
		prevLOD->higherQualityEntity = NULL;
398

399 400
		this->lowerQualityEntity = NULL;
	}
401

402 403 404 405 406 407
	if (lodInst)
	{
		if (Entity* prevHighLOD = lodInst->higherQualityEntity)
		{
			prevHighLOD->lowerQualityEntity = NULL;
		}
408

409
		this->lowerQualityEntity = lodInst;
410

411
		lodInst->lodChildrenCount++;
412 413
		lodInst->higherQualityEntity = this;
	}
414 415
}

416
Entity* Entity::GetLODEntity(void)
417
{
418
	return this->lowerQualityEntity;
419 420
}

421
bool Entity::IsLowerLODOf(Entity* inst) const
422
{
423
	Entity* entity = this->lowerQualityEntity;
424

425 426 427 428
	while (entity)
	{
		if (entity == inst)
			return true;
429

430 431
		entity = entity->lowerQualityEntity;
	}
432

433
	return false;
434 435
}

436
bool Entity::IsHigherLODOf(Entity* inst) const
437
{
438
	Entity* entity = this->higherQualityEntity;
439

440 441 442 443
	while (entity)
	{
		if (entity == inst)
			return true;
444

445 446 447 448
		entity = entity->higherQualityEntity;
	}

	return false;
449 450
}

451
ModelManager::ModelResource* Entity::GetModelInfo(void) const
452
{
453
	return theGame->GetModelManager().GetModelByID(this->modelID);
454 455
}

456
void Entity::AddEntityWorldSectorReference(EntityReference* refPtr)
457
{
458
	this->worldSectorReferences.push_back(refPtr);
459 460
}

461
void Entity::RemoveEntityFromWorldSectors(void)
462
{
463 464 465
	while (this->worldSectorReferences.empty() == false)
	{
		EntityReference* refPtr = this->worldSectorReferences.front();
466

467 468
		refPtr->Unlink();
	}
469 470
}

471
void Entity::RemoveEntityWorldReference(EntityReference* refPtr)
472
{
473
	auto find_iter = std::find(this->worldSectorReferences.begin(), this->worldSectorReferences.end(), refPtr);
474

475 476
	if (find_iter == this->worldSectorReferences.end())
		return;
477

478
	this->worldSectorReferences.erase(find_iter);
479
}
480
}