1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * This file is based on WME.
25  * http://dead-code.org/redir.php?target=wme
26  * Copyright (c) 2003-2013 Jan Nedoma and contributors
27  */
28 
29 #include "common/math.h"
30 #include "common/util.h"
31 #include "engines/wintermute/ad/ad_block.h"
32 #include "engines/wintermute/ad/ad_game.h"
33 #include "engines/wintermute/ad/ad_generic.h"
34 #include "engines/wintermute/ad/ad_geom_ext.h"
35 #include "engines/wintermute/ad/ad_geom_ext_node.h"
36 #include "engines/wintermute/ad/ad_path3d.h"
37 #include "engines/wintermute/ad/ad_path_point3d.h"
38 #include "engines/wintermute/ad/ad_scene.h"
39 #include "engines/wintermute/ad/ad_scene_geometry.h"
40 #include "engines/wintermute/ad/ad_walkplane.h"
41 #include "engines/wintermute/ad/ad_waypoint_group3d.h"
42 #include "engines/wintermute/base/base_file_manager.h"
43 #include "engines/wintermute/base/base_game.h"
44 #include "engines/wintermute/base/base_sprite.h"
45 #include "engines/wintermute/base/file/base_file.h"
46 #include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
47 #include "engines/wintermute/base/gfx/3ds/camera3d.h"
48 #include "engines/wintermute/base/gfx/3ds/light3d.h"
49 #include "engines/wintermute/base/gfx/3ds/loader3ds.h"
50 #include "engines/wintermute/base/gfx/3ds/mesh3ds.h"
51 #include "engines/wintermute/math/math_util.h"
52 #include "engines/wintermute/system/sys_class_registry.h"
53 #include "engines/wintermute/wintermute.h"
54 #include "math/glmath.h"
55 
56 namespace Wintermute {
57 
IMPLEMENT_PERSISTENT(AdSceneGeometry,false)58 IMPLEMENT_PERSISTENT(AdSceneGeometry, false)
59 
60 //////////////////////////////////////////////////////////////////////////
61 AdSceneGeometry::AdSceneGeometry(BaseGame *gameRef) : BaseObject(gameRef) {
62 	_activeCamera = _activeLight = -1;
63 	_viewMatrix.setToIdentity();
64 	//m_WaypointHeight = 5.0f;
65 	//m_WaypointHeight = 1.0f;
66 	_waypointHeight = 10.0f;
67 	_wptMarker = NULL;
68 
69 	_PFReady = true;
70 	_PFTargetPath = NULL;
71 	_PFMaxTime = 15;
72 	_PFRerun = false;
73 
74 	_PFSource = _PFTarget = _PFAlternateTarget = Math::Vector3d(0, 0, 0);
75 	_PFAlternateDist = FLT_MAX;
76 
77 	_drawingViewport.setRect(0, 0, 0, 0);
78 
79 	_lastWorldMat.setToIdentity();
80 	_lastViewMat.setToIdentity();
81 	_lastProjMat.setToIdentity();
82 
83 	_lastOffsetX = _lastOffsetY = 0;
84 	_lastScrollX = _lastScrollY = 0;
85 
86 	_lastValuesInitialized = false;
87 	_maxLightsWarning = false;
88 }
89 
90 //////////////////////////////////////////////////////////////////////////
~AdSceneGeometry()91 AdSceneGeometry::~AdSceneGeometry() {
92 	cleanup();
93 	delete _wptMarker;
94 }
95 
96 //////////////////////////////////////////////////////////////////////////
cleanup()97 void AdSceneGeometry::cleanup() {
98 	uint i;
99 
100 	for (i = 0; i < _planes.size(); i++) {
101 		delete _planes[i];
102 	}
103 	_planes.clear();
104 
105 	for (i = 0; i < _blocks.size(); i++) {
106 		delete _blocks[i];
107 	}
108 	_blocks.clear();
109 
110 	for (i = 0; i < _generics.size(); i++) {
111 		delete _generics[i];
112 	}
113 	_generics.clear();
114 
115 	for (i = 0; i < _waypointGroups.size(); i++) {
116 		delete _waypointGroups[i];
117 	}
118 	_waypointGroups.clear();
119 
120 	for (i = 0; i < _cameras.size(); i++) {
121 		//		CBRenderD3D* _renderer = (CBRenderD3D*)_gameRef->_renderer;
122 		//		if(m_Renderer->m_Camera == _cameras[i]) m_Renderer->m_Camera = NULL;
123 
124 		delete _cameras[i];
125 	}
126 	_cameras.clear();
127 
128 	for (i = 0; i < _lights.size(); i++) {
129 		delete _lights[i];
130 	}
131 	_lights.clear();
132 
133 	_activeCamera = _activeLight = -1;
134 	_viewMatrix.setToIdentity();
135 
136 	for (i = 0; i < _PFPath.size(); i++) {
137 		delete _PFPath[i];
138 	}
139 	_PFPath.clear();
140 
141 	_PFTargetPath = NULL;
142 }
143 
144 //////////////////////////////////////////////////////////////////////////
getGeometryExtension(char * filename)145 AdGeomExt *AdSceneGeometry::getGeometryExtension(char *filename) {
146 	AdGeomExt *ret = new AdGeomExt(_gameRef);
147 
148 	bool loadOK = false;
149 	if (BaseFileManager::getEngineInstance()->openFile(filename) != nullptr) {
150 		loadOK = ret->loadFile(filename);
151 	}
152 
153 	// no ext file found, just use the defaults
154 	if (!loadOK) {
155 		ret->addStandardNodes();
156 	}
157 
158 	return ret;
159 }
160 
161 //////////////////////////////////////////////////////////////////////////
loadFile(const char * filename)162 bool AdSceneGeometry::loadFile(const char *filename) {
163 	cleanup();
164 
165 	// load waypoint graphics from resources
166 	if (!_wptMarker) {
167 		_wptMarker = new BaseSprite(_gameRef);
168 		if (_wptMarker) {
169 			if (!_wptMarker->loadFile("wpt.sprite")) {
170 				delete _wptMarker;
171 				_wptMarker = NULL;
172 			}
173 		}
174 	}
175 
176 	Common::String filenameTmp(filename);
177 
178 	if (!filenameTmp.hasSuffix(".3ds") && !filenameTmp.hasSuffix(".3DS")) {
179 		_gameRef->LOG(0, "Error: no suitable loader found for file '%s'", filename);
180 		return false;
181 	}
182 
183 	filenameTmp.replace(filenameTmp.size() - 3, 3, "geometry", 0, 8);
184 	AdGeomExt *geomExt = getGeometryExtension(filenameTmp.begin());
185 
186 	// Light3D, AdBlock, AdGeneric and AdWalkplane all inherit from BaseScriptable
187 	// the latter one is overriding the new operator such that instances are registered
188 	// in the system class registry
189 	// for the most part, the subclasses of BaseScriptable override the new operator themselves,
190 	// but here this is not the case. So these instances are not supposed to be registered
191 	// and doing so would create faulty savegames. The persistence of them will be handled by AdSceneGeometry
192 	SystemClassRegistry::getInstance()->_disabled = true;
193 
194 	BaseArray<Mesh3DS *> meshes;
195 	BaseArray<Common::String> meshNames;
196 
197 	if (!load3DSFile(filename, meshes, meshNames, _lights, _cameras, _gameRef)) {
198 		delete geomExt;
199 		return false;
200 	}
201 
202 	uint i;
203 
204 	// load meshes
205 	for (i = 0; i < meshes.size(); i++) {
206 		AdGeomExtNode *ExtNode = geomExt->matchName(meshNames[i].c_str());
207 
208 		if (!ExtNode) {
209 			continue;
210 		}
211 
212 		switch (ExtNode->_type) {
213 		case GEOM_WALKPLANE: {
214 			AdWalkplane *plane = new AdWalkplane(_gameRef);
215 			plane->setName(meshNames[i].c_str());
216 			plane->_mesh = meshes[i];
217 			// TODO: These constants are endianness dependent
218 			plane->_mesh->fillVertexBuffer(0xFF0000FF);
219 			plane->_receiveShadows = ExtNode->_receiveShadows;
220 			_planes.add(plane);
221 			} break;
222 
223 		case GEOM_BLOCKED: {
224 			AdBlock *block = new AdBlock(_gameRef);
225 			block->setName(meshNames[i].c_str());
226 			block->_mesh = meshes[i];
227 			block->_mesh->fillVertexBuffer(0xFFFF0000);
228 			block->_receiveShadows = ExtNode->_receiveShadows;
229 			_blocks.add(block);
230 			} break;
231 
232 		case GEOM_WAYPOINT: {
233 			Mesh3DS *mesh = meshes[i];
234 			// TODO: groups
235 			if (_waypointGroups.size() == 0) {
236 				_waypointGroups.add(new AdWaypointGroup3D(_gameRef));
237 			}
238 			_waypointGroups[0]->addFromMesh(mesh);
239 			delete mesh;
240 			} break;
241 
242 		case GEOM_GENERIC: {
243 			AdGeneric *generic = new AdGeneric(_gameRef);
244 			generic->setName(meshNames[i].c_str());
245 			generic->_mesh = meshes[i];
246 			generic->_mesh->fillVertexBuffer(0xFF00FF00);
247 			generic->_receiveShadows = ExtNode->_receiveShadows;
248 			_generics.add(generic);
249 			} break;
250 		}
251 	}
252 
253 	SystemClassRegistry::getInstance()->_disabled = false;
254 
255 	if (_cameras.size() > 0) {
256 		setActiveCamera(0, -1.0f, -1.0f, -1.0f);
257 	}
258 	createLights();
259 
260 	if (_lights.size() > 0) {
261 		uint activeLight = 0;
262 		setActiveLight(activeLight);
263 	}
264 
265 	delete geomExt;
266 
267 	// drop waypoints to the ground
268 	dropWaypoints();
269 
270 	if (getFilename() != filename) {
271 		setFilename(filename);
272 	}
273 
274 	return true;
275 }
276 
277 //////////////////////////////////////////////////////////////////////////
dropWaypoints()278 bool AdSceneGeometry::dropWaypoints() {
279 	for (uint i = 0; i < _waypointGroups.size(); i++) {
280 		for (uint j = 0; j < _waypointGroups[i]->_points.size(); j++) {
281 			Math::Vector3d *point = _waypointGroups[i]->_points[j];
282 			point->y() = getHeightAt(*point) + _waypointHeight;
283 		}
284 	}
285 	return true;
286 }
287 
288 //////////////////////////////////////////////////////////////////////////
setActiveCamera(int camera,float fov,float nearClipPlane,float farClipPlane)289 bool AdSceneGeometry::setActiveCamera(int camera, float fov, float nearClipPlane, float farClipPlane) {
290 	if (camera < 0 || static_cast<uint>(camera) >= _cameras.size()) {
291 		_gameRef->LOG(0, "Warning: Camera %d is out of bounds.", camera);
292 		return false;
293 	} else {
294 		_activeCamera = camera;
295 
296 		if (fov >= 0.0f) {
297 			_cameras[camera]->_fov = fov;
298 		} else {
299 			_cameras[camera]->_fov = _cameras[camera]->_originalFOV;
300 		}
301 
302 		_cameras[camera]->_nearClipPlane = nearClipPlane;
303 		_cameras[camera]->_farClipPlane = farClipPlane;
304 
305 		_cameras[camera]->getViewMatrix(&_viewMatrix);
306 		return true;
307 	}
308 }
309 
310 //////////////////////////////////////////////////////////////////////////
setActiveCamera(const char * camera,float fov,float nearClipPlane,float farClipPlane)311 bool AdSceneGeometry::setActiveCamera(const char *camera, float fov, float nearClipPlane, float farClipPlane) {
312 	for (uint i = 0; i < _cameras.size(); i++) {
313 		if (scumm_stricmp(_cameras[i]->getName(), camera) == 0)
314 			return setActiveCamera(i, fov, nearClipPlane, farClipPlane);
315 	}
316 
317 	_gameRef->LOG(0, "Warning: Camera '%s' not found.", camera);
318 	return false;
319 }
320 
321 //////////////////////////////////////////////////////////////////////////
getActiveCamera()322 Camera3D *AdSceneGeometry::getActiveCamera() {
323 	if (_activeCamera >= 0 && static_cast<uint>(_activeCamera) < _cameras.size()) {
324 		return _cameras[_activeCamera];
325 	} else {
326 		return nullptr;
327 	}
328 }
329 
330 //////////////////////////////////////////////////////////////////////////
setActiveLight(int light)331 bool AdSceneGeometry::setActiveLight(int light) {
332 	if (light < 0 || static_cast<uint>(light) >= _lights.size()) {
333 		_gameRef->LOG(0, "Warning: Light %d is out of bounds.", light);
334 		return false;
335 	} else {
336 		_activeLight = light;
337 		return true;
338 	}
339 }
340 
341 //////////////////////////////////////////////////////////////////////////
setActiveLight(char * light)342 bool AdSceneGeometry::setActiveLight(char *light) {
343 	for (uint i = 0; i < _lights.size(); i++) {
344 		if (scumm_stricmp(_lights[i]->getName(), light) == 0) {
345 			return setActiveLight(i);
346 		}
347 	}
348 
349 	_gameRef->LOG(0, "Warning: Light '%s' not found.", light);
350 	return false;
351 }
352 
353 //////////////////////////////////////////////////////////////////////////
getViewMatrix()354 Math::Matrix4 *AdSceneGeometry::getViewMatrix() {
355 	return &_viewMatrix;
356 }
357 
358 //////////////////////////////////////////////////////////////////////////
storeDrawingParams()359 bool AdSceneGeometry::storeDrawingParams() {
360 	// implement this later
361 	//	CBRenderD3D* m_Renderer = (CBRenderD3D*)_gameRef->m_Renderer;
362 
363 	//  // store values
364 	//	m_Renderer->m_Device->GetViewport(&m_DrawingViewport);
365 
366 	//	m_Renderer->m_Device->GetTransform(D3DTS_WORLD, &m_LastWorldMat);
367 	//	m_Renderer->m_Device->GetTransform(D3DTS_VIEW, &m_LastViewMat);
368 	//	m_Renderer->m_Device->GetTransform(D3DTS_PROJECTION, &m_LastProjMat);
369 
370 	warning("AdSceneGeometry::storeDrawingParams not yet implemented");
371 
372 	AdScene *scene = ((AdGame *)_gameRef)->_scene;
373 	if (scene) {
374 		_lastScrollX = scene->getOffsetLeft();
375 		_lastScrollY = scene->getOffsetTop();
376 	} else {
377 		_lastScrollX = 0;
378 		_lastScrollY = 0;
379 	}
380 
381 	Rect32 rc;
382 	_gameRef->getCurrentViewportRect(&rc);
383 //	float width = (float)rc.right - (float)rc.left;
384 //	float height = (float)rc.bottom - (float)rc.top;
385 
386 	// margins
387 	//	int mleft = rc.left;
388 	//	int mright = m_Renderer->m_Width - Width - rc.left;
389 	//	int mtop = rc.top;
390 	//	int mbottom = m_Renderer->m_Height - Height - rc.top;
391 
392 	//	m_LastOffsetX = _gameRef->_offsetX + (mleft - mright)/2;
393 	//	m_LastOffsetY = _gameRef->_offsetY + (mtop - mbottom)/2;
394 
395 	_lastValuesInitialized = true;
396 
397 	return true;
398 }
399 
400 //////////////////////////////////////////////////////////////////////////
render(bool render)401 bool AdSceneGeometry::render(bool render) {
402 	//	store values
403 	//	StoreDrawingParams();
404 	if (render) {
405 		_gameRef->_renderer3D->renderSceneGeometry(_planes, _blocks, _generics, _lights, getActiveCamera());
406 	}
407 
408 	return true;
409 }
410 
411 //////////////////////////////////////////////////////////////////////////
renderShadowGeometry()412 bool AdSceneGeometry::renderShadowGeometry() {
413 	_gameRef->_renderer3D->renderShadowGeometry(_planes, _blocks, _generics, getActiveCamera());
414 	return true;
415 }
416 
417 //////////////////////////////////////////////////////////////////////////
getHeightAt(Math::Vector3d pos,float tolerance,bool * intFound)418 float AdSceneGeometry::getHeightAt(Math::Vector3d pos, float tolerance, bool *intFound) {
419 	float ret = pos.y();
420 	Math::Vector3d intersection;
421 	Math::Vector3d dir = Math::Vector3d(0, -1, 0);
422 
423 	pos.y() += tolerance;
424 
425 	bool intFoundTmp = false;
426 
427 	for (uint32 i = 0; i < _planes.size(); i++) {
428 		for (int j = 0; j < _planes[i]->_mesh->faceCount(); j++) {
429 			uint16 *triangle = _planes[i]->_mesh->getFace(j);
430 			float *v0 = _planes[i]->_mesh->getVertexPosition(triangle[0]);
431 			float *v1 = _planes[i]->_mesh->getVertexPosition(triangle[1]);
432 			float *v2 = _planes[i]->_mesh->getVertexPosition(triangle[2]);
433 
434 			if (lineIntersectsTriangle(pos, dir,
435 			                           Math::Vector3d(v0[0], v0[1], v0[2]),
436 			                           Math::Vector3d(v1[0], v1[1], v1[2]),
437 			                           Math::Vector3d(v2[0], v2[1], v2[2]),
438 			                           intersection.x(), intersection.y(), intersection.z())) {
439 				if (intersection.y() > pos.y() + tolerance) {
440 					continue; // only fall down
441 				}
442 
443 				if (!intFoundTmp || ABS(ret - pos.y()) > ABS(intersection.y() - pos.y())) {
444 					ret = intersection.y();
445 				}
446 
447 				intFoundTmp = true;
448 			}
449 		}
450 	}
451 
452 	if (intFound) {
453 		*intFound = intFoundTmp;
454 	}
455 
456 	return ret;
457 }
458 
459 //////////////////////////////////////////////////////////////////////////
directPathExists(Math::Vector3d * p1,Math::Vector3d * p2)460 bool AdSceneGeometry::directPathExists(Math::Vector3d *p1, Math::Vector3d *p2) {
461 	// pretty sure this stuff can be somewhat simplified by factoring it out
462 	// into some functions but let's leave this to a later point
463 
464 	// test walkplanes
465 	for (uint i = 0; i < _planes.size(); i++) {
466 		for (int j = 0; j < _planes[i]->_mesh->faceCount(); j++) {
467 			uint16 *triangle = _planes[i]->_mesh->getFace(j);
468 			float *v0 = _planes[i]->_mesh->getVertexPosition(triangle[0]);
469 			float *v1 = _planes[i]->_mesh->getVertexPosition(triangle[1]);
470 			float *v2 = _planes[i]->_mesh->getVertexPosition(triangle[2]);
471 			Math::Vector3d intersection;
472 			float dist;
473 
474 			if (lineSegmentIntersectsTriangle(*p1, *p2, Math::Vector3d(v0[0], v0[1], v0[2]),
475 			                                  Math::Vector3d(v1[0], v1[1], v1[2]),
476 			                                  Math::Vector3d(v2[0], v2[1], v2[2]),
477 			                                  intersection, dist)) {
478 				if (lineIntersectsTriangle(*p1, *p1 - *p2, v0, v1, v2,
479 				                           intersection.x(), intersection.y(), intersection.z())) {
480 					return false;
481 				}
482 
483 				if (lineIntersectsTriangle(*p2, *p2 - *p1, v0, v1, v2,
484 				                           intersection.x(), intersection.y(), intersection.z())) {
485 					return false;
486 				}
487 			}
488 		}
489 	}
490 
491 	// test blocks
492 	for (uint i = 0; i < _blocks.size(); i++) {
493 		if (!_blocks[i]->_active) {
494 			continue;
495 		}
496 
497 		for (int j = 0; j < _blocks[i]->_mesh->faceCount(); j++) {
498 			uint16 *triangle = _blocks[i]->_mesh->getFace(j);
499 			float *v0 = _blocks[i]->_mesh->getVertexPosition(triangle[0]);
500 			float *v1 = _blocks[i]->_mesh->getVertexPosition(triangle[1]);
501 			float *v2 = _blocks[i]->_mesh->getVertexPosition(triangle[2]);
502 			Math::Vector3d intersection;
503 			float dist;
504 
505 			if (lineSegmentIntersectsTriangle(*p1, *p2, Math::Vector3d(v0[0], v0[1], v0[2]),
506 			                                  Math::Vector3d(v1[0], v1[1], v1[2]),
507 			                                  Math::Vector3d(v2[0], v2[1], v2[2]),
508 			                                  intersection, dist)) {
509 				if (lineIntersectsTriangle(*p1, *p1 - *p2, v0, v1, v2,
510 				                           intersection.x(), intersection.y(), intersection.z())) {
511 					return false;
512 				}
513 
514 				if (lineIntersectsTriangle(*p2, *p2 - *p1, v0, v1, v2,
515 				                           intersection.x(), intersection.y(), intersection.z())) {
516 					return false;
517 				}
518 			}
519 		}
520 	}
521 
522 	return true;
523 }
524 
525 //////////////////////////////////////////////////////////////////////////
getBlockIntersection(Math::Vector3d * p1,Math::Vector3d * p2)526 Math::Vector3d AdSceneGeometry::getBlockIntersection(Math::Vector3d *p1, Math::Vector3d *p2) {
527 	// test blocks
528 	for (uint i = 0; i < _blocks.size(); i++) {
529 		if (!_blocks[i]->_active) {
530 			continue;
531 		}
532 
533 		for (int j = 0; j < _blocks[i]->_mesh->faceCount(); j++) {
534 			uint16 *triangle = _blocks[i]->_mesh->getFace(j);
535 			float *v0 = _blocks[i]->_mesh->getVertexPosition(triangle[0]);
536 			float *v1 = _blocks[i]->_mesh->getVertexPosition(triangle[1]);
537 			float *v2 = _blocks[i]->_mesh->getVertexPosition(triangle[2]);
538 
539 			Math::Vector3d intersection;
540 			float dist;
541 
542 			if (lineSegmentIntersectsTriangle(*p1, *p2, v0, v1, v2, intersection, dist)) {
543 				if (lineIntersectsTriangle(*p1, *p1 - *p2, v0, v1, v2, intersection.x(), intersection.y(), intersection.z())) {
544 					return intersection;
545 				}
546 
547 				if (lineIntersectsTriangle(*p2, *p2 - *p1, v0, v1, v2, intersection.x(), intersection.y(), intersection.z())) {
548 					return intersection;
549 				}
550 			}
551 		}
552 	}
553 
554 	return Math::Vector3d(0, 0, 0);
555 }
556 
557 //////////////////////////////////////////////////////////////////////////
convert2Dto3DTolerant(int x,int y,Math::Vector3d * pos)558 bool AdSceneGeometry::convert2Dto3DTolerant(int x, int y, Math::Vector3d *pos) {
559 	bool Ret = convert2Dto3D(x, y, pos);
560 	if (Ret) {
561 		return Ret;
562 	}
563 
564 	int lenLeft = 0;
565 	int lenRight = 0;
566 	int lenDown = 0;
567 	int lenUp = 0;
568 
569 	int i;
570 
571 	// left
572 	for (i = 0; i < 1000; i += 10) {
573 		if (convert2Dto3D(x - i, y, pos)) {
574 			lenLeft = i;
575 			break;
576 		}
577 	}
578 
579 	// right
580 	for (i = 0; i < 1000; i += 10) {
581 		if (convert2Dto3D(x + i, y, pos)) {
582 			lenRight = i;
583 			break;
584 		}
585 	}
586 
587 	// up
588 	for (i = 0; i < 1000; i += 10) {
589 		if (convert2Dto3D(x, y - i, pos)) {
590 			lenUp = i;
591 			break;
592 		}
593 	}
594 
595 	// down
596 	for (i = 0; i < 1000; i += 10) {
597 		if (convert2Dto3D(x, y + i, pos)) {
598 			lenDown = i;
599 			break;
600 		}
601 	}
602 
603 	if (!lenLeft && !lenRight && !lenUp && !lenDown) {
604 		return false;
605 	}
606 
607 	int offsetX = INT_MAX_VALUE;
608 	int offsetY = INT_MAX_VALUE;
609 
610 	if (lenLeft || lenRight) {
611 		if (lenRight) {
612 			if (lenLeft && lenLeft < lenRight) {
613 				offsetX = -lenLeft;
614 			} else {
615 				offsetX = lenRight;
616 			}
617 		} else {
618 			offsetX = -lenLeft;
619 		}
620 	}
621 
622 	if (lenUp || lenDown) {
623 		if (lenDown) {
624 			if (lenUp && lenUp < lenDown)
625 				offsetY = -lenUp;
626 			else
627 				offsetY = lenDown;
628 		} else
629 			offsetY = -lenUp;
630 	}
631 
632 	if (abs(offsetX) < abs(offsetY)) {
633 		x += offsetX;
634 	} else {
635 		y += offsetY;
636 	}
637 
638 	return convert2Dto3D(x, y, pos);
639 }
640 
641 //////////////////////////////////////////////////////////////////////////
convert2Dto3D(int x,int y,Math::Vector3d * pos)642 bool AdSceneGeometry::convert2Dto3D(int x, int y, Math::Vector3d *pos) {
643 	bool intFound = false;
644 	float minDist = FLT_MAX;
645 
646 	Math::Ray ray = _gameRef->_renderer3D->rayIntoScene(x, y);
647 
648 	for (uint32 i = 0; i < _planes.size(); i++) {
649 		for (int j = 0; j < _planes[i]->_mesh->faceCount(); j++) {
650 			uint16 *triangle = _planes[i]->_mesh->getFace(j);
651 			float *v0 = _planes[i]->_mesh->getVertexPosition(triangle[0]);
652 			float *v1 = _planes[i]->_mesh->getVertexPosition(triangle[1]);
653 			float *v2 = _planes[i]->_mesh->getVertexPosition(triangle[2]);
654 			Math::Vector3d intersection;
655 
656 			if (lineIntersectsTriangle(ray.getOrigin(), ray.getDirection(),
657 			                           Math::Vector3d(v0[0], v0[1], v0[2]),
658 			                           Math::Vector3d(v1[0], v1[1], v1[2]),
659 			                           Math::Vector3d(v2[0], v2[1], v2[2]),
660 			                           intersection.x(), intersection.y(), intersection.z())) {
661 				Math::Vector3d lineSegement = intersection - getActiveCamera()->_position;
662 				float dist = lineSegement.getMagnitude();
663 
664 				if (dist < minDist) {
665 					*pos = intersection;
666 					minDist = dist;
667 				}
668 
669 				intFound = true;
670 			}
671 		}
672 	}
673 
674 	return intFound;
675 }
676 
677 //////////////////////////////////////////////////////////////////////////
getPath(Math::Vector3d source,Math::Vector3d target,AdPath3D * path,bool rerun)678 bool AdSceneGeometry::getPath(Math::Vector3d source, Math::Vector3d target, AdPath3D *path, bool rerun) {
679 	if (!_PFReady) {
680 		return false;
681 	} else {
682 		source.y() = getHeightAt(source, _waypointHeight) + _waypointHeight;
683 		target.y() = getHeightAt(target, _waypointHeight) + _waypointHeight;
684 
685 		_PFReady = false;
686 		_PFSource = source;
687 		_PFTarget = target;
688 		_PFTargetPath = path;
689 		_PFAlternateTarget = Math::Vector3d(0, 0, 0);
690 		_PFAlternateDist = FLT_MAX;
691 
692 		_PFTargetPath->reset();
693 		_PFTargetPath->setReady(false);
694 		_PFRerun = rerun;
695 
696 		// prepare working path
697 		uint i;
698 		uint j;
699 
700 		for (i = 0; i < _PFPath.size(); i++) {
701 			delete _PFPath[i];
702 		}
703 
704 		_PFPath.clear();
705 
706 		// first point
707 		_PFPath.add(new AdPathPoint3D(source, 0));
708 
709 		// last point
710 		_PFPath.add(new AdPathPoint3D(target, FLT_MAX));
711 
712 		// add all active waypoints
713 		for (i = 0; i < _waypointGroups.size(); i++) {
714 			if (_waypointGroups[i]->_active) {
715 				for (j = 0; j < _waypointGroups[i]->_points.size(); j++) {
716 					_PFPath.add(new AdPathPoint3D(*_waypointGroups[i]->_points[j], FLT_MAX));
717 				}
718 			}
719 		}
720 
721 		return true;
722 	}
723 }
724 
725 //////////////////////////////////////////////////////////////////////////
pathFinderStep()726 void AdSceneGeometry::pathFinderStep() {
727 	uint i;
728 
729 	// get lowest unmarked
730 	float lowest_dist = FLT_MAX;
731 	AdPathPoint3D *lowest_pt = NULL;
732 
733 	for (i = 0; i < _PFPath.size(); i++) {
734 		if (!_PFPath[i]->_marked && _PFPath[i]->_distance < lowest_dist) {
735 			lowest_dist = _PFPath[i]->_distance;
736 			lowest_pt = _PFPath[i];
737 		}
738 	}
739 
740 	if (lowest_pt == NULL) { // no path -> terminate PathFinder
741 		_PFReady = true;
742 
743 		if (!_PFRerun) {
744 			if (_PFAlternateTarget != Math::Vector3d(0, 0, 0)) {
745 				getPath(_PFSource, _PFAlternateTarget, _PFTargetPath, true);
746 			} else {
747 				_PFTargetPath->setReady(true);
748 			}
749 		} else {
750 			_PFTargetPath->setReady(true);
751 		}
752 
753 		return;
754 	}
755 
756 	lowest_pt->_marked = true;
757 
758 	// target point marked, generate path and terminate
759 	if (lowest_pt->_pos == _PFTarget) {
760 		while (lowest_pt != NULL) {
761 			_PFTargetPath->_points.insert_at(0, new Math::Vector3d(lowest_pt->_pos));
762 			lowest_pt = lowest_pt->_origin;
763 		}
764 		// remove current position
765 		if (_PFTargetPath->_points.size() > 0) {
766 			delete _PFTargetPath->_points[0];
767 			_PFTargetPath->_points.remove_at(0);
768 		}
769 
770 		_PFReady = true;
771 		_PFTargetPath->setReady(true);
772 		return;
773 	}
774 
775 	// otherwise keep on searching
776 	for (i = 0; i < _PFPath.size(); i++) {
777 		if (!_PFPath[i]->_marked) {
778 			float dist = getPointsDist(lowest_pt->_pos, _PFPath[i]->_pos);
779 			if (dist >= 0 && lowest_pt->_distance + dist < _PFPath[i]->_distance) {
780 				_PFPath[i]->_distance = lowest_pt->_distance + dist;
781 				_PFPath[i]->_origin = lowest_pt;
782 			} else {
783 				if (!_PFRerun && _PFPath[i]->_pos == _PFTarget) {
784 					Math::Vector3d Line = _PFPath[i]->_pos - lowest_pt->_pos;
785 					float Len = Line.getMagnitude();
786 
787 					if (Len < _PFAlternateDist) {
788 						_PFAlternateDist = Len;
789 						_PFAlternateTarget = getBlockIntersection(&lowest_pt->_pos, &_PFPath[i]->_pos);
790 
791 						Math::Vector3d Dir = _PFAlternateTarget - lowest_pt->_pos;
792 						Dir.normalize();
793 						_PFAlternateTarget -= Dir * 30;
794 					}
795 				}
796 			}
797 		}
798 	}
799 }
800 
801 //////////////////////////////////////////////////////////////////////////
getPointsDist(Math::Vector3d p1,Math::Vector3d p2)802 float AdSceneGeometry::getPointsDist(Math::Vector3d p1, Math::Vector3d p2) {
803 	if (!directPathExists(&p1, &p2)) {
804 		return -1;
805 	}
806 
807 	Math::Vector3d vect = p2 - p1;
808 	return vect.getMagnitude();
809 }
810 
811 //////////////////////////////////////////////////////////////////////////
initLoop()812 bool AdSceneGeometry::initLoop() {
813 	uint32 start = _gameRef->_currentTime;
814 	while (!_PFReady && _gameRef->_currentTime - start <= _PFMaxTime) {
815 		pathFinderStep();
816 	}
817 
818 	return true;
819 }
820 
821 //////////////////////////////////////////////////////////////////////////
createLights()822 bool AdSceneGeometry::createLights() {
823 	// disable all lights
824 	for (int i = 0; i < _gameRef->_renderer3D->maximumLightsCount(); i++) {
825 		_gameRef->_renderer3D->disableLight(i);
826 	}
827 
828 	int lightCount = MIN(static_cast<int>(_lights.size()), _gameRef->_renderer3D->maximumLightsCount());
829 
830 	for (int i = 0; i < lightCount; i++) {
831 		_lights[i]->setLight(i);
832 	}
833 
834 	return true;
835 }
836 
837 //////////////////////////////////////////////////////////////////////////
compareLights(const Light3D * light1,const Light3D * light2)838 bool compareLights(const Light3D *light1, const Light3D *light2) {
839 	return light1->_distance < light2->_distance;
840 }
841 
842 //////////////////////////////////////////////////////////////////////////
enableLights(Math::Vector3d point,BaseArray<char * > & ignoreLights)843 bool AdSceneGeometry::enableLights(Math::Vector3d point, BaseArray<char *> &ignoreLights) {
844 	const int maxLightCount = 100;
845 
846 	int activeLightCount = 0;
847 	for (uint i = 0; i < _lights.size(); i++) {
848 		_lights[i]->_isAvailable = false;
849 		if (_lights[i]->_active) {
850 			++activeLightCount;
851 		}
852 	}
853 
854 	if (activeLightCount <= _gameRef->_renderer3D->maximumLightsCount()) {
855 		for (uint i = 0; i < _lights.size(); i++) {
856 			_lights[i]->_isAvailable = true;
857 		}
858 	} else {
859 		if (!_maxLightsWarning) {
860 			_gameRef->LOG(0, "Warning: Using more lights than the hardware supports (%d)", _gameRef->_renderer3D->maximumLightsCount());
861 			_maxLightsWarning = true;
862 		}
863 
864 		Common::Array<Light3D *> activeLights;
865 
866 		// compute distance to point
867 		for (uint i = 0; i < _lights.size(); i++) {
868 			if (!_lights[i]->_active) {
869 				continue;
870 			}
871 
872 			Math::Vector3d dif;
873 
874 			if (_lights[i]->_isSpotlight) {
875 				Math::Vector3d dir = _lights[i]->_target - _lights[i]->_position;
876 				dif = (_lights[i]->_position + dir * 0.75f) - point;
877 			} else {
878 				dif = _lights[i]->_position - point;
879 			}
880 
881 			_lights[i]->_distance = dif.getMagnitude();
882 
883 			activeLights.push_back(_lights[i]);
884 		}
885 
886 		// sort by distance
887 		if (activeLights.size() > 0) {
888 			Common::sort(activeLights.begin(), activeLights.end(), compareLights);
889 
890 			for (uint i = 0; i < activeLights.size(); i++) {
891 				activeLights[i]->_isAvailable = static_cast<int>(i) < _gameRef->_renderer3D->maximumLightsCount();
892 			}
893 		}
894 	}
895 
896 	// light all available lights
897 	for (int i = 0; i < maxLightCount; i++) {
898 		_gameRef->_renderer3D->disableLight(i);
899 	}
900 
901 	activeLightCount = 0;
902 
903 	for (uint i = 0; i < _lights.size(); i++) {
904 		if (activeLightCount >= _gameRef->_renderer3D->maximumLightsCount()) {
905 			break;
906 		}
907 
908 		if (ignoreLights.size()) {
909 			bool ignore = false;
910 
911 			for (uint j = 0; j < ignoreLights.size(); j++) {
912 				if (scumm_stricmp(_lights[i]->getName(), ignoreLights[j]) == 0) {
913 					ignore = true;
914 					break;
915 				}
916 			}
917 
918 			if (ignore) {
919 				continue; // skip this light
920 			}
921 		}
922 
923 		if (_lights[i]->_isAvailable) {
924 			if (_lights[i]->_active) {
925 				_gameRef->_renderer3D->enableLight(i);
926 				++activeLightCount;
927 			}
928 		}
929 	}
930 
931 	return true;
932 }
933 
934 //////////////////////////////////////////////////////////////////////////
correctTargetPoint(const Math::Vector3d & source,Math::Vector3d * target)935 bool AdSceneGeometry::correctTargetPoint(const Math::Vector3d &source, Math::Vector3d *target) {
936 	// the source parameter is not even used in wme3d
937 	int i;
938 	int MaxLen = 1000;
939 	int Step = 10;
940 	Math::Vector3d newTarget;
941 
942 	int lenLeft = 0;
943 	int lenRight = 0;
944 	int lenUp = 0;
945 	int lenDown = 0;
946 
947 	// left
948 	newTarget = *target;
949 	for (i = 1; i <= MaxLen; i += Step) {
950 		newTarget.x() -= i;
951 		if (!directPathExists(target, &newTarget)) {
952 			lenLeft = i;
953 			break;
954 		}
955 	}
956 
957 	// right
958 	newTarget = *target;
959 	for (i = 1; i <= MaxLen; i += Step) {
960 		newTarget.x() += i;
961 		if (!directPathExists(target, &newTarget)) {
962 			lenRight = i;
963 			break;
964 		}
965 	}
966 
967 	// up
968 	newTarget = *target;
969 	for (i = 1; i <= MaxLen; i += Step) {
970 		newTarget.z() -= i;
971 		if (!directPathExists(target, &newTarget)) {
972 			lenUp = i;
973 			break;
974 		}
975 	}
976 
977 	// down
978 	newTarget = *target;
979 	for (i = 1; i <= MaxLen; i += Step) {
980 		newTarget.z() += i;
981 		if (!directPathExists(target, &newTarget)) {
982 			lenDown = i;
983 			break;
984 		}
985 	}
986 
987 	if (!lenLeft && !lenRight && !lenUp && !lenDown) {
988 		return true;
989 	}
990 
991 	int offsetX = INT_MAX_VALUE;
992 	int offsetZ = INT_MAX_VALUE;
993 
994 	if (lenLeft || lenRight) {
995 		if (lenRight) {
996 			if (lenLeft && lenLeft < lenRight) {
997 				offsetX = -lenLeft;
998 			} else {
999 				offsetX = lenRight;
1000 			}
1001 		} else {
1002 			offsetX = -lenLeft;
1003 		}
1004 	}
1005 
1006 	if (lenUp || lenDown) {
1007 		if (lenDown) {
1008 			if (lenUp && lenUp < lenDown) {
1009 				offsetZ = -lenUp;
1010 			} else {
1011 				offsetZ = lenDown;
1012 			}
1013 		} else {
1014 			offsetZ = -lenUp;
1015 		}
1016 	}
1017 
1018 	if (abs(offsetX) < abs(offsetZ)) {
1019 		target->x() += offsetX;
1020 	} else {
1021 		target->z() += offsetZ;
1022 	}
1023 
1024 	return true;
1025 }
1026 
1027 //////////////////////////////////////////////////////////////////////////
enableNode(const char * nodeName,bool enable)1028 bool AdSceneGeometry::enableNode(const char *nodeName, bool enable) {
1029 	bool ret = false;
1030 
1031 	uint i;
1032 	for (i = 0; i < _blocks.size(); i++) {
1033 		if (scumm_stricmp(nodeName, _blocks[i]->getName()) == 0) {
1034 			_blocks[i]->_active = enable;
1035 			ret = true;
1036 		}
1037 	}
1038 
1039 	for (i = 0; i < _planes.size(); i++) {
1040 		if (scumm_stricmp(nodeName, _planes[i]->getName()) == 0) {
1041 			_planes[i]->_active = enable;
1042 			ret = true;
1043 		}
1044 	}
1045 
1046 	for (i = 0; i < _generics.size(); i++) {
1047 		if (scumm_stricmp(nodeName, _generics[i]->getName()) == 0) {
1048 			_generics[i]->_active = enable;
1049 			ret = true;
1050 		}
1051 	}
1052 
1053 	return ret;
1054 }
1055 
1056 //////////////////////////////////////////////////////////////////////////
isNodeEnabled(const char * nodeName)1057 bool AdSceneGeometry::isNodeEnabled(const char *nodeName) {
1058 	for (uint i = 0; i < _blocks.size(); i++) {
1059 		if (scumm_stricmp(nodeName, _blocks[i]->getName()) == 0) {
1060 			return _blocks[i]->_active;
1061 		}
1062 	}
1063 	for (uint i = 0; i < _planes.size(); i++) {
1064 		if (scumm_stricmp(nodeName, _planes[i]->getName()) == 0) {
1065 			return _planes[i]->_active;
1066 		}
1067 	}
1068 
1069 	for (uint i = 0; i < _generics.size(); i++) {
1070 		if (scumm_stricmp(nodeName, _generics[i]->getName()) == 0) {
1071 			return _generics[i]->_active;
1072 		}
1073 	}
1074 
1075 	return false;
1076 }
1077 
1078 //////////////////////////////////////////////////////////////////////////
enableLight(const char * lightName,bool enable)1079 bool AdSceneGeometry::enableLight(const char *lightName, bool enable) {
1080 	bool ret = false;
1081 
1082 	uint i;
1083 	for (i = 0; i < _lights.size(); i++) {
1084 		if (scumm_stricmp(lightName, _lights[i]->getName()) == 0) {
1085 			_lights[i]->_active = enable;
1086 			ret = true;
1087 		}
1088 	}
1089 	createLights();
1090 
1091 	return ret;
1092 }
1093 
1094 //////////////////////////////////////////////////////////////////////////
isLightEnabled(const char * lightName)1095 bool AdSceneGeometry::isLightEnabled(const char *lightName) {
1096 	for (uint i = 0; i < _lights.size(); i++) {
1097 		if (scumm_stricmp(lightName, _lights[i]->getName()) == 0) {
1098 			return _lights[i]->_active;
1099 		}
1100 	}
1101 	return false;
1102 }
1103 
1104 //////////////////////////////////////////////////////////////////////////
setLightColor(const char * lightName,uint32 color)1105 bool AdSceneGeometry::setLightColor(const char *lightName, uint32 color) {
1106 	bool ret = false;
1107 
1108 	uint i;
1109 	for (i = 0; i < _lights.size(); i++) {
1110 		if (scumm_stricmp(lightName, _lights[i]->getName()) == 0) {
1111 			_lights[i]->_diffuseColor = color;
1112 			ret = true;
1113 		}
1114 	}
1115 	createLights();
1116 
1117 	return ret;
1118 }
1119 
1120 //////////////////////////////////////////////////////////////////////////
getLightColor(const char * lightName)1121 uint32 AdSceneGeometry::getLightColor(const char *lightName) {
1122 	for (uint i = 0; i < _lights.size(); i++) {
1123 		if (scumm_stricmp(lightName, _lights[i]->getName()) == 0) {
1124 			return _lights[i]->_diffuseColor;
1125 		}
1126 	}
1127 	return 0;
1128 }
1129 
1130 //////////////////////////////////////////////////////////////////////////
getLightPos(const char * lightName)1131 Math::Vector3d AdSceneGeometry::getLightPos(const char *lightName) {
1132 	for (uint i = 0; i < _lights.size(); i++) {
1133 		if (scumm_stricmp(lightName, _lights[i]->getName()) == 0) {
1134 			return _lights[i]->_position;
1135 		}
1136 	}
1137 	return Math::Vector3d(0, 0, 0);
1138 }
1139 
1140 //////////////////////////////////////////////////////////////////////////
persist(BasePersistenceManager * persistMgr)1141 bool AdSceneGeometry::persist(BasePersistenceManager *persistMgr) {
1142 	BaseObject::persist(persistMgr);
1143 
1144 	persistMgr->transferFloat(TMEMBER(_waypointHeight));
1145 	persistMgr->transferPtr(TMEMBER(_wptMarker));
1146 
1147 	if (!persistMgr->getIsSaving()) {
1148 		//m_WptMarker = NULL;
1149 		loadFile(getFilename());
1150 		_lastValuesInitialized = false;
1151 	}
1152 
1153 	persistMgr->transferSint32(TMEMBER(_activeCamera));
1154 	persistMgr->transferSint32(TMEMBER(_activeLight));
1155 	persistMgr->transferMatrix4(TMEMBER(_viewMatrix));
1156 
1157 	_PFPath.persist(persistMgr);
1158 	persistMgr->transferBool(TMEMBER(_PFReady));
1159 	persistMgr->transferVector3d(TMEMBER(_PFSource));
1160 	persistMgr->transferVector3d(TMEMBER(_PFTarget));
1161 	persistMgr->transferVector3d(TMEMBER(_PFAlternateTarget));
1162 	persistMgr->transferPtr(TMEMBER(_PFTargetPath));
1163 	persistMgr->transferUint32(TMEMBER(_PFMaxTime));
1164 	persistMgr->transferBool(TMEMBER(_PFRerun));
1165 
1166 	// now save/load light/blocks/walkplanes/generic node states by name
1167 	int i;
1168 
1169 	//////////////////////////////////////////////////////////////////////////
1170 	int32 numLights = _lights.size();
1171 	persistMgr->transferSint32(TMEMBER(numLights));
1172 	for (i = 0; i < numLights; i++) {
1173 		if (persistMgr->getIsSaving()) {
1174 			persistMgr->transferCharPtr(TMEMBER(_lights[i]->_name));
1175 			_lights[i]->persist(persistMgr);
1176 		} else {
1177 			char *name = nullptr;
1178 			persistMgr->transferCharPtr(TMEMBER(name));
1179 			bool found = false;
1180 
1181 			for (uint j = 0; j < _lights.size(); j++) {
1182 				if (scumm_stricmp(name, _lights[j]->getName()) == 0) {
1183 					_lights[j]->persist(persistMgr);
1184 					found = true;
1185 					break;
1186 				}
1187 			}
1188 
1189 			if (!found) {
1190 				Light3D *light = new Light3D(_gameRef);
1191 				light->persist(persistMgr);
1192 				delete light;
1193 			}
1194 
1195 			if (name) {
1196 				delete[] name;
1197 			}
1198 		}
1199 	}
1200 	createLights();
1201 
1202 	//////////////////////////////////////////////////////////////////////////
1203 	int32 numBlocks = _blocks.size();
1204 	persistMgr->transferSint32(TMEMBER(numBlocks));
1205 	for (i = 0; i < numBlocks; i++) {
1206 		if (persistMgr->getIsSaving()) {
1207 			persistMgr->transferCharPtr(TMEMBER(_blocks[i]->_name));
1208 			_blocks[i]->persist(persistMgr);
1209 		} else {
1210 			char *name = nullptr;
1211 			persistMgr->transferCharPtr(TMEMBER(name));
1212 			bool found = false;
1213 			for (uint j = 0; j < _blocks.size(); j++) {
1214 				if (scumm_stricmp(name, _blocks[j]->getName()) == 0) {
1215 					_blocks[j]->persist(persistMgr);
1216 					found = true;
1217 					break;
1218 				}
1219 			}
1220 			if (!found) {
1221 				AdBlock *block = new AdBlock(_gameRef);
1222 				block->persist(persistMgr);
1223 				delete block;
1224 			}
1225 
1226 			if (name) {
1227 				delete[] name;
1228 			}
1229 		}
1230 	}
1231 
1232 	//////////////////////////////////////////////////////////////////////////
1233 	int32 numPlanes = _planes.size();
1234 	persistMgr->transferSint32(TMEMBER(numPlanes));
1235 	for (i = 0; i < numPlanes; i++) {
1236 		if (persistMgr->getIsSaving()) {
1237 			persistMgr->transferCharPtr(TMEMBER(_planes[i]->_name));
1238 			_planes[i]->persist(persistMgr);
1239 		} else {
1240 			char *name = nullptr;
1241 			persistMgr->transferCharPtr(TMEMBER(name));
1242 			bool found = false;
1243 			for (uint j = 0; j < _planes.size(); j++) {
1244 				if (scumm_stricmp(name, _planes[j]->getName()) == 0) {
1245 					_planes[j]->persist(persistMgr);
1246 					found = true;
1247 					break;
1248 				}
1249 			}
1250 			if (!found) {
1251 				AdWalkplane *plane = new AdWalkplane(_gameRef);
1252 				plane->persist(persistMgr);
1253 				delete plane;
1254 			}
1255 
1256 			if (name) {
1257 				delete[] name;
1258 			}
1259 		}
1260 	}
1261 
1262 	//////////////////////////////////////////////////////////////////////////
1263 	int32 numGenerics = _generics.size();
1264 	persistMgr->transferSint32(TMEMBER(numGenerics));
1265 	for (i = 0; i < numGenerics; i++) {
1266 		if (persistMgr->getIsSaving()) {
1267 			persistMgr->transferCharPtr(TMEMBER(_generics[i]->_name));
1268 			_generics[i]->persist(persistMgr);
1269 		} else {
1270 			char *name = nullptr;
1271 			persistMgr->transferCharPtr(TMEMBER(name));
1272 			bool found = false;
1273 			for (uint j = 0; j < _generics.size(); j++) {
1274 				if (scumm_stricmp(name, _generics[j]->getName()) == 0) {
1275 					_generics[j]->persist(persistMgr);
1276 					found = true;
1277 					break;
1278 				}
1279 			}
1280 			if (!found) {
1281 				AdGeneric *generic = new AdGeneric(_gameRef);
1282 				generic->persist(persistMgr);
1283 				delete generic;
1284 			}
1285 
1286 			if (name) {
1287 				delete[] name;
1288 			}
1289 		}
1290 	}
1291 
1292 	if (!persistMgr->getIsSaving()) {
1293 		_maxLightsWarning = false;
1294 	}
1295 
1296 	return true;
1297 }
1298 
1299 //////////////////////////////////////////////////////////////////////////
convert3Dto2D(Math::Vector3d * pos,int32 * x,int32 * y)1300 bool AdSceneGeometry::convert3Dto2D(Math::Vector3d *pos, int32 *x, int32 *y) {
1301 	Math::Matrix4 worldMat;
1302 	worldMat.setToIdentity();
1303 
1304 	Math::Vector3d vect2D;
1305 	int viewportTmp[4];
1306 	// TODO: gluMathProject expects an OpenGL viewport,
1307 	// hence the first coordinates specify the lower left corner
1308 	// wme works with a Direct3D viewport, though
1309 	// so check if this does work
1310 	viewportTmp[0] = _drawingViewport.left;
1311 	viewportTmp[1] = _drawingViewport.bottom;
1312 	viewportTmp[2] = _drawingViewport.width();
1313 	viewportTmp[3] = _drawingViewport.height();
1314 	Math::Matrix4 modelViewMatrix = _lastViewMat * worldMat;
1315 	Math::gluMathProject(*pos, modelViewMatrix.getData(), _lastProjMat.getData(), viewportTmp, vect2D);
1316 	*x = vect2D.x() + _lastScrollX;
1317 	*y = vect2D.y() + _lastScrollY;
1318 
1319 	return true;
1320 }
1321 
1322 } // namespace Wintermute
1323