/*========================================================================= Program: Visualization Toolkit Module: vtkGeoTerrain.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /*------------------------------------------------------------------------- Copyright 2008 Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. -------------------------------------------------------------------------*/ #include "vtkGeoTerrain.h" #include "vtkActor.h" #include "vtkAssembly.h" #include "vtkBox.h" #include "vtkCamera.h" #include "vtkCellArray.h" #include "vtkClipPolyData.h" #include "vtkCollection.h" #include "vtkDoubleArray.h" #include "vtkExtractSelectedFrustum.h" #include "vtkGeoAlignedImageRepresentation.h" #include "vtkGeoCamera.h" #include "vtkGeoImageNode.h" #include "vtkGeoInteractorStyle.h" #include "vtkGeoTreeNodeCache.h" #include "vtkGeoSource.h" #include "vtkGeoTerrainNode.h" #include "vtkImageData.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkPlanes.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkProp3DCollection.h" #include "vtkProperty.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkSmartPointer.h" #include "vtkTimerLog.h" #include "vtkTransformFilter.h" #include "vtkXMLPolyDataWriter.h" #include #include vtkStandardNewMacro(vtkGeoTerrain); vtkCxxSetObjectMacro(vtkGeoTerrain, GeoSource, vtkGeoSource); vtkCxxSetObjectMacro(vtkGeoTerrain, GeoCamera, vtkGeoCamera); //---------------------------------------------------------------------------- vtkGeoTerrain::vtkGeoTerrain() { this->GeoSource = nullptr; this->Root = vtkGeoTerrainNode::New(); this->Origin[0] = 0.0; this->Origin[1] = 0.0; this->Origin[2] = 0.0; this->Extractor = vtkExtractSelectedFrustum::New(); this->GeoCamera = nullptr; this->MaxLevel = 20; this->Cache = vtkGeoTreeNodeCache::New(); } //---------------------------------------------------------------------------- vtkGeoTerrain::~vtkGeoTerrain() { this->SetGeoSource(nullptr); this->SetGeoCamera(nullptr); if (this->Root) { this->Root->Delete(); } if (this->Extractor) { this->Extractor->Delete(); } if (this->Cache) { this->Cache->Delete(); } } //---------------------------------------------------------------------------- void vtkGeoTerrain::SetSource(vtkGeoSource* source) { if (this->GeoSource != source) { this->SetGeoSource(source); if (this->GeoSource) { this->Initialize(); } } } //---------------------------------------------------------------------------- void vtkGeoTerrain::Initialize() { if (!this->GeoSource) { vtkErrorMacro(<< "Must set source before initializing."); return; } // Start by fetching the root. this->GeoSource->FetchRoot(this->Root); } //---------------------------------------------------------------------------- void vtkGeoTerrain::InitializeNodeAnalysis(vtkRenderer* ren) { vtkGeoInteractorStyle* style = vtkGeoInteractorStyle::SafeDownCast( ren->GetRenderWindow()->GetInteractor()->GetInteractorStyle()); if (!style) { vtkErrorMacro("vtkGeoTerrain requires vtkGeoInteractorStyle in order to get geo camera."); return; } vtkGeoCamera* camera = style->GetGeoCamera(); int* rendererSize = ren->GetSize(); camera->InitializeNodeAnalysis(rendererSize); this->SetGeoCamera(camera); // Setup the frustum extractor for finding // node intersections with the view frustum. double frustumPlanes[24]; double aspect = ren->GetTiledAspectRatio(); camera->GetVTKCamera()->GetFrustumPlanes( aspect, frustumPlanes ); vtkSmartPointer frustum = vtkSmartPointer::New(); frustum->SetFrustumPlanes(frustumPlanes); this->Extractor->SetFrustum(frustum); } //---------------------------------------------------------------------------- bool vtkGeoTerrain::NodeInViewport(vtkGeoTerrainNode* cur) { // Determine if node is within viewport double bbox[6]; cur->GetModel()->GetBounds(bbox); for (int i = 0; i < 6; ++i) { bbox[i] = bbox[i] - this->GeoCamera->GetOrigin()[i/2]; } int boundsTest = this->Extractor->OverallBoundsTest(bbox); return (boundsTest != 0); } //----------------------------------------------------------------------------- // Returns 0 if there should be no change, -1 if the node resolution is too // high, and +1 if the nodes resolution is too low. int vtkGeoTerrain::EvaluateNode(vtkGeoTerrainNode* node) { double sphereViewSize; if (!this->GeoCamera) { return 0; } // Size of the sphere in view area units (0 -> 1) sphereViewSize = this->GeoCamera->GetNodeCoverage(node); // Arbitrary thresholds if (sphereViewSize > 0.2) { return 1; } if (sphereViewSize < 0.05) { return -1; } // Do not change the node. return 0; } //---------------------------------------------------------------------------- void vtkGeoTerrain::AddActors( vtkRenderer* ren, vtkAssembly* assembly, vtkCollection* imageReps) { // This method requires that the render window graphics context // has been created. ren->GetRenderWindow()->MakeCurrent(); if (!ren->GetRenderWindow()->IsCurrent()) { return; } this->InitializeNodeAnalysis(ren); // Disable multitexturing, using deprecated API. bool multiTexturing = false; int textureUnits = 0; // Extract the image representations from the collection. vtkGeoAlignedImageRepresentation* textureTree1 = nullptr; if (imageReps->GetNumberOfItems() >= 1) { textureTree1 = vtkGeoAlignedImageRepresentation::SafeDownCast( imageReps->GetItemAsObject(0)); } vtkGeoAlignedImageRepresentation* textureTree2 = nullptr; if (imageReps->GetNumberOfItems() >= 2) { textureTree2 = vtkGeoAlignedImageRepresentation::SafeDownCast( imageReps->GetItemAsObject(1)); } int visibleActors = 0; vtkProp3DCollection* props = assembly->GetParts(); vtkDebugMacro("Number Of Props: " << props->GetNumberOfItems()); vtkSmartPointer timer = vtkSmartPointer::New(); timer->StartTimer(); // Remove actors at the beginning of the actor list until there are at most // 100 actors. while (props->GetNumberOfItems() > 100) { assembly->RemovePart(vtkActor::SafeDownCast(props->GetItemAsObject(0))); } // First turn off visibility of all actors for (int p = 0; p < props->GetNumberOfItems(); ++p) { vtkActor* actor = vtkActor::SafeDownCast(props->GetItemAsObject(p)); actor->VisibilityOff(); } // Use stack rather than recursion std::stack s; s.push(this->Root); vtkGeoTerrainNode* child = nullptr; vtkCollection* coll = nullptr; double llbounds[4]; while (!s.empty()) { vtkGeoTerrainNode* cur = s.top(); s.pop(); if (!cur->HasData() || cur->GetModel()->GetNumberOfCells() == 0) { continue; } if (!this->NodeInViewport(cur)) { // Totally outside, so prune node and subtree continue; } // Mark this node as "visited" so it will be less likely to // be deleted. this->Cache->SendToFront(cur); // Determine whether to traverse this node's children int refine = this->EvaluateNode(cur); child = cur->GetChild(0); if (((!child || !child->HasData()) && cur->GetLevel() < this->MaxLevel && refine == 1) || cur->GetStatus() == vtkGeoTreeNode::PROCESSING) { coll = this->GeoSource->GetRequestedNodes(cur); // Load children if (coll != nullptr && coll->GetNumberOfItems() == 4) { for (int c = 0; c < 4; ++c) { child = vtkGeoTerrainNode::SafeDownCast(coll->GetItemAsObject(c)); vtkGeoTerrainNode* oldChild = cur->GetChild(c); if (oldChild) { this->Cache->RemoveNode(oldChild); } this->Cache->SendToFront(child); cur->SetChild(child, c); child->SetParent(cur); } cur->SetStatus(vtkGeoTreeNode::NONE); } else if(cur->GetStatus() == vtkGeoTreeNode::NONE) { cur->SetStatus(vtkGeoTreeNode::PROCESSING); vtkGeoTerrainNode * temp = vtkGeoTerrainNode::New(); temp->DeepCopy(cur); this->GeoSource->RequestChildren(temp); } if (coll) { coll->Delete(); } } if (!cur->GetChild(0) || !cur->GetChild(0)->HasData() || refine != 1) { // Find the best texture for this geometry llbounds[0] = cur->GetLongitudeRange()[0]; llbounds[1] = cur->GetLongitudeRange()[1]; llbounds[2] = cur->GetLatitudeRange()[0]; llbounds[3] = cur->GetLatitudeRange()[1]; vtkGeoImageNode* textureNode1 = textureTree1->GetBestImageForBounds(llbounds); if (!textureNode1) { vtkWarningMacro(<< "could not find node for bounds: " << llbounds[0] << "," << llbounds[1] << "," << llbounds[2] << "," << llbounds[3]); } vtkGeoImageNode* textureNode2 = nullptr; if (textureTree2) { textureNode2 = textureTree2->GetBestImageForBounds(llbounds); } // See if we already have an actor for this geometry vtkActor* existingActor = nullptr; for (int p = 0; p < props->GetNumberOfItems(); ++p) { vtkActor* actor = vtkActor::SafeDownCast(props->GetItemAsObject(p)); bool sameTexture = false; if (multiTexturing) { sameTexture = ( !textureNode1 || actor->GetProperty()->GetNumberOfTextures() < 1 || actor->GetProperty()->GetTexture(vtkProperty::VTK_TEXTURE_UNIT_0) == textureNode1->GetTexture() ) && ( !textureNode2 || actor->GetProperty()->GetNumberOfTextures() < 2 || actor->GetProperty()->GetTexture(vtkProperty::VTK_TEXTURE_UNIT_1) == textureNode2->GetTexture() ); } else { sameTexture = !textureNode1 || actor->GetTexture() == textureNode1->GetTexture(); } if (actor && actor->GetMapper()->GetInputDataObject(0, 0) == cur->GetModel() && sameTexture) { existingActor = actor; existingActor->VisibilityOn(); visibleActors++; // Move the actor to the end of the list so it is less likely removed. actor->Register(this); assembly->RemovePart(actor); assembly->AddPart(actor); actor->Delete(); break; } } if (existingActor) { continue; } // Add the data to the view vtkSmartPointer mapper = vtkSmartPointer::New(); vtkSmartPointer actor = vtkSmartPointer::New(); mapper->SetInputData(cur->GetModel()); mapper->ScalarVisibilityOff(); actor->SetMapper(mapper); actor->SetPosition(-this->Origin[0], -this->Origin[1], -this->Origin[2] - 0.1); visibleActors++; if (textureNode1) { // Some implementations will report they support multi-texturing but have only 1 // texture unit. Seriously!! if (multiTexturing && textureUnits > 1) { // Multi texturing mapper->MapDataArrayToMultiTextureAttribute(vtkProperty::VTK_TEXTURE_UNIT_0, "LatLong", vtkDataObject::FIELD_ASSOCIATION_POINTS); textureNode1->GetTexture()->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_REPLACE); actor->GetProperty()->SetTexture(vtkProperty::VTK_TEXTURE_UNIT_0, textureNode1->GetTexture()); if (textureNode2) { mapper->MapDataArrayToMultiTextureAttribute(vtkProperty::VTK_TEXTURE_UNIT_1, "LatLong", vtkDataObject::FIELD_ASSOCIATION_POINTS); textureNode2->GetTexture()->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD); actor->GetProperty()->SetTexture(vtkProperty::VTK_TEXTURE_UNIT_1, textureNode2->GetTexture()); } } else { if(multiTexturing) { textureNode1->GetTexture()->SetBlendingMode( vtkTexture::VTK_TEXTURE_BLENDING_MODE_REPLACE); } // Single texturing cur->GetModel()->GetPointData()->SetActiveTCoords("LatLong"); actor->SetTexture(textureNode1->GetTexture()); } actor->GetProperty()->SetAmbient(1); assembly->AddPart(actor); } continue; } // Workaround for the isse where if refinement does not happen for some reason // then we don't see a tile as its visibility is turned off. else { llbounds[0] = cur->GetLongitudeRange()[0]; llbounds[1] = cur->GetLongitudeRange()[1]; llbounds[2] = cur->GetLatitudeRange()[0]; llbounds[3] = cur->GetLatitudeRange()[1]; vtkGeoImageNode* textureNode1 = textureTree1->GetBestImageForBounds(llbounds); vtkGeoImageNode* textureNode2 = nullptr; // See if we already have an actor for this geometry vtkActor* existingActor = nullptr; for (int p = 0; p < props->GetNumberOfItems(); ++p) { vtkActor* actor = vtkActor::SafeDownCast(props->GetItemAsObject(p)); bool sameTexture = false; if (multiTexturing) { sameTexture = ( !textureNode1 || actor->GetProperty()->GetNumberOfTextures() < 1 || actor->GetProperty()->GetTexture(vtkProperty::VTK_TEXTURE_UNIT_0) == textureNode1->GetTexture() ) && ( !textureNode2 || actor->GetProperty()->GetNumberOfTextures() < 2 || actor->GetProperty()->GetTexture(vtkProperty::VTK_TEXTURE_UNIT_1) == textureNode2->GetTexture() ); } else { sameTexture = !textureNode1 || actor->GetTexture() == textureNode1->GetTexture(); } if (actor && actor->GetMapper()->GetInputDataObject(0, 0) == cur->GetModel() && sameTexture) { existingActor = actor; existingActor->VisibilityOn(); visibleActors++; // Move the actor to the end of the list so it is less likely removed. actor->Register(this); assembly->RemovePart(actor); assembly->AddPart(actor); actor->Delete(); break; } } } s.push(cur->GetChild(0)); s.push(cur->GetChild(1)); s.push(cur->GetChild(2)); s.push(cur->GetChild(3)); } timer->StopTimer(); vtkDebugMacro("Visible Actors: " << visibleActors); vtkDebugMacro("AddActors time: " << timer->GetElapsedTime()); } //---------------------------------------------------------------------------- void vtkGeoTerrain::PrintSelf(ostream & os, vtkIndent indent) { this->Superclass::PrintSelf( os, indent ); os << indent << "GeoSource: " << this->GeoSource << "\n"; os << indent << "Origin: (" << this->Origin[0] << ", " << this->Origin[1] << ", " << this->Origin[2] << ")\n"; os << indent << "MaxLevel: " << this->MaxLevel << "\n"; this->PrintTree(os, indent, this->Root); // Root } //---------------------------------------------------------------------------- void vtkGeoTerrain::SaveDatabase(const char* path, int depth) { if (!this->Root) { this->Initialize(); } std::stack< vtkSmartPointer > s; s.push(this->Root); while (!s.empty()) { vtkSmartPointer node = s.top(); s.pop(); // Write out file. vtkSmartPointer storedData = vtkSmartPointer::New(); storedData->ShallowCopy(node->GetModel()); vtkSmartPointer writer = vtkSmartPointer::New(); char fn[512]; snprintf(fn, sizeof(fn), "%s/tile_%d_%ld.vtp", path, node->GetLevel(), node->GetId()); writer->SetFileName(fn); writer->SetInputData(storedData); writer->Write(); if (node->GetLevel() == depth) { continue; } // Recurse over children. for (int i = 0; i < 4; ++i) { vtkSmartPointer child = vtkSmartPointer::New(); if (this->GeoSource->FetchChild(node, i, child)) { s.push(child); } } } } //---------------------------------------------------------------------------- void vtkGeoTerrain::PrintTree(ostream & os, vtkIndent indent, vtkGeoTerrainNode* parent) { os << indent << "Error: " << parent->GetError() << endl; os << indent << "Level: " << parent->GetLevel() << " " << "Id: " << parent->GetId() << endl; os << indent << "LatitudeRange: " << parent->GetLatitudeRange()[0] << "," << parent->GetLatitudeRange()[1] << endl; os << indent << "LongitudeRange: " << parent->GetLongitudeRange()[0] << "," << parent->GetLongitudeRange()[1] << endl; os << indent << "ProjectionBounds: " << parent->GetProjectionBounds()[0] << "," << parent->GetProjectionBounds()[1] << "," << parent->GetProjectionBounds()[2] << "," << parent->GetProjectionBounds()[3] << endl; os << indent << "Number of cells: " << parent->GetModel()->GetNumberOfCells() << endl; if (parent->GetChild(0) == nullptr) { return; } for (int i = 0; i < 4; ++i) { this->PrintTree(os, indent.GetNextIndent(), parent->GetChild(i)); } }