void ArticulatedModel::BoundsCallback::operator() (ArticulatedModel::Part* part, const CFrame& worldToPartFrame, shared_ptr<ArticulatedModel> m, const int treeDepth) { for (int i = 0; i < m->meshArray().size(); ++i) { Mesh* mesh = m->meshArray()[i]; if (mesh->logicalPart == part) { const Box& b = worldToPartFrame.toWorldSpace(mesh->boxBounds); AABox partBounds; b.getBounds(partBounds); bounds.merge(partBounds); } } }
void VisibleEntity::onPose(Array<shared_ptr<Surface> >& surfaceArray) { // We have to pose in order to compute bounds that are used for selection in the editor // and collisions in simulation, so pose anyway if not visible, // but then roll back. debugAssert(isFinite(m_frame.translation.x)); debugAssert(! isNaN(m_frame.rotation[0][0])); const int oldLen = surfaceArray.size(); const bool boundsChangedSincePreviousFrame = poseModel(surfaceArray); // Compute bounds for objects that moved if (m_lastAABoxBounds.isEmpty() || boundsChangedSincePreviousFrame || (m_lastChangeTime > m_lastBoundsTime)) { m_lastSphereBounds = Sphere(m_frame.translation, 0); const CFrame& myFrameInverse = frame().inverse(); m_lastObjectSpaceAABoxBounds = AABox::empty(); m_lastBoxBoundArray.fastClear(); // Look at all surfaces produced for (int i = oldLen; i < surfaceArray.size(); ++i) { AABox b; Sphere s; const shared_ptr<Surface>& surf = surfaceArray[i]; // body to world transformation for the surface CoordinateFrame cframe; surf->getCoordinateFrame(cframe, false); debugAssertM(cframe.translation.x == cframe.translation.x, "NaN translation"); surf->getObjectSpaceBoundingSphere(s); s = cframe.toWorldSpace(s); m_lastSphereBounds.radius = max(m_lastSphereBounds.radius, (s.center - m_lastSphereBounds.center).length() + s.radius); // Take the entity's frame out of consideration, so that we get tight AA bounds // in the Entity's frame CFrame osFrame = myFrameInverse * cframe; surf->getObjectSpaceBoundingBox(b); m_lastBoxBoundArray.append(cframe.toWorldSpace(b)); const Box& temp = osFrame.toWorldSpace(b); m_lastObjectSpaceAABoxBounds.merge(temp); } // Box can't represent an empty box, so we make empty boxes into real boxes with zero volume here if (m_lastObjectSpaceAABoxBounds.isEmpty()) { m_lastObjectSpaceAABoxBounds = AABox(Point3::zero()); m_lastAABoxBounds = AABox(frame().translation); } m_lastBoxBounds = frame().toWorldSpace(m_lastObjectSpaceAABoxBounds); m_lastBoxBounds.getBounds(m_lastAABoxBounds); m_lastBoundsTime = System::time(); } if (! m_visible) { // Discard my surfaces if I'm invisible; they were only needed for bounds surfaceArray.resize(oldLen, false); } }
void ArticulatedViewer::onInit(const String& filename) { ArticulatedModel::clearCache(); Texture::clearCache(); m_filename = filename; m_selectedPart = NULL; m_selectedMesh = NULL; m_selectedTriangleIndex = -1; m_numFaces = 0; m_numVertices = 0; m_shadowMapDirty = true; UniversalMaterial::clearCache(); const RealTime start = System::time(); if (toLower(FilePath::ext(filename)) == "any") { if ((toLower(FilePath::ext(FilePath::base(filename))) == "material") || (toLower(FilePath::ext(FilePath::base(filename))) == "universalmaterial")) { // Assume that this is an .UniversalMaterial.Any file. Load a square and apply the material Any any(Any::TABLE, "ArticulatedModel::Specification"); any["filename"] = "model/crate/crate4xtex.obj"; any["stripMaterials"] = true; any["preprocess"] = Any(Any::ARRAY); Any arg(Any::ARRAY, "setMaterial"); arg.append(Any(Any::ARRAY, "all")); arg.append(Any::fromFile(filename)); any["preprocess"].append(arg); Any arg2(Any::ARRAY, "setTwoSided"); arg2.append(Any(Any::ARRAY, "all")); arg2.append(true); any["preprocess"].append(arg2); m_model = ArticulatedModel::create(ArticulatedModel::Specification(any)); } else { // Assume that this is an .ArticulatedModel.Any file Any any; any.load(filename); m_model = ArticulatedModel::create(ArticulatedModel::Specification(any)); } } else { m_model = ArticulatedModel::fromFile(filename); } debugPrintf("%s loaded in %f seconds\n", filename.c_str(), System::time() - start); Array<shared_ptr<Surface> > arrayModel; if (m_model->usesSkeletalAnimation()) { Array<G3D::String> animationNames; m_model->getAnimationNames(animationNames); // TODO: Add support for selecting animations. m_model->getAnimation(animationNames[0], m_animation); m_animation.getCurrentPose(0.0f, m_pose); } m_model->pose(arrayModel, CFrame(), m_pose); m_model->countTrianglesAndVertices(m_numFaces, m_numVertices); m_scale = 1; m_offset = Vector3::zero(); bool overwrite = true; // Find the size of the bounding box of the entire model AABox bounds; if (arrayModel.size() > 0) { for (int x = 0; x < arrayModel.size(); ++x) { //merges the bounding boxes of all the seperate parts into the bounding box of the entire object AABox temp; CFrame cframe; arrayModel[x]->getCoordinateFrame(cframe); arrayModel[x]->getObjectSpaceBoundingBox(temp); Box partBounds = cframe.toWorldSpace(temp); // Some models have screwed up bounding boxes if (partBounds.extent().isFinite()) { if (overwrite) { partBounds.getBounds(bounds); overwrite = false; } else { partBounds.getBounds(temp); bounds.merge(temp); } } } if (overwrite) { // We never found a part with a finite bounding box bounds = AABox(Vector3::zero()); } Vector3 extent = bounds.extent(); Vector3 center = bounds.center(); // Scale to X units float scale = 1.0f / max(extent.x, max(extent.y, extent.z)); if (scale <= 0) { scale = 1; } if (! isFinite(scale)) { scale = 1; } m_scale = scale; scale *= VIEW_SIZE; m_offset = -scale * center; if (! center.isFinite()) { center = Vector3(); } // Transform parts in-place m_model->scaleWholeModel(scale); ArticulatedModel::CleanGeometrySettings csg; // Merging vertices is slow and topology hasn't changed at all, so preclude vertex merging csg.allowVertexMerging = false; m_model->cleanGeometry(csg); } // Get the newly transformed animation if (m_model->usesSkeletalAnimation()) { Array<String> animationNames; m_model->getAnimationNames(animationNames); // TODO: Add support for selecting animations. m_model->getAnimation(animationNames[0], m_animation); m_animation.getCurrentPose(0.0f, m_pose); } // saveGeometry(); }