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