mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 17:00:13 +02:00
cleanup use of triangleSet for picking
This commit is contained in:
parent
32add6635d
commit
87bcced409
5 changed files with 134 additions and 272 deletions
|
@ -418,6 +418,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||||
// Enqueue updates for the next frame
|
// Enqueue updates for the next frame
|
||||||
if (_model) {
|
if (_model) {
|
||||||
|
|
||||||
|
#if 1 //def WANT_EXTRA_RENDER_DEBUGGING
|
||||||
|
// debugging...
|
||||||
|
gpu::Batch& batch = *args->_batch;
|
||||||
|
_model->renderDebugMeshBoxes(batch);
|
||||||
|
#endif
|
||||||
|
|
||||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||||
|
|
||||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||||
|
|
|
@ -96,9 +96,6 @@ Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestab
|
||||||
_isVisible(true),
|
_isVisible(true),
|
||||||
_blendNumber(0),
|
_blendNumber(0),
|
||||||
_appliedBlendNumber(0),
|
_appliedBlendNumber(0),
|
||||||
_calculatedMeshPartBoxesValid(false),
|
|
||||||
_calculatedMeshBoxesValid(false),
|
|
||||||
_calculatedMeshTrianglesValid(false),
|
|
||||||
_isWireframe(false),
|
_isWireframe(false),
|
||||||
_rig(rig)
|
_rig(rig)
|
||||||
{
|
{
|
||||||
|
@ -360,19 +357,14 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
||||||
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
|
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
|
||||||
// and testing intersection there.
|
// and testing intersection there.
|
||||||
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) {
|
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) {
|
||||||
|
QMutexLocker locker(&_mutex);
|
||||||
|
|
||||||
float bestDistance = std::numeric_limits<float>::max();
|
float bestDistance = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
float distanceToSubMesh;
|
|
||||||
BoxFace subMeshFace;
|
|
||||||
glm::vec3 subMeshSurfaceNormal;
|
|
||||||
int subMeshIndex = 0;
|
int subMeshIndex = 0;
|
||||||
|
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
|
|
||||||
// If we hit the models box, then consider the submeshes...
|
if (!_triangleSetsValid) {
|
||||||
_mutex.lock();
|
calculateTriangleSets();
|
||||||
if (!_calculatedMeshBoxesValid || (pickAgainstTriangles && !_calculatedMeshTrianglesValid)) {
|
|
||||||
recalculateMeshBoxes(pickAgainstTriangles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
||||||
|
@ -382,50 +374,26 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
||||||
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
|
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
|
||||||
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
|
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
|
||||||
|
|
||||||
|
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
||||||
|
float triangleSetDistance = 0.0f;
|
||||||
|
BoxFace triangleSetFace;
|
||||||
|
glm::vec3 triangleSetNormal;
|
||||||
|
if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetNormal, pickAgainstTriangles)) {
|
||||||
|
|
||||||
for (const auto& subMeshBox : _calculatedMeshBoxes) {
|
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
|
||||||
bool intersectedSubMesh = false;
|
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
|
||||||
float subMeshDistance = std::numeric_limits<float>::max();
|
float worldDistance = glm::distance(origin, worldIntersectionPoint);
|
||||||
|
|
||||||
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace, subMeshSurfaceNormal)) {
|
if (worldDistance < bestDistance) {
|
||||||
if (distanceToSubMesh < bestDistance) {
|
bestDistance = worldDistance;
|
||||||
if (pickAgainstTriangles) {
|
intersectedSomething = true;
|
||||||
|
face = triangleSetFace;
|
||||||
float subMeshDistance = 0.0f;
|
surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(triangleSetNormal, 0.0f));
|
||||||
glm::vec3 subMeshNormal;
|
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
||||||
const auto& meshTriangleSet = _modelSpaceMeshTriangleSets[subMeshIndex];
|
|
||||||
bool intersectedMesh = meshTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, subMeshDistance, subMeshNormal);
|
|
||||||
|
|
||||||
if (intersectedMesh) {
|
|
||||||
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * subMeshDistance);
|
|
||||||
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
|
|
||||||
float worldDistance = glm::distance(origin, worldIntersectionPoint);
|
|
||||||
|
|
||||||
if (worldDistance < bestDistance) {
|
|
||||||
bestDistance = subMeshDistance;
|
|
||||||
intersectedSomething = true;
|
|
||||||
face = subMeshFace;
|
|
||||||
surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(subMeshNormal, 0.0f));
|
|
||||||
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// this is the non-triangle picking case...
|
|
||||||
bestDistance = distanceToSubMesh;
|
|
||||||
intersectedSomething = true;
|
|
||||||
face = subMeshFace;
|
|
||||||
surfaceNormal = subMeshSurfaceNormal;
|
|
||||||
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
|
||||||
|
|
||||||
intersectedSubMesh = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
subMeshIndex++;
|
subMeshIndex++;
|
||||||
}
|
}
|
||||||
_mutex.unlock();
|
|
||||||
|
|
||||||
if (intersectedSomething) {
|
if (intersectedSomething) {
|
||||||
distance = bestDistance;
|
distance = bestDistance;
|
||||||
|
@ -461,182 +429,97 @@ bool Model::convexHullContains(glm::vec3 point) {
|
||||||
// we can use the AABox's contains() by mapping our point into the model frame
|
// we can use the AABox's contains() by mapping our point into the model frame
|
||||||
// and testing there.
|
// and testing there.
|
||||||
if (modelFrameBox.contains(modelFramePoint)){
|
if (modelFrameBox.contains(modelFramePoint)){
|
||||||
_mutex.lock();
|
QMutexLocker locker(&_mutex);
|
||||||
if (!_calculatedMeshTrianglesValid) {
|
|
||||||
recalculateMeshBoxes(true);
|
if (!_triangleSetsValid) {
|
||||||
|
calculateTriangleSets();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are inside the models box, then consider the submeshes...
|
// If we are inside the models box, then consider the submeshes...
|
||||||
int subMeshIndex = 0;
|
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
||||||
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
|
glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
|
||||||
if (subMeshBox.contains(point)) {
|
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
|
||||||
|
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
|
||||||
|
|
||||||
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
||||||
glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
|
const AABox& box = triangleSet.getBounds();
|
||||||
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
|
if (box.contains(meshFramePoint)) {
|
||||||
|
if (triangleSet.convexHullContains(meshFramePoint)) {
|
||||||
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
|
|
||||||
|
|
||||||
if (_modelSpaceMeshTriangleSets[subMeshIndex].convexHullContains(meshFramePoint)) {
|
|
||||||
// It's inside this mesh, return true.
|
// It's inside this mesh, return true.
|
||||||
_mutex.unlock();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subMeshIndex++;
|
|
||||||
}
|
}
|
||||||
_mutex.unlock();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// It wasn't in any mesh, return false.
|
// It wasn't in any mesh, return false.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we seem to call this too often when things haven't actually changed... look into optimizing this
|
void Model::calculateTriangleSets() {
|
||||||
// Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these
|
|
||||||
// can occur multiple times. In addition, rendering does it's own ray picking in order to decide which
|
|
||||||
// entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible)
|
|
||||||
// and then the calls use the most recent such result.
|
|
||||||
void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
|
|
||||||
PROFILE_RANGE(render, __FUNCTION__);
|
PROFILE_RANGE(render, __FUNCTION__);
|
||||||
bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid;
|
|
||||||
|
|
||||||
if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) {
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
|
int numberOfMeshes = geometry.meshes.size();
|
||||||
|
|
||||||
if (pickAgainstTriangles) {
|
_triangleSetsValid = true;
|
||||||
qDebug() << "RECALCULATING triangles!";
|
_modelSpaceMeshTriangleSets.clear();
|
||||||
} else {
|
_modelSpaceMeshTriangleSets.resize(numberOfMeshes);
|
||||||
qDebug() << "RECALCULATING boxes!";
|
|
||||||
}
|
|
||||||
|
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
for (int i = 0; i < numberOfMeshes; i++) {
|
||||||
int numberOfMeshes = geometry.meshes.size();
|
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||||
_calculatedMeshBoxes.resize(numberOfMeshes);
|
|
||||||
_calculatedMeshPartBoxes.clear();
|
|
||||||
|
|
||||||
_modelSpaceMeshTriangleSets.clear();
|
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||||
_modelSpaceMeshTriangleSets.resize(numberOfMeshes);
|
const FBXMeshPart& part = mesh.parts.at(j);
|
||||||
|
|
||||||
for (int i = 0; i < numberOfMeshes; i++) {
|
const int INDICES_PER_TRIANGLE = 3;
|
||||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
const int INDICES_PER_QUAD = 4;
|
||||||
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation);
|
|
||||||
|
|
||||||
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
|
if (part.quadIndices.size() > 0) {
|
||||||
|
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
|
||||||
|
int vIndex = 0;
|
||||||
|
for (int q = 0; q < numberOfQuads; q++) {
|
||||||
|
int i0 = part.quadIndices[vIndex++];
|
||||||
|
int i1 = part.quadIndices[vIndex++];
|
||||||
|
int i2 = part.quadIndices[vIndex++];
|
||||||
|
int i3 = part.quadIndices[vIndex++];
|
||||||
|
|
||||||
if (pickAgainstTriangles) {
|
// track the model space version... these points will be transformed by the FST's offset,
|
||||||
|
// which includes the scaling, rotation, and translation specified by the FST/FBX,
|
||||||
|
// this can't change at runtime, so we can safely store these in our TriangleSet
|
||||||
|
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
|
||||||
|
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
|
||||||
|
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
|
||||||
|
glm::vec3 v3 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i3], 1.0f));
|
||||||
|
|
||||||
for (int j = 0; j < mesh.parts.size(); j++) {
|
Triangle tri1 = { v0, v1, v3 };
|
||||||
const FBXMeshPart& part = mesh.parts.at(j);
|
Triangle tri2 = { v1, v2, v3 };
|
||||||
|
_modelSpaceMeshTriangleSets[i].insertTriangle(tri1);
|
||||||
bool atLeastOnePointInBounds = false;
|
_modelSpaceMeshTriangleSets[i].insertTriangle(tri2);
|
||||||
AABox thisPartBounds;
|
}
|
||||||
|
}
|
||||||
const int INDICES_PER_TRIANGLE = 3;
|
|
||||||
const int INDICES_PER_QUAD = 4;
|
if (part.triangleIndices.size() > 0) {
|
||||||
|
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
|
||||||
if (part.quadIndices.size() > 0) {
|
int vIndex = 0;
|
||||||
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
|
for (int t = 0; t < numberOfTris; t++) {
|
||||||
int vIndex = 0;
|
int i0 = part.triangleIndices[vIndex++];
|
||||||
for (int q = 0; q < numberOfQuads; q++) {
|
int i1 = part.triangleIndices[vIndex++];
|
||||||
int i0 = part.quadIndices[vIndex++];
|
int i2 = part.triangleIndices[vIndex++];
|
||||||
int i1 = part.quadIndices[vIndex++];
|
|
||||||
int i2 = part.quadIndices[vIndex++];
|
// track the model space version... these points will be transformed by the FST's offset,
|
||||||
int i3 = part.quadIndices[vIndex++];
|
// which includes the scaling, rotation, and translation specified by the FST/FBX,
|
||||||
|
// this can't change at runtime, so we can safely store these in our TriangleSet
|
||||||
glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f));
|
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
|
||||||
glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f));
|
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
|
||||||
glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f));
|
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
|
||||||
glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f));
|
|
||||||
|
Triangle tri = { v0, v1, v2 };
|
||||||
// track the mesh parts in model space
|
_modelSpaceMeshTriangleSets[i].insertTriangle(tri);
|
||||||
if (!atLeastOnePointInBounds) {
|
|
||||||
thisPartBounds.setBox(mv0, 0.0f);
|
|
||||||
atLeastOnePointInBounds = true;
|
|
||||||
} else {
|
|
||||||
thisPartBounds += mv0;
|
|
||||||
}
|
|
||||||
thisPartBounds += mv1;
|
|
||||||
thisPartBounds += mv2;
|
|
||||||
thisPartBounds += mv3;
|
|
||||||
|
|
||||||
// let's also track the model space version... (eventually using only this!)
|
|
||||||
// these points will be transformed by the FST's offset, which includes the
|
|
||||||
// scaling, rotation, and translation specified by the FST/FBX, this can't change
|
|
||||||
// at runtime, so we can safely store these in our TriangleSet
|
|
||||||
{
|
|
||||||
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
|
|
||||||
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
|
|
||||||
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
|
|
||||||
glm::vec3 v3 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i3], 1.0f));
|
|
||||||
|
|
||||||
Triangle tri1 = { v0, v1, v3 };
|
|
||||||
Triangle tri2 = { v1, v2, v3 };
|
|
||||||
_modelSpaceMeshTriangleSets[i].insertTriangle(tri1);
|
|
||||||
_modelSpaceMeshTriangleSets[i].insertTriangle(tri2);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part.triangleIndices.size() > 0) {
|
|
||||||
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
|
|
||||||
int vIndex = 0;
|
|
||||||
for (int t = 0; t < numberOfTris; t++) {
|
|
||||||
int i0 = part.triangleIndices[vIndex++];
|
|
||||||
int i1 = part.triangleIndices[vIndex++];
|
|
||||||
int i2 = part.triangleIndices[vIndex++];
|
|
||||||
|
|
||||||
glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f));
|
|
||||||
glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f));
|
|
||||||
glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f));
|
|
||||||
|
|
||||||
// track the mesh parts in model space
|
|
||||||
if (!atLeastOnePointInBounds) {
|
|
||||||
thisPartBounds.setBox(mv0, 0.0f);
|
|
||||||
atLeastOnePointInBounds = true;
|
|
||||||
} else {
|
|
||||||
thisPartBounds += mv0;
|
|
||||||
}
|
|
||||||
thisPartBounds += mv1;
|
|
||||||
thisPartBounds += mv2;
|
|
||||||
|
|
||||||
// let's also track the model space version... (eventually using only this!)
|
|
||||||
// these points will be transformed by the FST's offset, which includes the
|
|
||||||
// scaling, rotation, and translation specified by the FST/FBX, this can't change
|
|
||||||
// at runtime, so we can safely store these in our TriangleSet
|
|
||||||
{
|
|
||||||
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
|
|
||||||
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
|
|
||||||
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
|
|
||||||
|
|
||||||
Triangle tri = { v0, v1, v2 };
|
|
||||||
_modelSpaceMeshTriangleSets[i].insertTriangle(tri);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_calculatedMeshPartBoxes[QPair<int,int>(i, j)] = thisPartBounds;
|
|
||||||
}
|
}
|
||||||
_calculatedMeshPartBoxesValid = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_calculatedMeshBoxesValid = true;
|
|
||||||
_calculatedMeshTrianglesValid = pickAgainstTriangles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Model::renderSetup(RenderArgs* args) {
|
|
||||||
// set up dilated textures on first render after load/simulate
|
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
|
||||||
if (_dilatedTextures.isEmpty()) {
|
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
|
||||||
QVector<QSharedPointer<Texture> > dilated;
|
|
||||||
dilated.resize(mesh.parts.size());
|
|
||||||
_dilatedTextures.append(dilated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_addedToScene && isLoaded()) {
|
|
||||||
createRenderItemSet();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,7 +635,15 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
|
||||||
void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
|
void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
|
||||||
int colorNdx = 0;
|
int colorNdx = 0;
|
||||||
_mutex.lock();
|
_mutex.lock();
|
||||||
foreach(AABox box, _calculatedMeshBoxes) {
|
|
||||||
|
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
||||||
|
glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
|
||||||
|
Transform meshToWorld(meshToWorldMatrix);
|
||||||
|
batch.setModelTransform(meshToWorld);
|
||||||
|
|
||||||
|
for(const auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
||||||
|
auto box = triangleSet.getBounds();
|
||||||
|
|
||||||
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
|
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
|
||||||
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
|
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||||
}
|
}
|
||||||
|
@ -784,8 +675,8 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
|
||||||
points << blf << tlf;
|
points << blf << tlf;
|
||||||
|
|
||||||
glm::vec4 color[] = {
|
glm::vec4 color[] = {
|
||||||
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
|
|
||||||
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green
|
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green
|
||||||
|
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
|
||||||
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue
|
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue
|
||||||
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
|
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
|
||||||
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
|
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
|
||||||
|
@ -843,37 +734,6 @@ Extents Model::getUnscaledMeshExtents() const {
|
||||||
return scaledExtents;
|
return scaledExtents;
|
||||||
}
|
}
|
||||||
|
|
||||||
Extents Model::calculateScaledOffsetExtents(const Extents& extents,
|
|
||||||
glm::vec3 modelPosition, glm::quat modelOrientation) const {
|
|
||||||
// we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
|
|
||||||
glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
|
|
||||||
glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
|
|
||||||
|
|
||||||
Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
|
|
||||||
((maximum + _offset) * _scale) };
|
|
||||||
|
|
||||||
Extents rotatedExtents = scaledOffsetExtents.getRotated(modelOrientation);
|
|
||||||
|
|
||||||
Extents translatedExtents = { rotatedExtents.minimum + modelPosition,
|
|
||||||
rotatedExtents.maximum + modelPosition };
|
|
||||||
|
|
||||||
return translatedExtents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the world space equivalent of some box in model space.
|
|
||||||
AABox Model::calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const {
|
|
||||||
return AABox(calculateScaledOffsetExtents(Extents(box), modelPosition, modelOrientation));
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const {
|
|
||||||
// we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
|
|
||||||
glm::vec3 offsetPoint = glm::vec3(getFBXGeometry().offset * glm::vec4(point, 1.0f));
|
|
||||||
glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale);
|
|
||||||
glm::vec3 rotatedPoint = _rotation * scaledPoint;
|
|
||||||
glm::vec3 translatedPoint = rotatedPoint + _translation;
|
|
||||||
return translatedPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Model::clearJointState(int index) {
|
void Model::clearJointState(int index) {
|
||||||
_rig->clearJointState(index);
|
_rig->clearJointState(index);
|
||||||
}
|
}
|
||||||
|
@ -1159,8 +1019,11 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
||||||
// they really only become invalid if something about the transform to world space has changed. This is
|
// they really only become invalid if something about the transform to world space has changed. This is
|
||||||
// not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking
|
// not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking
|
||||||
// because ray picking needs valid boxes to work
|
// because ray picking needs valid boxes to work
|
||||||
_calculatedMeshBoxesValid = false;
|
//_calculatedMeshBoxesValid = false;
|
||||||
_calculatedMeshTrianglesValid = false;
|
//_calculatedMeshTrianglesValid = false;
|
||||||
|
|
||||||
|
// FIXME -- if the model URL changes, then we need to recalculate the triangle sets??!!!!
|
||||||
|
|
||||||
onInvalidate();
|
onInvalidate();
|
||||||
|
|
||||||
// check for scale to fit
|
// check for scale to fit
|
||||||
|
|
|
@ -96,7 +96,6 @@ public:
|
||||||
render::PendingChanges& pendingChanges,
|
render::PendingChanges& pendingChanges,
|
||||||
render::Item::Status::Getters& statusGetters);
|
render::Item::Status::Getters& statusGetters);
|
||||||
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||||
void renderSetup(RenderArgs* args);
|
|
||||||
bool isRenderable() const;
|
bool isRenderable() const;
|
||||||
|
|
||||||
bool isVisible() const { return _isVisible; }
|
bool isVisible() const { return _isVisible; }
|
||||||
|
@ -251,6 +250,9 @@ public:
|
||||||
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
||||||
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItems; }
|
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItems; }
|
||||||
|
|
||||||
|
void renderDebugMeshBoxes(gpu::Batch& batch);
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void loadURLFinished(bool success);
|
void loadURLFinished(bool success);
|
||||||
|
|
||||||
|
@ -267,15 +269,6 @@ protected:
|
||||||
/// Returns the unscaled extents of the model's mesh
|
/// Returns the unscaled extents of the model's mesh
|
||||||
Extents getUnscaledMeshExtents() const;
|
Extents getUnscaledMeshExtents() const;
|
||||||
|
|
||||||
/// Returns the scaled equivalent of some extents in model space.
|
|
||||||
Extents calculateScaledOffsetExtents(const Extents& extents, glm::vec3 modelPosition, glm::quat modelOrientation) const;
|
|
||||||
|
|
||||||
/// Returns the world space equivalent of some box in model space.
|
|
||||||
AABox calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const;
|
|
||||||
|
|
||||||
/// Returns the scaled equivalent of a point in model space.
|
|
||||||
glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const;
|
|
||||||
|
|
||||||
/// Clear the joint states
|
/// Clear the joint states
|
||||||
void clearJointState(int index);
|
void clearJointState(int index);
|
||||||
|
|
||||||
|
@ -332,14 +325,13 @@ protected:
|
||||||
|
|
||||||
/// Allow sub classes to force invalidating the bboxes
|
/// Allow sub classes to force invalidating the bboxes
|
||||||
void invalidCalculatedMeshBoxes() {
|
void invalidCalculatedMeshBoxes() {
|
||||||
_calculatedMeshBoxesValid = false;
|
_triangleSetsValid = false;
|
||||||
_calculatedMeshPartBoxesValid = false;
|
|
||||||
_calculatedMeshTrianglesValid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hook for derived classes to be notified when setUrl invalidates the current model.
|
// hook for derived classes to be notified when setUrl invalidates the current model.
|
||||||
virtual void onInvalidate() {};
|
virtual void onInvalidate() {};
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void deleteGeometry();
|
virtual void deleteGeometry();
|
||||||
|
@ -358,17 +350,12 @@ protected:
|
||||||
int _blendNumber;
|
int _blendNumber;
|
||||||
int _appliedBlendNumber;
|
int _appliedBlendNumber;
|
||||||
|
|
||||||
QHash<QPair<int,int>, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes
|
|
||||||
|
|
||||||
bool _calculatedMeshPartBoxesValid;
|
|
||||||
QVector<AABox> _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes
|
|
||||||
bool _calculatedMeshBoxesValid;
|
|
||||||
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
|
|
||||||
|
|
||||||
bool _calculatedMeshTrianglesValid;
|
|
||||||
QMutex _mutex;
|
QMutex _mutex;
|
||||||
|
|
||||||
void recalculateMeshBoxes(bool pickAgainstTriangles = false);
|
bool _triangleSetsValid { false };
|
||||||
|
void calculateTriangleSets();
|
||||||
|
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
|
||||||
|
|
||||||
|
|
||||||
void createRenderItemSet();
|
void createRenderItemSet();
|
||||||
virtual void createVisibleRenderItemSet();
|
virtual void createVisibleRenderItemSet();
|
||||||
|
@ -377,7 +364,6 @@ protected:
|
||||||
bool _isWireframe;
|
bool _isWireframe;
|
||||||
|
|
||||||
// debug rendering support
|
// debug rendering support
|
||||||
void renderDebugMeshBoxes(gpu::Batch& batch);
|
|
||||||
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
|
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,24 +28,31 @@ void TriangleSet::clear() {
|
||||||
// Determine of the given ray (origin/direction) in model space intersects with any triangles
|
// Determine of the given ray (origin/direction) in model space intersects with any triangles
|
||||||
// in the set. If an intersection occurs, the distance and surface normal will be provided.
|
// in the set. If an intersection occurs, the distance and surface normal will be provided.
|
||||||
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, glm::vec3& surfaceNormal) const {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const {
|
||||||
|
|
||||||
float bestDistance = std::numeric_limits<float>::max();
|
|
||||||
float thisTriangleDistance;
|
|
||||||
bool intersectedSomething = false;
|
bool intersectedSomething = false;
|
||||||
|
float boxDistance = std::numeric_limits<float>::max();
|
||||||
|
float bestDistance = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
for (const auto& triangle : _triangles) {
|
if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) {
|
||||||
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
if (precision) {
|
||||||
if (thisTriangleDistance < bestDistance) {
|
for (const auto& triangle : _triangles) {
|
||||||
bestDistance = thisTriangleDistance;
|
float thisTriangleDistance;
|
||||||
intersectedSomething = true;
|
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
||||||
surfaceNormal = triangle.getNormal();
|
if (thisTriangleDistance < bestDistance) {
|
||||||
distance = bestDistance;
|
bestDistance = thisTriangleDistance;
|
||||||
//face = subMeshFace;
|
intersectedSomething = true;
|
||||||
//extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
surfaceNormal = triangle.getNormal();
|
||||||
|
distance = bestDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
intersectedSomething = true;
|
||||||
|
distance = boxDistance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersectedSomething;
|
return intersectedSomething;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,10 @@ public:
|
||||||
// Determine of the given ray (origin/direction) in model space intersects with any triangles
|
// Determine of the given ray (origin/direction) in model space intersects with any triangles
|
||||||
// in the set. If an intersection occurs, the distance and surface normal will be provided.
|
// in the set. If an intersection occurs, the distance and surface normal will be provided.
|
||||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, glm::vec3& surfaceNormal) const;
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const;
|
||||||
|
|
||||||
bool convexHullContains(const glm::vec3& point) const; // this point is "inside" all triangles
|
bool convexHullContains(const glm::vec3& point) const; // this point is "inside" all triangles
|
||||||
const AABox& getBounds() const;
|
const AABox& getBounds() const { return _bounds; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<Triangle> _triangles;
|
QVector<Triangle> _triangles;
|
||||||
|
|
Loading…
Reference in a new issue