mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
218 lines
6.4 KiB
C++
218 lines
6.4 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "engines/stark/resources/floorface.h"
|
|
|
|
#include "engines/stark/debug.h"
|
|
#include "engines/stark/formats/xrc.h"
|
|
#include "engines/stark/resources/floor.h"
|
|
|
|
namespace Stark {
|
|
namespace Resources {
|
|
|
|
FloorFace::FloorFace(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
|
Object(parent, subType, index, name),
|
|
_distanceFromCamera(0),
|
|
_unk2(0) {
|
|
_type = TYPE;
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
|
|
_indices[i] = 0;
|
|
}
|
|
}
|
|
|
|
FloorFace::~FloorFace() {
|
|
}
|
|
|
|
bool FloorFace::isPointInside(const Math::Vector3d &point) const {
|
|
// Compute the barycentric coordinates of the point in the triangle
|
|
float area = 1.0f / 2.0f
|
|
* (-_vertices[1].y() * _vertices[2].x()
|
|
+ _vertices[0].y() * (-_vertices[1].x() + _vertices[2].x())
|
|
+ _vertices[0].x() * (_vertices[1].y() - _vertices[2].y())
|
|
+ _vertices[1].x() * _vertices[2].y());
|
|
|
|
float s = (_vertices[0].y() * _vertices[2].x() - _vertices[0].x() * _vertices[2].y()
|
|
+ (_vertices[2].y() - _vertices[0].y()) * point.x()
|
|
+ (_vertices[0].x() - _vertices[2].x()) * point.y())
|
|
/ (2.0f * area);
|
|
|
|
float t = (_vertices[0].x() * _vertices[1].y() - _vertices[0].y() * _vertices[1].x()
|
|
+ (_vertices[0].y() - _vertices[1].y()) * point.x()
|
|
+ (_vertices[1].x() - _vertices[0].x()) * point.y())
|
|
/ (2.0f * area);
|
|
|
|
// Check the coordinates are in the triangle
|
|
return s > 0.0f && t > 0.0f && (1.0f - s - t) > 0.0f;
|
|
}
|
|
|
|
void FloorFace::computePointHeight(Math::Vector3d &point) const {
|
|
// Compute the barycentric coordinates of the point in the triangle
|
|
float area = 1.0f / 2.0f
|
|
* (-_vertices[1].y() * _vertices[2].x()
|
|
+ _vertices[0].y() * (-_vertices[1].x() + _vertices[2].x())
|
|
+ _vertices[0].x() * (_vertices[1].y() - _vertices[2].y())
|
|
+ _vertices[1].x() * _vertices[2].y());
|
|
|
|
float s = (_vertices[0].y() * _vertices[2].x() - _vertices[0].x() * _vertices[2].y()
|
|
+ (_vertices[2].y() - _vertices[0].y()) * point.x()
|
|
+ (_vertices[0].x() - _vertices[2].x()) * point.y())
|
|
/ (2.0f * area);
|
|
|
|
float t = (_vertices[0].x() * _vertices[1].y() - _vertices[0].y() * _vertices[1].x()
|
|
+ (_vertices[0].y() - _vertices[1].y()) * point.x()
|
|
+ (_vertices[1].x() - _vertices[0].x()) * point.y())
|
|
/ (2.0f * area);
|
|
|
|
// Compute the Z coordinate of the point
|
|
float pointZ = (1.0f - s - t) * _vertices[0].z() + s * _vertices[1].z() + t * _vertices[2].z();
|
|
|
|
point.setValue(2, pointZ);
|
|
}
|
|
|
|
bool FloorFace::intersectRay(const Math::Ray &ray, Math::Vector3d &intersection) const {
|
|
// Compute the triangle plane normal
|
|
Math::Vector3d n = Math::Vector3d::crossProduct(_vertices[1] - _vertices[0], _vertices[2] - _vertices[0]);
|
|
if (n == Math::Vector3d()) {
|
|
return false; // We don't handle degenerate triangles
|
|
}
|
|
|
|
// Point on triangle plane: dot(P - _vertices[0], n) = 0
|
|
// Point on ray: P = origin + r * direction
|
|
// Point on both => r = - dot(n, origin - _vertices[0]) / dot(n, direction)
|
|
|
|
float num = -Math::Vector3d::dotProduct(n, ray.getOrigin() - _vertices[0]);
|
|
float denom = Math::Vector3d::dotProduct(n, ray.getDirection());
|
|
|
|
if (fabs(denom) < 0.00001) {
|
|
// The ray is parallel to the plane
|
|
return false;
|
|
}
|
|
|
|
float r = num / denom;
|
|
if (r < 0.0) {
|
|
// The ray goes away from the triangle
|
|
return false;
|
|
}
|
|
|
|
// Compute the intersection point between the triangle plane and the ray
|
|
intersection = ray.getOrigin() + r * ray.getDirection();
|
|
|
|
// Check the intersection point is inside the triangle
|
|
return isPointInside(intersection);
|
|
}
|
|
|
|
float FloorFace::distanceToRay(const Math::Ray &ray) const {
|
|
Math::Vector3d center = getCenter();
|
|
return Math::Vector3d::crossProduct(ray.getDirection(), center - ray.getOrigin()).getMagnitude();
|
|
}
|
|
|
|
float FloorFace::getDistanceFromCamera() const {
|
|
return _distanceFromCamera;
|
|
}
|
|
|
|
int16 FloorFace::getVertexIndex(int32 index) const {
|
|
assert(index < 3);
|
|
return _indices[index];
|
|
}
|
|
|
|
void FloorFace::addEdge(FloorEdge *edge) {
|
|
_edges.push_back(edge);
|
|
}
|
|
|
|
Common::Array<FloorEdge *> FloorFace::getEdges() const {
|
|
return _edges;
|
|
}
|
|
|
|
FloorEdge *FloorFace::findNearestEdge(const Math::Vector3d &point) const {
|
|
float minDistance = -1;
|
|
FloorEdge *edge = nullptr;
|
|
|
|
for (uint i = 0; i < _edges.size(); i++) {
|
|
if (!_edges[i]->isEnabled()) {
|
|
continue;
|
|
}
|
|
|
|
float distance = (point - _edges[i]->getPosition()).getSquareMagnitude();
|
|
|
|
|
|
if (!edge || distance < minDistance) {
|
|
minDistance = distance;
|
|
edge = _edges[i];
|
|
}
|
|
}
|
|
|
|
return edge;
|
|
}
|
|
|
|
Math::Vector3d FloorFace::getCenter() const {
|
|
return (_vertices[0] + _vertices[1] + _vertices[2]) / 3.0;
|
|
}
|
|
|
|
bool FloorFace::hasVertices() const {
|
|
return _indices[0] != 0 || _indices[1] != 0 || _indices[2] != 0;
|
|
}
|
|
|
|
void FloorFace::enable(bool e) {
|
|
for (uint i = 0; i < _edges.size(); i++) {
|
|
_edges[i]->enable(e);
|
|
}
|
|
}
|
|
|
|
bool FloorFace::isEnabled() const {
|
|
for (uint i = 0; i < _edges.size(); i++) {
|
|
if (_edges[i]->isEnabled()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FloorFace::readData(Formats::XRCReadStream *stream) {
|
|
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
|
|
_indices[i] = stream->readSint16LE();
|
|
}
|
|
|
|
_distanceFromCamera = stream->readFloatLE();
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
|
|
stream->readSint16LE(); // Skipped in the original
|
|
}
|
|
|
|
_unk2 = stream->readFloatLE();
|
|
}
|
|
|
|
void FloorFace::onAllLoaded() {
|
|
Object::onAllLoaded();
|
|
Floor *floor = Object::cast<Floor>(_parent);
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
|
|
_vertices[i] = floor->getVertex(_indices[i]);
|
|
}
|
|
}
|
|
|
|
void FloorFace::printData() {
|
|
debug("indices: %d %d %d, distanceFromCamera %f, unk2 %f", _indices[0], _indices[1], _indices[2], _distanceFromCamera, _unk2);
|
|
}
|
|
|
|
} // End of namespace Resources
|
|
} // End of namespace Stark
|