History

cleanup use of triangleSet for picking

ZappoMan committed 9 years ago

Commit 87bcced1 parent 32add665 files · +138 −276Browse files

Showing the changes to libraries/render-utils/src/Model.cpp (1 of 5 files). Show all

Unified · Split

libraries/render-utils/src/Model.cpp+103−240
@@ -96,9 +96,6 @@ Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestab · 95 unmodified lines
9696 _isVisible(true),
9797 _blendNumber(0),
9898 _appliedBlendNumber(0),
99− _calculatedMeshPartBoxesValid(false),
100− _calculatedMeshBoxesValid(false),
101− _calculatedMeshTrianglesValid(false),
10299 _isWireframe(false),
103100 _rig(rig)
104101 {
@@ -360,19 +357,14 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g · 255 unmodified lines
360357 // we can use the AABox's ray intersection by mapping our origin and direction into the model frame
361358 // and testing intersection there.
362359 if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) {
363− float bestDistance = std::numeric_limits<float>::max();
360+ QMutexLocker locker(&_mutex);
364361
365− float distanceToSubMesh;
366− BoxFace subMeshFace;
367− glm::vec3 subMeshSurfaceNormal;
362+ float bestDistance = std::numeric_limits<float>::max();
368363 int subMeshIndex = 0;
369−
370364 const FBXGeometry& geometry = getFBXGeometry();
371365
372− // If we hit the models box, then consider the submeshes...
373− _mutex.lock();
374− if (!_calculatedMeshBoxesValid || (pickAgainstTriangles && !_calculatedMeshTrianglesValid)) {
375− recalculateMeshBoxes(pickAgainstTriangles);
366+ if (!_triangleSetsValid) {
367+ calculateTriangleSets();
376368 }
377369
378370 glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
@@ -382,50 +374,26 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g · 3 unmodified lines
382374 glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
383375 glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
384376
385−
386− for (const auto& subMeshBox : _calculatedMeshBoxes) {
387− bool intersectedSubMesh = false;
388− float subMeshDistance = std::numeric_limits<float>::max();
389−
390− if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace, subMeshSurfaceNormal)) {
391− if (distanceToSubMesh < bestDistance) {
392− if (pickAgainstTriangles) {
393−
394− float subMeshDistance = 0.0f;
395− glm::vec3 subMeshNormal;
396− const auto& meshTriangleSet = _modelSpaceMeshTriangleSets[subMeshIndex];
397− bool intersectedMesh = meshTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, subMeshDistance, subMeshNormal);
398−
399− if (intersectedMesh) {
400− glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * subMeshDistance);
401− glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
402− float worldDistance = glm::distance(origin, worldIntersectionPoint);
403−
404− if (worldDistance < bestDistance) {
405− bestDistance = subMeshDistance;
406− intersectedSomething = true;
407− face = subMeshFace;
408− surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(subMeshNormal, 0.0f));
409− extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
410− }
411− }
412− } else {
413− // this is the non-triangle picking case...
414− bestDistance = distanceToSubMesh;
415− intersectedSomething = true;
416− face = subMeshFace;
417− surfaceNormal = subMeshSurfaceNormal;
418− extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
419−
420− intersectedSubMesh = true;
421− }
377+ for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
378+ float triangleSetDistance = 0.0f;
379+ BoxFace triangleSetFace;
380+ glm::vec3 triangleSetNormal;
381+ if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetNormal, pickAgainstTriangles)) {
382+
383+ glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
384+ glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
385+ float worldDistance = glm::distance(origin, worldIntersectionPoint);
386+
387+ if (worldDistance < bestDistance) {
388+ bestDistance = worldDistance;
389+ intersectedSomething = true;
390+ face = triangleSetFace;
391+ surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(triangleSetNormal, 0.0f));
392+ extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
422393 }
423394 }
424−
425−
426395 subMeshIndex++;
427396 }
428− _mutex.unlock();
429397
430398 if (intersectedSomething) {
431399 distance = bestDistance;
@@ -461,183 +429,98 @@ bool Model::convexHullContains(glm::vec3 point) { · 29 unmodified lines
461429 // we can use the AABox's contains() by mapping our point into the model frame
462430 // and testing there.
463431 if (modelFrameBox.contains(modelFramePoint)){
464− _mutex.lock();
465− if (!_calculatedMeshTrianglesValid) {
466− recalculateMeshBoxes(true);
432+ QMutexLocker locker(&_mutex);
433+
434+ if (!_triangleSetsValid) {
435+ calculateTriangleSets();
467436 }
468437
469438 // If we are inside the models box, then consider the submeshes...
470− int subMeshIndex = 0;
471− foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
472− if (subMeshBox.contains(point)) {
473−
474− glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
475− glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
476− glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
477−
478− glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
439+ glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
440+ glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
441+ glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
442+ glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
479443
480− if (_modelSpaceMeshTriangleSets[subMeshIndex].convexHullContains(meshFramePoint)) {
444+ for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
445+ const AABox& box = triangleSet.getBounds();
446+ if (box.contains(meshFramePoint)) {
447+ if (triangleSet.convexHullContains(meshFramePoint)) {
481448 // It's inside this mesh, return true.
482− _mutex.unlock();
483449 return true;
484450 }
485451 }
486− subMeshIndex++;
487452 }
488− _mutex.unlock();
453+
454+
489455 }
490456 // It wasn't in any mesh, return false.
491457 return false;
492458 }
493459
494−// TODO: we seem to call this too often when things haven't actually changed... look into optimizing this
495−// Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these
496−// can occur multiple times. In addition, rendering does it's own ray picking in order to decide which
497−// entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible)
498−// and then the calls use the most recent such result.
499−void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
460+void Model::calculateTriangleSets() {
500461 PROFILE_RANGE(render, __FUNCTION__);
501− bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid;
502462
503− if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) {
463+ const FBXGeometry& geometry = getFBXGeometry();
464+ int numberOfMeshes = geometry.meshes.size();
504465
505− if (pickAgainstTriangles) {
506− qDebug() << "RECALCULATING triangles!";
507− } else {
508− qDebug() << "RECALCULATING boxes!";
509− }
466+ _triangleSetsValid = true;
467+ _modelSpaceMeshTriangleSets.clear();
468+ _modelSpaceMeshTriangleSets.resize(numberOfMeshes);
510469
511− const FBXGeometry& geometry = getFBXGeometry();
512− int numberOfMeshes = geometry.meshes.size();
513− _calculatedMeshBoxes.resize(numberOfMeshes);
514− _calculatedMeshPartBoxes.clear();
515−
516− _modelSpaceMeshTriangleSets.clear();
517− _modelSpaceMeshTriangleSets.resize(numberOfMeshes);
518−
519− for (int i = 0; i < numberOfMeshes; i++) {
520− const FBXMesh& mesh = geometry.meshes.at(i);
521− Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation);
522−
523− _calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
524−
525− if (pickAgainstTriangles) {
526−
527− for (int j = 0; j < mesh.parts.size(); j++) {
528− const FBXMeshPart& part = mesh.parts.at(j);
529−
530− bool atLeastOnePointInBounds = false;
531− AABox thisPartBounds;
532−
533− const int INDICES_PER_TRIANGLE = 3;
534− const int INDICES_PER_QUAD = 4;
535−
536− if (part.quadIndices.size() > 0) {
537− int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
538− int vIndex = 0;
539− for (int q = 0; q < numberOfQuads; q++) {
540− int i0 = part.quadIndices[vIndex++];
541− int i1 = part.quadIndices[vIndex++];
542− int i2 = part.quadIndices[vIndex++];
543− int i3 = part.quadIndices[vIndex++];
544−
545− glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f));
546− glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f));
547− glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f));
548− glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f));
549−
550− // track the mesh parts in model space
551− if (!atLeastOnePointInBounds) {
552− thisPartBounds.setBox(mv0, 0.0f);
553− atLeastOnePointInBounds = true;
554− } else {
555− thisPartBounds += mv0;
556− }
557− thisPartBounds += mv1;
558− thisPartBounds += mv2;
559− thisPartBounds += mv3;
560−
561− // let's also track the model space version... (eventually using only this!)
562− // these points will be transformed by the FST's offset, which includes the
563− // scaling, rotation, and translation specified by the FST/FBX, this can't change
564− // at runtime, so we can safely store these in our TriangleSet
565− {
566− glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
567− glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
568− glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
569− glm::vec3 v3 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i3], 1.0f));
570−
571− Triangle tri1 = { v0, v1, v3 };
572− Triangle tri2 = { v1, v2, v3 };
573− _modelSpaceMeshTriangleSets[i].insertTriangle(tri1);
574− _modelSpaceMeshTriangleSets[i].insertTriangle(tri2);
575− }
576−
577− }
578− }
470+ for (int i = 0; i < numberOfMeshes; i++) {
471+ const FBXMesh& mesh = geometry.meshes.at(i);
579472
580− if (part.triangleIndices.size() > 0) {
581− int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
582− int vIndex = 0;
583− for (int t = 0; t < numberOfTris; t++) {
584− int i0 = part.triangleIndices[vIndex++];
585− int i1 = part.triangleIndices[vIndex++];
586− int i2 = part.triangleIndices[vIndex++];
587−
588− glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f));
589− glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f));
590− glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f));
591−
592− // track the mesh parts in model space
593− if (!atLeastOnePointInBounds) {
594− thisPartBounds.setBox(mv0, 0.0f);
595− atLeastOnePointInBounds = true;
596− } else {
597− thisPartBounds += mv0;
598− }
599− thisPartBounds += mv1;
600− thisPartBounds += mv2;
601−
602− // let's also track the model space version... (eventually using only this!)
603− // these points will be transformed by the FST's offset, which includes the
604− // scaling, rotation, and translation specified by the FST/FBX, this can't change
605− // at runtime, so we can safely store these in our TriangleSet
606− {
607− glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
608− glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
609− glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
610−
611− Triangle tri = { v0, v1, v2 };
612− _modelSpaceMeshTriangleSets[i].insertTriangle(tri);
613− }
614−
615− }
616− }
617− _calculatedMeshPartBoxes[QPair<int,int>(i, j)] = thisPartBounds;
473+ for (int j = 0; j < mesh.parts.size(); j++) {
474+ const FBXMeshPart& part = mesh.parts.at(j);
475+
476+ const int INDICES_PER_TRIANGLE = 3;
477+ const int INDICES_PER_QUAD = 4;
478+
479+ if (part.quadIndices.size() > 0) {
480+ int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
481+ int vIndex = 0;
482+ for (int q = 0; q < numberOfQuads; q++) {
483+ int i0 = part.quadIndices[vIndex++];
484+ int i1 = part.quadIndices[vIndex++];
485+ int i2 = part.quadIndices[vIndex++];
486+ int i3 = part.quadIndices[vIndex++];
487+
488+ // track the model space version... these points will be transformed by the FST's offset,
489+ // which includes the scaling, rotation, and translation specified by the FST/FBX,
490+ // this can't change at runtime, so we can safely store these in our TriangleSet
491+ glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
492+ glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
493+ glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
494+ glm::vec3 v3 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i3], 1.0f));
495+
496+ Triangle tri1 = { v0, v1, v3 };
497+ Triangle tri2 = { v1, v2, v3 };
498+ _modelSpaceMeshTriangleSets[i].insertTriangle(tri1);
499+ _modelSpaceMeshTriangleSets[i].insertTriangle(tri2);
618500 }
619− _calculatedMeshPartBoxesValid = true;
620501 }
621− }
622− _calculatedMeshBoxesValid = true;
623− _calculatedMeshTrianglesValid = pickAgainstTriangles;
624− }
625−}
626502
627−void Model::renderSetup(RenderArgs* args) {
628− // set up dilated textures on first render after load/simulate
629− const FBXGeometry& geometry = getFBXGeometry();
630− if (_dilatedTextures.isEmpty()) {
631− foreach (const FBXMesh& mesh, geometry.meshes) {
632− QVector<QSharedPointer<Texture> > dilated;
633− dilated.resize(mesh.parts.size());
634− _dilatedTextures.append(dilated);
503+ if (part.triangleIndices.size() > 0) {
504+ int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
505+ int vIndex = 0;
506+ for (int t = 0; t < numberOfTris; t++) {
507+ int i0 = part.triangleIndices[vIndex++];
508+ int i1 = part.triangleIndices[vIndex++];
509+ int i2 = part.triangleIndices[vIndex++];
510+
511+ // track the model space version... these points will be transformed by the FST's offset,
512+ // which includes the scaling, rotation, and translation specified by the FST/FBX,
513+ // this can't change at runtime, so we can safely store these in our TriangleSet
514+ glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
515+ glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
516+ glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
517+
518+ Triangle tri = { v0, v1, v2 };
519+ _modelSpaceMeshTriangleSets[i].insertTriangle(tri);
520+ }
521+ }
635522 }
636523 }
637−
638− if (!_addedToScene && isLoaded()) {
639− createRenderItemSet();
640− }
641524 }
642525
643526 void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene) {
@@ -752,7 +635,15 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin · 108 unmodified lines
752635 void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
753636 int colorNdx = 0;
754637 _mutex.lock();
755− foreach(AABox box, _calculatedMeshBoxes) {
638+
639+ glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
640+ glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
641+ Transform meshToWorld(meshToWorldMatrix);
642+ batch.setModelTransform(meshToWorld);
643+
644+ for(const auto& triangleSet : _modelSpaceMeshTriangleSets) {
645+ auto box = triangleSet.getBounds();
646+
756647 if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
757648 _debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
758649 }
@@ -784,8 +675,8 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { · 25 unmodified lines
784675 points << blf << tlf;
785676
786677 glm::vec4 color[] = {
787− { 1.0f, 0.0f, 0.0f, 1.0f }, // red
788678 { 0.0f, 1.0f, 0.0f, 1.0f }, // green
679+ { 1.0f, 0.0f, 0.0f, 1.0f }, // red
789680 { 0.0f, 0.0f, 1.0f, 1.0f }, // blue
790681 { 1.0f, 0.0f, 1.0f, 1.0f }, // purple
791682 { 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
@@ -843,37 +734,6 @@ Extents Model::getUnscaledMeshExtents() const { · 51 unmodified lines
843734 return scaledExtents;
844735 }
845736
846−Extents Model::calculateScaledOffsetExtents(const Extents& extents,
847− glm::vec3 modelPosition, glm::quat modelOrientation) const {
848− // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
849− glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
850− glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
851−
852− Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
853− ((maximum + _offset) * _scale) };
854−
855− Extents rotatedExtents = scaledOffsetExtents.getRotated(modelOrientation);
856−
857− Extents translatedExtents = { rotatedExtents.minimum + modelPosition,
858− rotatedExtents.maximum + modelPosition };
859−
860− return translatedExtents;
861−}
862−
863−/// Returns the world space equivalent of some box in model space.
864−AABox Model::calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const {
865− return AABox(calculateScaledOffsetExtents(Extents(box), modelPosition, modelOrientation));
866−}
867−
868−glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const {
869− // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
870− glm::vec3 offsetPoint = glm::vec3(getFBXGeometry().offset * glm::vec4(point, 1.0f));
871− glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale);
872− glm::vec3 rotatedPoint = _rotation * scaledPoint;
873− glm::vec3 translatedPoint = rotatedPoint + _translation;
874− return translatedPoint;
875−}
876−
877737 void Model::clearJointState(int index) {
878738 _rig->clearJointState(index);
879739 }
@@ -1159,8 +1019,11 @@ void Model::simulate(float deltaTime, bool fullUpdate) { · 279 unmodified lines
11591019 // they really only become invalid if something about the transform to world space has changed. This is
11601020 // not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking
11611021 // because ray picking needs valid boxes to work
1162− _calculatedMeshBoxesValid = false;
1163− _calculatedMeshTrianglesValid = false;
1022+ //_calculatedMeshBoxesValid = false;
1023+ //_calculatedMeshTrianglesValid = false;
1024+
1025+ // FIXME -- if the model URL changes, then we need to recalculate the triangle sets??!!!!
1026+
11641027 onInvalidate();
11651028
11661029 // check for scale to fit