2009-05-26 14:13:08 +00:00
|
|
|
/* Residual - A 3D game interpreter
|
2008-06-13 14:57:47 +00:00
|
|
|
*
|
|
|
|
* Residual is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
|
|
* file distributed with this source distribution.
|
2006-04-02 14:20:45 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
2003-08-19 07:20:03 +00:00
|
|
|
|
2009-05-27 13:28:49 +00:00
|
|
|
#include "common/util.h"
|
2008-01-26 11:47:23 +00:00
|
|
|
|
2009-06-18 11:52:26 +00:00
|
|
|
#include "engines/grim/grim.h"
|
2009-05-24 19:13:58 +00:00
|
|
|
#include "engines/grim/walkplane.h"
|
|
|
|
#include "engines/grim/textsplit.h"
|
2011-03-21 05:16:27 +08:00
|
|
|
#include "engines/grim/savegame.h"
|
2003-08-19 07:20:03 +00:00
|
|
|
|
2009-05-25 06:49:57 +00:00
|
|
|
namespace Grim {
|
|
|
|
|
2011-03-21 05:16:27 +08:00
|
|
|
void Sector::saveState(SaveGame *savedState) const {
|
|
|
|
savedState->writeLESint32(_numVertices);
|
|
|
|
savedState->writeLESint32(_id);
|
|
|
|
savedState->writeLESint32(_type);
|
|
|
|
savedState->writeLESint32(_visible);
|
|
|
|
savedState->writeFloat(_height);
|
|
|
|
|
2011-03-21 17:18:04 +01:00
|
|
|
savedState->writeString(_name);
|
2011-03-21 05:16:27 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < _numVertices + 1; ++i) {
|
|
|
|
savedState->writeVector3d(_vertices[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
savedState->writeVector3d(_normal);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Sector::restoreState(SaveGame *savedState) {
|
|
|
|
_numVertices = savedState->readLESint32();
|
|
|
|
_id = savedState->readLESint32();
|
2011-04-05 12:37:20 +02:00
|
|
|
_type = (SectorType)savedState->readLESint32();
|
2011-03-21 05:16:27 +08:00
|
|
|
_visible = savedState->readLESint32();
|
|
|
|
_height = savedState->readFloat();
|
|
|
|
|
2011-03-21 17:18:04 +01:00
|
|
|
_name = savedState->readString();
|
2011-03-21 05:16:27 +08:00
|
|
|
|
|
|
|
_vertices = new Graphics::Vector3d[_numVertices + 1];
|
|
|
|
for (int i = 0; i < _numVertices + 1; ++i) {
|
|
|
|
_vertices[i] = savedState->readVector3d();
|
|
|
|
}
|
|
|
|
|
|
|
|
_normal = savedState->readVector3d();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-08-19 07:20:03 +00:00
|
|
|
void Sector::load(TextSplitter &ts) {
|
2004-02-25 08:21:31 +00:00
|
|
|
char buf[256];
|
2009-10-17 12:48:23 +00:00
|
|
|
int ident = 0, i = 0;
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d tempVert;
|
2004-02-25 08:21:31 +00:00
|
|
|
|
|
|
|
// Sector NAMES can be null, but ts isn't flexible enough
|
|
|
|
if (strlen(ts.currentLine()) > strlen(" sector"))
|
|
|
|
ts.scanString(" sector %256s", 1, buf);
|
|
|
|
else {
|
|
|
|
ts.nextLine();
|
|
|
|
strcpy(buf, "");
|
|
|
|
}
|
|
|
|
|
2009-10-17 12:48:23 +00:00
|
|
|
ts.scanString(" id %d", 1, &ident);
|
2004-02-25 08:21:31 +00:00
|
|
|
|
2005-12-28 05:22:18 +00:00
|
|
|
_name = buf;
|
2009-10-17 12:48:23 +00:00
|
|
|
_id = ident;
|
2004-02-25 08:21:31 +00:00
|
|
|
ts.scanString(" type %256s", 1, buf);
|
|
|
|
|
|
|
|
if (strstr(buf, "walk"))
|
2011-04-05 12:37:20 +02:00
|
|
|
_type = WalkType;
|
2004-02-25 08:21:31 +00:00
|
|
|
|
|
|
|
else if (strstr(buf, "funnel"))
|
2011-04-05 12:37:20 +02:00
|
|
|
_type = FunnelType;
|
2004-02-25 08:21:31 +00:00
|
|
|
else if (strstr(buf, "camera"))
|
2011-04-05 12:37:20 +02:00
|
|
|
_type = CameraType;
|
2004-02-25 08:21:31 +00:00
|
|
|
else if (strstr(buf, "special"))
|
2011-04-05 12:37:20 +02:00
|
|
|
_type = SpecialType;
|
2004-02-25 08:21:31 +00:00
|
|
|
else if (strstr(buf, "chernobyl"))
|
2011-04-05 12:37:20 +02:00
|
|
|
_type = HotType;
|
2009-05-25 19:21:58 +00:00
|
|
|
else if (gDebugLevel == DEBUG_ERROR || gDebugLevel == DEBUG_ALL)
|
2004-02-25 08:21:31 +00:00
|
|
|
error("Unknown sector type '%s' in room setup", buf);
|
|
|
|
|
|
|
|
ts.scanString(" default visibility %256s", 1, buf);
|
|
|
|
if (strcmp(buf, "visible") == 0)
|
2004-12-09 23:55:43 +00:00
|
|
|
_visible = true;
|
2004-02-25 08:21:31 +00:00
|
|
|
else if (strcmp(buf, "invisible") == 0)
|
2004-12-09 23:55:43 +00:00
|
|
|
_visible = false;
|
2004-02-25 08:21:31 +00:00
|
|
|
else
|
2008-09-28 15:23:15 +00:00
|
|
|
error("Invalid visibility spec: %s", buf);
|
2004-12-09 23:55:43 +00:00
|
|
|
ts.scanString(" height %f", 1, &_height);
|
|
|
|
ts.scanString(" numvertices %d", 1, &_numVertices);
|
2009-05-25 12:13:35 +00:00
|
|
|
_vertices = new Graphics::Vector3d[_numVertices + 1];
|
2004-02-25 08:21:31 +00:00
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
ts.scanString(" vertices: %f %f %f", 3, &_vertices[0].x(), &_vertices[0].y(), &_vertices[0].z());
|
|
|
|
for (i = 1; i < _numVertices; i++)
|
|
|
|
ts.scanString(" %f %f %f", 3, &_vertices[i].x(), &_vertices[i].y(), &_vertices[i].z());
|
2004-02-25 08:21:31 +00:00
|
|
|
|
|
|
|
// Repeat the last vertex for convenience
|
2004-12-09 23:55:43 +00:00
|
|
|
_vertices[_numVertices] = _vertices[0];
|
2004-03-25 09:33:17 +00:00
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
_normal = cross(_vertices[1] - _vertices[0], _vertices[_numVertices - 1] - _vertices[0]);
|
|
|
|
float length = _normal.magnitude();
|
2004-03-25 09:33:17 +00:00
|
|
|
if (length > 0)
|
2004-12-09 23:55:43 +00:00
|
|
|
_normal /= length;
|
2003-08-19 07:20:03 +00:00
|
|
|
}
|
|
|
|
|
2009-10-17 12:48:23 +00:00
|
|
|
void Sector::setVisible(bool vis) {
|
|
|
|
_visible = vis;
|
2003-10-12 13:04:46 +00:00
|
|
|
}
|
|
|
|
|
2009-05-25 12:13:35 +00:00
|
|
|
bool Sector::isPointInSector(Graphics::Vector3d point) const {
|
2004-02-25 08:21:31 +00:00
|
|
|
// The algorithm: for each edge A->B, check whether the z-component
|
|
|
|
// of (B-A) x (P-A) is >= 0. Then the point is at least in the
|
|
|
|
// cylinder above&below the polygon. (This works because the polygons'
|
|
|
|
// vertices are always given in counterclockwise order, and the
|
|
|
|
// polygons are always convex.)
|
|
|
|
//
|
2005-08-01 03:49:02 +00:00
|
|
|
// Checking the box height on the first point fixes problems with Manny
|
|
|
|
// changing sectors outside Velasco's storeroom. We make an exceptions
|
|
|
|
// for heights of 0 and 9999 since these appear to have special meaning.
|
|
|
|
// In order to have the entrance to the Blue Casket work we need to
|
|
|
|
// handle the vertices having different z-coordinates.
|
|
|
|
// TODO: Improve height checking for when vertices have different
|
|
|
|
// z-coordinates so the railing in Cafe Calavera works properly.
|
|
|
|
if (_height != 0.0f && _height != 9999.0f) {
|
|
|
|
bool heightOK = false;
|
|
|
|
// Handle height above Z
|
|
|
|
if ((point.z() >= _vertices[0].z()) && (point.z() <= _vertices[0].z() + _height))
|
|
|
|
heightOK = true;
|
|
|
|
// Handle height below Z
|
|
|
|
if ((point.z() <= _vertices[0].z()) && (point.z() >= _vertices[0].z() - _height))
|
|
|
|
heightOK = true;
|
2009-05-09 17:47:28 +00:00
|
|
|
|
2005-08-01 03:49:02 +00:00
|
|
|
for (int i = 0; i < _numVertices; i++) {
|
|
|
|
if (_vertices[i + 1].z() != _vertices[i].z())
|
|
|
|
heightOK = true;
|
|
|
|
}
|
|
|
|
if (!heightOK) {
|
|
|
|
/* Use this for debugging problems at height interfaces
|
2009-05-25 19:21:58 +00:00
|
|
|
if (gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL) {
|
2005-08-01 03:49:02 +00:00
|
|
|
printf("Rejected trigger due to height: %s (%f)\n", _name.c_str(), _height);
|
|
|
|
printf("Actor Z: %f\n", point.z());
|
|
|
|
for (int i = 0; i < _numVertices; i++)
|
|
|
|
printf("(%d) Z: %f\n", i, _vertices[i].z());
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2004-02-25 08:21:31 +00:00
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
for (int i = 0; i < _numVertices; i++) {
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d edge = _vertices[i + 1] - _vertices[i];
|
|
|
|
Graphics::Vector3d delta = point - _vertices[i];
|
2004-02-25 08:21:31 +00:00
|
|
|
if (edge.x() * delta.y() < edge.y() * delta.x())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2003-08-20 00:35:37 +00:00
|
|
|
}
|
2004-03-25 09:33:17 +00:00
|
|
|
|
2011-04-03 20:41:00 +02:00
|
|
|
bool Sector::isAdjacentTo(Sector *sector) const {
|
|
|
|
int vertices[2] = {-1, -1};
|
|
|
|
Graphics::Vector3d *sectorVertices = sector->getVertices();
|
|
|
|
for (int j = 0; j < _numVertices; ++j) {
|
|
|
|
Graphics::Vector3d &vect = _vertices[j];
|
|
|
|
for (int k = 0; k < sector->getNumVertices(); ++k) {
|
|
|
|
if (vect == sectorVertices[k] && j != vertices[0] && j != vertices[1]) {
|
|
|
|
if (vertices[0] > -1) {
|
|
|
|
vertices[1] = j;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
vertices[0] = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d Sector::projectToPlane(Graphics::Vector3d point) const {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_normal.z() == 0)
|
2008-09-28 15:23:15 +00:00
|
|
|
error("Trying to walk along vertical plane");
|
2004-03-25 09:33:17 +00:00
|
|
|
|
|
|
|
// Formula: return p - (n . (p - v_0))/(n . k) k
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d result = point;
|
2004-12-09 23:55:43 +00:00
|
|
|
result.z() -= dot(_normal, point - _vertices[0]) / _normal.z();
|
2004-03-25 09:33:17 +00:00
|
|
|
return result;
|
|
|
|
}
|
2004-03-25 15:43:05 +00:00
|
|
|
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d Sector::projectToPuckVector(Graphics::Vector3d v) const {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_normal.z() == 0)
|
2008-09-28 15:23:15 +00:00
|
|
|
error("Trying to walk along vertical plane");
|
2004-03-25 15:43:05 +00:00
|
|
|
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d result = v;
|
2004-12-09 23:55:43 +00:00
|
|
|
result.z() -= dot(_normal, v) / _normal.z();
|
2004-03-25 15:43:05 +00:00
|
|
|
return result;
|
|
|
|
}
|
2004-03-26 09:28:13 +00:00
|
|
|
|
|
|
|
// Find the closest point on the walkplane to the given point
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d Sector::closestPoint(Graphics::Vector3d point) const {
|
2004-03-26 09:28:13 +00:00
|
|
|
// First try to project to the plane
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d p2 = point;
|
2004-12-09 23:55:43 +00:00
|
|
|
p2 -= (dot(_normal, p2 - _vertices[0])) * _normal;
|
2004-03-26 09:28:13 +00:00
|
|
|
if (isPointInSector(p2))
|
|
|
|
return p2;
|
|
|
|
|
|
|
|
// Now try to project to some edge
|
2004-12-09 23:55:43 +00:00
|
|
|
for (int i = 0; i < _numVertices; i++) {
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d edge = _vertices[i + 1] - _vertices[i];
|
|
|
|
Graphics::Vector3d delta = point - _vertices[i];
|
2004-03-26 09:28:13 +00:00
|
|
|
float scalar = dot(delta, edge) / dot(edge, edge);
|
2008-07-30 07:04:32 +00:00
|
|
|
if (scalar >= 0 && scalar <= 1 && delta.x() * edge.y() > delta.y() * edge.x())
|
2004-03-26 09:28:13 +00:00
|
|
|
// That last test is just whether the z-component
|
|
|
|
// of delta cross edge is positive; we don't
|
|
|
|
// want to return opposite edges.
|
2004-12-09 23:55:43 +00:00
|
|
|
return _vertices[i] + scalar * edge;
|
2004-03-26 09:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, just find the closest vertex
|
2004-12-09 23:55:43 +00:00
|
|
|
float minDist = (point - _vertices[0]).magnitude();
|
2004-03-26 09:28:13 +00:00
|
|
|
int index = 0;
|
2004-12-09 23:55:43 +00:00
|
|
|
for (int i = 1; i < _numVertices; i++) {
|
|
|
|
float currDist = (point - _vertices[i]).magnitude();
|
2004-03-26 09:28:13 +00:00
|
|
|
if (currDist < minDist) {
|
|
|
|
minDist = currDist;
|
|
|
|
index = i;
|
|
|
|
}
|
|
|
|
}
|
2004-12-09 23:55:43 +00:00
|
|
|
return _vertices[index];
|
2004-03-26 09:28:13 +00:00
|
|
|
}
|
2005-03-28 01:56:40 +00:00
|
|
|
|
2009-05-25 12:13:35 +00:00
|
|
|
void Sector::getExitInfo(Graphics::Vector3d start, Graphics::Vector3d dir, struct ExitInfo *result) {
|
2005-03-28 01:56:40 +00:00
|
|
|
start = projectToPlane(start);
|
|
|
|
dir = projectToPuckVector(dir);
|
|
|
|
|
|
|
|
// First find the edge the ray exits through: this is where
|
|
|
|
// the z-component of (v_i - start) x dir changes sign from
|
|
|
|
// positive to negative.
|
|
|
|
|
|
|
|
// First find a vertex such that the cross product has
|
|
|
|
// positive z-component.
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < _numVertices; i++) {
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d delta = _vertices[i] - start;
|
2005-03-28 01:56:40 +00:00
|
|
|
if (delta.x() * dir.y() > delta.y() * dir.x())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now continue until the cross product has negative
|
|
|
|
// z-component.
|
|
|
|
while (i < _numVertices) {
|
|
|
|
i++;
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d delta = _vertices[i] - start;
|
2005-03-28 01:56:40 +00:00
|
|
|
if (delta.x() * dir.y() <= delta.y() * dir.x())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
result->edgeDir = _vertices[i] - _vertices[i - 1];
|
|
|
|
result->angleWithEdge = angle(dir, result->edgeDir);
|
|
|
|
|
2009-05-25 12:13:35 +00:00
|
|
|
Graphics::Vector3d edgeNormal(result->edgeDir.y(), -result->edgeDir.x(), 0);
|
2008-07-30 07:04:32 +00:00
|
|
|
result->exitPoint = start + (dot(_vertices[i] - start, edgeNormal) / dot(dir, edgeNormal)) * dir;
|
2005-03-28 01:56:40 +00:00
|
|
|
}
|
2009-05-25 06:49:57 +00:00
|
|
|
|
|
|
|
} // end of namespace Grim
|