// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkRIBExporter.h" #include "vtkAssemblyNode.h" #include "vtkAssemblyPath.h" #include "vtkCamera.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkFieldData.h" #include "vtkGeometryFilter.h" #include "vtkImageAppendComponents.h" #include "vtkImageConstantPad.h" #include "vtkImageExtractComponents.h" #include "vtkLightCollection.h" #include "vtkMapper.h" #include "vtkMath.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkPolygon.h" #include "vtkRIBLight.h" #include "vtkRIBProperty.h" #include "vtkRenderWindow.h" #include "vtkRendererCollection.h" #include "vtkStringFormatter.h" #include "vtkStructuredPoints.h" #include "vtkTIFFWriter.h" #include "vtkTexture.h" #include "vtkUnsignedCharArray.h" #include "vtksys/SystemTools.hxx" #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkRIBExporter); typedef double RtColor[3]; typedef double RtPoint[3]; typedef char* RtPointer; typedef float RtFloat; vtkRIBExporter::vtkRIBExporter() { this->FilePrefix = nullptr; this->FilePtr = nullptr; this->TexturePrefix = nullptr; this->Size[0] = this->Size[1] = -1; this->PixelSamples[0] = this->PixelSamples[1] = 2; this->Background = 0; this->ExportArrays = 0; } vtkRIBExporter::~vtkRIBExporter() { delete[] this->FilePrefix; delete[] this->TexturePrefix; } void vtkRIBExporter::WriteData() { // make sure the user specified a FilePrefix if (this->FilePrefix == nullptr) { vtkErrorMacro(<< "Please specify file name for the rib file"); delete[] this->FilePrefix; delete[] this->TexturePrefix; return; } vtkActorCollection* ac; vtkLightCollection* lc; vtkActor* anActor; vtkLight* aLight; vtkTexture* aTexture; // get the renderer vtkRenderer* ren = this->ActiveRenderer; if (!ren) { ren = this->RenderWindow->GetRenderers()->GetFirstRenderer(); } // make sure it has at least one actor if (ren->GetActors()->GetNumberOfItems() < 1) { vtkErrorMacro(<< "No actors found for writing .RIB file."); return; } auto ribFileName = vtk::format("{}{}", this->FilePrefix, ".rib"); this->FilePtr = vtksys::SystemTools::Fopen(ribFileName, "w"); if (this->FilePtr == nullptr) { vtkErrorMacro(<< "Cannot open " << ribFileName); return; } // // Write Header // this->WriteHeader(ren); // // All textures must be made first // ac = ren->GetActors(); vtkCollection* textures = vtkCollection::New(); vtkCollectionSimpleIterator ait; for (ac->InitTraversal(ait); (anActor = ac->GetNextActor(ait));) { // see if the actor has a mapper. it could be an assembly if (anActor->GetMapper() == nullptr) { continue; } // if it's invisible, don't make the texture if (anActor->GetVisibility()) { aTexture = anActor->GetTexture(); if (aTexture && textures->IndexOfFirstOccurrence(aTexture) < 0) { this->WriteTexture(aTexture); textures->AddItem(aTexture); } } } // // Write viewport // this->WriteViewport(ren, this->Size); // // Write camera // this->WriteCamera(ren->GetActiveCamera()); vtk::print(this->FilePtr, "WorldBegin\n"); // // Write all lights // lc = ren->GetLights(); // // If there is no light defined, create one // vtkCollectionSimpleIterator sit; lc->InitTraversal(sit); if (lc->GetNextLight(sit) == nullptr) { vtkWarningMacro(<< "No light defined, creating one at camera position"); ren->CreateLight(); } // Create an ambient light this->WriteAmbientLight(1); int lightCount = 2; for (lc->InitTraversal(sit); (aLight = lc->GetNextLight(sit));) { if (aLight->GetSwitch()) { this->WriteLight(aLight, lightCount++); } } // // Write all actors // vtkAssemblyNode* node; ac = ren->GetActors(); vtkAssemblyPath* apath; for (ac->InitTraversal(ait); (anActor = ac->GetNextActor(ait));) { for (anActor->InitPathTraversal(); (apath = anActor->GetNextPath());) { node = apath->GetLastNode(); if (node->GetViewProp()->GetVisibility()) { if (node->GetViewProp()->IsA("vtkActor")) { this->WriteActor((vtkActor*)(node->GetViewProp())); } } } } // RiWorldEnd (); vtk::print(this->FilePtr, "WorldEnd\n"); // // Write trailer // this->WriteTrailer(); // RiEnd (); fclose(this->FilePtr); textures->Delete(); } void vtkRIBExporter::WriteHeader(vtkRenderer* aRen) { // create a FileName to hold the rendered image auto imageFileName = vtk::format("{}{}", this->FilePrefix, ".tif"); vtk::print(this->FilePtr, "FrameBegin {:d}\n", 1); vtk::print(this->FilePtr, "Display \"{:s}\" \"file\" \"rgb\"\n", imageFileName.c_str()); vtk::print(this->FilePtr, "Declare \"color\" \"uniform color\"\n"); if (this->Background) { double* color = aRen->GetBackground(); vtk::print(this->FilePtr, "Imager \"background\" \"color\" [{:f} {:f} {:f}]\n", color[0], color[1], color[2]); } vtk::print( this->FilePtr, "PixelSamples {:d} {:d}\n", this->PixelSamples[0], this->PixelSamples[1]); } void vtkRIBExporter::WriteTrailer() { vtk::print(this->FilePtr, "FrameEnd\n"); } void vtkRIBExporter::WriteProperty(vtkProperty* aProperty, vtkTexture* aTexture) { char* mapName; double Ambient, Diffuse, Specular; double Opacity; double *DiffuseColor, *SpecularColor; double Roughness; RtColor opacity; Opacity = aProperty->GetOpacity(); // set the opacity opacity[0] = Opacity; opacity[1] = Opacity; opacity[2] = Opacity; vtk::print(this->FilePtr, "Opacity [{:f} {:f} {:f}]\n", opacity[0], opacity[1], opacity[2]); // set the color of the surface DiffuseColor = aProperty->GetDiffuseColor(); vtk::print( this->FilePtr, "Color [{:f} {:f} {:f}]\n", DiffuseColor[0], DiffuseColor[1], DiffuseColor[2]); // set the shader parameters Ambient = aProperty->GetAmbient(); Diffuse = aProperty->GetDiffuse(); Specular = aProperty->GetSpecular(); SpecularColor = aProperty->GetSpecularColor(); Roughness = (RtFloat)(1.0 / aProperty->GetSpecularPower()); // // if there is a texture map we need to declare it // mapName = nullptr; if (aTexture) { mapName = this->GetTextureName(aTexture); if (mapName) { vtk::print(this->FilePtr, "Declare \"texturename\" \"uniform string\"\n"); } } // // Now we need to check to see if an RIBProperty has been specified // if (strcmp("vtkRIBProperty", aProperty->GetClassName()) == 0) { vtkRIBProperty* aRIBProperty = (vtkRIBProperty*)aProperty; if (aRIBProperty->GetDeclarations()) { vtk::print(this->FilePtr, "{:s}", aRIBProperty->GetDeclarations()); } if (aRIBProperty->GetSurfaceShader()) { vtk::print(this->FilePtr, "{:s} \"{:s}\" ", "Surface", aRIBProperty->GetSurfaceShader()); if (aRIBProperty->GetSurfaceShaderUsesDefaultParameters()) { vtk::print(this->FilePtr, "\"Ka\" [{:f}] ", Ambient); vtk::print(this->FilePtr, "\"Kd\" [{:f}] ", Diffuse); vtk::print(this->FilePtr, "\"Ks\" [{:f}] ", Specular); vtk::print(this->FilePtr, "\"roughness\" [{:f}] ", Roughness); vtk::print(this->FilePtr, "\"specularcolor\" [{:f} {:f} {:f}]\n", SpecularColor[0], SpecularColor[1], SpecularColor[2]); if (mapName) { vtk::print(this->FilePtr, " \"texturename\" [\"{:s}\"]", mapName); } } if (aRIBProperty->GetSurfaceShaderParameters()) { vtk::print(this->FilePtr, "{:s}\n", aRIBProperty->GetSurfaceShaderParameters()); } } if (aRIBProperty->GetDisplacementShader()) { vtk::print( this->FilePtr, "{:s} \"{:s}\" ", "Displacement", aRIBProperty->GetDisplacementShader()); if (mapName) { vtk::print(this->FilePtr, " \"texturename\" [\"{:s}\"]", mapName); } if (aRIBProperty->GetDisplacementShaderParameters()) { vtk::print(this->FilePtr, "{:s}", aRIBProperty->GetDisplacementShaderParameters()); } vtk::print(this->FilePtr, "\n"); } } // Default Property else { vtk::print(this->FilePtr, "Surface \"{:s}\" ", mapName ? "paintedplastic" : "plastic"); vtk::print(this->FilePtr, "\"Ka\" [{:f}] ", Ambient); vtk::print(this->FilePtr, "\"Kd\" [{:f}] ", Diffuse); vtk::print(this->FilePtr, "\"Ks\" [{:f}] ", Specular); vtk::print(this->FilePtr, "\"roughness\" [{:f}] ", Roughness); vtk::print(this->FilePtr, "\"specularcolor\" [{:f} {:f} {:f}] ", SpecularColor[0], SpecularColor[1], SpecularColor[2]); if (mapName) { vtk::print(this->FilePtr, " \"texturename\" [\"{:s}\"]", mapName); } vtk::print(this->FilePtr, "\n"); } } void vtkRIBExporter::WriteLight(vtkLight* aLight, int count) { double color[4]; double* Color; double *Position, *FocalPoint; double Intensity; // get required info from light Intensity = aLight->GetIntensity(); Color = aLight->GetDiffuseColor(); color[0] = Color[0]; color[1] = Color[1]; color[2] = Color[2]; color[3] = 1.0; FocalPoint = aLight->GetFocalPoint(); Position = aLight->GetPosition(); // // Now we need to check to see if an RIBLight has been specified // if (strcmp("vtkRIBLight", aLight->GetClassName()) == 0) { if (((vtkRIBLight*)aLight)->GetShadows()) { vtk::print(this->FilePtr, "Attribute \"light\" \"shadows\" \"on\"\n"); } } // define the light source if (!aLight->GetPositional()) { vtk::print(this->FilePtr, "LightSource \"distantlight\" {:d} ", count); vtk::print(this->FilePtr, "\"intensity\" [{:f}] ", Intensity); vtk::print(this->FilePtr, "\"lightcolor\" [{:f} {:f} {:f}] ", color[0], color[1], color[2]); vtk::print(this->FilePtr, "\"from\" [{:f} {:f} {:f}] ", Position[0], Position[1], Position[2]); vtk::print( this->FilePtr, "\"to\" [{:f} {:f} {:f}]\n", FocalPoint[0], FocalPoint[1], FocalPoint[2]); } else { double coneAngle = aLight->GetConeAngle(); double coneAngleRadians = vtkMath::RadiansFromDegrees(coneAngle); double exponent = aLight->GetExponent(); vtk::print(this->FilePtr, "LightSource \"spotlight\" {:d} ", count); vtk::print(this->FilePtr, "\"intensity\" [{:f}] ", Intensity); vtk::print(this->FilePtr, "\"lightcolor\" [{:f} {:f} {:f}] ", color[0], color[1], color[2]); vtk::print(this->FilePtr, "\"from\" [{:f} {:f} {:f}] ", Position[0], Position[1], Position[2]); vtk::print( this->FilePtr, "\"to\" [{:f} {:f} {:f}]\n", FocalPoint[0], FocalPoint[1], FocalPoint[2]); vtk::print(this->FilePtr, "\"coneangle\" [{:f}]\n", coneAngleRadians); vtk::print(this->FilePtr, "\"beamdistribution\" [{:f}]\n", exponent); vtk::print(this->FilePtr, "\"conedeltaangle\" [{:f}]\n", 0.0); } if (strcmp("vtkRIBLight", aLight->GetClassName()) == 0) { if (((vtkRIBLight*)aLight)->GetShadows()) { vtk::print(this->FilePtr, "Attribute \"light\" \"shadows\" \"off\"\n"); } } } void vtkRIBExporter::WriteAmbientLight(int count) { vtk::print(this->FilePtr, "LightSource \"ambientlight\" {:d}\n", count); } void vtkRIBExporter::WriteViewport(vtkRenderer* ren, int size[2]) { double aspect[2]; double* vport; int left, right, bottom, top; if (size[0] != -1 || size[1] != -1) { vport = ren->GetViewport(); left = (int)(vport[0] * (size[0] - 1)); right = (int)(vport[2] * (size[0] - 1)); bottom = (int)(vport[1] * (size[1] - 1)); top = (int)(vport[3] * (size[1] - 1)); vtk::print(this->FilePtr, "Format {:d} {:d} 1\n", size[0], size[1]); vtk::print( this->FilePtr, "CropWindow {:f} {:f} {:f} {:f}\n", vport[0], vport[2], vport[1], vport[3]); aspect[0] = (double)(right - left + 1) / (double)(top - bottom + 1); aspect[1] = 1.0; vtk::print( this->FilePtr, "ScreenWindow {:f} {:f} {:f} {:f}\n", -aspect[0], aspect[0], -1.0, 1.0); } } static void PlaceCamera(FILE* filePtr, RtPoint, RtPoint, double); static void AimZ(FILE* filePtr, RtPoint); void vtkRIBExporter::WriteCamera(vtkCamera* aCamera) { RtPoint direction; double position[3], focalPoint[3]; aCamera->GetPosition(position); aCamera->GetFocalPoint(focalPoint); direction[0] = focalPoint[0] - position[0]; direction[1] = focalPoint[1] - position[1]; direction[2] = focalPoint[2] - position[2]; vtkMath::Normalize(direction); RtFloat angle = aCamera->GetViewAngle(); vtk::print(this->FilePtr, "Projection \"perspective\" \"fov\" [{:f}]\n", angle); PlaceCamera(this->FilePtr, position, direction, aCamera->GetRoll()); vtk::print(this->FilePtr, "Orientation \"rh\"\n"); } /* * PlaceCamera(): establish a viewpoint, viewing direction and orientation * for a scene. This routine must be called before RiWorldBegin(). * position: a point giving the camera position * direction: a point giving the camera direction relative to position * roll: an optional rotation of the camera about its direction axis */ static double cameraMatrix[4][4] = { { -1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; void PlaceCamera(FILE* filePtr, RtPoint position, RtPoint direction, double roll) { vtk::print(filePtr, "Identity\n"); vtk::print(filePtr, "Transform [{:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} " "]\n", cameraMatrix[0][0], cameraMatrix[0][1], cameraMatrix[0][2], cameraMatrix[0][3], cameraMatrix[1][0], cameraMatrix[1][1], cameraMatrix[1][2], cameraMatrix[1][3], cameraMatrix[2][0], cameraMatrix[2][1], cameraMatrix[2][2], cameraMatrix[2][3], cameraMatrix[3][0], cameraMatrix[3][1], cameraMatrix[3][2], cameraMatrix[3][3]); vtk::print(filePtr, "Rotate {:f} {:f} {:f} {:f}\n", -roll, 0.0, 0.0, 1.0); AimZ(filePtr, direction); vtk::print(filePtr, "Translate {:f} {:f} {:f}\n", -position[0], -position[1], -position[2]); } /* * AimZ(): rotate the world so the directionvector points in * positive z by rotating about the y axis, then x. The cosine * of each rotation is given by components of the normalized * direction vector. Before the y rotation the direction vector * might be in negative z, but not afterward. */ static void AimZ(FILE* filePtr, RtPoint direction) { double xzlen, yzlen, yrot, xrot; if (direction[0] == 0 && direction[1] == 0 && direction[2] == 0) { return; } /* * The initial rotation about the y axis is given by the projection of * the direction vector onto the x,z plane: the x and z components * of the direction. */ xzlen = sqrt(direction[0] * direction[0] + direction[2] * direction[2]); if (xzlen == 0) { yrot = (direction[1] < 0) ? 180 : 0; } else { yrot = 180 * acos(direction[2] / xzlen) / vtkMath::Pi(); } /* * The second rotation, about the x axis, is given by the projection on * the y,z plane of the y-rotated direction vector: the original y * component, and the rotated x,z vector from above. */ yzlen = sqrt(direction[1] * direction[1] + xzlen * xzlen); xrot = 180 * acos(xzlen / yzlen) / vtkMath::Pi(); /* yzlen should never be 0 */ if (direction[1] > 0) { vtk::print(filePtr, "Rotate {:f} {:f} {:f} {:f}\n", xrot, 1.0, 0.0, 0.0); } else { vtk::print(filePtr, "Rotate {:f} {:f} {:f} {:f}\n", -xrot, 1.0, 0.0, 0.0); } /* The last rotation declared gets performed first */ if (direction[0] > 0) { vtk::print(filePtr, "Rotate {:f} {:f} {:f} {:f}\n", -yrot, 0.0, 1.0, 0.0); } else { vtk::print(filePtr, "Rotate {:f} {:f} {:f} {:f}\n", yrot, 0.0, 1.0, 0.0); } } void vtkRIBExporter::WriteActor(vtkActor* anActor) { vtkDataSet* aDataSet; vtkPolyData* polyData; vtkGeometryFilter* geometryFilter = nullptr; vtkMatrix4x4* matrix = vtkMatrix4x4::New(); // see if the actor has a mapper. it could be an assembly if (anActor->GetMapper() == nullptr) { return; } vtk::print(this->FilePtr, "AttributeBegin\n"); vtk::print(this->FilePtr, "TransformBegin\n"); // write out the property this->WriteProperty(anActor->GetProperty(), anActor->GetTexture()); // get the mappers input and matrix aDataSet = anActor->GetMapper()->GetInput(); anActor->GetMatrix(matrix); matrix->Transpose(); // insert model transformation vtk::print(this->FilePtr, "ConcatTransform [{:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} " "{:f} ]\n", matrix->Element[0][0], matrix->Element[0][1], matrix->Element[0][2], matrix->Element[0][3], matrix->Element[1][0], matrix->Element[1][1], matrix->Element[1][2], matrix->Element[1][3], matrix->Element[2][0], matrix->Element[2][1], matrix->Element[2][2], matrix->Element[2][3], matrix->Element[3][0], matrix->Element[3][1], matrix->Element[3][2], matrix->Element[3][3]); // we really want polydata if (aDataSet->GetDataObjectType() != VTK_POLY_DATA) { geometryFilter = vtkGeometryFilter::New(); geometryFilter->SetInputConnection(anActor->GetMapper()->GetInputConnection(0, 0)); geometryFilter->Update(); polyData = geometryFilter->GetOutput(); } else { polyData = (vtkPolyData*)aDataSet; } // Let us start with point data and then we can copy to other if (this->ExportArrays) { vtkPointData* pointData = polyData->GetPointData(); if (pointData && pointData->GetNumberOfArrays()) { int cc; for (cc = 0; cc < pointData->GetNumberOfArrays(); cc++) { vtkDataArray* array = pointData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); vtk::print(this->FilePtr, "Declare \"{:s}\" \"varying double\"\n", buffer); } } vtkCellData* cellData = polyData->GetCellData(); if (cellData && cellData->GetNumberOfArrays()) { int cc; for (cc = 0; cc < cellData->GetNumberOfArrays(); cc++) { vtkDataArray* array = cellData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); vtk::print(this->FilePtr, "Declare \"{:s}\" \"varying double\"\n", buffer); } } vtkFieldData* fieldData = polyData->GetFieldData(); if (fieldData && fieldData->GetNumberOfArrays()) { int cc; for (cc = 0; cc < fieldData->GetNumberOfArrays(); cc++) { vtkDataArray* array = fieldData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); vtk::print(this->FilePtr, "Declare \"{:s}\" \"varying double\"\n", buffer); } } } if (polyData->GetNumberOfPolys()) { this->WritePolygons(polyData, anActor->GetMapper()->MapScalars(1.0), anActor->GetProperty()); } if (polyData->GetNumberOfStrips()) { this->WriteStrips(polyData, anActor->GetMapper()->MapScalars(1.0), anActor->GetProperty()); } vtk::print(this->FilePtr, "TransformEnd\n"); vtk::print(this->FilePtr, "AttributeEnd\n"); if (geometryFilter) { geometryFilter->Delete(); } matrix->Delete(); } void vtkRIBExporter::WritePolygons( vtkPolyData* polyData, vtkUnsignedCharArray* c, vtkProperty* aProperty) { double vertexColors[512][3]; double* TCoords; double* normals; double points[3]; RtPoint vertexNormals[512]; RtPoint vertexPoints[512]; double poly_norm[3]; double vertexTCoords[512][2]; const vtkIdType* pts = nullptr; vtkIdType npts = 0; int k, kk; int rep, j, interpolation; int tDim; unsigned char* colors; vtkCellArray* polys; vtkDataArray* n = nullptr; vtkPoints* p; vtkPolygon* polygon; vtkDataArray* t; // get the representation rep = aProperty->GetRepresentation(); switch (rep) { case VTK_SURFACE: break; default: vtkErrorMacro(<< "Bad representation. Only Surface is supported."); break; } // get the shading interpolation interpolation = aProperty->GetInterpolation(); // and draw the display list polygon = vtkPolygon::New(); p = polyData->GetPoints(); polys = polyData->GetPolys(); t = polyData->GetPointData()->GetTCoords(); if (t) { tDim = t->GetNumberOfComponents(); if (tDim != 2) { vtkDebugMacro(<< "Currently only 2d textures are supported.\n"); t = nullptr; } } // Get point data vtkPointData* pointData = polyData->GetPointData(); vtkCellData* cellData = polyData->GetCellData(); vtkFieldData* fieldData = polyData->GetFieldData(); if (interpolation == VTK_FLAT || !(polyData->GetPointData()) || !(n = polyData->GetPointData()->GetNormals())) { n = nullptr; } for (polys->InitTraversal(); polys->GetNextCell(npts, pts);) { if (!n) { vtkPolygon::ComputeNormal(p, npts, pts, poly_norm); } for (j = 0; j < npts; j++) { k = j; if (c) { colors = c->GetPointer(4 * pts[k]); vertexColors[k][0] = colors[0] / 255.0; vertexColors[k][1] = colors[1] / 255.0; vertexColors[k][2] = colors[2] / 255.0; } if (t) { TCoords = t->GetTuple(pts[k]); vertexTCoords[k][0] = TCoords[0]; // Renderman Textures have origin at upper left vertexTCoords[k][1] = 1.0 - TCoords[1]; } if (n) { normals = n->GetTuple(pts[k]); vertexNormals[k][0] = normals[0]; vertexNormals[k][1] = normals[1]; vertexNormals[k][2] = normals[2]; } else { vertexNormals[k][0] = poly_norm[0]; vertexNormals[k][1] = poly_norm[1]; vertexNormals[k][2] = poly_norm[2]; } p->GetPoint(pts[k], points); vertexPoints[k][0] = points[0]; vertexPoints[k][1] = points[1]; vertexPoints[k][2] = points[2]; } vtk::print(this->FilePtr, "Polygon "); vtk::print(this->FilePtr, "\"P\" ["); for (kk = 0; kk < npts; kk++) { vtk::print(this->FilePtr, "{:f} {:f} {:f} ", vertexPoints[kk][0], vertexPoints[kk][1], vertexPoints[kk][2]); } vtk::print(this->FilePtr, "] "); vtk::print(this->FilePtr, "\"N\" ["); for (kk = 0; kk < npts; kk++) { vtk::print(this->FilePtr, "{:f} {:f} {:f} ", vertexNormals[kk][0], vertexNormals[kk][1], vertexNormals[kk][2]); } vtk::print(this->FilePtr, "] "); if (c) { vtk::print(this->FilePtr, "\"Cs\" ["); for (kk = 0; kk < npts; kk++) { vtk::print(this->FilePtr, "{:f} {:f} {:f} ", vertexColors[kk][0], vertexColors[kk][1], vertexColors[kk][2]); } vtk::print(this->FilePtr, "] "); } if (t) { vtk::print(this->FilePtr, "\"st\" ["); for (kk = 0; kk < npts; kk++) { vtk::print(this->FilePtr, "{:f} {:f} ", vertexTCoords[kk][0], vertexTCoords[kk][1]); } vtk::print(this->FilePtr, "] "); } if (this->ExportArrays) { if (pointData) { int cc, aa; std::ostringstream str_with_warning_C4701; for (cc = 0; cc < pointData->GetNumberOfArrays(); cc++) { vtkDataArray* array = pointData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); str_with_warning_C4701 << "\"" << buffer << "\" ["; for (kk = 0; kk < npts; kk++) { double tuple[3]; array->GetTuple(pts[kk], tuple); for (aa = 0; aa < array->GetNumberOfComponents(); aa++) { str_with_warning_C4701 << ((!kk && !aa) ? "" : " ") << tuple[aa]; } } str_with_warning_C4701 << "] "; } vtk::print(this->FilePtr, "{:s}", str_with_warning_C4701.str().c_str()); } if (cellData) { int cc, aa; std::ostringstream str_with_warning_C4701; for (cc = 0; cc < cellData->GetNumberOfArrays(); cc++) { vtkDataArray* array = cellData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); str_with_warning_C4701 << "\"" << buffer << "\" ["; for (kk = 0; kk < npts; kk++) { double tuple[3]; array->GetTuple(pts[kk], tuple); for (aa = 0; aa < array->GetNumberOfComponents(); aa++) { str_with_warning_C4701 << ((!kk && !aa) ? "" : " ") << tuple[aa]; } } str_with_warning_C4701 << "] "; } vtk::print(this->FilePtr, "{:s}", str_with_warning_C4701.str().c_str()); } if (fieldData) { int cc, aa; std::ostringstream str_with_warning_C4701; for (cc = 0; cc < fieldData->GetNumberOfArrays(); cc++) { vtkDataArray* array = fieldData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); str_with_warning_C4701 << "\"" << buffer << "\" ["; for (kk = 0; kk < npts; kk++) { double tuple[3]; array->GetTuple(pts[kk], tuple); for (aa = 0; aa < array->GetNumberOfComponents(); aa++) { str_with_warning_C4701 << ((!kk && !aa) ? "" : " ") << tuple[aa]; } } str_with_warning_C4701 << "] "; } vtk::print(this->FilePtr, "{:s}", str_with_warning_C4701.str().c_str()); } } vtk::print(this->FilePtr, "\n"); } polygon->Delete(); } void vtkRIBExporter::WriteStrips( vtkPolyData* polyData, vtkUnsignedCharArray* c, vtkProperty* aProperty) { double vertexColors[512][3]; double* TCoords; double* normals; double points[3]; RtPoint vertexNormals[512]; RtPoint vertexPoints[512]; double poly_norm[3]; double vertexTCoords[512][2]; const vtkIdType* pts = nullptr; vtkIdType npts = 0; int p1, p2, p3; int k, kk; int rep, j, interpolation; int tDim; unsigned char* colors; vtkCellArray* strips; vtkDataArray* n = nullptr; vtkPoints* p; vtkDataArray* t; vtkPolygon* polygon; vtkIdType idx[3]; // get the representation rep = aProperty->GetRepresentation(); switch (rep) { case VTK_SURFACE: break; default: vtkErrorMacro(<< "Bad representation. Only Surface is supported."); break; } // get the shading interpolation interpolation = aProperty->GetInterpolation(); // and draw the display list p = polyData->GetPoints(); strips = polyData->GetStrips(); polygon = vtkPolygon::New(); t = polyData->GetPointData()->GetTCoords(); if (t) { tDim = t->GetNumberOfComponents(); if (tDim != 2) { vtkDebugMacro(<< "Currently only 2d textures are supported.\n"); t = nullptr; } } if (interpolation == VTK_FLAT || !(polyData->GetPointData()) || !(n = polyData->GetPointData()->GetNormals())) { n = nullptr; } // Get point data vtkPointData* pointData = polyData->GetPointData(); vtkCellData* cellData = polyData->GetCellData(); vtkFieldData* fieldData = polyData->GetFieldData(); // each iteration returns a triangle strip for (strips->InitTraversal(); strips->GetNextCell(npts, pts);) { // each triangle strip is converted into a bunch of triangles p1 = pts[0]; p2 = pts[1]; p3 = pts[2]; for (j = 0; j < (npts - 2); j++) { if (j % 2) { idx[0] = p2; idx[1] = p1; idx[2] = p3; } else { idx[0] = p1; idx[1] = p2; idx[2] = p3; } if (!n) { vtkPolygon::ComputeNormal(p, 3, idx, poly_norm); } // build colors, texture coordinates and normals for the triangle for (k = 0; k < 3; k++) { if (c) { colors = c->GetPointer(4 * idx[k]); vertexColors[k][0] = colors[0] / 255.0; vertexColors[k][1] = colors[1] / 255.0; vertexColors[k][2] = colors[2] / 255.0; } if (t) { TCoords = t->GetTuple(idx[k]); vertexTCoords[k][0] = TCoords[0]; // Renderman Textures have origin at upper left vertexTCoords[k][1] = 1.0 - TCoords[1]; } if (n) { normals = n->GetTuple(idx[k]); vertexNormals[k][0] = normals[0]; vertexNormals[k][1] = normals[1]; vertexNormals[k][2] = normals[2]; } else { vertexNormals[k][0] = poly_norm[0]; vertexNormals[k][1] = poly_norm[1]; vertexNormals[k][2] = poly_norm[2]; } p->GetPoint(idx[k], points); vertexPoints[k][0] = points[0]; vertexPoints[k][1] = points[1]; vertexPoints[k][2] = points[2]; } vtk::print(this->FilePtr, "Polygon "); vtk::print(this->FilePtr, "\"P\" ["); for (kk = 0; kk < 3; kk++) { vtk::print(this->FilePtr, "{:f} {:f} {:f} ", vertexPoints[kk][0], vertexPoints[kk][1], vertexPoints[kk][2]); } vtk::print(this->FilePtr, "] "); vtk::print(this->FilePtr, "\"N\" ["); for (kk = 0; kk < 3; kk++) { vtk::print(this->FilePtr, "{:f} {:f} {:f} ", vertexNormals[kk][0], vertexNormals[kk][1], vertexNormals[kk][2]); } vtk::print(this->FilePtr, "] "); if (c) { vtk::print(this->FilePtr, "\"Cs\" ["); for (kk = 0; kk < 3; kk++) { vtk::print(this->FilePtr, "{:f} {:f} {:f} ", vertexColors[kk][0], vertexColors[kk][1], vertexColors[kk][2]); } vtk::print(this->FilePtr, "] "); } if (t) { vtk::print(this->FilePtr, "\"st\" ["); for (kk = 0; kk < 3; kk++) { vtk::print(this->FilePtr, "{:f} {:f} ", vertexTCoords[kk][0], vertexTCoords[kk][1]); } vtk::print(this->FilePtr, "] "); } if (this->ExportArrays) { if (pointData) { int cc, aa; std::ostringstream str_with_warning_C4701; for (cc = 0; cc < pointData->GetNumberOfArrays(); cc++) { vtkDataArray* array = pointData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); str_with_warning_C4701 << "\"" << buffer << "\" ["; for (kk = 0; kk < npts; kk++) { double tuple[3]; array->GetTuple(pts[kk], tuple); for (aa = 0; aa < array->GetNumberOfComponents(); aa++) { str_with_warning_C4701 << ((!kk && !aa) ? "" : " ") << tuple[aa]; } } str_with_warning_C4701 << "] "; } vtk::print(this->FilePtr, "{:s}", str_with_warning_C4701.str().c_str()); } if (cellData) { int cc, aa; std::ostringstream str_with_warning_C4701; for (cc = 0; cc < cellData->GetNumberOfArrays(); cc++) { vtkDataArray* array = cellData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); str_with_warning_C4701 << "\"" << buffer << "\" ["; for (kk = 0; kk < npts; kk++) { double tuple[3]; array->GetTuple(pts[kk], tuple); for (aa = 0; aa < array->GetNumberOfComponents(); aa++) { str_with_warning_C4701 << ((!kk && !aa) ? "" : " ") << tuple[aa]; } } str_with_warning_C4701 << "] "; } vtk::print(this->FilePtr, "{:s}", str_with_warning_C4701.str().c_str()); } if (fieldData) { int cc, aa; std::ostringstream str_with_warning_C4701; for (cc = 0; cc < fieldData->GetNumberOfArrays(); cc++) { vtkDataArray* array = fieldData->GetArray(cc); char buffer[1024]; this->ModifyArrayName(buffer, array->GetName()); str_with_warning_C4701 << "\"" << buffer << "\" ["; for (kk = 0; kk < npts; kk++) { double tuple[3]; array->GetTuple(pts[kk], tuple); for (aa = 0; aa < array->GetNumberOfComponents(); aa++) { str_with_warning_C4701 << ((!kk && !aa) ? "" : " ") << tuple[aa]; } } str_with_warning_C4701 << "] "; } vtk::print(this->FilePtr, "{:s}", str_with_warning_C4701.str().c_str()); } } vtk::print(this->FilePtr, "\n"); // Get ready for next triangle p1 = p2; p2 = p3; if (j + 3 < npts) { p3 = pts[j + 3]; } } } polygon->Delete(); } void vtkRIBExporter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); if (this->FilePrefix) { os << indent << "FilePrefix: " << this->FilePrefix << "\n"; } else { os << indent << "FilePrefix: (none)\n"; } if (this->TexturePrefix) { os << indent << "TexturePrefix: " << this->TexturePrefix << "\n"; } else { os << indent << "TexturePrefix: (none)\n"; } os << indent << "Background: " << (this->Background ? "On\n" : "Off\n"); os << indent << "Size: " << this->Size[0] << " " << this->Size[1] << "\n"; os << indent << "PixelSamples: " << this->PixelSamples[0] << " " << this->PixelSamples[1] << "\n"; os << indent << "Export Arrays: " << (this->ExportArrays ? "On" : "Off") << "\n"; } void vtkRIBExporter::WriteTexture(vtkTexture* aTexture) { vtkDataArray* scalars; vtkDataArray* mappedScalars; int* size; int xsize, ysize; unsigned short xs, ys; // RtToken wrap = aTexture->GetRepeat () ? RI_PERIODIC : RI_CLAMP; // RiMakeTexture (this->GetTIFFName (aTexture), // this->GetTextureName (aTexture), // wrap, wrap, // RiBoxFilter, // 1, 1, // RI_NULL); const char* wrap = aTexture->GetRepeat() ? "periodic" : "clamp"; vtk::print(this->FilePtr, "MakeTexture \"{:s}\" ", this->GetTIFFName(aTexture)); vtk::print(this->FilePtr, "\"{:s}\" ", this->GetTextureName(aTexture)); vtk::print(this->FilePtr, "\"{:s}\" \"{:s}\" ", wrap, wrap); vtk::print(this->FilePtr, "\"{:s}\" 1 1\n", "box"); // do an Update and get some info if (aTexture->GetInput() == nullptr) { vtkErrorMacro(<< "texture has no input!\n"); return; } aTexture->Update(); size = aTexture->GetInput()->GetDimensions(); scalars = aTexture->GetInput()->GetPointData()->GetScalars(); // make sure scalars are non null if (!scalars) { vtkErrorMacro(<< "No scalar values found for texture input!\n"); return; } // make sure using unsigned char data of color scalars type if (aTexture->GetColorMode() == VTK_COLOR_MODE_MAP_SCALARS || (scalars->GetDataType() != VTK_UNSIGNED_CHAR)) { mappedScalars = aTexture->GetMappedScalars(); } else { mappedScalars = scalars; } // we only support 2d texture maps right now // so one of the three sizes must be 1, but it // could be any of them, so lets find it if (size[0] == 1) { xsize = size[1]; ysize = size[2]; } else { xsize = size[0]; if (size[1] == 1) { ysize = size[2]; } else { ysize = size[1]; if (size[2] != 1) { vtkErrorMacro(<< "3D texture maps currently are not supported!\n"); return; } } } // xsize and ysize must be a power of 2 in OpenGL xs = (unsigned short)xsize; ys = (unsigned short)ysize; while (!(xs & 0x01)) { xs = xs >> 1; } while (!(ys & 0x01)) { ys = ys >> 1; } if ((xs > 1) || (ys > 1)) { vtkWarningMacro(<< "Texture map's width and height must be a power of two in RenderMan\n"); } vtkTIFFWriter* aWriter = vtkTIFFWriter::New(); vtkImageConstantPad* icp = nullptr; vtkImageExtractComponents* iec = nullptr; vtkImageAppendComponents* iac1 = nullptr; vtkImageAppendComponents* iac2 = nullptr; vtkStructuredPoints* anImage = vtkStructuredPoints::New(); anImage->SetDimensions(xsize, ysize, 1); anImage->GetPointData()->SetScalars(mappedScalars); int bpp = mappedScalars->GetNumberOfComponents(); // renderman and bmrt seem to require r,g,b and alpha in all their // texture maps. So if our tmap doesn't have the right components // we add them if (bpp == 1) // needs intensity intensity and alpha { iac1 = vtkImageAppendComponents::New(); iac2 = vtkImageAppendComponents::New(); icp = vtkImageConstantPad::New(); iac1->SetInputData(0, anImage); iac1->SetInputData(1, anImage); iac2->SetInputConnection(0, iac1->GetOutputPort()); iac2->SetInputData(1, anImage); icp->SetInputConnection(iac2->GetOutputPort()); icp->SetConstant(255); icp->SetOutputNumberOfScalarComponents(4); aWriter->SetInputConnection(icp->GetOutputPort()); } else if (bpp == 2) // needs intensity intensity { iec = vtkImageExtractComponents::New(); iac1 = vtkImageAppendComponents::New(); iac2 = vtkImageAppendComponents::New(); iec->SetInputData(anImage); iec->SetComponents(0); iac1->SetInputConnection(0, iec->GetOutputPort()); iac1->SetInputData(1, anImage); iac2->SetInputConnection(0, iec->GetOutputPort()); iac2->SetInputConnection(1, iac1->GetOutputPort()); aWriter->SetInputConnection(iac2->GetOutputPort()); } else if (bpp == 3) // needs alpha { icp = vtkImageConstantPad::New(); icp->SetInputData(anImage); icp->SetConstant(255); icp->SetOutputNumberOfScalarComponents(4); aWriter->SetInputConnection(icp->GetOutputPort()); } else // needs nothing { aWriter->SetInputData(anImage); } aWriter->SetFileName(this->GetTIFFName(aTexture)); aWriter->Write(); if (bpp == 1) { iac1->Delete(); iac2->Delete(); icp->Delete(); } else if (bpp == 2) { iec->Delete(); iac1->Delete(); iac2->Delete(); } else if (bpp == 3) { icp->Delete(); } aWriter->Delete(); anImage->Delete(); } static char tiffName[4096]; static char textureName[4096]; char* vtkRIBExporter::GetTIFFName(vtkTexture* aTexture) { auto result = vtk::format_to_n(tiffName, sizeof(tiffName), "{}_{:p}_{:d}.tif", this->TexturePrefix, static_cast(aTexture), static_cast(aTexture->GetMTime())); *result.out = '\0'; return tiffName; } char* vtkRIBExporter::GetTextureName(vtkTexture* aTexture) { auto result = vtk::format_to_n(textureName, sizeof(textureName), "{}_{:p}_{:d}.txt", this->TexturePrefix, static_cast(aTexture), static_cast(aTexture->GetMTime())); *result.out = '\0'; return textureName; } void vtkRIBExporter::ModifyArrayName(char* newname, const char* name) { if (!newname) { return; } if (!name) { *newname = 0; return; } int cc = 0; for (cc = 0; name[cc]; cc++) { if ((name[cc] >= 'A' && name[cc] <= 'Z') || (name[cc] >= '0' && name[cc] <= '9') || (name[cc] >= 'a' && name[cc] <= 'z')) { newname[cc] = name[cc]; } else { newname[cc] = '_'; } } newname[cc] = 0; } VTK_ABI_NAMESPACE_END