/*========================================================================= Program: Visualization Toolkit Module: vtkWebGLExporter.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. =========================================================================*/ #include "vtkWebGLExporter.h" #include "vtkAbstractMapper.h" #include "vtkActor2D.h" #include "vtkActorCollection.h" #include "vtkBase64Utilities.h" #include "vtkCamera.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkCompositeDataGeometryFilter.h" #include "vtkCompositeDataSet.h" #include "vtkDataSet.h" #include "vtkDataSetAttributes.h" #include "vtkDiscretizableColorTransferFunction.h" #include "vtkFollower.h" #include "vtkGenericCell.h" #include "vtkMapper.h" #include "vtkMapper2D.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyDataMapper2D.h" #include "vtkPolyDataNormals.h" #include "vtkProperty.h" #include "vtkProperty2D.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkRendererCollection.h" #include "vtkScalarBarActor.h" #include "vtkScalarBarRepresentation.h" #include "vtkSmartPointer.h" #include "vtkTriangleFilter.h" #include "vtkViewport.h" #include "vtkWidgetRepresentation.h" #include "vtkWebGLObject.h" #include "vtkWebGLPolyData.h" #include "vtkWebGLWidget.h" #include #include #include #include #include #include #include "glMatrix.h" #include "webglRenderer.h" #include "vtksys/FStream.hxx" #include "vtksys/MD5.h" #include "vtksys/SystemTools.hxx" //***************************************************************************** class vtkWebGLExporter::vtkInternal { public: std::string LastMetaData; std::map ActorTimestamp; std::map OldActorTimestamp; std::vector Objects; std::vector tempObj; }; //***************************************************************************** vtkStandardNewMacro(vtkWebGLExporter); vtkWebGLExporter::vtkWebGLExporter() { this->meshObjMaxSize = 65532 / 3; this->lineObjMaxSize = 65534 / 2; this->Internal = new vtkInternal(); this->TriangleFilter = nullptr; this->GradientBackground = false; this->SetCenterOfRotation(0.0, 0.0, 0.0); this->renderersMetaData = ""; this->SceneSize[0] = 0; this->SceneSize[1] = 0; this->SceneSize[2] = 0; this->hasWidget = false; } vtkWebGLExporter::~vtkWebGLExporter() { while (!this->Internal->Objects.empty()) { vtkWebGLObject* obj = this->Internal->Objects.back(); obj->Delete(); this->Internal->Objects.pop_back(); } delete this->Internal; if (this->TriangleFilter) { this->TriangleFilter->Delete(); } } void vtkWebGLExporter::SetMaxAllowedSize(int mesh, int lines) { this->meshObjMaxSize = mesh; this->lineObjMaxSize = lines; if (this->meshObjMaxSize * 3 > 65532) this->meshObjMaxSize = 65532 / 3; if (this->lineObjMaxSize * 2 > 65534) this->lineObjMaxSize = 65534 / 2; if (this->meshObjMaxSize < 10) this->meshObjMaxSize = 10; if (this->lineObjMaxSize < 10) this->lineObjMaxSize = 10; for (size_t i = 0; i < this->Internal->Objects.size(); i++) this->Internal->Objects[i]->GenerateBinaryData(); } void vtkWebGLExporter::SetMaxAllowedSize(int size) { this->SetMaxAllowedSize(size, size); } void vtkWebGLExporter::SetCenterOfRotation(float a1, float a2, float a3) { this->CenterOfRotation[0] = a1; this->CenterOfRotation[1] = a2; this->CenterOfRotation[2] = a3; } void vtkWebGLExporter::parseRenderer( vtkRenderer* renderer, const char* vtkNotUsed(viewId), bool onlyWidget, void* vtkNotUsed(mapTime)) { vtkPropCollection* propCollection = renderer->GetViewProps(); for (int i = 0; i < propCollection->GetNumberOfItems(); i++) { vtkProp* prop = (vtkProp*)propCollection->GetItemAsObject(i); vtkWidgetRepresentation* trt = vtkWidgetRepresentation::SafeDownCast(prop); if (trt != nullptr) this->hasWidget = true; if ((!onlyWidget || trt != nullptr) && prop->GetVisibility()) { vtkPropCollection* allactors = vtkPropCollection::New(); prop->GetActors(allactors); for (int j = 0; j < allactors->GetNumberOfItems(); j++) { vtkActor* actor = vtkActor::SafeDownCast(allactors->GetItemAsObject(j)); vtkActor* key = actor; vtkMTimeType previousValue = this->Internal->OldActorTimestamp[key]; this->parseActor( actor, previousValue, (size_t)renderer, renderer->GetLayer(), trt != nullptr); } allactors->Delete(); } if (!onlyWidget && prop->GetVisibility()) { vtkPropCollection* all2dactors = vtkPropCollection::New(); prop->GetActors2D(all2dactors); for (int k = 0; k < all2dactors->GetNumberOfItems(); k++) { vtkActor2D* actor = vtkActor2D::SafeDownCast(all2dactors->GetItemAsObject(k)); vtkActor2D* key = actor; vtkMTimeType previousValue = this->Internal->OldActorTimestamp[key]; this->parseActor2D( actor, previousValue, (size_t)renderer, renderer->GetLayer(), trt != nullptr); } all2dactors->Delete(); } } } void vtkWebGLExporter::parseActor2D( vtkActor2D* actor, vtkMTimeType actorTime, size_t renderId, int layer, bool isWidget) { vtkActor2D* key = actor; vtkScalarBarActor* scalarbar = vtkScalarBarActor::SafeDownCast(actor); vtkMTimeType dataMTime = actor->GetMTime() + actor->GetRedrawMTime() + actor->GetProperty()->GetMTime(); dataMTime += (vtkMTimeType)actor->GetMapper(); if (scalarbar) dataMTime += scalarbar->GetLookupTable()->GetMTime(); if (dataMTime != actorTime && actor->GetVisibility()) { this->Internal->ActorTimestamp[key] = dataMTime; if (actor->GetMapper()) { std::string name = actor->GetMapper()->GetClassName(); if (vtkPolyDataMapper2D::SafeDownCast(actor->GetMapper())) { } } else { if (scalarbar) { vtkWebGLWidget* obj = vtkWebGLWidget::New(); obj->GetDataFromColorMap(actor); std::stringstream ss; ss << (size_t)actor; obj->SetId(ss.str()); obj->SetRendererId(static_cast(renderId)); this->Internal->Objects.push_back(obj); obj->SetLayer(layer); obj->SetVisibility(actor->GetVisibility() != 0); obj->SetIsWidget(isWidget); obj->SetInteractAtServer(false); obj->GenerateBinaryData(); } } } else { this->Internal->ActorTimestamp[key] = dataMTime; std::stringstream ss; ss << (vtkMTimeType)actor; for (size_t i = 0; i < this->Internal->tempObj.size(); i++) { if (this->Internal->tempObj[i]->GetId() == ss.str()) { vtkWebGLObject* obj = this->Internal->tempObj[i]; this->Internal->tempObj.erase(this->Internal->tempObj.begin() + i); obj->SetVisibility(actor->GetVisibility() != 0); this->Internal->Objects.push_back(obj); } } } } void vtkWebGLExporter::parseActor( vtkActor* actor, vtkMTimeType actorTime, size_t rendererId, int layer, bool isWidget) { vtkMapper* mapper = actor->GetMapper(); if (mapper) { vtkMTimeType dataMTime; vtkTriangleFilter* polydata = this->GetPolyData(mapper, dataMTime); vtkActor* key = actor; dataMTime = actor->GetMTime() + mapper->GetLookupTable()->GetMTime(); dataMTime += actor->GetProperty()->GetMTime() + mapper->GetMTime() + actor->GetRedrawMTime(); dataMTime += polydata->GetOutput()->GetNumberOfLines() + polydata->GetOutput()->GetNumberOfPolys(); dataMTime += actor->GetProperty()->GetRepresentation() + mapper->GetScalarMode() + actor->GetVisibility(); dataMTime += polydata->GetInput()->GetMTime(); if (vtkFollower::SafeDownCast(actor)) dataMTime += vtkFollower::SafeDownCast(actor)->GetCamera()->GetMTime(); if (dataMTime != actorTime && actor->GetVisibility()) { double bb[6]; actor->GetBounds(bb); double m1 = std::max(bb[1] - bb[0], bb[3] - bb[2]); m1 = std::max(m1, bb[5] - bb[4]); double m2 = std::max(this->SceneSize[0], this->SceneSize[1]); m2 = std::max(m2, this->SceneSize[2]); if (m1 > m2) { this->SceneSize[0] = bb[1] - bb[0]; this->SceneSize[1] = bb[3] - bb[2]; this->SceneSize[2] = bb[5] - bb[4]; } this->Internal->ActorTimestamp[key] = dataMTime; vtkWebGLObject* obj = nullptr; std::stringstream ss; ss << (size_t)actor; for (size_t i = 0; i < this->Internal->tempObj.size(); i++) { if (this->Internal->tempObj[i]->GetId() == ss.str()) { obj = this->Internal->tempObj[i]; this->Internal->tempObj.erase(this->Internal->tempObj.begin() + i); } } if (obj == nullptr) obj = vtkWebGLPolyData::New(); if (polydata->GetOutput()->GetNumberOfPolys() != 0) { if (actor->GetProperty()->GetRepresentation() == VTK_WIREFRAME) { ((vtkWebGLPolyData*)obj) ->GetLinesFromPolygon(mapper, actor, this->lineObjMaxSize, nullptr); } else { if (actor->GetProperty()->GetEdgeVisibility()) { vtkWebGLPolyData* newobj = vtkWebGLPolyData::New(); double ccc[3]; actor->GetProperty()->GetEdgeColor(&ccc[0]); ((vtkWebGLPolyData*)newobj) ->GetLinesFromPolygon(mapper, actor, this->lineObjMaxSize, ccc); newobj->SetId(ss.str() + "1"); newobj->SetRendererId(static_cast(rendererId)); this->Internal->Objects.push_back(newobj); newobj->SetLayer(layer); newobj->SetTransformationMatrix(actor->GetMatrix()); newobj->SetVisibility(actor->GetVisibility() != 0); newobj->SetHasTransparency(actor->HasTranslucentPolygonalGeometry() != 0); newobj->SetIsWidget(isWidget); newobj->SetInteractAtServer(isWidget); newobj->GenerateBinaryData(); } switch (mapper->GetScalarMode()) { case VTK_SCALAR_MODE_USE_POINT_FIELD_DATA: ((vtkWebGLPolyData*)obj) ->GetPolygonsFromPointData(polydata, actor, this->meshObjMaxSize); break; case VTK_SCALAR_MODE_USE_CELL_FIELD_DATA: ((vtkWebGLPolyData*)obj) ->GetPolygonsFromCellData(polydata, actor, this->meshObjMaxSize); break; default: ((vtkWebGLPolyData*)obj) ->GetPolygonsFromPointData(polydata, actor, this->meshObjMaxSize); break; } } obj->SetId(ss.str()); obj->SetRendererId(static_cast(rendererId)); this->Internal->Objects.push_back(obj); obj->SetLayer(layer); obj->SetTransformationMatrix(actor->GetMatrix()); obj->SetVisibility(actor->GetVisibility() != 0); obj->SetHasTransparency(actor->HasTranslucentPolygonalGeometry() != 0); obj->SetIsWidget(isWidget); obj->SetInteractAtServer(isWidget); obj->GenerateBinaryData(); } else if (polydata->GetOutput()->GetNumberOfLines() != 0) { ((vtkWebGLPolyData*)obj)->GetLines(polydata, actor, this->lineObjMaxSize); obj->SetId(ss.str()); obj->SetRendererId(static_cast(rendererId)); this->Internal->Objects.push_back(obj); obj->SetLayer(layer); obj->SetTransformationMatrix(actor->GetMatrix()); obj->SetVisibility(actor->GetVisibility() != 0); obj->SetHasTransparency(actor->HasTranslucentPolygonalGeometry() != 0); obj->SetIsWidget(isWidget); obj->SetInteractAtServer(isWidget); obj->GenerateBinaryData(); } else if (polydata->GetOutput()->GetNumberOfPoints() != 0) { ((vtkWebGLPolyData*)obj)->GetPoints(polydata, actor, 65534); // Wendel obj->SetId(ss.str()); obj->SetRendererId(static_cast(rendererId)); this->Internal->Objects.push_back(obj); obj->SetLayer(layer); obj->SetTransformationMatrix(actor->GetMatrix()); obj->SetVisibility(actor->GetVisibility() != 0); obj->SetHasTransparency(actor->HasTranslucentPolygonalGeometry() != 0); obj->SetIsWidget(false); obj->SetInteractAtServer(false); obj->GenerateBinaryData(); } if (polydata->GetOutput()->GetNumberOfPolys() != 0 && polydata->GetOutput()->GetNumberOfLines() != 0) { obj = vtkWebGLPolyData::New(); ((vtkWebGLPolyData*)obj)->GetLines(polydata, actor, this->lineObjMaxSize); ss << "1"; obj->SetId(ss.str()); obj->SetRendererId(static_cast(rendererId)); this->Internal->Objects.push_back(obj); obj->SetLayer(layer); obj->SetTransformationMatrix(actor->GetMatrix()); obj->SetVisibility(actor->GetVisibility() != 0); obj->SetHasTransparency(actor->HasTranslucentPolygonalGeometry() != 0); obj->SetIsWidget(isWidget); obj->SetInteractAtServer(isWidget); obj->GenerateBinaryData(); } if (polydata->GetOutput()->GetNumberOfLines() == 0 && polydata->GetOutput()->GetNumberOfPolys() == 0 && polydata->GetOutput()->GetNumberOfPoints() == 0) { obj->Delete(); } } else { this->Internal->ActorTimestamp[key] = actorTime; std::stringstream ss; ss << (size_t)actor; for (size_t i = 0; i < this->Internal->tempObj.size(); i++) { if (this->Internal->tempObj[i]->GetId() == ss.str()) { vtkWebGLObject* obj = this->Internal->tempObj[i]; this->Internal->tempObj.erase(this->Internal->tempObj.begin() + i); obj->SetVisibility(actor->GetVisibility() != 0); this->Internal->Objects.push_back(obj); } } } } } void vtkWebGLExporter::parseScene( vtkRendererCollection* renderers, const char* viewId, int parseType) { if (!renderers) return; bool onlyWidget = parseType == VTK_ONLYWIDGET; bool cameraOnly = onlyWidget && !this->hasWidget; this->SceneId = viewId ? viewId : ""; if (cameraOnly) { this->generateRendererData(renderers, viewId); return; } if (onlyWidget) { for (int i = static_cast(this->Internal->Objects.size()) - 1; i >= 0; i--) { vtkWebGLObject* obj = this->Internal->Objects[i]; if (obj->InteractAtServer()) { this->Internal->tempObj.push_back(obj); this->Internal->Objects.erase(this->Internal->Objects.begin() + i); } } } else { while (!this->Internal->Objects.empty()) { this->Internal->tempObj.push_back(this->Internal->Objects.back()); this->Internal->Objects.pop_back(); } } this->Internal->OldActorTimestamp = this->Internal->ActorTimestamp; if (!onlyWidget) this->Internal->ActorTimestamp.clear(); this->hasWidget = false; for (int i = 0; i < renderers->GetNumberOfItems(); i++) { vtkRenderer* renderer = vtkRenderer::SafeDownCast(renderers->GetItemAsObject(i)); if (renderer->GetDraw()) this->parseRenderer(renderer, viewId, onlyWidget, nullptr); } while (!this->Internal->tempObj.empty()) { vtkWebGLObject* obj = this->Internal->tempObj.back(); this->Internal->tempObj.pop_back(); obj->Delete(); } this->generateRendererData(renderers, viewId); } bool sortLayer(vtkRenderer* i, vtkRenderer* j) { return (i->GetLayer() < j->GetLayer()); } void vtkWebGLExporter::generateRendererData( vtkRendererCollection* renderers, const char* vtkNotUsed(viewId)) { std::stringstream ss; ss << "\"Renderers\": ["; std::vector orderedList; orderedList.reserve(renderers->GetNumberOfItems()); for (int i = 0; i < renderers->GetNumberOfItems(); i++) orderedList.push_back(vtkRenderer::SafeDownCast(renderers->GetItemAsObject(i))); std::sort(orderedList.begin(), orderedList.begin() + orderedList.size(), sortLayer); int* fullSize = nullptr; for (size_t i = 0; i < orderedList.size(); i++) { vtkRenderer* renderer = orderedList[i]; if (i == 0) { fullSize = renderer->GetSize(); } double cam[10]; cam[0] = renderer->GetActiveCamera()->GetViewAngle(); renderer->GetActiveCamera()->GetFocalPoint(&cam[1]); renderer->GetActiveCamera()->GetViewUp(&cam[4]); renderer->GetActiveCamera()->GetPosition(&cam[7]); int *s, *o; s = renderer->GetSize(); o = renderer->GetOrigin(); ss << "{\"layer\":" << renderer->GetLayer() << ","; // Render Layer if (renderer->GetLayer() == 0) // Render Background { double back[3]; renderer->GetBackground(back); ss << "\"Background1\":[" << back[0] << "," << back[1] << "," << back[2] << "],"; if (renderer->GetGradientBackground()) { renderer->GetBackground2(back); ss << "\"Background2\":[" << back[0] << "," << back[1] << "," << back[2] << "],"; } } ss << "\"LookAt\":["; // Render Camera for (int j = 0; j < 9; j++) ss << cam[j] << ","; ss << cam[9] << "], "; ss << "\"size\": [" << (float)(s[0] / (float)fullSize[0]) << "," << (float)(s[1] / (float)fullSize[1]) << "],"; // Render Size ss << "\"origin\": [" << (float)(o[0] / (float)fullSize[0]) << "," << (float)(o[1] / (float)fullSize[1]) << "]"; // Render Position ss << "}"; if (static_cast(i + 1) != renderers->GetNumberOfItems()) ss << ", "; } ss << "]"; this->renderersMetaData = ss.str(); } vtkTriangleFilter* vtkWebGLExporter::GetPolyData(vtkMapper* mapper, vtkMTimeType& dataMTime) { vtkDataSet* dataset = nullptr; vtkSmartPointer tempDS; vtkDataObject* dObj = mapper->GetInputDataObject(0, 0); vtkCompositeDataSet* cd = vtkCompositeDataSet::SafeDownCast(dObj); if (cd) { dataMTime = cd->GetMTime(); vtkCompositeDataGeometryFilter* gf = vtkCompositeDataGeometryFilter::New(); gf->SetInputData(cd); gf->Update(); tempDS = gf->GetOutput(); gf->Delete(); dataset = tempDS; } else { dataset = mapper->GetInput(); dataMTime = dataset->GetMTime(); } // Converting to triangles. WebGL only support triangles. if (this->TriangleFilter) this->TriangleFilter->Delete(); this->TriangleFilter = vtkTriangleFilter::New(); this->TriangleFilter->SetInputData(dataset); this->TriangleFilter->Update(); return this->TriangleFilter; } /* Function: GenerateMetaData Description: - Generates the metadata of the scene in JSON format Ex.: { "id": ,"LookAt": ,"Background1": ,"Background2": "Objects": [{"id": ,"md5": ,"parts": }, {"id": ,"md5": ,"parts": }] } */ const char* vtkWebGLExporter::GenerateMetadata() { double max = std::max(this->SceneSize[0], this->SceneSize[1]); max = std::max(max, this->SceneSize[2]); std::stringstream ss; ss << "{\"id\":" << this->SceneId.c_str() << ","; ss << "\"MaxSize\":" << max << ","; ss << "\"Center\":["; for (int i = 0; i < 2; i++) ss << this->CenterOfRotation[i] << ", "; ss << this->CenterOfRotation[2] << "],"; ss << this->renderersMetaData << ","; ss << " \"Objects\":["; bool first = true; for (size_t i = 0; i < this->Internal->Objects.size(); i++) { vtkWebGLObject* obj = this->Internal->Objects[i]; if (obj->isVisible()) { if (first) first = false; else ss << ", "; ss << "{\"id\":" << obj->GetId() << ", \"md5\":\"" << obj->GetMD5() << "\"" << ", \"parts\":" << obj->GetNumberOfParts() << ", \"interactAtServer\":" << obj->InteractAtServer() << ", \"transparency\":" << obj->HasTransparency() << ", \"layer\":" << obj->GetLayer() << ", \"wireframe\":" << obj->isWireframeMode() << "}"; } } ss << "]}"; this->Internal->LastMetaData = ss.str(); return this->Internal->LastMetaData.c_str(); } const char* vtkWebGLExporter::GenerateExportMetadata() { double max = std::max(this->SceneSize[0], this->SceneSize[1]); max = std::max(max, this->SceneSize[2]); std::stringstream ss; ss << "{\"id\":" << this->SceneId << ","; ss << "\"MaxSize\":" << max << ","; ss << "\"Center\":["; for (int i = 0; i < 2; i++) ss << this->CenterOfRotation[i] << ", "; ss << this->CenterOfRotation[2] << "],"; ss << this->renderersMetaData << ","; ss << " \"Objects\":["; bool first = true; for (size_t i = 0; i < this->Internal->Objects.size(); i++) { vtkWebGLObject* obj = this->Internal->Objects[i]; if (obj->isVisible()) { for (int j = 0; j < obj->GetNumberOfParts(); j++) { if (first) first = false; else ss << ", "; ss << "{\"id\":" << obj->GetId() << ", \"md5\":\"" << obj->GetMD5() << "\"" << ", \"parts\":" << 1 << ", \"interactAtServer\":" << obj->InteractAtServer() << ", \"transparency\":" << obj->HasTransparency() << ", \"layer\":" << obj->GetLayer() << ", \"wireframe\":" << obj->isWireframeMode() << "}"; } } } ss << "]}"; this->Internal->LastMetaData = ss.str(); return this->Internal->LastMetaData.c_str(); } vtkWebGLObject* vtkWebGLExporter::GetWebGLObject(int index) { return this->Internal->Objects[index]; } int vtkWebGLExporter::GetNumberOfObjects() { return static_cast(this->Internal->Objects.size()); } void vtkWebGLExporter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } const char* vtkWebGLExporter::GetId() { return this->SceneId.c_str(); } bool vtkWebGLExporter::hasChanged() { for (size_t i = 0; i < this->Internal->Objects.size(); i++) if (this->Internal->Objects[i]->HasChanged()) return true; return false; } void vtkWebGLExporter::exportStaticScene( vtkRendererCollection* renderers, int width, int height, std::string path) { std::stringstream ss; ss << width << "," << height; std::string resultHTML = ""; resultHTML += "
\n"; resultHTML += ""; vtksys::ofstream file; file.open(path.c_str()); file << resultHTML; file.close(); } //------------------------------------------------------------------------------ void vtkWebGLExporter::ComputeMD5(const unsigned char* content, int size, std::string& hash) { unsigned char digest[16]; char md5Hash[33]; md5Hash[32] = '\0'; vtksysMD5* md5 = vtksysMD5_New(); vtksysMD5_Initialize(md5); vtksysMD5_Append(md5, content, size); vtksysMD5_Finalize(md5, digest); vtksysMD5_DigestToHex(digest, md5Hash); vtksysMD5_Delete(md5); hash = md5Hash; }