ATLAS Offline Software
Loading...
Searching...
No Matches
VP1SoMaterialMixer.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6// Class: VP1SoMaterialMixer //
7// First version: November 2007 //
8// Author: Thomas.Kittelmann@cern.ch //
10
12#include "VP1Base/VP1Msg.h"
13#include <Inventor/C/errors/debugerror.h>
14#include <Inventor/nodes/SoMaterial.h>
15#include <Inventor/sensors/SoNodeSensor.h>
16
17//____________________________________________________________________
19public:
21 void setMaterialFieldsAsAverageOfMatList(SoMaterial*mat, const std::set<SoMaterial*>& matlist );
22 void setMaterialFieldsAsAverageOfMatList(SoMaterial*mat, const std::map<SoMaterial*,double>& matlist );
23 static float norm(const float& x) { return (x<0.0f?0.0f:(x>1.0f?1.0f:x));}
24
25 //Callback used to monitor changes in materials:
26 static void materialChangedCB(void *data, SoSensor *);
27 static std::map<SoSensor*,VP1SoMaterialMixer::Imp*> sensor2matmixerimp;
28 void monitorMaterial(SoMaterial*);
29 void materialChanged(SoMaterial*);
30
31 //For dealing with invalid input materials:
32 static bool inputMaterialValid(SoMaterial*);
33 SoMaterial *defmat;
34 SoMaterial * defaultMaterial()
35 {
36 if (!defmat) {
37 defmat = new SoMaterial;
38 defmat->ref();
39 }
40 return defmat;
41 }
42
43 //Data members:
44 std::map<std::set<SoMaterial*>,SoMaterial*> matlists2mixedmats;
45 std::map<std::map<SoMaterial*,double>,SoMaterial*> matlists2mixedmats_weighted;
46 std::map<SoMaterial*,SoNodeSensor*> mat2sensors;
47
48 //We could make it a bit more optimal by having different mat2sensor
49 //map + callback methods for weighted mixing. But since a given
50 //instance of the material mixer class is likely used mainly for
51 //either weighted or unweighted mixing, this is questionable how
52 //often this would actually help.
53};
54
55std::map<SoSensor*,VP1SoMaterialMixer::Imp*> VP1SoMaterialMixer::Imp::sensor2matmixerimp;
56
57//____________________________________________________________________
59 : VP1HelperClassBase(sys,"VP1SoMaterialMixer"), m_d(new Imp)
60{
61 m_d->theclass = this;
62 m_d->defmat = 0;
63}
64
65//____________________________________________________________________
67{
68 {
69 std::map<std::set<SoMaterial*>,SoMaterial*>::const_iterator it, itE = m_d->matlists2mixedmats.end();
70 std::set<SoMaterial*>::iterator it2, it2E;
71 for (it = m_d->matlists2mixedmats.begin();it!=itE;++it) {
72 for (it2=it->first.begin(), it2E=it->first.end();it2!=it2E;++it2)
73 (*it2)->unref();
74 it->second->unref();
75 }
76 }
77 {
78 std::map<std::map<SoMaterial*,double>,SoMaterial*>::iterator it, itE = m_d->matlists2mixedmats_weighted.end();
79 std::map<SoMaterial*,double>::const_iterator it2,it2E;
80 for (it = m_d->matlists2mixedmats_weighted.begin();it!=itE;++it) {
81 for (it2=it->first.begin(),it2E=it->first.end();it2!=it2E;++it2)
82 it2->first->unref();
83 it->second->unref();
84 }
85 }
86
87 {
88 std::map<SoMaterial*,SoNodeSensor*>::iterator it(m_d->mat2sensors.begin()), itE(m_d->mat2sensors.end());
89 for (;it!=itE;++it) {
90 m_d->sensor2matmixerimp.erase(m_d->sensor2matmixerimp.find(it->second));
91 delete it->second;
92 it->first->unref();
93 }
94 }
95 if (m_d->defmat)
96 m_d->defmat->unref();
97
98 //Print out warning if too many different mixed materials, to give
99 //the developers a chance to notice if they are doing something not
100 //optimal.
101
102 if (m_d->matlists2mixedmats.size()+m_d->matlists2mixedmats_weighted.size()>100)
103 if(VP1Msg::debug()){
104 messageDebug("WARNING: Watched more than 100 ("
105 +str(m_d->matlists2mixedmats.size()+m_d->matlists2mixedmats_weighted.size())
106 + ") different material combinations. Try to use fewer combinations for better performance!");
107 }
108 if (m_d->mat2sensors.size()>1000)
109 if(VP1Msg::debug()){
110 messageDebug("WARNING: Monitored more than 1000 (" +str(m_d->mat2sensors.size())+
111 ") different materials. Try to lower this number for better performance!");
112 }
113
114 delete m_d;
115}
116
117//____________________________________________________________________
119{
120 return m
121 && m->ambientColor.getNum() == 1
122 && m->diffuseColor.getNum() == 1
123 && m->specularColor.getNum() == 1
124 && m->emissiveColor.getNum() == 1
125 && m->transparency.getNum() == 1
126 && m->shininess.getNum() == 1;
127
128}
129
130//____________________________________________________________________
132{
133 SoMaterial *material = (SoMaterial *)data;
134 if (!inputMaterialValid(material)) {
135 VP1Msg::message("ERROR: Monitored material changed to invalid state"
136 " (multiple values in a field). Ignore changes.");
137 return;
138 }
139
140 std::map<SoSensor*,VP1SoMaterialMixer::Imp*>::iterator it = sensor2matmixerimp.find(sensor);
141 if (it!=sensor2matmixerimp.end())
142 it->second->materialChanged(material);
143}
144
145//____________________________________________________________________
147{
148 if (mat2sensors.find(mat)!=mat2sensors.end())
149 return;//We already monitor this material.
150
151 SoNodeSensor * sensor = new SoNodeSensor(Imp::materialChangedCB, mat);
152 sensor->attach(mat);
153 sensor2matmixerimp[sensor] = this;
154 mat2sensors[mat]=sensor;
155 mat->ref();
156
157}
158
159//____________________________________________________________________
161{
162 //First look in lists for un-weighted mixtures:
163 std::map<std::set<SoMaterial*>,SoMaterial*>::const_iterator it, itE = matlists2mixedmats.end();
164 for (it = matlists2mixedmats.begin();it!=itE;++it) {
165 std::set<SoMaterial*>::iterator it2(it->first.begin()),it2E(it->first.end());
166 for (;it2!=it2E;++it2) {
167 if (mat==*it2) {
168 setMaterialFieldsAsAverageOfMatList(it->second,it->first);
169 break;
170 }
171 }
172 }
173 //Then look in lists for weighted mixtures:
174 std::map<std::map<SoMaterial*,double>,SoMaterial*>::const_iterator it3, it3E = matlists2mixedmats_weighted.end();
175 for (it3 = matlists2mixedmats_weighted.begin();it3!=it3E;++it3) {
176 std::map<SoMaterial*,double>::const_iterator it4(it3->first.begin()),it4E(it3->first.end());
177 for (;it4!=it4E;++it4) {
178 if (mat==it4->first) {
179 setMaterialFieldsAsAverageOfMatList(it3->second,it3->first);
180 break;
181 }
182 }
183 }
184
185}
186
187//____________________________________________________________________
188void VP1SoMaterialMixer::Imp::setMaterialFieldsAsAverageOfMatList(SoMaterial*mat, const std::set<SoMaterial*>& matlist )
189{
190 float ambient_r(0.0f), ambient_g(0.0f), ambient_b(0.0f);
191 float diffuse_r(0.0f), diffuse_g(0.0f), diffuse_b(0.0f);
192 float specular_r(0.0f), specular_g(0.0f), specular_b(0.0f);
193 float emissive_r(0.0f), emissive_g(0.0f), emissive_b(0.0f);
194 float shininess(0.0f), transparency(0.0f);
195 float r,g,b;
196 std::set<SoMaterial*>::const_iterator it(matlist.begin()), itE(matlist.end());
197 for (;it!=itE;++it) {
198 (*it)->ambientColor[0].getValue(r,g,b); ambient_r += r; ambient_g += g; ambient_b += b;
199 (*it)->diffuseColor[0].getValue(r,g,b); diffuse_r += r; diffuse_g += g; diffuse_b += b;
200 (*it)->specularColor[0].getValue(r,g,b); specular_r += r; specular_g += g; specular_b += b;
201 (*it)->emissiveColor[0].getValue(r,g,b); emissive_r += r; emissive_g += g; emissive_b += b;
202 shininess += (*it)->shininess[0];
203 transparency += (*it)->transparency[0];
204 }
205
206 float n = 1.0f/matlist.size();
207 bool save = mat->enableNotify(false);
208 mat->ambientColor.setValue(norm(n*ambient_r),norm(n*ambient_g),norm(n*ambient_b));
209 mat->diffuseColor.setValue(norm(n*diffuse_r),norm(n*diffuse_g),norm(n*diffuse_b));
210 mat->specularColor.setValue(norm(n*specular_r),norm(n*specular_g),norm(n*specular_b));
211 mat->emissiveColor.setValue(norm(n*emissive_r),norm(n*emissive_g),norm(n*emissive_b));
212 mat->shininess.setValue(norm(n*shininess));
213 mat->transparency.setValue(norm(n*transparency));
214 if (save) {
215 mat->enableNotify(true);
216 mat->touch();
217 if (VP1Msg::verbose()){
218 theclass->messageVerbose("Material ("+str(mat)+") updated and touched");
219 }
220 } else {
221 if (VP1Msg::verbose()){
222 theclass->messageVerbose("Material ("+str(mat)+") updated but notifications were off");
223 }
224 }
225}
226
227//____________________________________________________________________
228void VP1SoMaterialMixer::Imp::setMaterialFieldsAsAverageOfMatList(SoMaterial*mat, const std::map<SoMaterial*,double>& matlist )
229{
230 float w{}, totweight{};
231 float ambient_r(0.0f), ambient_g(0.0f), ambient_b(0.0f);
232 float diffuse_r(0.0f), diffuse_g(0.0f), diffuse_b(0.0f);
233 float specular_r(0.0f), specular_g(0.0f), specular_b(0.0f);
234 float emissive_r(0.0f), emissive_g(0.0f), emissive_b(0.0f);
235 float shininess(0.0f), transparency(0.0f);
236 float r{},g{},b{};
237 std::map<SoMaterial*,double>::const_iterator it(matlist.begin()), itE(matlist.end());
238 SoMaterial * m{};
239 for (;it!=itE;++it) {
240 m = it->first;
241 w = it->second;
242 totweight += w;
243 m->ambientColor[0].getValue(r,g,b); ambient_r += w*r; ambient_g += w*g; ambient_b += w*b;
244 m->diffuseColor[0].getValue(r,g,b); diffuse_r += w*r; diffuse_g += w*g; diffuse_b += w*b;
245 m->specularColor[0].getValue(r,g,b); specular_r += w*r; specular_g += w*g; specular_b += w*b;
246 m->emissiveColor[0].getValue(r,g,b); emissive_r += w*r; emissive_g += w*g; emissive_b += w*b;
247 shininess += m->shininess[0]*w;
248 transparency += m->transparency[0]*w;
249 }
250 if (totweight==0){
251 VP1Msg::message("ERROR: denominator is zero; setting to 1");
252 totweight = 1.f;
253 }
254 float n = 1.0f/totweight;
255 bool save = mat->enableNotify(false);
256 mat->ambientColor.setValue(norm(n*ambient_r),norm(n*ambient_g),norm(n*ambient_b));
257 mat->diffuseColor.setValue(norm(n*diffuse_r),norm(n*diffuse_g),norm(n*diffuse_b));
258 mat->specularColor.setValue(norm(n*specular_r),norm(n*specular_g),norm(n*specular_b));
259 mat->emissiveColor.setValue(norm(n*emissive_r),norm(n*emissive_g),norm(n*emissive_b));
260 mat->shininess.setValue(norm(n*shininess));
261 mat->transparency.setValue(norm(n*transparency));
262 if (save) {
263 mat->enableNotify(true);
264 mat->touch();
265 if (VP1Msg::verbose()){
266 theclass->messageVerbose("Material ("+str(mat)+") updated and touched");
267 }
268 } else {
269 if (VP1Msg::verbose()){
270 theclass->messageVerbose("Material ("+str(mat)+") updated but notifications were off");
271 }
272 }
273}
274
275//____________________________________________________________________
276SoMaterial * VP1SoMaterialMixer::getMixedMaterial(const std::set<SoMaterial*>& matlist) {
277 //Check incoming materials!
278 bool error(false);
279 std::set<SoMaterial*>::const_iterator it(matlist.begin()), itE(matlist.end());
280 for (;it!=itE;++it) {
281 if (!m_d->inputMaterialValid(*it)) {
282 error=true;
283 break;
284 }
285 }
286 if (matlist.empty()) {
287 VP1Msg::message("ERROR: asked to handle empty material list.");
288 return m_d->defaultMaterial();
289 }
290 if (error) {
291 VP1Msg::message("ERROR: asked to handle invalid material list.");
292 return m_d->defaultMaterial();
293 }
294
295 //Someone might be silly enough to call with just one material:
296 if (matlist.size()==1)
297 return *(matlist.begin());
298
299 //Do we already have a mixed material for this list:
300 std::map<std::set<SoMaterial*>,SoMaterial*>::const_iterator it2 = m_d->matlists2mixedmats.find(matlist);
301 if (it2!=m_d->matlists2mixedmats.end())
302 return it2->second;
303
304 //Create new mixed material:
305 SoMaterial * mixmat = new SoMaterial;
306 m_d->setMaterialFieldsAsAverageOfMatList(mixmat,matlist);
307
308 //Set up monitoring, add to maps and refcount materials:
309 std::set<SoMaterial*>::const_iterator it3(matlist.begin()), it3E(matlist.end());
310 for (;it3!=it3E;++it3) {
311 m_d->monitorMaterial(*it3);
312 (*it3)->ref();//since we keep the pointer.
313 }
314 mixmat->ref();
315 m_d->matlists2mixedmats[matlist] = mixmat;
316
317 return mixmat;
318}
319
320//____________________________________________________________________
321SoMaterial * VP1SoMaterialMixer::getMixedMaterial(const std::map<SoMaterial*,double>& matlist)
322{
323 //Check incoming materials!
324 bool error(false);
325 std::map<SoMaterial*,double>::const_iterator it(matlist.begin()), itE(matlist.end());
326 for (;it!=itE;++it) {
327 if (!m_d->inputMaterialValid(it->first)||it->second<=0.0) {
328 error=true;
329 break;
330 }
331 }
332 if (matlist.empty()) {
333 VP1Msg::message("ERROR: asked to handle empty material list.");
334 return m_d->defaultMaterial();
335 }
336 if (error) {
337 VP1Msg::message("ERROR: asked to handle invalid material list.");
338 return m_d->defaultMaterial();
339 }
340
341 //Someone might be silly enough to call with just one material:
342 if (matlist.size()==1)
343 return matlist.begin()->first;
344
345 //Do we already have a mixed material for this list:
346 std::map<std::map<SoMaterial*,double>,SoMaterial*>::const_iterator it2 = m_d->matlists2mixedmats_weighted.find(matlist);
347 if (it2!=m_d->matlists2mixedmats_weighted.end())
348 return it2->second;
349
350 //Create new mixed material:
351 SoMaterial * mixmat = new SoMaterial;
352 m_d->setMaterialFieldsAsAverageOfMatList(mixmat,matlist);
353
354 //Set up monitoring, add to maps and refcount materials:
355 std::map<SoMaterial*,double>::const_iterator it3(matlist.begin()), it3E(matlist.end());
356 for (;it3!=it3E;++it3) {
357 m_d->monitorMaterial(it3->first);
358 it3->first->ref();//since we keep the pointer.
359 }
360 mixmat->ref();
361 m_d->matlists2mixedmats_weighted[matlist] = mixmat;
362
363 return mixmat;
364}
365
366//____________________________________________________________________
367SoMaterial * VP1SoMaterialMixer::getMixedMaterial(SoMaterial*m1,SoMaterial*m2)
368{
369 std::set<SoMaterial*> matlist;
370 matlist.insert(m1);
371 matlist.insert(m2);
372 return getMixedMaterial(matlist);
373}
374
375//____________________________________________________________________
376SoMaterial * VP1SoMaterialMixer::getMixedMaterial( SoMaterial* mat1, const double& weight1,
377 SoMaterial* mat2, const double& weight2 )
378{
379 std::map<SoMaterial*,double> matlist;
380 matlist[mat1] = weight1;
381 matlist[mat2] = weight2;
382 return getMixedMaterial(matlist);
383}
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
#define x
VP1HelperClassBase(IVP1System *sys=0, QString helpername="")
void messageDebug(const QString &) const
static bool debug()
Definition VP1Msg.h:32
static bool verbose()
Definition VP1Msg.h:31
static void message(const QString &, IVP1System *sys=0)
Definition VP1Msg.cxx:30
static float norm(const float &x)
void materialChanged(SoMaterial *)
static void materialChangedCB(void *data, SoSensor *)
std::map< std::map< SoMaterial *, double >, SoMaterial * > matlists2mixedmats_weighted
std::map< SoMaterial *, SoNodeSensor * > mat2sensors
std::map< std::set< SoMaterial * >, SoMaterial * > matlists2mixedmats
void setMaterialFieldsAsAverageOfMatList(SoMaterial *mat, const std::set< SoMaterial * > &matlist)
VP1SoMaterialMixer * theclass
void monitorMaterial(SoMaterial *)
static std::map< SoSensor *, VP1SoMaterialMixer::Imp * > sensor2matmixerimp
static bool inputMaterialValid(SoMaterial *)
VP1SoMaterialMixer(IVP1System *sys=0)
SoMaterial * getMixedMaterial(const std::set< SoMaterial * > &)
int r
Definition globals.cxx:22