2016-08-11 20:39:14 +02:00
|
|
|
// Cyphesis Online RPG Server and AI Engine
|
|
|
|
|
// Copyright (C) 2016 Erik Ogenvik
|
|
|
|
|
//
|
|
|
|
|
// 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 2 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, write to the Free Software Foundation,
|
|
|
|
|
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
|
|
|
|
#include "GeometryProperty.h"
|
2017-01-02 23:52:49 +01:00
|
|
|
#include "physics/Convert.h"
|
|
|
|
|
#include "common/log.h"
|
2018-01-29 12:23:40 +01:00
|
|
|
#include "common/globals.h"
|
2018-01-29 22:49:01 +01:00
|
|
|
#include "common/TypeNode.h"
|
2018-01-29 12:23:40 +01:00
|
|
|
#include "OgreMeshDeserializer.h"
|
2018-01-29 22:49:01 +01:00
|
|
|
#include "BBoxProperty.h"
|
|
|
|
|
|
|
|
|
|
#include <wfmath/atlasconv.h>
|
2017-01-02 23:52:49 +01:00
|
|
|
|
|
|
|
|
#include <BulletCollision/CollisionShapes/btSphereShape.h>
|
|
|
|
|
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
|
|
|
|
#include <BulletCollision/CollisionShapes/btCylinderShape.h>
|
|
|
|
|
#include <BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h>
|
|
|
|
|
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
|
|
|
|
|
#include <BulletCollision/CollisionShapes/btCapsuleShape.h>
|
2018-03-05 12:42:10 +01:00
|
|
|
#include <BulletCollision/Gimpact/btGImpactShape.h>
|
2018-01-29 12:23:40 +01:00
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
|
#include <common/AtlasQuery.h>
|
2018-02-01 11:48:52 +01:00
|
|
|
#include <boost/filesystem/path.hpp>
|
|
|
|
|
#include <boost/filesystem/fstream.hpp>
|
2018-02-02 14:13:18 +01:00
|
|
|
#include <common/debug.h>
|
2018-03-05 12:42:10 +01:00
|
|
|
#include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>
|
2018-03-06 16:30:08 +01:00
|
|
|
#include <BulletCollision/CollisionShapes/btConvexHullShape.h>
|
2018-03-07 15:22:13 +01:00
|
|
|
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
|
|
|
|
#include <common/compose.hpp>
|
2017-01-02 23:52:49 +01:00
|
|
|
|
2016-08-11 20:39:14 +02:00
|
|
|
const std::string GeometryProperty::property_name = "geometry";
|
|
|
|
|
const std::string GeometryProperty::property_atlastype = "map";
|
|
|
|
|
|
2018-03-07 15:22:13 +01:00
|
|
|
auto createBoxFn = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
|
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
|
|
|
|
auto btSize = Convert::toBullet(size * 0.5).absolute();
|
|
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
|
|
|
|
return std::make_pair(new btBoxShape(btSize), std::shared_ptr<btCollisionShape>());
|
|
|
|
|
};
|
|
|
|
|
|
2017-03-19 14:36:35 +01:00
|
|
|
void GeometryProperty::set(const Atlas::Message::Element& data)
|
2017-01-02 23:52:49 +01:00
|
|
|
{
|
|
|
|
|
Property<Atlas::Message::MapType>::set(data);
|
|
|
|
|
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
std::shared_ptr<OgreMeshDeserializer> deserializer;
|
|
|
|
|
AtlasQuery::find<std::string>(data, "path", [&](const std::string& path) {
|
|
|
|
|
try {
|
|
|
|
|
if (boost::algorithm::ends_with(path, ".mesh")) {
|
|
|
|
|
boost::filesystem::path fullpath = boost::filesystem::path(assets_directory) / path;
|
|
|
|
|
boost::filesystem::fstream fileStream(fullpath);
|
|
|
|
|
if (fileStream) {
|
|
|
|
|
deserializer.reset(new OgreMeshDeserializer(fileStream));
|
|
|
|
|
deserializer->deserialize();
|
|
|
|
|
m_meshBounds = deserializer->m_bounds;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
log(ERROR, "Could not find geometry file at " + fullpath.string());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log(ERROR, "Could not recognize geometry file type: " + path);
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::exception& ex) {
|
|
|
|
|
log(ERROR, "Exception when trying to parse geometry at " + path);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2018-03-19 22:05:48 +01:00
|
|
|
auto sphereCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
|
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
|
|
|
|
float minRadius = std::min(size.x(), std::min(size.y(), size.z())) * 0.5f;
|
|
|
|
|
float xOffset = bbox.lowCorner().x() + (size.x() / 2.0f);
|
|
|
|
|
float yOffset = bbox.lowCorner().y() + (size.y() / 2.0f);
|
|
|
|
|
float zOffset = bbox.lowCorner().z() + (size.z() / 2.0f);
|
|
|
|
|
|
|
|
|
|
centerOfMassOffset = -btVector3(xOffset, yOffset, zOffset);
|
|
|
|
|
return std::make_pair(new btSphereShape(minRadius), std::shared_ptr<btCollisionShape>());
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-02 14:13:18 +01:00
|
|
|
auto I = m_data.find("type");
|
2017-01-02 23:52:49 +01:00
|
|
|
if (I != m_data.end() && I->second.isString()) {
|
2017-03-19 14:36:35 +01:00
|
|
|
const std::string& shapeType = I->second.String();
|
2017-01-02 23:52:49 +01:00
|
|
|
if (shapeType == "sphere") {
|
2018-03-19 22:05:48 +01:00
|
|
|
mShapeCreator = sphereCreator;
|
2018-01-10 15:11:20 +01:00
|
|
|
} else if (shapeType == "capsule-y") {
|
2018-03-19 22:05:48 +01:00
|
|
|
mShapeCreator = [&, sphereCreator](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
2018-02-20 22:23:48 +01:00
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2017-01-02 23:52:49 +01:00
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
2018-01-10 15:11:20 +01:00
|
|
|
float minRadius = std::min(size.x(), size.z()) * 0.5f;
|
2017-01-02 23:52:49 +01:00
|
|
|
//subtract the radius times 2 from the height
|
2018-01-10 15:11:20 +01:00
|
|
|
float height = size.y() - (minRadius * 2.0f);
|
2017-01-02 23:52:49 +01:00
|
|
|
//If the resulting height is negative we need to use a sphere instead.
|
|
|
|
|
if (height > 0) {
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(new btCapsuleShape(minRadius, height), std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
} else {
|
2018-03-19 22:05:48 +01:00
|
|
|
return sphereCreator(bbox, size, centerOfMassOffset, 0);
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} else if (shapeType == "capsule-x") {
|
2018-03-05 12:42:10 +01:00
|
|
|
mShapeCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
2018-02-20 22:23:48 +01:00
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2017-01-02 23:52:49 +01:00
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
|
|
|
|
float minRadius = std::min(size.z(), size.y()) * 0.5f;
|
|
|
|
|
//subtract the radius times 2 from the height
|
|
|
|
|
float height = size.x() - (minRadius * 2.0f);
|
|
|
|
|
//If the resulting height is negative we need to use a sphere instead.
|
|
|
|
|
if (height > 0) {
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(new btCapsuleShapeX(minRadius, height), std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
} else {
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(new btSphereShape(minRadius), std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
};
|
2018-01-10 15:11:20 +01:00
|
|
|
} else if (shapeType == "capsule-z") {
|
2018-03-05 12:42:10 +01:00
|
|
|
mShapeCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
2018-02-20 22:23:48 +01:00
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2017-01-02 23:52:49 +01:00
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
2018-01-10 15:11:20 +01:00
|
|
|
float minRadius = std::min(size.x(), size.y()) * 0.5f;
|
2017-01-02 23:52:49 +01:00
|
|
|
//subtract the radius times 2 from the height
|
2018-01-10 15:11:20 +01:00
|
|
|
float height = size.z() - (minRadius * 2.0f);
|
2017-01-02 23:52:49 +01:00
|
|
|
//If the resulting height is negative we need to use a sphere instead.
|
|
|
|
|
if (height > 0) {
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(new btCapsuleShapeZ(minRadius, height), std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
} else {
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(new btSphereShape(minRadius), std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} else if (shapeType == "box") {
|
|
|
|
|
mShapeCreator = createBoxFn;
|
2018-01-10 15:11:20 +01:00
|
|
|
} else if (shapeType == "cylinder-y") {
|
2018-03-05 12:42:10 +01:00
|
|
|
mShapeCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
2018-02-20 22:23:48 +01:00
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2017-01-02 23:52:49 +01:00
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
2017-03-19 14:36:35 +01:00
|
|
|
btCylinderShape* shape = new btCylinderShape(btVector3(1, 1, 1));
|
2017-01-02 23:52:49 +01:00
|
|
|
shape->setLocalScaling(Convert::toBullet(size * 0.5f));
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(shape, std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if (shapeType == "cylinder-x") {
|
2018-03-05 12:42:10 +01:00
|
|
|
mShapeCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
2018-02-20 22:23:48 +01:00
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2017-01-02 23:52:49 +01:00
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
2017-03-19 14:36:35 +01:00
|
|
|
btCylinderShape* shape = new btCylinderShapeX(btVector3(1, 1, 1));
|
2017-01-02 23:52:49 +01:00
|
|
|
shape->setLocalScaling(Convert::toBullet(size * 0.5f));
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(shape, std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
};
|
2018-01-10 15:11:20 +01:00
|
|
|
} else if (shapeType == "cylinder-z") {
|
2018-03-05 12:42:10 +01:00
|
|
|
mShapeCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size, btVector3& centerOfMassOffset, float)
|
2018-02-20 22:23:48 +01:00
|
|
|
-> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2017-01-02 23:52:49 +01:00
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
2017-03-19 14:36:35 +01:00
|
|
|
btCylinderShape* shape = new btCylinderShapeZ(btVector3(1, 1, 1));
|
2017-01-02 23:52:49 +01:00
|
|
|
shape->setLocalScaling(Convert::toBullet(size * 0.5f));
|
2017-03-19 14:36:35 +01:00
|
|
|
return std::make_pair(shape, std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
};
|
|
|
|
|
} else if (shapeType == "mesh") {
|
2018-02-15 11:18:15 +01:00
|
|
|
buildMeshCreator(std::move(deserializer));
|
2018-03-07 15:22:13 +01:00
|
|
|
} else if (shapeType == "compound") {
|
|
|
|
|
buildCompoundCreator();
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
2018-02-02 14:13:18 +01:00
|
|
|
} else {
|
|
|
|
|
log(WARNING, "Geometry property without 'type' attribute set. Property value: " + debug_tostring(data));
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-05 12:42:10 +01:00
|
|
|
std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> GeometryProperty::createShape(const WFMath::AxisBox<3>& bbox, btVector3& centerOfMassOffset, float mass) const
|
2017-01-02 23:52:49 +01:00
|
|
|
{
|
|
|
|
|
auto size = bbox.highCorner() - bbox.lowCorner();
|
|
|
|
|
if (mShapeCreator) {
|
2018-03-05 12:42:10 +01:00
|
|
|
return mShapeCreator(bbox, size, centerOfMassOffset, mass);
|
2017-01-02 23:52:49 +01:00
|
|
|
} else {
|
|
|
|
|
auto btSize = Convert::toBullet(size * 0.5).absolute();
|
|
|
|
|
centerOfMassOffset = -Convert::toBullet(bbox.getCenter());
|
2018-03-05 12:42:10 +01:00
|
|
|
auto shape = new btBoxShape(btSize);
|
|
|
|
|
return std::make_pair(shape, std::shared_ptr<btCollisionShape>());
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
void GeometryProperty::buildMeshCreator(std::shared_ptr<OgreMeshDeserializer> meshDeserializer)
|
2017-01-02 23:52:49 +01:00
|
|
|
{
|
2018-02-15 11:18:15 +01:00
|
|
|
//Shared pointers since we want these values to survive as long as "meshShape" is alive.
|
|
|
|
|
std::shared_ptr<std::vector<float>> verts(new std::vector<float>());
|
2018-02-15 16:52:33 +01:00
|
|
|
std::shared_ptr<std::vector<unsigned int>> indices(new std::vector<unsigned int>());
|
2017-01-02 23:52:49 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
if (!meshDeserializer) {
|
2017-01-02 23:52:49 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
auto vertsI = m_data.find("vertices");
|
|
|
|
|
if (vertsI != m_data.end() && vertsI->second.isList()) {
|
|
|
|
|
auto trisI = m_data.find("indices");
|
|
|
|
|
if (trisI != m_data.end() && trisI->second.isList()) {
|
|
|
|
|
auto& vertsList = vertsI->second.List();
|
|
|
|
|
auto& trisList = trisI->second.List();
|
2017-01-02 23:52:49 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
if (vertsList.empty()) {
|
|
|
|
|
log(ERROR, "Vertices is empty for mesh.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-01-02 23:52:49 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
if (vertsList.size() % 3 != 0) {
|
|
|
|
|
log(ERROR, "Vertices is not even with 3.");
|
2017-03-19 14:36:35 +01:00
|
|
|
return;
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
if (trisList.empty()) {
|
|
|
|
|
log(ERROR, "Triangles is empty for mesh.");
|
2017-03-19 14:36:35 +01:00
|
|
|
return;
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
2018-02-15 11:18:15 +01:00
|
|
|
|
|
|
|
|
if (trisList.size() % 3 != 0) {
|
|
|
|
|
log(ERROR, "Triangles is not even with 3.");
|
2017-03-19 14:36:35 +01:00
|
|
|
return;
|
2017-01-02 23:52:49 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
int numberOfVertices = static_cast<int>(vertsList.size() / 3);
|
2016-08-11 20:39:14 +02:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
auto& local_verts = *verts.get();
|
|
|
|
|
auto& local_indices = *indices.get();
|
|
|
|
|
local_verts.resize(vertsList.size());
|
2017-02-12 21:41:11 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
for (size_t i = 0; i < vertsList.size(); i += 3) {
|
|
|
|
|
if (!vertsList[i].isFloat() || !vertsList[i + 1].isFloat() || !vertsList[i + 2].isFloat()) {
|
|
|
|
|
log(ERROR, "Vertex data was not a float for mesh.");
|
|
|
|
|
return;
|
2018-01-29 12:23:40 +01:00
|
|
|
}
|
2018-02-15 11:18:15 +01:00
|
|
|
local_verts[i] = (float) vertsList[i].Float();
|
|
|
|
|
local_verts[i + 1] = (float) vertsList[i + 1].Float();
|
|
|
|
|
local_verts[i + 2] = (float) vertsList[i + 2].Float();
|
|
|
|
|
}
|
2018-01-29 12:23:40 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
local_indices.resize(trisList.size());
|
|
|
|
|
for (size_t i = 0; i < trisList.size(); i += 3) {
|
|
|
|
|
if (!trisList[i].isInt() || !trisList[i + 1].isInt() || !trisList[i + 2].isInt()) {
|
|
|
|
|
log(ERROR, "Index data was not an int for mesh.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (trisList[i].Int() >= numberOfVertices || trisList[i + 1].Int() >= numberOfVertices || trisList[i + 2].Int() >= numberOfVertices) {
|
|
|
|
|
log(ERROR, "Index data was out of bounds for vertices for mesh.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-02-15 16:52:33 +01:00
|
|
|
local_indices[i] = (unsigned int) trisList[i].Int();
|
|
|
|
|
local_indices[i + 1] = (unsigned int) trisList[i + 1].Int();
|
|
|
|
|
local_indices[i + 2] = (unsigned int) trisList[i + 2].Int();
|
2018-02-15 11:18:15 +01:00
|
|
|
}
|
2018-01-29 12:23:40 +01:00
|
|
|
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
} else {
|
|
|
|
|
log(ERROR, "Could not find list of triangles for mesh.");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log(ERROR, "Could not find list of vertices for mesh.");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (meshDeserializer) {
|
|
|
|
|
*indices = std::move(meshDeserializer->m_indices);
|
|
|
|
|
*verts = std::move(meshDeserializer->m_vertices);
|
|
|
|
|
} else {
|
|
|
|
|
//No mesh deserializer, and no other mesh data, return.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-29 12:23:40 +01:00
|
|
|
|
|
|
|
|
|
2018-02-15 16:52:33 +01:00
|
|
|
if (indices->empty() || verts->empty()) {
|
|
|
|
|
log(ERROR, "Vertices or indices were empty.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto index : *indices) {
|
|
|
|
|
if (index >= verts->size() / 3) {
|
|
|
|
|
log(ERROR, "Index out of bounds.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-29 12:23:40 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
int vertStride = sizeof(float) * 3;
|
2018-02-15 16:52:33 +01:00
|
|
|
int indexStride = sizeof(unsigned int) * 3;
|
|
|
|
|
|
|
|
|
|
int indicesCount = static_cast<int>(indices->size() / 3);
|
|
|
|
|
int vertexCount = static_cast<int>(verts->size() / 3);
|
2018-01-29 12:23:40 +01:00
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
//Make sure to capture "verts" and "indices" so that they are kept around.
|
2018-03-05 12:42:10 +01:00
|
|
|
std::shared_ptr<btTriangleIndexVertexArray> triangleVertexArray(new btTriangleIndexVertexArray(indicesCount, reinterpret_cast<int*>(indices->data()), indexStride,
|
|
|
|
|
vertexCount, verts->data(), vertStride),
|
|
|
|
|
[verts, indices](btTriangleIndexVertexArray* p) {
|
|
|
|
|
delete p;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<btBvhTriangleMeshShape> meshShape(new btBvhTriangleMeshShape(triangleVertexArray.get(), true, true),
|
|
|
|
|
[triangleVertexArray](btBvhTriangleMeshShape* p) {
|
2018-02-20 22:23:48 +01:00
|
|
|
delete p;
|
|
|
|
|
});
|
2018-02-15 11:18:15 +01:00
|
|
|
meshShape->setLocalScaling(btVector3(1, 1, 1));
|
|
|
|
|
//Store the bounds, so that the "bbox" property can be updated when this is applied to a TypeNode
|
2018-02-20 22:23:48 +01:00
|
|
|
m_meshBounds = WFMath::AxisBox<3>(Convert::toWF<WFMath::Point<3>>(meshShape->getLocalAabbMin()),
|
|
|
|
|
Convert::toWF<WFMath::Point<3>>(meshShape->getLocalAabbMax()));
|
2018-02-15 11:18:15 +01:00
|
|
|
|
2018-03-06 16:30:08 +01:00
|
|
|
mShapeCreator = [meshShape, verts, triangleVertexArray](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size,
|
|
|
|
|
btVector3& centerOfMassOffset, float mass) -> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
2018-02-15 11:18:15 +01:00
|
|
|
//In contrast to other shapes there's no centerOfMassOffset for mesh shapes
|
|
|
|
|
centerOfMassOffset = btVector3(0, 0, 0);
|
|
|
|
|
btVector3 meshSize = meshShape->getLocalAabbMax() - meshShape->getLocalAabbMin();
|
|
|
|
|
btVector3 scaling(size.x() / meshSize.x(), size.y() / meshSize.y(), size.z() / meshSize.z());
|
2018-03-05 12:42:10 +01:00
|
|
|
|
|
|
|
|
//Due to performance reasons we should use different shapes depending on whether it's static (i.e. mass == 0) or not
|
|
|
|
|
if (mass == 0) {
|
|
|
|
|
return std::make_pair(new btScaledBvhTriangleMeshShape(meshShape.get(), scaling), meshShape);
|
|
|
|
|
} else {
|
2018-03-06 16:30:08 +01:00
|
|
|
auto shape = new btConvexHullShape(verts.get()->data(), verts.get()->size() / 3, sizeof(float) * 3);
|
2018-03-08 22:01:13 +01:00
|
|
|
|
|
|
|
|
//btConvexHullShape::optimizeConvexHull was introduced in 2.84. It's useful, but not necessary.
|
2018-03-08 22:14:16 +01:00
|
|
|
//version number 285 corresponds to version 2.84...
|
|
|
|
|
#if BT_BULLET_VERSION > 284
|
2018-03-06 16:30:08 +01:00
|
|
|
shape->optimizeConvexHull();
|
2018-03-08 22:01:13 +01:00
|
|
|
#endif
|
2018-03-06 16:30:08 +01:00
|
|
|
shape->recalcLocalAabb();
|
2018-03-05 12:42:10 +01:00
|
|
|
shape->setLocalScaling(scaling);
|
|
|
|
|
return std::make_pair(shape, meshShape);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
};
|
|
|
|
|
|
2018-01-29 12:23:40 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 11:18:15 +01:00
|
|
|
GeometryProperty* GeometryProperty::copy() const
|
|
|
|
|
{
|
|
|
|
|
return new GeometryProperty(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 22:49:01 +01:00
|
|
|
void GeometryProperty::install(TypeNode* typeNode, const std::string&)
|
|
|
|
|
{
|
2018-03-09 14:37:11 +01:00
|
|
|
//If there are valid mesh bounds read, and there's no bbox property already, add one.
|
2018-01-29 22:49:01 +01:00
|
|
|
if (m_meshBounds.isValid()) {
|
2018-03-09 14:53:53 +01:00
|
|
|
BBoxProperty* bBoxProperty = nullptr;
|
|
|
|
|
auto I = typeNode->defaults().find("bbox");
|
|
|
|
|
if (I == typeNode->defaults().end()) {
|
2018-02-22 19:34:08 +01:00
|
|
|
//Update the bbox property of the type if there are valid bounds from the mesh.
|
2018-03-09 14:53:53 +01:00
|
|
|
bBoxProperty = new BBoxProperty();
|
2018-02-22 19:34:08 +01:00
|
|
|
bBoxProperty->set(m_meshBounds.toAtlas());
|
2018-03-09 14:53:53 +01:00
|
|
|
//Mark the property as ephemeral since it's calulcated.
|
|
|
|
|
bBoxProperty->setFlags(flag_class | per_ephem);
|
2018-03-09 14:37:11 +01:00
|
|
|
bBoxProperty->install(typeNode, "bbox");
|
2018-03-09 14:53:53 +01:00
|
|
|
} else if ((I->second->flags() & per_ephem) != 0) {
|
|
|
|
|
bBoxProperty = dynamic_cast<BBoxProperty*>(I->second);
|
|
|
|
|
if (bBoxProperty) {
|
|
|
|
|
bBoxProperty->set(m_meshBounds.toAtlas());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bBoxProperty) {
|
2018-02-22 19:34:08 +01:00
|
|
|
typeNode->injectProperty("bbox", bBoxProperty);
|
|
|
|
|
}
|
2018-01-29 22:49:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-07 15:22:13 +01:00
|
|
|
void GeometryProperty::buildCompoundCreator()
|
|
|
|
|
{
|
|
|
|
|
mShapeCreator = [&](const WFMath::AxisBox<3>& bbox, const WFMath::Vector<3>& size,
|
|
|
|
|
btVector3& centerOfMassOffset, float mass) -> std::pair<btCollisionShape*, std::shared_ptr<btCollisionShape>> {
|
|
|
|
|
auto I = m_data.find("shapes");
|
|
|
|
|
if (I != m_data.end() && I->second.isList()) {
|
|
|
|
|
auto shapes = I->second.List();
|
2018-03-08 22:29:47 +01:00
|
|
|
#if BT_BULLET_VERSION > 283
|
2018-03-07 15:22:13 +01:00
|
|
|
btCompoundShape* compoundShape = new btCompoundShape(true, shapes.size());
|
2018-03-08 22:29:47 +01:00
|
|
|
#else
|
|
|
|
|
btCompoundShape* compoundShape = new btCompoundShape(true);
|
|
|
|
|
#endif
|
2018-03-07 15:22:13 +01:00
|
|
|
std::vector<btCollisionShape*> childShapes(shapes.size());
|
|
|
|
|
for (auto& shapeElement : shapes) {
|
|
|
|
|
if (shapeElement.isMap()) {
|
|
|
|
|
auto& shapeMap = shapeElement.Map();
|
|
|
|
|
AtlasQuery::find<std::string>(shapeMap, "type", [&](const std::string& type) {
|
|
|
|
|
if (type == "box") {
|
|
|
|
|
AtlasQuery::find<Atlas::Message::ListType>(shapeMap, "points", [&](const Atlas::Message::ListType& points) {
|
|
|
|
|
WFMath::AxisBox<3> shapeBox(points);
|
|
|
|
|
|
|
|
|
|
btTransform transform(btQuaternion::getIdentity());
|
|
|
|
|
transform.setOrigin(Convert::toBullet(shapeBox.getCenter()));
|
|
|
|
|
|
|
|
|
|
AtlasQuery::find<Atlas::Message::ListType>(shapeMap, "orientation", [&](const Atlas::Message::ListType& orientationList) {
|
|
|
|
|
transform.setRotation(Convert::toBullet(WFMath::Quaternion(orientationList)));
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto boxSize = shapeBox.highCorner() - shapeBox.lowCorner();
|
|
|
|
|
|
|
|
|
|
btBoxShape* boxShape = new btBoxShape(Convert::toBullet(boxSize / 2.f));
|
|
|
|
|
childShapes.emplace_back(boxShape);
|
|
|
|
|
compoundShape->addChildShape(transform, boxShape);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
//TODO: implement more shapes when needed. "box" should go a long way though.
|
|
|
|
|
log(WARNING, String::compose("Unrecognized compound shape type '%1'.", type));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
btVector3 aabbMin, aabbMax;
|
|
|
|
|
compoundShape->getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
|
|
|
|
|
|
|
|
|
|
centerOfMassOffset = btVector3(0, 0, 0);
|
|
|
|
|
btVector3 meshSize = aabbMax - aabbMin;
|
|
|
|
|
btVector3 scaling(size.x() / meshSize.x(), size.y() / meshSize.y(), size.z() / meshSize.z());
|
|
|
|
|
compoundShape->setLocalScaling(scaling);
|
|
|
|
|
|
|
|
|
|
return std::make_pair(compoundShape, std::shared_ptr<btCollisionShape>(nullptr, [childShapes](btCollisionShape* p) {
|
|
|
|
|
//Don't delete the shape, just the child shapes.
|
|
|
|
|
for (btCollisionShape* childShape : childShapes) {
|
|
|
|
|
delete childShape;
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
return createBoxFn(bbox, size, centerOfMassOffset, mass);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|