1 /*******************************************************
2       Lightwave Object Loader for OSG
3 
4   Copyright (C) 2004 Marco Jez <marco.jez@poste.it>
5   OpenSceneGraph is (C) 2004 Robert Osfield
6 ********************************************************/
7 
8 #include "Surface.h"
9 
10 #include <osg/Material>
11 #include <osg/CullFace>
12 #include <osg/Texture2D>
13 #include <osg/TexEnvCombine>
14 #include <osg/TexGen>
15 #include <osg/BlendFunc>
16 #include <osg/Notify>
17 
18 #include <osgFX/SpecularHighlights>
19 
20 #include <osgDB/ReadFile>
21 
22 using namespace lwosg;
23 
24 namespace
25 {
26 
osg_wrap_mode(Image_map::Wrap_type w)27     osg::Texture::WrapMode osg_wrap_mode(Image_map::Wrap_type w)
28     {
29         switch (w) {
30             case Image_map::RESET: return osg::Texture::CLAMP;
31             case Image_map::REPEAT: return osg::Texture::REPEAT;
32             case Image_map::MIRROR: return osg::Texture::MIRROR;
33             case Image_map::EDGE: return osg::Texture::CLAMP_TO_EDGE;
34             default: return osg::Texture::REPEAT;
35         };
36     }
37 
38 }
39 
Surface()40 Surface::Surface()
41 :    base_color_(0.784f, 0.784f, 0.784f),
42     diffuse_(1.0f),
43     luminosity_(0),
44     specularity_(0),
45     reflection_(0),
46     transparency_(0),
47     translucency_(0),
48     glossiness_(0.4f),
49     sidedness_(FRONT_ONLY),
50     max_smoothing_angle_(0)
51 {
52 }
53 
Surface(const lwo2::FORM::SURF * surf,const Clip_map & clips)54 Surface::Surface(const lwo2::FORM::SURF *surf, const Clip_map &clips)
55 :    base_color_(0.784f, 0.784f, 0.784f),
56     diffuse_(1.0f),
57     luminosity_(0),
58     specularity_(0),
59     reflection_(0),
60     transparency_(0),
61     translucency_(0),
62     glossiness_(0.4f),
63     sidedness_(FRONT_ONLY),
64     max_smoothing_angle_(0)
65 {
66     compile(surf, clips);
67 }
68 
compile(const lwo2::FORM::SURF * surf,const Clip_map & clips)69 void Surface::compile(const lwo2::FORM::SURF *surf, const Clip_map &clips)
70 {
71     // invalidate the stateset so it will be rebuilt
72     stateset_ = 0;
73 
74     name_ = surf->name;
75 
76     for (iff::Chunk_list::const_iterator j=surf->attributes.begin(); j!=surf->attributes.end(); ++j) {
77 
78         const lwo2::FORM::SURF::COLR *colr = dynamic_cast<const lwo2::FORM::SURF::COLR *>(*j);
79         if (colr) base_color_ = osg::Vec3(colr->base_color.red, colr->base_color.green, colr->base_color.blue);
80 
81         const lwo2::FORM::SURF::DIFF *diff = dynamic_cast<const lwo2::FORM::SURF::DIFF *>(*j);
82         if (diff) diffuse_ = diff->intensity.fraction;
83 
84         const lwo2::FORM::SURF::LUMI *lumi = dynamic_cast<const lwo2::FORM::SURF::LUMI *>(*j);
85         if (lumi) luminosity_ = lumi->intensity.fraction;
86 
87         const lwo2::FORM::SURF::SPEC *spec = dynamic_cast<const lwo2::FORM::SURF::SPEC *>(*j);
88         if (spec) specularity_ = spec->intensity.fraction;
89 
90         const lwo2::FORM::SURF::REFL *refl = dynamic_cast<const lwo2::FORM::SURF::REFL *>(*j);
91         if (refl) reflection_ = refl->intensity.fraction;
92 
93         const lwo2::FORM::SURF::TRAN *tran = dynamic_cast<const lwo2::FORM::SURF::TRAN *>(*j);
94         if (tran) transparency_ = tran->intensity.fraction;
95 
96         const lwo2::FORM::SURF::TRNL *trnl = dynamic_cast<const lwo2::FORM::SURF::TRNL *>(*j);
97         if (trnl) translucency_ = trnl->intensity.fraction;
98 
99         const lwo2::FORM::SURF::GLOS *glos = dynamic_cast<const lwo2::FORM::SURF::GLOS *>(*j);
100         if (glos) glossiness_ = glos->glossiness.fraction;
101 
102         const lwo2::FORM::SURF::SIDE *side = dynamic_cast<const lwo2::FORM::SURF::SIDE *>(*j);
103         if (side) sidedness_ = static_cast<Sidedness>(side->sidedness);
104 
105         const lwo2::FORM::SURF::SMAN *sman = dynamic_cast<const lwo2::FORM::SURF::SMAN *>(*j);
106         if (sman) max_smoothing_angle_ = sman->max_smoothing_angle.radians;
107 
108         const lwo2::FORM::SURF::VCOL *vcol = dynamic_cast<const lwo2::FORM::SURF::VCOL *>(*j);
109         if (vcol) {
110             color_map_intensity_ = vcol->intensity.fraction;
111             color_map_type_ = std::string(vcol->vmap_type.id, 4);
112             color_map_name_ = vcol->name;
113         }
114 
115         const lwo2::FORM::SURF::BLOK *blok = dynamic_cast<const lwo2::FORM::SURF::BLOK *>(*j);
116         if (blok) {
117             Block new_block(blok);
118             if (new_block.get_type() == "IMAP") {
119                 Clip_map::const_iterator i = clips.find(new_block.get_image_map().image_map);
120                 if (i != clips.end()) {
121                     new_block.get_image_map().clip = &i->second;
122                 } else {
123                     OSG_WARN << "Warning: lwosg::Surface: cannot find clip number " << new_block.get_image_map().image_map << std::endl;
124                 }
125             }
126             blocks_.insert(Block_map::value_type(new_block.get_ordinal(), new_block));
127         }
128     }
129 }
130 
generate_stateset(unsigned int max_tex_units,bool force_arb_compression,const osgDB::ReaderWriter::Options * db_options) const131 void Surface::generate_stateset(unsigned int max_tex_units, bool force_arb_compression, const osgDB::ReaderWriter::Options* db_options) const
132 {
133     if (!stateset_.valid()) {
134 
135         stateset_ = new osg::StateSet;
136 
137         osg::ref_ptr<osg::Material> material = new osg::Material;
138         material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(base_color_ * diffuse_, 1-transparency_));
139         material->setAmbient(osg::Material::FRONT_AND_BACK, material->getDiffuse(osg::Material::FRONT_AND_BACK));
140         material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(specularity_, specularity_, specularity_, 1));
141         material->setShininess(osg::Material::FRONT_AND_BACK, powf(2, 10 * glossiness_ + 2));
142         material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4(base_color_ * luminosity_, 1-transparency_));
143         stateset_->setAttributeAndModes(material.get());
144 
145         if (!color_map_name_.empty()) {
146             material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
147         }
148 
149         if (transparency_ > 0) {
150             osg::ref_ptr<osg::BlendFunc> bf = new osg::BlendFunc;
151             bf->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
152             stateset_->setAttributeAndModes(bf.get());
153             stateset_->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
154         }
155 
156         if (sidedness_ == FRONT_AND_BACK) {
157             stateset_->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
158         } else {
159             osg::ref_ptr<osg::CullFace> cf = new osg::CullFace;
160             switch (sidedness_) {
161                 case NONE:  cf->setMode(osg::CullFace::FRONT_AND_BACK); break;
162                 case FRONT_ONLY: cf->setMode(osg::CullFace::BACK); break;
163                 case BACK_ONLY:  cf->setMode(osg::CullFace::FRONT); break;
164                 default: ;
165             }
166             stateset_->setAttributeAndModes(cf.get());
167         }
168 
169         std::map< std::string, unsigned int > unitmap;
170         unitmap[ "COLR" ] = 0;
171         unitmap[ "TRAN" ] = 1;
172         unsigned int unit = 0;
173         for (Block_map::const_iterator i=blocks_.begin(); i!=blocks_.end(); ++i) {
174 
175             const Block &block = i->second;
176             if (!block.enabled()) {
177                 continue;
178             }
179 
180             if (block.get_type() == "IMAP")
181             {
182                 std::string channel = block.get_channel();
183                 if ( ( channel == "COLR" ) ||
184                      ( channel == "TRAN" ) )
185                 {
186                     unit = unitmap[ channel ];
187                     if (block.get_image_map().clip)
188                     {
189                         std::string image_file = block.get_image_map().clip->get_still_filename();
190                         if (!image_file.empty())
191                         {
192 
193                             if (unit >= max_tex_units && max_tex_units > 0)
194                             {
195                                 OSG_WARN << "Warning: lwosg::Surface: maximum number of texture units (" << max_tex_units << ") has been reached, skipping incoming blocks" << std::endl;
196                                 break;
197                             }
198 
199                             osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(image_file, db_options);
200                             if (!image) break;
201 
202                             osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
203                             if (force_arb_compression)
204                                 texture->setInternalFormatMode(osg::Texture::USE_ARB_COMPRESSION);
205                             texture->setImage(image.get());
206                             texture->setWrap(osg::Texture::WRAP_S, osg_wrap_mode(block.get_image_map().width_wrap));
207                             texture->setWrap(osg::Texture::WRAP_T, osg_wrap_mode(block.get_image_map().height_wrap));
208                             texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
209                             texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
210                             stateset_->setTextureAttributeAndModes(unit, texture.get());
211 
212                             osg::ref_ptr<osg::TexEnvCombine> tec = new osg::TexEnvCombine;
213                             switch (block.get_opacity_type())
214                             {
215                                 case Block::NORMAL:
216                                 {
217                                     float s = block.get_opacity_amount();
218                                     if (unit == 0)
219                                     {
220                                         tec->setCombine_RGB(osg::TexEnvCombine::MODULATE);
221                                         osg::Vec3 color(diffuse_, diffuse_, diffuse_);
222                                         color = color * s + base_color_ * (1 - s);
223                                         material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(color, 1-transparency_));
224                                         material->setAmbient(osg::Material::FRONT_AND_BACK, material->getDiffuse(osg::Material::FRONT_AND_BACK));
225                                         material->setColorMode(osg::Material::OFF);
226                                     }
227                                     else
228                                     {
229                                         tec->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);
230                                         tec->setConstantColor(osg::Vec4(s, s, s, s));
231                                     }
232                                 }
233                                 break;
234 
235                                 case Block::ADDITIVE:
236                                     tec->setCombine_RGB(osg::TexEnvCombine::ADD);
237                                     break;
238 
239                                 case Block::SUBTRACTIVE:
240                                     OSG_WARN << "Warning: lwosg::Surface: 'Subtractive' blending mode is not supported, falling back to 'Difference' mode" << std::endl;
241                                 case Block::DIFFERENCE:
242                                     tec->setCombine_RGB(osg::TexEnvCombine::SUBTRACT);
243                                     break;
244 
245                                 case Block::MULTIPLY:
246                                     tec->setCombine_RGB(osg::TexEnvCombine::MODULATE);
247                                     break;
248 
249                                 case Block::DIVIDE:
250                                     OSG_WARN << "Warning: lwosg::Surface: 'Divide' blending mode is not supported" << std::endl;
251                                     break;
252 
253                                 case Block::ALPHA:
254                                     OSG_WARN << "Warning: lwosg::Surface: 'Alpha' blending mode is not supported" << std::endl;
255                                     break;
256 
257                                 case Block::TEXTURE_DISPLACEMENT:
258                                     OSG_WARN << "Warning: lwosg::Surface: 'Texture Displacement' blending mode is not supported" << std::endl;
259                                     break;
260 
261                                 default:
262                                 break;
263                             };
264 
265                             stateset_->setTextureAttributeAndModes(unit, tec.get());
266                             ++unit;
267                         }
268                     }
269                 }
270                 else
271                 {
272                     OSG_WARN << "Warning: lwosg::Surface: texture channels of type '" << block.get_channel() << "' are not supported, this block will be ignored" << std::endl;
273                 }
274             }
275         }
276     }
277 }
278 
apply(osg::Geometry * geo,const VertexMap_map * texture_maps,const VertexMap_map * rgb_maps,const VertexMap_map * rgba_maps,int max_tex_units,bool use_osgfx,bool force_arb_compression,const VertexMap_binding_map & texmap_bindings,const osgDB::ReaderWriter::Options * db_options) const279 osg::Group *Surface::apply(osg::Geometry *geo, const VertexMap_map *texture_maps, const VertexMap_map *rgb_maps, const VertexMap_map *rgba_maps, int max_tex_units, bool use_osgfx, bool force_arb_compression, const VertexMap_binding_map &texmap_bindings, const osgDB::ReaderWriter::Options* db_options) const
280 {
281     int num_points = 0;
282 
283     if (geo->getVertexArray()) {
284         num_points = static_cast<int>(geo->getVertexArray()->getNumElements());
285     }
286 
287     generate_stateset(max_tex_units, force_arb_compression, db_options);
288     geo->setStateSet(stateset_.get());
289 
290     int unit = 0;
291     for (Block_map::const_iterator i=blocks_.begin(); i!=blocks_.end(); ++i) {
292         const Block &block = i->second;
293         if (block.get_type() == "IMAP" && block.get_channel() == "COLR" && block.get_image_map().clip) {
294             std::string image_file = block.get_image_map().clip->get_still_filename();
295             if (!image_file.empty()) {
296                 if (block.get_image_map().projection == Image_map::UV) {
297                     VertexMap_map::const_iterator i = texture_maps->find(block.get_image_map().uv_map);
298                     if (i != texture_maps->end()) {
299                         geo->setTexCoordArray(unit, i->second->asVec2Array(num_points));
300                     } else {
301                         OSG_WARN << "Warning: lwosg::Surface: surface '" << name_ << "' needs texture map named '" << block.get_image_map().uv_map << "' but I can't find it" << std::endl;
302                     }
303                 }
304                 ++unit;
305             }
306         }
307     }
308 
309     for (VertexMap_binding_map::const_iterator vi=texmap_bindings.begin(); vi!=texmap_bindings.end(); ++vi)
310     {
311         for (VertexMap_map::const_iterator j=texture_maps->begin(); j!=texture_maps->end(); ++j)
312         {
313             if (j->first == vi->first)
314             {
315                 if (geo->getTexCoordArray(vi->second) != 0)
316                 {
317                     OSG_WARN << "Warning: lwosg::Surface: explicing binding of texture map '" << vi->first << "' to texunit " << vi->second << " will replace existing texture map" << std::endl;
318                 }
319                 geo->setTexCoordArray(vi->second, j->second->asVec2Array(num_points));
320             }
321             else
322             {
323                 OSG_WARN << "Warning: lwosg::Surface: explicit binding of texture map '" << vi->first << "' to texunit " << vi->second << " was requested but there is no such map in this LWO file" << std::endl;
324             }
325         }
326     }
327 
328     osg::Vec4 color = osg::Vec4(base_color_, 1-transparency_);
329 
330     const VertexMap_map *color_maps = 0;
331 
332     if (color_map_type_ == "RGB ") {
333         color_maps = rgb_maps;
334     }
335 
336     if (color_map_type_ == "RGBA") {
337         color_maps = rgba_maps;
338     }
339 
340     if (color_maps) {
341         VertexMap_map::const_iterator i = color_maps->find(color_map_name_);
342         if (i != color_maps->end() && !i->second->empty()) {
343             geo->setColorArray(i->second->asVec4Array(num_points, color * color_map_intensity_, color * color_map_intensity_), osg::Array::BIND_PER_VERTEX);
344         } else {
345             OSG_WARN << "Warning: lwosg::Surface: surface '" << name_ << "' needs color map named '" << color_map_name_ << "' but I can't find it" << std::endl;
346         }
347     }
348 
349     // create osgFX specularity if needed
350     if (use_osgfx && glossiness_ > 0 && specularity_ > 0) {
351         if (unit >= max_tex_units && max_tex_units > 0) {
352             OSG_WARN << "Warning: lwosg::Surface: can't apply osgFX specular lighting: maximum number of texture units (" << max_tex_units << ") has been reached" << std::endl;
353         } else {
354             osg::ref_ptr<osgFX::SpecularHighlights> sh = new osgFX::SpecularHighlights;
355             sh->setTextureUnit(unit);
356             osg::Material *material = dynamic_cast<osg::Material *>(stateset_->getAttribute(osg::StateAttribute::MATERIAL));
357             if (material) {
358                 sh->setSpecularColor(material->getSpecular(osg::Material::FRONT_AND_BACK));
359                 sh->setSpecularExponent(powf(2, 10 * glossiness_ + 2));
360                 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 0, 0));
361                 material->setShininess(osg::Material::FRONT_AND_BACK, 0);
362             }
363             return sh.release();
364         }
365     }
366 
367     return 0;
368 }
369