1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkOSPRayMaterialHelpers.cpp
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkOSPRayMaterialHelpers.h"
17 #include "vtkImageData.h"
18 #include "vtkOSPRayMaterialLibrary.h"
19 #include "vtkOSPRayRendererNode.h"
20 #include "vtkProperty.h"
21 #include "vtkTexture.h"
22 
23 #include "RTWrapper/RTWrapper.h"
24 
25 #include <limits>
26 
27 //------------------------------------------------------------------------------
NewTexture2D(RTW::Backend * backend,const osp::vec2i & size,const OSPTextureFormat type,void * data,const uint32_t _flags)28 OSPTexture vtkOSPRayMaterialHelpers::NewTexture2D(RTW::Backend* backend, const osp::vec2i& size,
29   const OSPTextureFormat type, void* data, const uint32_t _flags)
30 {
31   auto texture = ospNewTexture("texture2d");
32   if (texture == nullptr)
33   {
34     return nullptr;
35   }
36 
37   auto flags = _flags; // because the input value is declared const, use a copy
38 
39   OSPDataType dataType = OSP_UNKNOWN;
40   if (type == OSP_TEXTURE_R32F)
41   {
42     dataType = OSP_FLOAT;
43   }
44   else if (type == OSP_TEXTURE_RGB32F)
45   {
46     dataType = OSP_VEC3F;
47   }
48   else if (type == OSP_TEXTURE_RGBA32F)
49   {
50     dataType = OSP_VEC4F;
51   }
52   else if (type == OSP_TEXTURE_R8)
53   {
54     dataType = OSP_UCHAR;
55   }
56   else if (type == OSP_TEXTURE_RGB8 || type == OSP_TEXTURE_SRGB)
57   {
58     dataType = OSP_VEC3UC;
59   }
60   else if (type == OSP_TEXTURE_RGBA8 || type == OSP_TEXTURE_SRGBA)
61   {
62     dataType = OSP_VEC4UC;
63   }
64   else
65   {
66     throw std::runtime_error("vtkOSPRayMaterialHelpers::NewTexture2D: Unknown texture format");
67   }
68 
69 #if defined(__GNUC__)
70 #pragma GCC diagnostic push
71 #pragma GCC diagnostic ignored "-Wextra"
72 #endif
73   auto data_handle = ospNewCopyData2D(data, dataType, size.x, size.y);
74 #if defined(__GNUC__)
75 #pragma GCC diagnostic pop
76 #endif
77 
78   ospCommit(data_handle);
79   ospSetObject(texture, "data", data_handle);
80   ospRelease(data_handle);
81 
82   ospSetInt(texture, "format", static_cast<int>(type));
83   if (flags & OSP_TEXTURE_FILTER_NEAREST)
84   {
85     ospSetInt(texture, "filter", OSP_TEXTURE_FILTER_NEAREST);
86   }
87   ospCommit(texture);
88 
89   return texture;
90 }
91 
92 //------------------------------------------------------------------------------
VTKToOSPTexture(RTW::Backend * backend,vtkImageData * vColorTextureMap,bool isSRGB)93 OSPTexture vtkOSPRayMaterialHelpers::VTKToOSPTexture(
94   RTW::Backend* backend, vtkImageData* vColorTextureMap, bool isSRGB)
95 {
96   if (backend == nullptr)
97   {
98     return OSPTexture2D();
99   }
100 
101   int xsize = vColorTextureMap->GetExtent()[1] - vColorTextureMap->GetExtent()[0];
102   int ysize = vColorTextureMap->GetExtent()[3] - vColorTextureMap->GetExtent()[2];
103 
104   if (xsize < 0 || ysize < 0)
105   {
106     return nullptr;
107   }
108   int scalartype = vColorTextureMap->GetScalarType();
109   int comps = vColorTextureMap->GetNumberOfScalarComponents();
110 
111   OSPTexture t2d = nullptr;
112 
113   if (scalartype == VTK_UNSIGNED_CHAR || scalartype == VTK_CHAR || scalartype == VTK_SIGNED_CHAR)
114   {
115     OSPTextureFormat formatSRGB[4] = { OSP_TEXTURE_L8, OSP_TEXTURE_LA8, OSP_TEXTURE_SRGB,
116       OSP_TEXTURE_SRGBA };
117     OSPTextureFormat formatLinear[4] = { OSP_TEXTURE_R8, OSP_TEXTURE_RGB8, OSP_TEXTURE_RGB8,
118       OSP_TEXTURE_RGBA8 };
119     std::vector<unsigned char> chars;
120 
121     if ((!isSRGB && comps == 2) || comps > 4)
122     {
123       // no native formats, we need to copy the components to a 3 channels texture
124       chars.resize((xsize + 1) * (ysize + 1) * 3, 0);
125       unsigned char* oc = chars.data();
126       unsigned char* ptr =
127         reinterpret_cast<unsigned char*>(vColorTextureMap->GetScalarPointer(0, 0, 0));
128       for (int i = 0; i <= xsize; ++i)
129       {
130         for (int j = 0; j <= ysize; ++j)
131         {
132           for (int k = 0; k < comps && k < 3; k++)
133           {
134             oc[k] = ptr[k];
135           }
136           ptr += comps;
137           oc += 3;
138         }
139       }
140       comps = 3;
141     }
142 
143     t2d = vtkOSPRayMaterialHelpers::NewTexture2D(backend, osp::vec2i{ xsize + 1, ysize + 1 },
144       isSRGB ? formatSRGB[comps - 1] : formatLinear[comps - 1],
145       chars.empty() ? vColorTextureMap->GetScalarPointer() : chars.data(),
146       OSP_TEXTURE_FILTER_NEAREST);
147   }
148   else if (scalartype == VTK_FLOAT)
149   {
150     OSPTextureFormat format[4] = { OSP_TEXTURE_R32F, OSP_TEXTURE_RGB32F, OSP_TEXTURE_RGB32F,
151       OSP_TEXTURE_RGBA32F };
152     std::vector<float> floats;
153     if (comps == 2 || comps > 4)
154     {
155       // no native formats, we need to copy the components to a 3 channels texture
156       floats.resize((xsize + 1) * (ysize + 1) * 3, 0);
157       float* of = floats.data();
158       for (int i = 0; i <= ysize; ++i)
159       {
160         for (int j = 0; j <= xsize; ++j)
161         {
162           for (int k = 0; k < comps && k < 3; k++)
163           {
164             of[k] = vColorTextureMap->GetScalarComponentAsFloat(j, i, 0, k);
165           }
166           of += 3;
167         }
168       }
169       comps = 3;
170     }
171     t2d = vtkOSPRayMaterialHelpers::NewTexture2D(backend, osp::vec2i{ xsize + 1, ysize + 1 },
172       format[comps - 1], floats.empty() ? vColorTextureMap->GetScalarPointer() : floats.data(),
173       OSP_TEXTURE_FILTER_NEAREST);
174   }
175   else
176   {
177     // All other types are converted to float
178     int newComps = comps;
179     OSPTextureFormat format[4] = { OSP_TEXTURE_R32F, OSP_TEXTURE_RGB32F, OSP_TEXTURE_RGB32F,
180       OSP_TEXTURE_RGBA32F };
181 
182     if (comps == 2 || comps > 4)
183     {
184       newComps = 3;
185     }
186 
187     float multiplier = 1.f;
188     float shift = 0.f;
189 
190     // 16-bits integer are not supported yet in OSPRay
191     switch (scalartype)
192     {
193       case VTK_SHORT:
194         shift += std::numeric_limits<short>::min();
195         multiplier /= std::numeric_limits<unsigned short>::max();
196         break;
197       case VTK_UNSIGNED_SHORT:
198         multiplier /= std::numeric_limits<unsigned short>::max();
199         break;
200       default:
201         break;
202     }
203 
204     std::vector<float> floats;
205     floats.resize((xsize + 1) * (ysize + 1) * newComps, 0);
206     float* of = floats.data();
207     for (int i = 0; i <= ysize; ++i)
208     {
209       for (int j = 0; j <= xsize; ++j)
210       {
211         for (int k = 0; k < newComps && k < comps; k++)
212         {
213           of[k] = (vColorTextureMap->GetScalarComponentAsFloat(j, i, 0, k) + shift) * multiplier;
214         }
215         of += newComps;
216       }
217     }
218     t2d = vtkOSPRayMaterialHelpers::NewTexture2D(backend, osp::vec2i{ xsize + 1, ysize + 1 },
219       format[newComps - 1], floats.data(), OSP_TEXTURE_FILTER_NEAREST);
220   }
221 
222   if (t2d != nullptr)
223   {
224     ospCommit(t2d);
225   }
226 
227   return t2d;
228 }
229 
230 //------------------------------------------------------------------------------
MakeMaterials(vtkOSPRayRendererNode * orn,OSPRenderer oRenderer,std::map<std::string,OSPMaterial> & mats)231 void vtkOSPRayMaterialHelpers::MakeMaterials(
232   vtkOSPRayRendererNode* orn, OSPRenderer oRenderer, std::map<std::string, OSPMaterial>& mats)
233 {
234   vtkOSPRayMaterialLibrary* ml = vtkOSPRayRendererNode::GetMaterialLibrary(orn->GetRenderer());
235   if (!ml)
236   {
237     cout << "No material Library in this renderer." << endl;
238     return;
239   }
240   std::set<std::string> nicknames = ml->GetMaterialNames();
241   std::set<std::string>::iterator it = nicknames.begin();
242   while (it != nicknames.end())
243   {
244     OSPMaterial newmat = vtkOSPRayMaterialHelpers::MakeMaterial(orn, oRenderer, *it);
245     mats[*it] = newmat;
246     ++it;
247   }
248 }
249 
250 //------------------------------------------------------------------------------
MakeMaterial(vtkOSPRayRendererNode * orn,OSPRenderer oRenderer,std::string nickname)251 OSPMaterial vtkOSPRayMaterialHelpers::MakeMaterial(
252   vtkOSPRayRendererNode* orn, OSPRenderer oRenderer, std::string nickname)
253 {
254   RTW::Backend* backend = orn->GetBackend();
255   OSPMaterial oMaterial;
256   vtkOSPRayMaterialLibrary* ml = vtkOSPRayRendererNode::GetMaterialLibrary(orn->GetRenderer());
257   if (!ml)
258   {
259     vtkGenericWarningMacro("No material Library in this renderer. Using obj by default.");
260     return NewMaterial(orn, oRenderer, "obj");
261   }
262 
263   const auto& dic = vtkOSPRayMaterialLibrary::GetParametersDictionary();
264 
265   std::string implname = ml->LookupImplName(nickname);
266 
267   if (dic.find(implname) != dic.end())
268   {
269     oMaterial = NewMaterial(orn, oRenderer, implname);
270 
271     const auto& paramList = dic.at(implname);
272     for (auto param : paramList)
273     {
274       switch (param.second)
275       {
276         case vtkOSPRayMaterialLibrary::ParameterType::BOOLEAN:
277         {
278           auto values = ml->GetDoubleShaderVariable(nickname, param.first);
279           if (values.size() == 1)
280           {
281             ospSetInt(oMaterial, param.first.c_str(), static_cast<int>(values[0]));
282           }
283         }
284         break;
285         case vtkOSPRayMaterialLibrary::ParameterType::FLOAT:
286         case vtkOSPRayMaterialLibrary::ParameterType::NORMALIZED_FLOAT:
287         {
288           auto values = ml->GetDoubleShaderVariable(nickname, param.first);
289           if (values.size() == 1)
290           {
291             ospSetFloat(oMaterial, param.first.c_str(), static_cast<float>(values[0]));
292           }
293         }
294         break;
295         case vtkOSPRayMaterialLibrary::ParameterType::FLOAT_DATA:
296         {
297           auto values = ml->GetDoubleShaderVariable(nickname, param.first);
298           if (!values.empty())
299           {
300             std::vector<float> fvalues(values.begin(), values.end());
301             OSPData data = ospNewCopyData1D(fvalues.data(), OSP_VEC3F, fvalues.size() / 3);
302             ospSetObject(oMaterial, param.first.c_str(), data);
303           }
304         }
305         break;
306         case vtkOSPRayMaterialLibrary::ParameterType::VEC2:
307         {
308           auto values = ml->GetDoubleShaderVariable(nickname, param.first);
309           if (values.size() == 2)
310           {
311             std::vector<float> fvalues(values.begin(), values.end());
312             ospSetVec2f(oMaterial, param.first.c_str(), fvalues[0], fvalues[1]);
313           }
314         }
315         break;
316         case vtkOSPRayMaterialLibrary::ParameterType::VEC3:
317         case vtkOSPRayMaterialLibrary::ParameterType::COLOR_RGB:
318         {
319           auto values = ml->GetDoubleShaderVariable(nickname, param.first);
320           if (values.size() == 3)
321           {
322             std::vector<float> fvalues(values.begin(), values.end());
323             ospSetVec3f(oMaterial, param.first.c_str(), fvalues[0], fvalues[1], fvalues[2]);
324           }
325         }
326         break;
327         case vtkOSPRayMaterialLibrary::ParameterType::VEC4:
328         {
329           auto values = ml->GetDoubleShaderVariable(nickname, param.first);
330           if (values.size() == 4)
331           {
332             std::vector<float> fvalues(values.begin(), values.end());
333             ospSetVec4f(
334               oMaterial, param.first.c_str(), fvalues[0], fvalues[1], fvalues[2], fvalues[3]);
335           }
336         }
337         break;
338         case vtkOSPRayMaterialLibrary::ParameterType::TEXTURE:
339         {
340           vtkTexture* texname = ml->GetTexture(nickname, param.first);
341           if (texname)
342           {
343             vtkImageData* vColorTextureMap = vtkImageData::SafeDownCast(texname->GetInput());
344             OSPTexture t2d = vtkOSPRayMaterialHelpers::VTKToOSPTexture(backend, vColorTextureMap);
345             ospSetObject(oMaterial, param.first.c_str(), static_cast<OSPTexture>(t2d));
346             ospRelease(t2d);
347           }
348         }
349         break;
350         default:
351           break;
352       }
353     }
354   }
355   else
356   {
357     vtkGenericWarningMacro(
358       "Warning: unrecognized material \"" << implname.c_str() << "\", using a default obj");
359     return NewMaterial(orn, oRenderer, "obj");
360   }
361 
362   ospCommit(oMaterial);
363   return oMaterial;
364 }
365 
366 //------------------------------------------------------------------------------
NewMaterial(vtkOSPRayRendererNode * orn,OSPRenderer oRenderer,std::string ospMatName)367 OSPMaterial vtkOSPRayMaterialHelpers::NewMaterial(
368   vtkOSPRayRendererNode* orn, OSPRenderer oRenderer, std::string ospMatName)
369 {
370   RTW::Backend* backend = orn->GetBackend();
371   OSPMaterial result = nullptr;
372 
373   if (backend == nullptr)
374     return result;
375 
376   (void)oRenderer;
377   const std::string rendererType = vtkOSPRayRendererNode::GetRendererType(orn->GetRenderer());
378   result = ospNewMaterial(rendererType.c_str(), ospMatName.c_str());
379 
380   if (!result)
381   {
382     vtkGenericWarningMacro(
383       "OSPRay failed to create material: " << ospMatName << ". Trying obj instead.");
384     result = ospNewMaterial(rendererType.c_str(), "obj");
385   }
386 
387   ospCommit(result);
388   return result;
389 }
390