26 #include <vtkBMPReader.h>
27 #include <vtkDataArray.h>
28 #include <vtkJPEGReader.h>
29 #include <vtkLODActor.h>
30 #include <vtkOpenGLRenderWindow.h>
31 #include <vtkPNGReader.h>
32 #include <vtkPNMReader.h>
33 #include <vtkPointData.h>
34 #include <vtkPolyData.h>
35 #include <vtkPolyDataMapper.h>
36 #include <vtkProperty.h>
37 #include <vtkQImageToImageSource.h>
38 #include <vtkRenderWindow.h>
39 #include <vtkRenderer.h>
40 #include <vtkTIFFReader.h>
41 #include <vtkTexture.h>
42 #include <vtkTextureUnitManager.h>
52 bool has_pbr_textures,
53 bool has_multiple_map_kd)
const {
60 return has_multiple_map_kd || (material_count > 1 && !has_pbr_textures);
69 vtkPolyData* polydata,
70 vtkRenderer* renderer) {
76 "[MultiTextureRenderer::Apply] Applying multi-texture rendering "
81 vtkRenderWindow* render_window =
nullptr;
83 render_window = renderer->GetRenderWindow();
86 CVLog::Error(
"[MultiTextureRenderer::Apply] Cannot get render window");
91 ::vtkTextureUnitManager* tex_manager = GetTextureUnitManager(render_window);
94 "[MultiTextureRenderer::Apply] Cannot get texture unit "
100 int texture_units = tex_manager->GetNumberOfTextureUnits();
101 if (
static_cast<size_t>(texture_units) < materials->size()) {
103 "[MultiTextureRenderer::Apply] GPU texture units %d < mesh "
105 texture_units, materials->size());
114 size_t materialIndex;
115 size_t textureIndexInMaterial;
117 std::vector<TextureInfo> allMapKdTextures;
120 for (
size_t mat_idx = 0; mat_idx < materials->size(); ++mat_idx) {
121 auto material = materials->at(mat_idx);
122 if (!material)
continue;
125 std::vector<QString> diffuseTextures =
126 material->getTextureFilenames(TexType::DIFFUSE);
128 for (
size_t tex_idx = 0; tex_idx < diffuseTextures.size(); ++tex_idx) {
129 if (!diffuseTextures[tex_idx].isEmpty()) {
131 info.material = material;
132 info.texturePath = diffuseTextures[tex_idx];
133 info.materialIndex = mat_idx;
134 info.textureIndexInMaterial = tex_idx;
135 allMapKdTextures.push_back(info);
144 int map_kd_count =
static_cast<int>(allMapKdTextures.size());
148 std::map<size_t, int> materialTextureCounts;
149 for (
const auto& texInfo : allMapKdTextures) {
150 materialTextureCounts[texInfo.materialIndex]++;
158 float global_intensity_scale = 1.0f;
159 if (map_kd_count > 1) {
163 global_intensity_scale = 1.0f / map_kd_count;
166 if (map_kd_count > 1) {
168 "[MultiTextureRenderer::Apply] Detected %d map_Kd textures "
169 "across %zu materials. Applying global intensity scale %.3f "
170 "to maintain balanced brightness. All textures will be "
172 "and blended together.",
173 map_kd_count, materials->size(), global_intensity_scale);
174 }
else if (map_kd_count == 0) {
176 "[MultiTextureRenderer::Apply] No map_Kd textures found in "
185 size_t last_tex_id = std::min(allMapKdTextures.size(),
186 static_cast<size_t>(texture_units));
187 vtkPolyDataMapper* mapper =
188 vtkPolyDataMapper::SafeDownCast(actor->GetMapper());
192 std::set<size_t> appliedMaterials;
194 for (
size_t tex_id = 0; tex_id < last_tex_id; ++tex_id) {
195 const TextureInfo& texInfo = allMapKdTextures[tex_id];
198 #if (VTK_MAJOR_VERSION == 8 && VTK_MINOR_VERSION >= 2) || VTK_MAJOR_VERSION > 8
199 std::string tex_name =
201 std::stringstream tu_ss;
202 tu_ss << tex_name <<
"_tex" << tex_id;
203 std::string tu_str = tu_ss.str();
204 const char* tu = tu_str.c_str();
206 int tu = vtkProperty::VTK_TEXTURE_UNIT_0 + tex_id;
213 if (qimage.isNull()) {
215 "[MultiTextureRenderer::Apply] Failed to load texture %s "
216 "for material %s, skipping!",
223 bool hasAlpha = qimage.hasAlphaChannel();
226 if (qimage.format() != QImage::Format_RGBA8888 &&
227 qimage.format() != QImage::Format_ARGB32 &&
228 qimage.format() != QImage::Format_ARGB32_Premultiplied) {
229 qimage = qimage.convertToFormat(QImage::Format_RGBA8888);
232 "[MultiTextureRenderer::Apply] Texture %zu has alpha "
234 "enabling transparency support",
240 qimageToImageSource->SetQImage(&qimage);
241 qimageToImageSource->Update();
242 texture->SetInputConnection(qimageToImageSource->GetOutputPort());
246 texture->SetBlendingMode(
247 vtkTexture::VTK_TEXTURE_BLENDING_MODE_INTERPOLATE);
249 "[MultiTextureRenderer::Apply] Texture %zu: Using "
251 "blending mode for alpha transparency",
267 texture->SetBlendingMode(
268 vtkTexture::VTK_TEXTURE_BLENDING_MODE_REPLACE);
270 "[MultiTextureRenderer::Apply] Texture %zu (material "
272 "REPLACE mode (base layer)",
273 tex_id, texInfo.materialIndex);
275 texture->SetBlendingMode(
276 vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD);
278 "[MultiTextureRenderer::Apply] Texture %zu (material "
280 "MODULATE mode (blending layer)",
281 tex_id, texInfo.materialIndex);
302 if (mapper && polydata) {
303 std::stringstream ss;
307 if (last_tex_id == 1) {
310 ss <<
"TCoords" << tex_id;
312 std::string coords_name = ss.str();
315 vtkDataArray* tcoords_array =
316 polydata->GetPointData()->GetArray(coords_name.c_str());
318 mapper->MapDataArrayToMultiTextureAttribute(
319 tu, coords_name.c_str(),
320 vtkDataObject::FIELD_ASSOCIATION_POINTS);
322 "[MultiTextureRenderer::Apply] Mapped texture %zu to "
323 "texture coordinates array '%s'",
324 tex_id, coords_name.c_str());
327 "[MultiTextureRenderer::Apply] Texture coordinates "
329 "'%s' not found for texture %zu (material %zu, texture "
330 "index %zu), texture may not display correctly",
331 coords_name.c_str(), tex_id, texInfo.materialIndex,
332 texInfo.textureIndexInMaterial);
337 actor->GetProperty()->SetTexture(tu, texture);
342 if (appliedMaterials.find(texInfo.materialIndex) ==
343 appliedMaterials.end()) {
347 ApplyMaterial(texInfo.material, actor, global_intensity_scale);
348 appliedMaterials.insert(texInfo.materialIndex);
357 float modelOpacity = 1.0f;
358 bool hasMaterialOpacity =
false;
361 for (
size_t i = 0; i < materials->size(); ++i) {
362 auto mat = materials->at(i);
366 float opacity = std::max(diffuse.
a, ambient.
a);
367 if (opacity < 1.0f) {
368 hasMaterialOpacity =
true;
369 modelOpacity = std::min(modelOpacity, opacity);
375 float currentOpacity = actor->GetProperty()->GetOpacity();
379 if (hasMaterialOpacity) {
381 actor->GetProperty()->SetOpacity(modelOpacity);
383 "[MultiTextureRenderer::Apply] Set model opacity from "
387 }
else if (currentOpacity < 1.0f) {
389 actor->GetProperty()->SetOpacity(currentOpacity);
391 "[MultiTextureRenderer::Apply] Preserving external opacity "
396 actor->GetProperty()->SetOpacity(1.0f);
400 float finalOpacity = actor->GetProperty()->GetOpacity();
401 if (finalOpacity < 1.0f) {
404 vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
407 renderWindow->SetAlphaBitPlanes(1);
411 renderer->SetUseDepthPeeling(1);
412 renderer->SetMaximumNumberOfPeels(4);
413 renderer->SetOcclusionRatio(0.0);
416 "[MultiTextureRenderer::Apply] Enabled transparency "
417 "rendering support: opacity=%.3f, depth peeling "
430 vtkPolyData* polydata,
431 vtkRenderer* renderer) {
436 vtkLODActor* lod_actor = vtkLODActor::SafeDownCast(actor);
439 "[MultiTextureRenderer::Update] Actor is not a vtkLODActor");
443 return Apply(lod_actor, materials, polydata, renderer);
447 return "MultiTextureRenderer";
451 ::vtkTexture* vtk_tex)
const {
452 if (!material || !vtk_tex) {
459 QString tex_file = material->getTextureFilename(TexType::DIFFUSE);
460 if (tex_file.isEmpty()) {
462 tex_file = material->getTextureFilename();
465 if (!qimage.isNull()) {
468 qimageToImageSource->SetQImage(&qimage);
469 qimageToImageSource->Update();
470 vtk_tex->SetInputConnection(qimageToImageSource->GetOutputPort());
474 if (tex_file.isEmpty()) {
480 std::string parent_dir =
484 std::string real_name;
490 "[MultiTextureRenderer::LoadTexture] Parent directory "
491 "'%s' doesn't exist!",
498 "[MultiTextureRenderer::LoadTexture] Parent '%s' is "
505 std::vector<std::string> paths_vector;
507 parent_dir, paths_vector);
509 for (
const auto&
path : paths_vector) {
518 if (real_name.empty()) {
520 "[MultiTextureRenderer::LoadTexture] Can not find "
525 }
catch (
const std::exception& ex) {
527 "[MultiTextureRenderer::LoadTexture] Error %s when "
528 "looking for file %s!",
529 ex.what(), full_path.c_str());
533 full_path = real_name;
536 std::string extension =
540 if ((extension ==
"jpg") || (extension ==
"jpeg")) {
543 jpeg_reader->SetFileName(full_path.c_str());
544 jpeg_reader->Update();
545 vtk_tex->SetInputConnection(jpeg_reader->GetOutputPort());
546 }
else if (extension ==
"bmp") {
549 bmp_reader->SetFileName(full_path.c_str());
550 bmp_reader->Update();
551 vtk_tex->SetInputConnection(bmp_reader->GetOutputPort());
552 }
else if (extension ==
"pnm") {
555 pnm_reader->SetFileName(full_path.c_str());
556 pnm_reader->Update();
557 vtk_tex->SetInputConnection(pnm_reader->GetOutputPort());
558 }
else if (extension ==
"png") {
561 png_reader->SetFileName(full_path.c_str());
562 png_reader->Update();
563 vtk_tex->SetInputConnection(png_reader->GetOutputPort());
564 }
else if ((extension ==
"tiff") || (extension ==
"tif")) {
567 tiff_reader->SetFileName(full_path.c_str());
568 tiff_reader->Update();
569 vtk_tex->SetInputConnection(tiff_reader->GetOutputPort());
572 "[MultiTextureRenderer::LoadTexture] Unhandled image %s "
573 "(extension: '%s') for material %s!",
574 full_path.c_str(), extension.c_str(),
584 float intensity_scale)
const {
585 if (!actor || !material)
return false;
590 actor->GetProperty()->SetDiffuseColor(diffuseColor.
r, diffuseColor.
g,
592 actor->GetProperty()->SetSpecularColor(specularColor.
r, specularColor.
g,
594 actor->GetProperty()->SetAmbientColor(ambientColor.
r, ambientColor.
g,
599 float materialOpacity = std::max(0.0f, std::min(1.0f, diffuseColor.
a));
600 if (materialOpacity <= 0.0f) {
601 materialOpacity = std::max(0.0f, std::min(1.0f, ambientColor.
a));
606 float currentOpacity = actor->GetProperty()->GetOpacity();
607 if (currentOpacity >= 1.0f || materialOpacity < currentOpacity) {
608 actor->GetProperty()->SetOpacity(materialOpacity);
610 actor->GetProperty()->SetInterpolationToPhong();
617 switch (material->getIllum()) {
620 actor->GetProperty()->SetDiffuse(1.0f * intensity_scale);
621 actor->GetProperty()->SetSpecular(0.0f);
622 actor->GetProperty()->SetAmbient(1.0f * intensity_scale);
627 actor->GetProperty()->SetDiffuse(1.0f * intensity_scale);
628 actor->GetProperty()->SetSpecular(1.0f * intensity_scale);
629 actor->GetProperty()->SetAmbient(1.0f * intensity_scale);
631 float shininess = std::max(
632 0.0f, std::min(128.0f, material->getShininessFront()));
633 actor->GetProperty()->SetSpecularPower(shininess > 0.0f ? shininess
640 actor->GetProperty()->SetLighting(
false);
641 actor->GetProperty()->SetDiffuse(0.0f);
642 actor->GetProperty()->SetSpecular(0.0f);
643 actor->GetProperty()->SetAmbient(1.0f * intensity_scale);
644 actor->GetProperty()->SetColor(diffuseColor.
r, diffuseColor.
g,
653 ::vtkTextureUnitManager* MultiTextureRenderer::GetTextureUnitManager(
654 ::vtkRenderWindow* render_window)
const {
655 if (!render_window) {
659 vtkOpenGLRenderWindow* gl_window =
660 vtkOpenGLRenderWindow::SafeDownCast(render_window);
665 return gl_window->GetTextureUnitManager();
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
RenderingMode GetMode() const override
Get the rendering mode this renderer implements.
bool Update(vtkActor *actor, const class ccMaterialSet *materials, vtkPolyData *polydata, vtkRenderer *renderer) override
Update existing actor with new materials.
bool Apply(vtkLODActor *actor, const class ccMaterialSet *materials, vtkPolyData *polydata, vtkRenderer *renderer) override
Apply rendering to actor.
std::string GetName() const override
Get renderer name for logging.
bool CanHandle(size_t material_count, bool has_pbr_textures, bool has_multiple_map_kd) const override
Check if this renderer can handle the given material.
bool ApplyMaterial(ccMaterial::CShared material, vtkActor *actor, float intensity_scale=1.0f) const
Apply material properties to actor.
void ClearTextures(vtkActor *actor)
Helper: Clear all textures from actor.
bool ValidateActor(vtkActor *actor) const
Helper: Validate actor.
bool ValidateMaterials(const class ccMaterialSet *materials) const
Helper: Validate materials.
Mesh (triangle) material.
TextureMapType
Texture map types for PBR materials.
QSharedPointer< const ccMaterial > CShared
Const + Shared type.
static QImage GetTexture(const QString &absoluteFilename)
Returns the texture image associated to a given name.
RenderingMode
Rendering mode enumeration.
static const std::string path
bool ListFilesInDirectory(const std::string &directory, std::vector< std::string > &filenames)
bool IsDirectory(const std::string &directory)
std::string GetFileParentDirectory(const std::string &filename)
bool DirectoryExists(const std::string &directory)
std::string GetFileExtensionInLowerCase(const std::string &filename)
bool FileExists(const std::string &filename)
bool IsFile(const std::string &filename)
std::string ToUpper(const std::string &s)
Convert string to the upper case.