-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathOgre2SegmentationMaterialSwitcher.cc
438 lines (379 loc) · 13.6 KB
/
Ogre2SegmentationMaterialSwitcher.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
/*
* Copyright (C) 2021 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "Ogre2SegmentationMaterialSwitcher.hh"
#include <algorithm>
#include <utility>
#include <vector>
#include <ignition/common/Console.hh>
#include "ignition/rendering/ogre2/Ogre2Heightmap.hh"
#include "ignition/rendering/ogre2/Ogre2RenderEngine.hh"
#include "ignition/rendering/ogre2/Ogre2Scene.hh"
#include "ignition/rendering/ogre2/Ogre2Visual.hh"
#include "ignition/rendering/RenderTypes.hh"
#include "Terra/Terra.h"
#ifdef _MSC_VER
#pragma warning(push, 0)
#endif
#include <OgreItem.h>
#include <OgreMaterialManager.h>
#include <OgrePass.h>
#include <OgreSceneManager.h>
#include <OgreTechnique.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using namespace ignition;
using namespace rendering;
/////////////////////////////////////////////////
Ogre2SegmentationMaterialSwitcher::Ogre2SegmentationMaterialSwitcher(
Ogre2ScenePtr _scene, SegmentationCamera *_camera)
{
this->scene = _scene;
this->segmentationCamera = _camera;
}
/////////////////////////////////////////////////
Ogre2SegmentationMaterialSwitcher::~Ogre2SegmentationMaterialSwitcher()
{
this->segmentationCamera = nullptr;
}
/////////////////////////////////////////////////
bool Ogre2SegmentationMaterialSwitcher::IsTakenColor(const math::Color &_color)
{
// Get the int value of the 24 bit color
// Multiply by 255 as color values are normalized
int64_t colorId = (_color.R() * 255) * 256 * 256 +
(_color.G() * 255) * 256 + (_color.B() * 255);
if (this->takenColors.count(colorId))
{
return true;
}
else
{
this->takenColors.insert(colorId);
return false;
}
}
/////////////////////////////////////////////////
Ogre::Vector4 Ogre2SegmentationMaterialSwitcher::ColorForVisual(
const VisualPtr &_visual, std::string &_prevParentName)
{
// get class user data
Variant labelAny = _visual->UserData("label");
int label;
try
{
label = std::get<int>(labelAny);
}
catch (std::bad_variant_access &)
{
// items with no class are considered background
label = this->segmentationCamera->BackgroundLabel();
}
// sub item custom parameter to set the pixel color material
Ogre::Vector4 customParameter;
// Material Switching
if (this->segmentationCamera->Type() == SegmentationType::ST_SEMANTIC)
{
if (this->segmentationCamera->IsColoredMap())
{
// semantic material (each pixel has item's color)
math::Color color = this->LabelToColor(label);
customParameter = Ogre::Vector4(color.R(), color.G(), color.B(), 1.0);
}
else
{
// labels ids material (each pixel has item's label)
float labelColor = label / 255.0f;
customParameter = Ogre::Vector4(labelColor, labelColor, labelColor, 1.0);
}
}
else if (this->segmentationCamera->Type() == SegmentationType::ST_PANOPTIC)
{
auto itemName = _visual->Name();
std::string parentName = this->TopLevelModelVisual(_visual)->Name();
auto it = this->instancesCount.find(label);
if (it == this->instancesCount.end())
it = this->instancesCount.insert(std::make_pair(label, 0)).first;
// Multi link model has many links with the same first name and should
// have the same pixels color
bool isMultiLink = false;
if (parentName == _prevParentName)
{
isMultiLink = true;
}
else
{
it->second++;
_prevParentName = parentName;
}
const int instanceCount = it->second;
if (this->segmentationCamera->IsColoredMap())
{
math::Color color;
if (label == this->segmentationCamera->BackgroundLabel())
{
color = this->LabelToColor(label, isMultiLink);
}
else
{
// convert 24 bit number to int64
const int compositeId = label * 256 * 256 + instanceCount;
color = this->LabelToColor(compositeId, isMultiLink);
}
customParameter = Ogre::Vector4(color.R(), color.G(), color.B(), 1.0);
}
else
{
// 256 => 8 bits .. 255 => color percentage
float labelColor = label / 255.0f;
float instanceColor1 = (instanceCount / 256) / 255.0f;
float instanceColor2 = (instanceCount % 256) / 255.0f;
customParameter =
Ogre::Vector4(instanceColor2, instanceColor1, labelColor, 1.0);
}
}
return customParameter;
}
/////////////////////////////////////////////////
math::Color Ogre2SegmentationMaterialSwitcher::LabelToColor(int64_t _label,
bool _isMultiLink)
{
if (_label == this->segmentationCamera->BackgroundLabel())
return this->segmentationCamera->BackgroundColor();
// use label as seed to generate the same color for the label
this->generator.seed(_label);
std::uniform_int_distribution<int> distribution(0, 255);
// random color
int r = distribution(this->generator);
int g = distribution(this->generator);
int b = distribution(this->generator);
math::Color color(r, g, b);
// if the label is colored before return the color
// don't check for taken colors in that case, all items
// with the same label will have the same color
if (this->segmentationCamera->Type() == SegmentationType::ST_SEMANTIC &&
this->coloredLabel.count(_label))
return color;
if (_isMultiLink)
return color;
// loop recursivly till finding a unique color
if (this->IsTakenColor(color))
return this->LabelToColor(_label);
this->coloredLabel.insert(_label);
// We don't multiply by 255 here as (r,g,b) are in [0-255] range
int64_t colorId = r * 256 * 256 + g * 256 + b;
this->colorToLabel[colorId] = _label;
return color;
}
////////////////////////////////////////////////
VisualPtr Ogre2SegmentationMaterialSwitcher::TopLevelModelVisual(
VisualPtr _visual) const
{
if (!_visual)
return _visual;
VisualPtr p = _visual;
while (p->Parent() && p->Parent() != _visual->Scene()->RootVisual())
p = std::dynamic_pointer_cast<Visual>(p->Parent());
return p;
}
////////////////////////////////////////////////
void Ogre2SegmentationMaterialSwitcher::cameraPreRenderScene(
Ogre::Camera * /*_cam*/)
{
this->colorToLabel.clear();
auto itor = this->scene->OgreSceneManager()->getMovableObjectIterator(
Ogre::ItemFactory::FACTORY_TYPE_NAME);
auto engine = Ogre2RenderEngine::Instance();
engine->SetIgnOgreRenderingMode(IORM_SOLID_COLOR);
// Used for multi-link models, where each model has many ogre items but
// belongs to the same object, and all of them has the same parent name
std::string prevParentName = "";
// Store the ogre objects in a vector
std::vector<Ogre::MovableObject *> ogreObjects;
while (itor.hasMoreElements())
{
Ogre::MovableObject *object = itor.peekNext();
ogreObjects.push_back(object);
itor.moveNext();
}
// Sort the ogre objects by name
// The algorithm of handeling multi-link models depends on a sorted objects
// by name, so all links that belongs to the same object come in order
std::sort(ogreObjects.begin(), ogreObjects.end(),
[] (Ogre::MovableObject * object1, Ogre::MovableObject * object2) {
return object1->getName() > object2->getName();
});
this->materialMap.clear();
this->datablockMap.clear();
Ogre::HlmsManager *hlmsManager = engine->OgreRoot()->getHlmsManager();
Ogre::HlmsDatablock *defaultPbs =
hlmsManager->getHlms(Ogre::HLMS_PBS)->getDefaultDatablock();
// Construct one now so that datablock->setBlendblock
// each is as fast as possible
const Ogre::HlmsBlendblock *noBlend =
hlmsManager->getBlendblock(Ogre::HlmsBlendblock());
for (auto object : ogreObjects)
{
Ogre::Item *item = static_cast<Ogre::Item *>(object);
// get visual from ogre item
Ogre::Any userAny = item->getUserObjectBindings().getUserAny();
if (!userAny.isEmpty() && userAny.getType() == typeid(unsigned int))
{
// get visual id for the ogre item
auto visualId = Ogre::any_cast<unsigned int>(userAny);
VisualPtr visual;
try
{
visual = this->scene->VisualById(visualId);
}
catch(Ogre::Exception &e)
{
ignerr << "Ogre Error:" << e.getFullDescription() << "\n";
}
const Ogre::Vector4 customParameter =
ColorForVisual(visual, prevParentName);
const size_t numSubItems = item->getNumSubItems();
for (size_t i = 0; i < numSubItems; ++i)
{
// Set the custom value to the sub item to render
Ogre::SubItem *subItem = item->getSubItem(i);
subItem->setCustomParameter(1, customParameter);
if (!subItem->getMaterial().isNull())
{
this->materialMap.push_back({ subItem, subItem->getMaterial() });
// We need to keep the material's vertex shader
// to keep vertex deformation consistent; so we use
// a cloned material with a different pixel shader
// https://github.com/ignitionrobotics/ign-rendering/issues/544
auto material = Ogre::MaterialManager::getSingleton().getByName(
subItem->getMaterial()->getName() + "_solid",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
if (material->getNumSupportedTechniques() > 0u)
{
subItem->setMaterial(material);
}
else
{
// The supplied vertex shader could not pair with the
// pixel shader we provide. Try to salvage the situation
// using PBS shader. Custom deformation won't work but
// if we're lucky that won't matter
subItem->setDatablock(defaultPbs);
}
}
else
{
Ogre::HlmsDatablock *datablock = subItem->getDatablock();
const Ogre::HlmsBlendblock *blendblock = datablock->getBlendblock();
// We can't do any sort of blending. This isn't colour what we're
// storing, but rather an ID.
if (blendblock->mSourceBlendFactor != Ogre::SBF_ONE ||
blendblock->mDestBlendFactor != Ogre::SBF_ZERO ||
blendblock->mBlendOperation != Ogre::SBO_ADD ||
(blendblock->mSeparateBlend &&
(blendblock->mSourceBlendFactorAlpha != Ogre::SBF_ONE ||
blendblock->mDestBlendFactorAlpha != Ogre::SBF_ZERO ||
blendblock->mBlendOperationAlpha != Ogre::SBO_ADD)))
{
hlmsManager->addReference(blendblock);
this->datablockMap[datablock] = blendblock;
datablock->setBlendblock(noBlend);
}
}
}
}
}
// Do the same with heightmaps / terrain
auto heightmaps = this->scene->Heightmaps();
for (auto h : heightmaps)
{
auto heightmap = h.lock();
if (heightmap)
{
// TODO(anyone): Retrieve datablock and make sure it's not blending
// like we do with Items (it should be impossible?)
VisualPtr visual = heightmap->Parent();
const Ogre::Vector4 customParameter =
ColorForVisual(visual, prevParentName);
heightmap->Terra()->SetSolidColor(1u, customParameter);
}
}
// Remove the reference count on noBlend we created
hlmsManager->destroyBlendblock(noBlend);
// reset the count & colors tracking
this->instancesCount.clear();
this->takenColors.clear();
this->coloredLabel.clear();
}
////////////////////////////////////////////////
void Ogre2SegmentationMaterialSwitcher::cameraPostRenderScene(
Ogre::Camera * /*_cam*/)
{
auto engine = Ogre2RenderEngine::Instance();
Ogre::HlmsManager *hlmsManager = engine->OgreRoot()->getHlmsManager();
// Restore original blending to modified materials
for (const auto &[datablock, blendblock] : this->datablockMap)
{
datablock->setBlendblock(blendblock);
// Remove the reference we added (this won't actually destroy it)
hlmsManager->destroyBlendblock(blendblock);
}
this->datablockMap.clear();
// Remove the custom parameter. Why? If there are multiple cameras that
// use IORM_SOLID_COLOR (or any other mode), we want them to throw if
// that code forgot to call setCustomParameter. We may miss those errors
// if that code forgets to call but it was already carrying the value
// we set here.
//
// This consumes more performance but it's the price to pay for
// safety.
auto itor = this->scene->OgreSceneManager()->getMovableObjectIterator(
Ogre::ItemFactory::FACTORY_TYPE_NAME);
while (itor.hasMoreElements())
{
Ogre::MovableObject *object = itor.peekNext();
Ogre::Item *item = static_cast<Ogre::Item *>(object);
const size_t numSubItems = item->getNumSubItems();
for (size_t i = 0; i < numSubItems; ++i)
{
Ogre::SubItem *subItem = item->getSubItem(i);
subItem->removeCustomParameter(1u);
}
itor.moveNext();
}
// Restore Items with low level materials
for (auto subItemMat : this->materialMap)
{
subItemMat.first->setMaterial(subItemMat.second);
}
this->materialMap.clear();
// Remove the custom parameter (same reason as with Items)
auto heightmaps = this->scene->Heightmaps();
for (auto h : heightmaps)
{
auto heightmap = h.lock();
if (heightmap)
heightmap->Terra()->UnsetSolidColors();
}
engine->SetIgnOgreRenderingMode(IORM_NORMAL);
}
////////////////////////////////////////////////
const std::unordered_map<int64_t, int64_t> &
Ogre2SegmentationMaterialSwitcher::ColorToLabel() const
{
return this->colorToLabel;
}