//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2013 Ceetron AS // // This library may be used under the terms of either the GNU General Public License or // the GNU Lesser General Public License as follows: // // GNU General Public License Usage // This library 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 library 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 at <> // for more details. // // GNU Lesser General Public License Usage // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This library 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 Lesser General Public License at <> // for more details. // //################################################################################################## #include "cvfBase.h" #include "cvfModelBasicList.h" #include "cvfPart.h" #include "cvfCamera.h" #include "cvfCullSettings.h" #include "cvfPartRenderHintCollection.h" #include "cvfDrawableGeo.h" #include "cvfTransform.h" #include "cvfRayIntersectSpec.h" #include "cvfRay.h" namespace cvf { //================================================================================================== /// /// \class cvf::ModelBasicList /// \ingroup Viewing /// /// A simple model implementation using a list of parts. /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- String ModelBasicList::name() const { return m_modelName; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::setName(const String& name) { m_modelName = name; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::addPart(Part* part) { CVF_ASSERT(part); m_parts.push_back(part); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- Part* ModelBasicList::part(size_t index) { CVF_ASSERT(index < m_parts.size()); return m_parts.at(index); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t ModelBasicList::partCount() const { return m_parts.size(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::removePart(Part* part) { if (part != nullptr) m_parts.erase(part); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::removeAllParts() { m_parts.clear(); m_boundingBox.reset(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::shrinkPartCount(size_t newPartCount) { CVF_ASSERT(newPartCount <= m_parts.size()); m_parts.resize(newPartCount); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::updateBoundingBoxesRecursive() { m_boundingBox.reset(); size_t i; for (i = 0; i < m_parts.size(); i++) { Part* part = m_parts.at(i); part->updateBoundingBox(); m_boundingBox.add(part->boundingBox()); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- BoundingBox ModelBasicList::boundingBox() const { return m_boundingBox; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::findVisibleParts(PartRenderHintCollection* visibleParts, const Camera& camera, const CullSettings& cullSettings, uint enableMask) { // Combination of model's and incoming enable mask uint combinedEnableMask = (partEnableMask() & enableMask); size_t numParts = m_parts.size(); size_t i; for (i = 0; i < numParts; i++) { Part* part = m_parts[i].p(); if (part == nullptr) continue; double projectedAreaPixels = -1; if (partVisible(part, &camera, &cullSettings, combinedEnableMask, &projectedAreaPixels)) { if (projectedAreaPixels >= 0) { visibleParts->add(part, static_cast(projectedAreaPixels), -1.0f); } else { visibleParts->add(part); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool ModelBasicList::partVisible(cvf::Part* part, const Camera* camera, const CullSettings* cullSettings, uint enableMask, double* projectedAreaPixels) { if (projectedAreaPixels) { *projectedAreaPixels = -1.0; } // Check if part is enabled (visible) if (((part->enableMask() & enableMask) > 0) && (part->drawable())) { // View Frustum Culling if (cullSettings && camera && cullSettings->isViewFrustumCullingEnabled()) { const BoundingBox& bb = part->boundingBox(); if (!bb.isValid()) return false; if (camera->frustum().isOutside(bb)) { return false; } } // Pixel size (small feature) culling // Cull based on objects projected screen area. Objects with area smaller than specified threshold will be culled if (cullSettings && cullSettings->isPixelSizeCullingEnabled()) { const BoundingBox& bb = part->boundingBox(); if (!bb.isValid()) return false; *projectedAreaPixels = camera->computeProjectedBoundingSpherePixelArea(bb.center(), bb.radius()); if (*projectedAreaPixels < cullSettings->pixelSizeCullingAreaThreshold()) { return false; } } return true; } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::allParts(Collection* partCollection) { size_t numParts = m_parts.size(); size_t i; for (i = 0; i < numParts; i++) { partCollection->push_back(m_parts.at(i)); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool ModelBasicList::rayIntersect(const RayIntersectSpec& rayIntersectSpec, HitItemCollection* hitItemCollection) { // Example implementation cref ray = rayIntersectSpec.ray(); if (ray.isNull()) { return false; } // Combination of model's and incoming enable mask unsigned int combinedEnableMask = (partEnableMask() & rayIntersectSpec.enableMask()); bool anyPartsHit = false; Collection allPartsColl; allParts(&allPartsColl); size_t numParts = allPartsColl.size(); size_t partIdx; for (partIdx = 0; partIdx < numParts; partIdx++) { Part* part = allPartsColl.at(partIdx); if (part == nullptr) continue; if (partVisible(part, rayIntersectSpec.camera(), rayIntersectSpec.cullSettings(), combinedEnableMask, NULL)) { anyPartsHit |= part->rayIntersect(*ray, hitItemCollection); } } return anyPartsHit; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- Part* ModelBasicList::findPartByID(int64 id) { size_t numParts = partCount(); size_t i; for (i = 0; i < numParts; i++) { Part* p = m_parts.at(i); if (p == nullptr) continue; if (p->id() == id) { return p; } } return NULL; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- Part* ModelBasicList::findPartByName(String name) { size_t numParts = partCount(); size_t i; for (i = 0; i < numParts; i++) { Part* p = m_parts.at(i); if (p == nullptr) continue; if (p->name() == name) { return p; } } return NULL; } //-------------------------------------------------------------------------------------------------- /// Merge all parts with primitive count below given limit. A new part is created /// containing merged drawableGeos. Vertices are transformed if a transformation matrix exists. /// //-------------------------------------------------------------------------------------------------- void ModelBasicList::mergeParts(double maxExtent, uint minimumPrimitiveCount) { // Gather all parts to be merged in a collection Collection candidates; size_t numParts = m_parts.size(); size_t i; for (i = 0; i < numParts; i++) { Part* part = m_parts.at(i); // Update BB for all parts, as the BB is used to find parts that are close // to each other part->updateBoundingBox(); DrawableGeo* drawable = dynamic_cast(part->drawable()); if (drawable) { size_t primitiveCount = drawable->faceCount(); if (primitiveCount < minimumPrimitiveCount) { candidates.push_back(part); } } } if (candidates.size() > 1) { IntArray newPartIDs; newPartIDs.resize(candidates.size()); newPartIDs.setAll(-1); int newPartID = 0; size_t j; for (j = 0; j < candidates.size(); j++) { BoundingBox bbMerged; if (newPartIDs[j] == -1) { Part* part = candidates.at(j); bbMerged = part->boundingBox(); newPartIDs.set(j, newPartID); if (bbMerged.isValid()) { size_t k; for (k = j + 1; k < candidates.size(); k++) { Part* candidateBB = candidates.at(k); BoundingBox bbCandidate = candidateBB->boundingBox(); bbCandidate.add(bbMerged); if (bbCandidate.extent().length() < maxExtent) { bbMerged = bbCandidate; newPartIDs[k] = newPartID; } } } newPartID++; } } // Gather all parts to be merged in a collection Collection partsToMerge; Part* currentCandidate = candidates.at(0); partsToMerge.push_back(currentCandidate); int currentPartID = 0; for (j = 1; j < newPartIDs.size(); j++) { if (newPartIDs.get(j) == currentPartID) { Part* candidate = candidates.at(j); partsToMerge.push_back(candidate); } if (newPartIDs.get(j) != currentPartID || j == newPartIDs.size() - 1) { if (partsToMerge.size() > 1) { ref mergedPart = mergeAndAddPart(partsToMerge); addPart(mergedPart.p()); // Remove merged parts from part list size_t k; for (k = 0; k < partsToMerge.size(); k++) { Part* part = partsToMerge.at(k); removePart(part); } } partsToMerge.clear(); Part* candidate = candidates.at(j); partsToMerge.push_back(candidate); currentPartID = newPartIDs.get(j); } } } } //-------------------------------------------------------------------------------------------------- /// Merge all parts in given part collection. Create and insert merged part. /// Incoming parts are removed from list. //-------------------------------------------------------------------------------------------------- ref ModelBasicList::mergeAndAddPart(Collection& partCollection) const { CVF_ASSERT(!partCollection.empty()); // Build one new drawable from collection of small drawables. Compute normal of new drawable to be able to render. ref mergedDrawable = new DrawableGeo; // Merge all drawables size_t j; for (j = 0; j < partCollection.size(); j++) { Part* part = partCollection.at(j); DrawableGeo* drawable = dynamic_cast(part->drawable()); if (drawable) { Transform* transform = part->transform(); if (transform) { Mat4d mat = transform->worldTransform(); mergedDrawable->mergeInto(*drawable, &mat); } else { mergedDrawable->mergeInto(*drawable, NULL); } } } mergedDrawable->computeNormals(); // First part defines properties to be used in the new merged part Part* firstPart = partCollection.at(0); CVF_ASSERT(firstPart); Effect* effect = firstPart->effect(0); ref mergedPart = new Part; mergedPart->setDrawable(mergedDrawable.p()); mergedPart->setEffect(0, effect); mergedPart->updateBoundingBox(); return mergedPart; } } // namespace cvf