/*========================================================================= Program: Visualization Toolkit Module: vtkOBJImporterInternals.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 "vtkSmartPointer.h" #include "vtkOBJImporter.h" #include "vtkOBJImporterInternals.h" #include "vtkJPEGReader.h" #include "vtkPNGReader.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtksys/SystemTools.hxx" #include #include #include #include #include #if defined(_WIN32) #pragma warning(disable : 4800) #endif const int OBJ_LINE_SIZE = 4096; namespace { bool strequal(const char *s1, const char *s2) { if(strcmp(s1, s2) == 0) { return true; } return false; } int localVerbosity = 0; } void obj_set_material_defaults(vtkOBJImportedMaterial* mtl) { mtl->amb[0] = 0.2; mtl->amb[1] = 0.2; mtl->amb[2] = 0.2; mtl->diff[0] = 0.8; mtl->diff[1] = 0.8; mtl->diff[2] = 0.8; mtl->spec[0] = 1.0; mtl->spec[1] = 1.0; mtl->spec[2] = 1.0; mtl->reflect = 0.0; mtl->trans = 1; mtl->glossy = 98; mtl->shiny = 0; mtl->refract_index = 1; mtl->texture_filename[0] = '\0'; if( localVerbosity > 0 ) { vtkGenericWarningMacro("Created a default vtkOBJImportedMaterial, texture filename is " << std::string(mtl->texture_filename)); } } std::vector vtkOBJPolyDataProcessor::ParseOBJandMTL( std::string Filename, int& result_code) { std::vector listOfMaterials; result_code = 0; if (Filename.empty()) { return listOfMaterials; } const char* filename = Filename.c_str(); int line_number = 0; char *current_token; char current_line[OBJ_LINE_SIZE]; char material_open = 0; vtkOBJImportedMaterial* current_mtl = nullptr; FILE *mtl_file_stream; // open scene mtl_file_stream = fopen( filename, "r"); if(mtl_file_stream == nullptr) { vtkErrorMacro("Error reading file: " << filename); result_code = -1; return listOfMaterials; } while( fgets(current_line, OBJ_LINE_SIZE, mtl_file_stream) ) { // watch out for BOM if (current_line[0] == -17 && current_line[1] == -69 && current_line[2] == -65) { current_token = strtok( current_line + 3, " \t\n\r"); } else { current_token = strtok( current_line, " \t\n\r"); } line_number++; //skip comments if( current_token == nullptr || strequal(current_token, "//") || strequal(current_token, "#")) { continue; } //start material else if( strequal(current_token, "newmtl")) { material_open = 1; current_mtl = (new vtkOBJImportedMaterial); listOfMaterials.push_back(current_mtl); obj_set_material_defaults(current_mtl); // material names can have spaces in them // get the name strncpy(current_mtl->name, strtok(nullptr, "\t\n\r"), MATERIAL_NAME_SIZE); // be safe with strncpy if (current_mtl->name[MATERIAL_NAME_SIZE-1] != '\0') { current_mtl->name[MATERIAL_NAME_SIZE-1] = '\0'; vtkErrorMacro("material name too long, truncated"); } // trim trailing spaces char *end = current_mtl->name + strlen(current_mtl->name) - 1; while(end > current_mtl->name && isspace((unsigned char)*end)) { end--; } // Write new null terminator *(end+1) = 0; } //ambient else if( strequal(current_token, "Ka") && material_open) { // But this is ... right? no? current_mtl->amb[0] = atof( strtok(nullptr, " \t")); current_mtl->amb[1] = atof( strtok(nullptr, " \t")); current_mtl->amb[2] = atof( strtok(nullptr, " \t")); } //diff else if( strequal(current_token, "Kd") && material_open) { current_mtl->diff[0] = atof( strtok(nullptr, " \t")); current_mtl->diff[1] = atof( strtok(nullptr, " \t")); current_mtl->diff[2] = atof( strtok(nullptr, " \t")); } //specular else if( strequal(current_token, "Ks") && material_open) { current_mtl->spec[0] = atof( strtok(nullptr, " \t")); current_mtl->spec[1] = atof( strtok(nullptr, " \t")); current_mtl->spec[2] = atof( strtok(nullptr, " \t")); } //shiny else if( strequal(current_token, "Ns") && material_open) { current_mtl->shiny = atof( strtok(nullptr, " \t")); } //transparent else if( strequal(current_token, "d") && material_open) { current_mtl->trans = atof( strtok(nullptr, " \t")); } //reflection else if( strequal(current_token, "r") && material_open) { current_mtl->reflect = atof( strtok(nullptr, " \t")); } //glossy else if( strequal(current_token, "sharpness") && material_open) { current_mtl->glossy = atof( strtok(nullptr, " \t")); } //refract index else if( strequal(current_token, "Ni") && material_open) { current_mtl->refract_index = atof( strtok(nullptr, " \t")); } // illumination type else if( strequal(current_token, "illum") && material_open) { } // texture map else if( (strequal(current_token, "map_kd") || strequal(current_token, "map_Kd")) && material_open) { /** (pk note: why was this map_Ka initially? should map_Ka be supported? ) */ // tmap may be null so we test first before doing a strncpy char *tmap = strtok(nullptr, " \t\n\r"); if (tmap) { strncpy(current_mtl->texture_filename, tmap, OBJ_FILENAME_LENGTH); // be safe with strncpy if (current_mtl->texture_filename[OBJ_FILENAME_LENGTH-1] != '\0') { current_mtl->texture_filename[OBJ_FILENAME_LENGTH-1] = '\0'; vtkErrorMacro("texture name too long, truncated"); } bool bFileExistsNoPath = vtksys::SystemTools::FileExists(current_mtl->texture_filename); std::vector path_and_file(2); path_and_file[0] = this->GetTexturePath(); path_and_file[1] = std::string(current_mtl->texture_filename); std::string joined = vtksys::SystemTools::JoinPath(path_and_file); bool bFileExistsInPath = vtksys::SystemTools::FileExists( joined ); if(! (bFileExistsNoPath || bFileExistsInPath ) ) { vtkGenericWarningMacro( << "mtl file " << current_mtl->name << "requests texture file that appears not to exist: " << current_mtl->texture_filename << "; texture path: " << this->TexturePath << "\n"); } } } else { // just skip it; got an unsupported feature or a comment in file. vtkDebugMacro("Unknown command " << current_token << " in material file " << filename << " at line " << line_number << ":\n\t" << current_line); } } fclose(mtl_file_stream); return listOfMaterials; } void bindTexturedPolydataToRenderWindow( vtkRenderWindow* renderWindow, vtkRenderer* renderer, vtkOBJPolyDataProcessor* reader ) { if( nullptr == (renderWindow) ) { vtkErrorWithObjectMacro(reader, "RenderWindow is null, failure!"); return; } if( nullptr == (renderer) ) { vtkErrorWithObjectMacro(reader, "Renderer is null, failure!"); return; } if( nullptr == (reader) ) { vtkErrorWithObjectMacro(reader, "vtkOBJPolyDataProcessor is null, failure!"); return; } reader->actor_list.clear(); reader->actor_list.reserve( reader->GetNumberOfOutputPorts() ); for( int port_idx=0; port_idx < reader->GetNumberOfOutputPorts(); port_idx++) { vtkPolyData* objPoly = reader->GetOutput(port_idx); vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInputData(objPoly); vtkDebugWithObjectMacro(reader, "Grabbed objPoly " << objPoly << ", port index " << port_idx << "\n" << "numPolys = " << objPoly->GetNumberOfPolys() << " numPoints = " << objPoly->GetNumberOfPoints()); // For each named material, load and bind the texture, add it to the renderer vtkSmartPointer vtk_texture = vtkSmartPointer::New(); std::string textureFilename = reader->GetTextureFilename(port_idx); vtkSmartPointer tex_jpg_Loader = vtkSmartPointer::New(); vtkSmartPointer tex_png_Loader = vtkSmartPointer::New(); int bIsReadableJPEG = tex_jpg_Loader->CanReadFile( textureFilename.c_str() ); int bIsReadablePNG = tex_png_Loader->CanReadFile( textureFilename.c_str() ); bool haveTexture = false; if (!textureFilename.empty()) { if( bIsReadableJPEG ) { tex_jpg_Loader->SetFileName( textureFilename.c_str() ); tex_jpg_Loader->Update(); vtk_texture->AddInputConnection( tex_jpg_Loader->GetOutputPort() ); haveTexture = true; } else if( bIsReadablePNG ) { tex_png_Loader->SetFileName( textureFilename.c_str() ); tex_png_Loader->Update(); vtk_texture->AddInputConnection( tex_png_Loader->GetOutputPort() ); haveTexture = true; } else { if(!textureFilename.empty()) // OK to have no texture image, but if its not empty it ought to exist. { vtkErrorWithObjectMacro(reader, "Nonexistent texture image type!? imagefile: " <InterpolateOff(); // Faster?? (yes clearly faster for largish texture) } vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); if (haveTexture) { actor->SetTexture(vtk_texture); } vtkSmartPointer properties = vtkSmartPointer::New(); vtkOBJImportedMaterial* raw_mtl_data = reader->GetMaterial(port_idx); if (raw_mtl_data) { properties->SetDiffuseColor(raw_mtl_data->diff); properties->SetSpecularColor(raw_mtl_data->spec); properties->SetAmbientColor(raw_mtl_data->amb); properties->SetOpacity(raw_mtl_data->trans); properties->SetInterpolationToPhong(); properties->SetLighting(true); properties->SetSpecular( raw_mtl_data->get_spec_coeff() ); properties->SetAmbient( raw_mtl_data->get_amb_coeff() ); properties->SetDiffuse( raw_mtl_data->get_diff_coeff() ); actor->SetProperty(properties); } renderer->AddActor(actor); //properties->ShadingOn(); // use ShadingOn() if loading vtkMaterial from xml // available in mtl parser are: // double amb[3]; // double diff[3]; // double spec[3]; // double reflect; // double refract; // double trans; // double shiny; // double glossy; // double refract_index; reader->actor_list.push_back(actor); // keep a handle on actors to animate later } /** post-condition of this function: the renderer has had a bunch of actors added to it */ } vtkOBJImportedMaterial::vtkOBJImportedMaterial() { name[0] = 'x'; name[1] = '\0'; obj_set_material_defaults(this); }