ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

floor.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/stark/resources/floor.h"
00024 
00025 #include "engines/stark/formats/xrc.h"
00026 
00027 #include "engines/stark/resources/floorface.h"
00028 #include "engines/stark/resources/floorfield.h"
00029 
00030 #include "engines/stark/services/stateprovider.h"
00031 
00032 #include "common/math.h"
00033 
00034 namespace Stark {
00035 namespace Resources {
00036 
00037 Floor::Floor(Object *parent, byte subType, uint16 index, const Common::String &name) :
00038         Object(parent, subType, index, name),
00039         _facesCount(0) {
00040     _type = TYPE;
00041 }
00042 
00043 Floor::~Floor() {
00044 }
00045 
00046 Math::Vector3d Floor::getVertex(uint32 index) const {
00047     return _vertices[index];
00048 }
00049 
00050 int32 Floor::findFaceContainingPoint(const Math::Vector3d &point) const {
00051     for (uint32 i = 0; i < _faces.size(); i++) {
00052         if (_faces[i]->hasVertices() && _faces[i]->isPointInside(point)) {
00053             return i;
00054         }
00055     }
00056 
00057     return -1;
00058 }
00059 
00060 void Floor::computePointHeightInFace(Math::Vector3d &point, uint32 faceIndex) const {
00061     _faces[faceIndex]->computePointHeight(point);
00062 }
00063 
00064 int32 Floor::findFaceHitByRay(const Math::Ray &ray, Math::Vector3d &intersection) const {
00065     for (uint32 i = 0; i < _faces.size(); i++) {
00066         // TODO: Check the ray's intersection with an AABB first if this ends up being slow
00067         if (_faces[i]->intersectRay(ray, intersection)) {
00068             if (_faces[i]->isEnabled()) {
00069                 return i;
00070             } else {
00071                 return -1; // Disabled faces block the ray
00072             }
00073         }
00074     }
00075 
00076     return -1;
00077 }
00078 
00079 int32 Floor::findFaceClosestToRay(const Math::Ray &ray, Math::Vector3d &center) const {
00080     float minDistance = FLT_MAX;
00081     int32 minFace = -1;
00082     for (uint32 i = 0; i < _faces.size(); i++) {
00083         if (_faces[i]->isEnabled() && _faces[i]->hasVertices()) {
00084             float distance = _faces[i]->distanceToRay(ray);
00085             if (distance < minDistance) {
00086                 minFace = i;
00087                 minDistance = distance;
00088             }
00089         }
00090     }
00091 
00092     if (minFace >= 0) {
00093         center = _faces[minFace]->getCenter();
00094     }
00095 
00096     return minFace;
00097 }
00098 
00099 float Floor::getDistanceFromCamera(uint32 faceIndex) const {
00100     FloorFace *face = _faces[faceIndex];
00101     return face->getDistanceFromCamera();
00102 }
00103 
00104 FloorFace *Floor::getFace(uint32 index) const {
00105     return _faces[index];
00106 }
00107 
00108 bool Floor::isSegmentInside(const Math::Line3d &segment) const {
00109     // The segment is inside the floor if at least one of its extremities is,
00110     // and it does not cross any floor border / disabled floor faces
00111 
00112     int32 beginFace = findFaceContainingPoint(segment.begin());
00113     if (beginFace < 0) {
00114         // The segment begin point is not on the floor
00115         return false;
00116     }
00117 
00118     if (!_faces[beginFace]->isEnabled()) {
00119         // The segment begin point is not enabled
00120         return false;
00121     }
00122 
00123     for (uint i = 0; i < _edges.size(); i++) {
00124         const FloorEdge &edge = _edges[i];
00125         if ((edge.isFloorBorder() || !edge.isEnabled()) && edge.intersectsSegment(this, segment)) {
00126             return false;
00127         }
00128     }
00129 
00130     return true;
00131 }
00132 
00133 void Floor::readData(Formats::XRCReadStream *stream) {
00134     _facesCount = stream->readUint32LE();
00135     uint32 vertexCount = stream->readUint32LE();
00136 
00137     for (uint i = 0; i < vertexCount; i++) {
00138         Math::Vector3d v = stream->readVector3();
00139         _vertices.push_back(v);
00140     }
00141 }
00142 
00143 void Floor::onAllLoaded() {
00144     Object::onAllLoaded();
00145 
00146     _faces = listChildren<FloorFace>();
00147 
00148     buildEdgeList();
00149 }
00150 
00151 void Floor::saveLoad(ResourceSerializer *serializer) {
00152     for (uint i = 0; i < _edges.size(); i++) {
00153         _edges[i].saveLoad(serializer);
00154     }
00155 }
00156 
00157 void Floor::buildEdgeList() {
00158     _edges.clear();
00159 
00160     // Add the triangle edges from all our faces
00161     for (uint i = 0; i < _faces.size(); i++) {
00162         if (_faces[i]->hasVertices()) {
00163             addFaceEdgeToList(i, 2, 0);
00164             addFaceEdgeToList(i, 0, 1);
00165             addFaceEdgeToList(i, 1, 2);
00166         }
00167     }
00168 
00169     // Add the edges to their faces
00170     for (uint i = 0; i < _edges.size(); i++) {
00171         int32 faceIndex1 = _edges[i].getFaceIndex1();
00172         int32 faceIndex2 = _edges[i].getFaceIndex2();
00173 
00174         if (faceIndex1 >= 0) {
00175             _faces[faceIndex1]->addEdge(&_edges[i]);
00176         }
00177 
00178         if (faceIndex2 >= 0) {
00179             _faces[faceIndex2]->addEdge(&_edges[i]);
00180         }
00181     }
00182 
00183     // Build a list of neighbours for each edge
00184     for (uint i = 0; i < _edges.size(); i++) {
00185         _edges[i].buildNeighbours(this);
00186         _edges[i].computeMiddle(this);
00187     }
00188 }
00189 
00190 void Floor::addFaceEdgeToList(uint32 faceIndex, uint32 index1, uint32 index2) {
00191     uint32 vertexIndex1 = _faces[faceIndex]->getVertexIndex(index1);
00192     uint32 vertexIndex2 = _faces[faceIndex]->getVertexIndex(index2);
00193     uint32 startIndex = MIN(vertexIndex1, vertexIndex2);
00194     uint32 endIndex = MAX(vertexIndex1, vertexIndex2);
00195 
00196     // Check if we already have an edge with the same vertices
00197     for (uint i = 0; i < _edges.size(); i++) {
00198         if (_edges[i].hasVertices(startIndex, endIndex)) {
00199             _edges[i].setOtherFace(faceIndex);
00200             return;
00201         }
00202     }
00203 
00204     _edges.push_back(FloorEdge(startIndex, endIndex, faceIndex));
00205 }
00206 
00207 void Floor::enableFloorField(FloorField *floorfield, bool enable) {
00208     for (uint i = 0; i < _faces.size(); i++) {
00209         if (floorfield->hasFace(i)) {
00210             _faces[i]->enable(enable);
00211         }
00212     }
00213 }
00214 
00215 void Floor::printData() {
00216     debug("face count: %d", _facesCount);
00217 
00218     Common::Debug debug = streamDbg();
00219     for (uint i = 0; i < _vertices.size(); i++) {
00220         debug << i << ": " << _vertices[i] << "\n";
00221     }
00222 }
00223 
00224 FloorEdge::FloorEdge(uint16 vertexIndex1, uint16 vertexIndex2, uint32 faceIndex1) :
00225         _vertexIndex1(vertexIndex1),
00226         _vertexIndex2(vertexIndex2),
00227         _faceIndex1(faceIndex1),
00228         _faceIndex2(-1),
00229         _enabled(true) {
00230 }
00231 
00232 bool FloorEdge::hasVertices(uint16 vertexIndex1, uint16 vertexIndex2) const {
00233     return _vertexIndex1 == vertexIndex1 && _vertexIndex2 == vertexIndex2;
00234 }
00235 
00236 void FloorEdge::setOtherFace(uint32 faceIndex) {
00237     _faceIndex2 = faceIndex;
00238 }
00239 
00240 Common::Array<FloorEdge *> FloorEdge::getNeighbours() const {
00241     return _neighbours;
00242 }
00243 
00244 float FloorEdge::costTo(const FloorEdge *other) const {
00245     return _middle.getDistanceTo(other->_middle);
00246 }
00247 
00248 Math::Vector3d FloorEdge::getPosition() const {
00249     return _middle;
00250 }
00251 
00252 void FloorEdge::buildNeighbours(const Floor *floor) {
00253     _neighbours.clear();
00254 
00255     if (_faceIndex1 >= 0) {
00256         addNeighboursFromFace(floor->getFace(_faceIndex1));
00257     }
00258 
00259     if (_faceIndex2 >= 0) {
00260         addNeighboursFromFace(floor->getFace(_faceIndex2));
00261     }
00262 }
00263 
00264 void FloorEdge::addNeighboursFromFace(const FloorFace *face) {
00265     Common::Array<FloorEdge *> faceEdges = face->getEdges();
00266     for (uint i = 0; i < faceEdges.size(); i++) {
00267         if (faceEdges[i] != this) {
00268             _neighbours.push_back(faceEdges[i]);
00269         }
00270     }
00271 }
00272 
00273 void FloorEdge::computeMiddle(const Floor *floor) {
00274     Math::Vector3d vertex1 = floor->getVertex(_vertexIndex1);
00275     Math::Vector3d vertex2 = floor->getVertex(_vertexIndex2);
00276     _middle = (vertex1 + vertex2) / 2.0;
00277 }
00278 
00279 int32 FloorEdge::getFaceIndex1() const {
00280     return _faceIndex1;
00281 }
00282 
00283 int32 FloorEdge::getFaceIndex2() const {
00284     return _faceIndex2;
00285 }
00286 
00287 bool FloorEdge::isFloorBorder() const {
00288     return _faceIndex2 == -1;
00289 }
00290 
00291 bool FloorEdge::intersectLine2d(const Math::Line3d &s1, const Math::Line3d &s2) {
00292     const Math::Vector3d &s1begin = s1.begin();
00293     const Math::Vector3d &s1end = s1.end();
00294     const Math::Vector3d &s2begin = s2.begin();
00295     const Math::Vector3d &s2end = s2.end();
00296 
00297     float denom = ((s2end.y() - s2begin.y()) * (s1end.x() - s1begin.x())) -
00298                   ((s2end.x() - s2begin.x()) * (s1end.y() - s1begin.y()));
00299 
00300     float nume_a = ((s2end.x() - s2begin.x()) * (s1begin.y() - s2begin.y())) -
00301                    ((s2end.y() - s2begin.y()) * (s1begin.x() - s2begin.x()));
00302 
00303     float nume_b = ((s1end.x() - s1begin.x()) * (s1begin.y() - s2begin.y())) -
00304                    ((s1end.y() - s1begin.y()) * (s1begin.x() - s2begin.x()));
00305 
00306     if (denom == 0.0f) {
00307         return false; // Segments are collinear
00308     }
00309 
00310     float ua = nume_a / denom;
00311     float ub = nume_b / denom;
00312 
00313     // Non inclusive bounds check, one of the vertices of one segment being inside
00314     // the other segment is not considered to be an intersection.
00315     // This is the only difference with Line3d::intersectLine2d.
00316     return ua > 0 && ua < 1 && ub > 0 && ub < 1;
00317 }
00318 
00319 bool FloorEdge::intersectsSegment(const Floor *floor, const Math::Line3d &segment) const {
00320     Math::Vector3d vertex1 = floor->getVertex(_vertexIndex1);
00321     Math::Vector3d vertex2 = floor->getVertex(_vertexIndex2);
00322     Math::Line3d edgeSegment = Math::Line3d(vertex1, vertex2);
00323 
00324     return intersectLine2d(edgeSegment, segment);
00325 }
00326 
00327 void FloorEdge::enable(bool e) {
00328     _enabled = e;
00329 }
00330 
00331 bool FloorEdge::isEnabled() const {
00332     return _enabled;
00333 }
00334 
00335 void FloorEdge::saveLoad(ResourceSerializer *serializer) {
00336     serializer->syncAsUint32LE(_enabled);
00337 }
00338 
00339 } // End of namespace Resources
00340 } // End of namespace Stark


Generated on Sat Feb 16 2019 05:00:51 for ResidualVM by doxygen 1.7.1
curved edge   curved edge