From 99313d225bc392c6d823b61c0683a972ab54e03c Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 21 Jan 2009 18:47:55 +0000 Subject: [PATCH] From Roger James, The changes are as follows:- 1. Support for ambient occlusion maps. 2. A fix for the incorrect handling of normals on all geometries. The optimizer usually fixed this bug so it probably was not noticed very often. 3. A new option flag on the reader. "StrictTransparency" // Process transparent and transparency settings according to a strict interpretation of the spec // See https://collada.org/public_forum/viewtopic.php?f=12&t=1210 otherwise // Jump through various hoops to accomodate the multiplicity of different ways // that various people have interpreted the specification 4. Handling of texures in the transparent channel. This is allowed provided that they are the same texture that is specified in the diffuse channel. Accomodating a different texture would require use of programmable pipeline functionality which I have tried to avoid. 5. Handling of elements. "" --- src/osgPlugins/dae/ReaderWriterDAE.cpp | 2 +- src/osgPlugins/dae/daeRGeometry.cpp | 219 +++++---- src/osgPlugins/dae/daeRMaterials.cpp | 647 +++++++++++++++++++------ src/osgPlugins/dae/daeReader.cpp | 5 +- src/osgPlugins/dae/daeReader.h | 26 +- 5 files changed, 649 insertions(+), 250 deletions(-) diff --git a/src/osgPlugins/dae/ReaderWriterDAE.cpp b/src/osgPlugins/dae/ReaderWriterDAE.cpp index dc73d2b30..31405c41d 100644 --- a/src/osgPlugins/dae/ReaderWriterDAE.cpp +++ b/src/osgPlugins/dae/ReaderWriterDAE.cpp @@ -57,7 +57,7 @@ ReaderWriterDAE::readNode(const std::string& fname, pDAE = new DAE; } - osgdae::daeReader daeReader(pDAE) ; + osgdae::daeReader daeReader(pDAE, options && options->getOptionString().find("StrictTransparency") != std::string::npos ) ; // Convert file name to URI std::string fileURI = ConvertFilePathToColladaCompatibleURI(fileName); diff --git a/src/osgPlugins/dae/daeRGeometry.cpp b/src/osgPlugins/dae/daeRGeometry.cpp index 076f9c846..6f0168a96 100644 --- a/src/osgPlugins/dae/daeRGeometry.cpp +++ b/src/osgPlugins/dae/daeRGeometry.cpp @@ -35,26 +35,22 @@ osg::Geode* daeReader::processInstanceGeometry( domInstance_geometry *ig ) } // Check cache if geometry already exists - osg::Geode *geode; + osg::Geode* cachedGeode; domGeometryGeodeMap::iterator iter = geometryMap.find( geom ); if ( iter != geometryMap.end() ) { - osg::Geode* cachedGeode = iter->second; - - // Create a copy of the cached Geode with a copy of the drawables, - // because the may be using a different material. - // TODO Cloning is not necessary if the material layouts are exactly the same. - // To check this we need to compare the material bindings used by the cached Geode - // and this new instance_geometry material bindings. - geode = static_cast(cachedGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); + cachedGeode = iter->second; } else { - geode = processGeometry( geom ); - geometryMap.insert( std::make_pair( geom, geode ) ); + cachedGeode = processGeometry( geom ); + geometryMap.insert( std::make_pair( geom, cachedGeode ) ); } + // Create a copy of the cached Geode with a copy of the drawables, + // because we may be using a different material or texture unit bindings. + osg::Geode *geode = static_cast(cachedGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); if ( geode == NULL ) { osg::notify( osg::WARN ) << "Failed to load geometry " << ig->getUrl().getURI() << std::endl; @@ -64,7 +60,7 @@ osg::Geode* daeReader::processInstanceGeometry( domInstance_geometry *ig ) // process material bindings if ( ig->getBind_material() != NULL ) { - processBindMaterial( ig->getBind_material(), geom, geode ); + processBindMaterial( ig->getBind_material(), geom, geode, cachedGeode ); } return geode; @@ -119,26 +115,21 @@ osg::Geode* daeReader::processInstanceController( domInstance_controller *ictrl } // Check cache if geometry already exists - osg::Geode *geode; - + osg::Geode* cachedGeode; domGeometryGeodeMap::iterator iter = geometryMap.find( geom ); if ( iter != geometryMap.end() ) { - osg::Geode* cachedGeode = iter->second; - - // Create a copy of the cached Geode with a copy of the drawables, - // because the may be using a different material. - // TODO Cloning is not necessary if the material layouts are exactly the same. - // To check this we need to compare the material bindings used by the cached Geode - // and this new instance_geometry material bindings. - geode = static_cast(cachedGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); + cachedGeode = iter->second; } else { - geode = processGeometry( geom ); - geometryMap.insert( std::make_pair( geom, geode ) ); + cachedGeode = processGeometry( geom ); + geometryMap.insert( std::make_pair( geom, cachedGeode ) ); } + // Create a copy of the cached Geode with a copy of the drawables, + // because we may be using a different material or texture unit bindings. + osg::Geode *geode = static_cast(cachedGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); if ( geode == NULL ) { osg::notify( osg::WARN ) << "Failed to load geometry " << src->getURI() << std::endl; @@ -147,7 +138,7 @@ osg::Geode* daeReader::processInstanceController( domInstance_controller *ictrl //process material bindings if ( ictrl->getBind_material() != NULL ) { - processBindMaterial( ictrl->getBind_material(), geom, geode ); + processBindMaterial( ictrl->getBind_material(), geom, geode, cachedGeode ); } return geode; @@ -248,7 +239,7 @@ osg::Geode *daeReader::processGeometry( domGeometry *geo ) template< typename T > void daeReader::processSinglePPrimitive(osg::Geode* geode, T *group, SourceMap &sources, GLenum mode ) { - osg::Geometry *geometry = new osg::Geometry(); + osg::Geometry *geometry = new ReaderGeometry(); geometry->setName(group->getMaterial()); IndexMap index_map; @@ -264,7 +255,7 @@ void daeReader::processSinglePPrimitive(osg::Geode* geode, T *group, SourceMap & template< typename T > void daeReader::processMultiPPrimitive(osg::Geode* geode, T *group, SourceMap &sources, GLenum mode ) { - osg::Geometry *geometry = new osg::Geometry(); + osg::Geometry *geometry = new ReaderGeometry(); geometry->setName(group->getMaterial()); IndexMap index_map; @@ -283,7 +274,7 @@ void daeReader::processMultiPPrimitive(osg::Geode* geode, T *group, SourceMap &s void daeReader::processPolylist(osg::Geode* geode, domPolylist *group, SourceMap &sources ) { - osg::Geometry *geometry = new osg::Geometry(); + osg::Geometry *geometry = new ReaderGeometry(); geometry->setName(group->getMaterial()); IndexMap index_map; @@ -340,7 +331,7 @@ void daeReader::processP( domP *p, osg::Geometry *&/*geom*/, IndexMap &index_map } } -void daeReader::resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry *&geom, +void daeReader::resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry *geom, SourceMap &sources, IndexMap &index_map ) { domVertices* vertices = NULL; @@ -349,10 +340,11 @@ void daeReader::resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry daeElement* normal_source = NULL; daeElement* texcoord_source = NULL; - int offset; - int set; daeElement *tmp_el; domInputLocalOffset *tmp_input; + ReaderGeometry* GeometryWrapper = dynamic_cast(geom); + + int TexCoordSetsUsed = 0; if ( findInputSourceBySemantic( inputs, "VERTEX", tmp_el, &tmp_input ) ) { @@ -362,52 +354,97 @@ void daeReader::resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry osg::notify( osg::WARN )<<"Could not get vertices"<getOffset(); - set = tmp_input->getSet(); //if it wasn't actually set it will initialize to 0 which is - //the value we would want anyways. + // Process mandatory offset attribute + int offset = tmp_input->getOffset(); + if ( index_map[offset] == NULL ) + index_map[offset] = new osg::IntArray(); + geom->setVertexIndices( index_map[offset] ); + + // Process input elements within the vertices element. These are of the unshared type + // and therefore cannot have set and offset attributes + + // The vertices POSITION semantic input element is mandatory domInputLocal *tmp; findInputSourceBySemantic( vertices->getInput_array(), "POSITION", position_source, &tmp ); - findInputSourceBySemantic( vertices->getInput_array(), "COLOR", color_source, &tmp ); - findInputSourceBySemantic( vertices->getInput_array(), "NORMAL", normal_source, &tmp ); - findInputSourceBySemantic( vertices->getInput_array(), "TEXCOORD", texcoord_source, &tmp ); - - if ( index_map[offset] == NULL ) - { - index_map[offset] = new osg::IntArray(); - } - geom->setVertexIndices( index_map[offset] ); if ( position_source != NULL ) { geom->setVertexArray( sources[position_source].getVec3Array() ); } + else + { + osg::notify( osg::FATAL )<<"Mandatory POSITION semantic missing"<getInput_array(), "COLOR", color_source, &tmp ); + findInputSourceBySemantic( vertices->getInput_array(), "NORMAL", normal_source, &tmp ); + findInputSourceBySemantic( vertices->getInput_array(), "TEXCOORD", texcoord_source, &tmp ); + + + int VertexCount = sources[position_source].getCount(); if ( color_source != NULL ) { - geom->setColorArray( sources[color_source].getVec4Array() ); - geom->setColorIndices( index_map[offset] ); - geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); + // Check matching arrays + if ( sources[color_source].getCount() >= VertexCount ) + { + geom->setColorArray( sources[color_source].getVec4Array() ); + geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); + geom->setColorIndices( index_map[offset] ); // Use the vertex indices + } + else + { + osg::notify( osg::WARN )<<"Not enough entries in color array"<setNormalArray( sources[normal_source].getVec3Array() ); - geom->setNormalIndices( index_map[offset] ); - geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); + // Check matching arrays + if ( sources[normal_source].getCount() >= VertexCount ) + { + geom->setNormalArray( sources[normal_source].getVec3Array() ); + geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); + geom->setNormalIndices( index_map[offset] ); // Use the vertex indices + } + else + { + osg::notify( osg::WARN )<<"Not enough entries in normal array"<getArrayType() ) { - case domSourceReader::Vec2: - geom->setTexCoordArray( set, sc->getVec2Array() ); - break; - case domSourceReader::Vec3: - geom->setTexCoordArray( set, sc->getVec3Array() ); - break; - default: - osg::notify( osg::WARN )<<"Unsupported array type: "<< sc->getArrayType() <getCount() >= VertexCount ) + { + if (NULL != GeometryWrapper) + GeometryWrapper->_TexcoordSetMap[0] = TexCoordSetsUsed; + switch( sc->getArrayType() ) + { + case domSourceReader::Vec2: + geom->setTexCoordArray( TexCoordSetsUsed, sc->getVec2Array() ); + break; + case domSourceReader::Vec3: + geom->setTexCoordArray( TexCoordSetsUsed, sc->getVec3Array() ); + break; + default: + osg::notify( osg::WARN )<<"Unsupported array type: "<< sc->getArrayType() <setTexCoordIndices( set, index_map[offset] ); + else + { + osg::notify( osg::WARN )<<"Not enough entries in texcoord array"<getOffset(); - if ( index_map[offset] == NULL ) { + if (color_source != NULL) + osg::notify( osg::WARN )<<"Overwriting vertices input(COLOR) with input from primitive"<setColorBinding( osg::Geometry::BIND_PER_VERTEX ); + + int offset = tmp_input->getOffset(); + if ( index_map[offset] == NULL ) index_map[offset] = new osg::IntArray(); - } geom->setColorIndices( index_map[offset] ); + geom->setColorArray( sources[tmp_el].getVec4Array() ); } - if ( color_source != NULL ) + if ( findInputSourceBySemantic( inputs, "NORMAL", tmp_el, &tmp_input ) ) { - geom->setColorArray( sources[color_source].getVec4Array() ); - geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); - } + if (normal_source != NULL) + osg::notify( osg::WARN )<<"Overwriting vertices input(NORMAL) with input from primitive"<setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); - if ( findInputSourceBySemantic( inputs, "NORMAL", normal_source, &tmp_input ) ) - { - offset = tmp_input->getOffset(); - if ( index_map[offset] == NULL ) - { + int offset = tmp_input->getOffset(); + if ( index_map[offset] == NULL ) index_map[offset] = new osg::IntArray(); - } geom->setNormalIndices( index_map[offset] ); + geom->setNormalArray( sources[tmp_el].getVec3Array() ); } - if ( normal_source ) + int inputNumber = 0; + while ( findInputSourceBySemantic( inputs, "TEXCOORD", texcoord_source, &tmp_input, inputNumber ) ) { - geom->setNormalArray( sources[normal_source].getVec3Array() ); - geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); - } - - int unit = 0; - while ( findInputSourceBySemantic( inputs, "TEXCOORD", texcoord_source, &tmp_input, unit ) ) - { - offset = tmp_input->getOffset(); - set = tmp_input->getSet(); + int offset = tmp_input->getOffset(); + int set = tmp_input->getSet(); + if (NULL != GeometryWrapper) + { + if (GeometryWrapper->_TexcoordSetMap.find(set) != GeometryWrapper->_TexcoordSetMap.end()) + osg::notify( osg::WARN )<<"Duplicate texcoord set: "<< set <_TexcoordSetMap[set] = TexCoordSetsUsed; + } if ( index_map[offset] == NULL ) { index_map[offset] = new osg::IntArray(); } - //this should really be set. Then when you bind_vertex_input you can adjust accordingly for the - //effect which currently only puts textures in texunit 0 - geom->setTexCoordIndices( unit/*set*/, index_map[offset] ); + geom->setTexCoordIndices( TexCoordSetsUsed, index_map[offset] ); if ( texcoord_source != NULL ) { domSourceReader &sc = sources[texcoord_source]; switch( sc.getArrayType() ) { case domSourceReader::Vec2: - geom->setTexCoordArray( unit/*set*/, sc.getVec2Array() ); + geom->setTexCoordArray( TexCoordSetsUsed, sc.getVec2Array() ); break; case domSourceReader::Vec3: - geom->setTexCoordArray( unit/*set*/, sc.getVec3Array() ); + geom->setTexCoordArray( TexCoordSetsUsed, sc.getVec3Array() ); break; default: osg::notify( osg::WARN )<<"Unsupported array type: "<< sc.getArrayType() < #include #include +#include +#include #include #include @@ -49,7 +51,7 @@ using namespace osgdae; // id // name // type -void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, osg::Geode *geode ) +void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, osg::Geode *geode, osg::Geode *cachedGeode ) { if (bm->getTechnique_common() == NULL ) { @@ -61,6 +63,7 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os { osg::Drawable* drawable = geode->getDrawable(i); std::string materialName = drawable->getName(); + ReaderGeometry *cachedGeometry = dynamic_cast(cachedGeode->getDrawable(i)->asGeometry()); domInstance_material_Array &ima = bm->getTechnique_common()->getInstance_material_array(); std::string symbol; @@ -75,24 +78,133 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os if (mat) { // Check material cache if this material already exists + osg::StateSet* ss; domMaterialStateSetMap::iterator iter = materialMap.find( mat ); if ( iter != materialMap.end() ) { // Reuse material - drawable->setStateSet(iter->second); + ss = iter->second; } else { // Create new material - osg::StateSet* ss = new osg::StateSet; + ss = new osg::StateSet; processMaterial(ss, mat); - drawable->setStateSet(ss); materialMap.insert(std::make_pair(mat, ss)); } + drawable->setStateSet(ss); + // Need to process bind_vertex_inputs here + // This all feels like a horrible kludge to me + // I wish somebody with a better knowledge of Collada and OSG than me would have a go at it! + // 1. Clear the texcoord arrays and associated texcoord vertex indices + // from the current (cloned) drawable. + osg::Geometry *clonedGeometry = drawable->asGeometry(); + if (NULL == clonedGeometry) + { + osg::notify( osg::WARN ) << "Failed to convert drawable to geometry object" << std::endl; + break; + } + clonedGeometry->getTexCoordArrayList().clear(); + // 2. For each possible texture unit find the correct texcoord array and + // indices from the original (uncloned) drawable and place in the cloned drawable + // in the correct texture unit slot + std::string TransparencyMapTexcoordName; + osg::Texture2D *Texture; + if (NULL != (Texture = dynamic_cast(ss->getTextureAttribute(AMBIENT_OCCLUSION_UNIT, osg::StateAttribute::TEXTURE)))) + { + std::string AmbientOcclusionTexcoordName = Texture->getName(); + if (!AmbientOcclusionTexcoordName.empty()) + { + domInstance_material::domBind_vertex_input_Array &bvia = ima[j]->getBind_vertex_input_array(); + size_t k; + for ( k = 0; k < bvia.getCount(); k++) + { + if (!strcmp(bvia[k]->getSemantic(), AmbientOcclusionTexcoordName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), "TEXCOORD")) + { + // OK - found the effect name, now see if I can find a matching set in the cachedGeometry + if (NULL != cachedGeometry) + { + std::map::iterator iTr; + if (cachedGeometry->_TexcoordSetMap.end() != (iTr = cachedGeometry->_TexcoordSetMap.find(bvia[k]->getInput_set()))) + { + // Copy the texture cordinates and indices (if any) into the cloned geometry + clonedGeometry->setTexCoordData(AMBIENT_OCCLUSION_UNIT, cachedGeometry->getTexCoordData(iTr->second)); + } + } + break; + } + } + if (k == bvia.getCount()) + osg::notify( osg::WARN ) << "Failed to find matching for " << AmbientOcclusionTexcoordName << std::endl; + } + } + if (NULL != (Texture = dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE)))) + { + std::string MainTextureTexcoordName = Texture->getName(); + if (!MainTextureTexcoordName.empty()) + { + domInstance_material::domBind_vertex_input_Array &bvia = ima[j]->getBind_vertex_input_array(); + size_t k; + for ( k = 0; k < bvia.getCount(); k++) + { + if (!strcmp(bvia[k]->getSemantic(), MainTextureTexcoordName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), "TEXCOORD")) + { + // OK - found the effect name, now see if I can find a matching set in the cachedGeometry + if (NULL != cachedGeometry) + { + std::map::iterator iTr; + if (cachedGeometry->_TexcoordSetMap.end() != (iTr = cachedGeometry->_TexcoordSetMap.find(bvia[k]->getInput_set()))) + { + // Copy the texture cordinates and indices (if any) into the cloned geometry + clonedGeometry->setTexCoordData(MAIN_TEXTURE_UNIT, cachedGeometry->getTexCoordData(iTr->second)); + } + } + break; + } + } + if (k == bvia.getCount()) + { + osg::notify( osg::WARN ) << "Failed to find matching for " << MainTextureTexcoordName << std::endl; + // This may be a departure from the spec. For the time being I am only going to do this + // for the MAIN_TEXTURE_UNIT. + // Not found so just use the first TEXCOORD we have if any. + if (cachedGeometry->_TexcoordSetMap.size() > 0) + clonedGeometry->setTexCoordData(MAIN_TEXTURE_UNIT, cachedGeometry->getTexCoordData(cachedGeometry->_TexcoordSetMap.begin()->second)); + } + } + } + if (NULL != (Texture = dynamic_cast(ss->getTextureAttribute(TRANSPARENCY_MAP_UNIT, osg::StateAttribute::TEXTURE)))) + { + std::string TransparencyMapTexcoordName = Texture->getName(); + if (!TransparencyMapTexcoordName.empty()) + { + domInstance_material::domBind_vertex_input_Array &bvia = ima[j]->getBind_vertex_input_array(); + size_t k; + for ( k = 0; k < bvia.getCount(); k++) + { + if (!strcmp(bvia[k]->getSemantic(), TransparencyMapTexcoordName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), "TEXCOORD")) + { + // OK - found the effect name, now see if I can find a matching set in the cachedGeometry + if (NULL != cachedGeometry) + { + std::map::iterator iTr; + if (cachedGeometry->_TexcoordSetMap.end() != (iTr = cachedGeometry->_TexcoordSetMap.find(bvia[k]->getInput_set()))) + { + // Copy the texture cordinates and indices (if any) into the cloned geometry + clonedGeometry->setTexCoordData(TRANSPARENCY_MAP_UNIT, cachedGeometry->getTexCoordData(iTr->second)); + } + } + break; + } + } + if (k == bvia.getCount()) + osg::notify( osg::WARN ) << "Failed to find matching for " << TransparencyMapTexcoordName << std::endl; + } + } } else { - osg::notify( osg::WARN ) << "Failed to locate wit id " << ima[i]->getTarget().getURI() << std::endl; + osg::notify( osg::WARN ) << "Failed to locate with id " << ima[i]->getTarget().getURI() << std::endl; } break; @@ -184,7 +296,7 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) domProfile_COMMON::domTechnique::domPhong *p = teq->getPhong(); domProfile_COMMON::domTechnique::domBlinn *b = teq->getBlinn(); - ss->setMode( GL_CULL_FACE, GL_TRUE ); + ss->setMode( GL_CULL_FACE, osg::StateAttribute::ON ); // Cull Back faces // See if there are any extra's that are supported by OpenSceneGraph const domExtra_Array& ExtraArray = pc->getExtra_array(); @@ -212,15 +324,15 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) { daeString Value = pAny->getValue(); if (strcmp(Value, "1") == 0) - ss->setMode( GL_CULL_FACE, GL_FALSE ); + ss->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); } } } } } + xsNCName DiffuseTextureName = NULL; osg::ref_ptr< osg::Material > mat = new osg::Material(); - bool insertMat = false; // // elements: // 0..1 @@ -235,39 +347,65 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 if ( b != NULL ) { - bool tmp; - tmp = processColorOrTextureType( b->getEmission(), osg::Material::EMISSION, mat.get() ); - insertMat = insertMat || tmp; - - tmp = processColorOrTextureType( b->getAmbient(), osg::Material::AMBIENT, mat.get() ); - insertMat = insertMat || tmp; - - osg::StateAttribute *sa = NULL; - tmp = processColorOrTextureType( b->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &sa ); - insertMat = insertMat || tmp; - if ( sa != NULL ) - { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa ); - } + osg::StateAttribute *EmissionStateAttribute = NULL; + osg::StateAttribute *AmbientStateAttribute = NULL; + osg::StateAttribute *DiffuseStateAttribute = NULL; + processColorOrTextureType( b->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); + if (NULL != EmissionStateAttribute) + osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; - tmp = processColorOrTextureType( b->getSpecular(), osg::Material::SPECULAR, mat.get(), b->getShininess(), NULL, true ); - insertMat = insertMat || tmp; - - osg::StateAttribute *sa2 = NULL; - sa2 = processTransparencySettings(b->getTransparent(), b->getTransparency(), ss); - if ( sa2 != NULL ) + processColorOrTextureType( b->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute ); + + processColorOrTextureType( b->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); + if ( DiffuseStateAttribute != NULL ) { - if ( sa == NULL ) + if ( AmbientStateAttribute != NULL ) { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa2 ); + // Set the ambient and diffuse colour white so that the incoming fragment colour ends up as a + // lit white colour. I modulate both textures onto this to approximate the lighting equation. + // Using a zero diffuse and then an ADD of the diffuse texture seems overlit to me. + mat->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + // Use the ambient texture map as an occlusion map. + ss->setTextureMode( AMBIENT_OCCLUSION_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(AMBIENT_OCCLUSION_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( AMBIENT_OCCLUSION_UNIT, AmbientStateAttribute ); + // Modulate in the diffuse texture + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, DiffuseStateAttribute ); } else { - osg::notify( osg::WARN ) << "Already have a texture in the diffuse channel" << std::endl; + // Set the diffuse colour white so that the incoming fragment colour ends up as the global diffuse lighting colour + // plus any constant ambient contribution after the lighting calculation. This means that I am modulating the the + // ambient with the texture as well but I cannot see a way of avoiding that. + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, DiffuseStateAttribute ); } + + // Save the texture name for later + DiffuseTextureName = b->getDiffuse()->getTexture()->getTexture(); } + else + { + if ( NULL != AmbientStateAttribute ) + osg::notify( osg::WARN ) << "Ambient occlusion map only supported when diffuse texture also specified" << std::endl; + } + + if(processColorOrTextureType( b->getSpecular(), osg::Material::SPECULAR, mat.get(), b->getShininess() ) && (NULL != DiffuseStateAttribute) ) + { + // Diffuse texture will defeat specular highlighting + // So postpone specular - Not sure if I should do this here + // beacuse it will override any global light model states + osg::LightModel* lightmodel = new osg::LightModel; + lightmodel->setColorControl(osg::LightModel::SEPARATE_SPECULAR_COLOR); + ss->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); + } + + processTransparencySettings(b->getTransparent(), b->getTransparency(), ss, mat.get(), DiffuseTextureName ); } // // elements: @@ -283,46 +421,65 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 else if ( p != NULL ) { - bool tmp; - tmp = processColorOrTextureType( p->getEmission(), osg::Material::EMISSION, mat.get() ); - insertMat = insertMat || tmp; + osg::StateAttribute *EmissionStateAttribute = NULL; + osg::StateAttribute *AmbientStateAttribute = NULL; + osg::StateAttribute *DiffuseStateAttribute = NULL; + processColorOrTextureType( p->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); + if (NULL != EmissionStateAttribute) + osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; - osg::StateAttribute *sa1 = NULL; - tmp = processColorOrTextureType( p->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &sa1 ); - insertMat = insertMat || tmp; - if ( sa1 != NULL ) - { - ss->setTextureMode( 1, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa1 ); - } + processColorOrTextureType( p->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute ); - osg::StateAttribute *sa = NULL; - tmp = processColorOrTextureType( p->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &sa ); - insertMat = insertMat || tmp; - if ( sa != NULL ) + processColorOrTextureType( p->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); + if ( DiffuseStateAttribute != NULL ) { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa ); - } - - tmp = processColorOrTextureType( p->getSpecular(), osg::Material::SPECULAR, mat.get(), p->getShininess() ); - insertMat = insertMat || tmp; - - osg::StateAttribute *sa2 = NULL; - sa2 = processTransparencySettings(p->getTransparent(), p->getTransparency(), ss); - if ( sa2 != NULL ) - { - if ( sa == NULL ) + if ( AmbientStateAttribute != NULL ) { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa2 ); + // Set the ambient and diffuse colour white so that the incoming fragment colour ends up as a + // lit white colour. I modulate both textures onto this to approximate the lighting equation. + // Using a zero diffuse and then an ADD of the diffuse texture seems overlit to me. + mat->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + // Use the ambient texture map as an occlusion map. + ss->setTextureMode( AMBIENT_OCCLUSION_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(AMBIENT_OCCLUSION_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( AMBIENT_OCCLUSION_UNIT, AmbientStateAttribute ); + // Modulate in the diffuse texture + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, DiffuseStateAttribute ); } else { - osg::notify( osg::WARN ) << "Already have a texture in the diffuse channel" << std::endl; + // Set the diffuse colour white so that the incoming fragment colour ends up as the global diffuse lighting colour + // plus any constant ambient contribution after the lighting calculation. This means that I am modulating the the + // ambient with the texture as well but I cannot see a way of avoiding that. + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, DiffuseStateAttribute ); } + + // Save the texture name for later + DiffuseTextureName = p->getDiffuse()->getTexture()->getTexture(); + } + else + { + if ( NULL != AmbientStateAttribute ) + osg::notify( osg::WARN ) << "Ambient occlusion map only supported when diffuse texture also specified" << std::endl; + } + + if(processColorOrTextureType( p->getSpecular(), osg::Material::SPECULAR, mat.get(), p->getShininess() ) && (NULL != DiffuseStateAttribute) ) + { + // Diffuse texture will defeat specular highlighting + // So postpone specular - Not sure if I should do this here + // beacuse it will override any global light model states + osg::LightModel* lightmodel = new osg::LightModel; + lightmodel->setColorControl(osg::LightModel::SEPARATE_SPECULAR_COLOR); + ss->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); } + processTransparencySettings(p->getTransparent(), p->getTransparency(), ss, mat.get(), DiffuseTextureName ); } // // elements: @@ -336,37 +493,55 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 else if ( l != NULL ) { - bool tmp; - tmp = processColorOrTextureType( l->getEmission(), osg::Material::EMISSION, mat.get() ); - insertMat = insertMat || tmp; + osg::StateAttribute *EmissionStateAttribute = NULL; + osg::StateAttribute *AmbientStateAttribute = NULL; + osg::StateAttribute *DiffuseStateAttribute = NULL; + processColorOrTextureType( l->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); + if (NULL != EmissionStateAttribute) + osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; - tmp = processColorOrTextureType( l->getAmbient(), osg::Material::AMBIENT, mat.get() ); - insertMat = insertMat || tmp; - - osg::StateAttribute *sa = NULL; - tmp = processColorOrTextureType( l->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &sa ); - insertMat = insertMat || tmp; - if ( sa != NULL ) - { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa ); - } + processColorOrTextureType( l->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute); - osg::StateAttribute *sa2 = NULL; - sa2 = processTransparencySettings(l->getTransparent(), l->getTransparency(), ss); - if ( sa2 != NULL ) + processColorOrTextureType( l->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); + if ( DiffuseStateAttribute != NULL ) { - if ( sa == NULL ) + if ( AmbientStateAttribute != NULL ) { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa2 ); + // Set the ambient and diffuse colour white so that the incoming fragment colour ends up as a + // lit white colour. I modulate both textures onto this to approximate the lighting equation. + // Using a zero diffuse and then an ADD of the diffuse texture seems overlit to me. + mat->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + // Use the ambient texture map as an occlusion map. + ss->setTextureMode( AMBIENT_OCCLUSION_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(AMBIENT_OCCLUSION_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( AMBIENT_OCCLUSION_UNIT, AmbientStateAttribute ); + // Modulate in the diffuse texture + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, DiffuseStateAttribute ); } else { - osg::notify( osg::WARN ) << "Already have a texture in the diffuse channel" << std::endl; + // Set the diffuse colour white so that the incoming fragment colour ends up as the global diffuse lighting colour + // plus any constant ambient contribution after the lighting calculation. This means that I am modulating the the + // ambient with the texture as well but I cannot see a way of avoiding that. + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::MODULATE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, DiffuseStateAttribute ); } + + // Save the texture name for later + DiffuseTextureName = l->getDiffuse()->getTexture()->getTexture(); } - + else + { + if ( NULL != AmbientStateAttribute ) + osg::notify( osg::WARN ) << "Ambient occlusion map only supported when diffuse texture also specified" << std::endl; + } + + processTransparencySettings(l->getTransparent(), l->getTransparency(), ss, mat.get(), DiffuseTextureName ); } // // elements: @@ -378,20 +553,25 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 else if ( c != NULL ) { - insertMat = processColorOrTextureType( c->getEmission(), osg::Material::EMISSION, mat.get() ); - - osg::StateAttribute *sa2 = NULL; - sa2 = processTransparencySettings(c->getTransparent(), c->getTransparency(), ss); - if ( sa2 != NULL ) + osg::StateAttribute *sa = NULL; + processColorOrTextureType( c->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &sa ); + if ( sa != NULL ) { - ss->setTextureMode( 0, GL_TEXTURE_2D, GL_TRUE ); - ss->setTextureAttribute( 0, sa2 ); + ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); + ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::REPLACE) ); + ss->setTextureAttribute( MAIN_TEXTURE_UNIT, sa ); } + + // Use the emission colour as the main colour in transparency calculations + mat->setDiffuse(osg::Material::FRONT_AND_BACK, mat->getEmission(osg::Material::FRONT_AND_BACK)); + + processTransparencySettings(c->getTransparent(), c->getTransparency(), ss, mat.get(), NULL ); + + // Kill the lighting + mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0)); + mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0)); } - if ( insertMat ) - { - ss->setAttribute( mat.get() ); - } + ss->setAttribute( mat.get() ); } // colorOrTexture @@ -439,7 +619,13 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c } else if (cot->getTexture() != NULL) { - osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; + if (sa != NULL) + { + *sa = processTexture( cot->getTexture() ); + retVal = true; + } + else + osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; } else { @@ -463,11 +649,17 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c retVal = true; } } - else if (cot->getTexture() != NULL && sa != NULL) + else if (cot->getTexture() != NULL) { - *sa = processTexture( cot->getTexture() ); - //osg::notify( osg::WARN ) << "Currently no support for in Ambient channel " << std::endl; - } + if (sa != NULL) + *sa = processTexture( cot->getTexture() ); + else + { + osg::notify( osg::WARN ) << "Currently no support for in Ambient channel " << std::endl; + mat->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4( 0.2f, 0.2f, 0.2f, 1.0f ) ); + } + retVal = true; + } else { osg::notify( osg::WARN ) << "Missing , or in Ambient channel " << std::endl; @@ -481,9 +673,15 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( f4[0], f4[1], f4[2], f4[3] ) ); retVal = true; } - else if ( cot->getTexture() != NULL && sa != NULL ) + else if ( cot->getTexture() != NULL) { - *sa = processTexture( cot->getTexture() ); + if (sa != NULL) + *sa = processTexture( cot->getTexture() ); + else + { + osg::notify( osg::WARN ) << "Currently no support for in Diffuse channel " << std::endl; + mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 0.8f, 0.8f, 0.8f, 1.0f ) ); + } domExtra *extra = cot->getTexture()->getExtra(); if ( extra != NULL && extra->getType() != NULL && strcmp( extra->getType(), "color" ) == 0 ) { @@ -498,11 +696,11 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c std::istringstream diffuse_colour((const char *)dcol->getValue()); diffuse_colour >> col.r() >> col.g() >> col.b() >> col.a(); mat->setDiffuse( osg::Material::FRONT_AND_BACK, col ); - retVal = true; break; } } } + retVal = true; } else if (cot->getParam() != NULL) { @@ -554,7 +752,6 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c shininess *= 128.0f; } mat->setShininess( osg::Material::FRONT_AND_BACK, shininess ); - retVal = true; } } @@ -735,7 +932,7 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ return NULL; } //Got a sampler and a surface and an imaged. Time to create the texture stuff for osg - osg::ref_ptr img; + osg::ref_ptr img = NULL; if ( dImg->getInit_from() != NULL ) { // daeURI uri = dImg->getInit_from()->getValue(); @@ -928,6 +1125,8 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ t2D->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); } + // Store the texcoord name in the texture object + t2D->setName(tex->getTexcoord()); return t2D; } @@ -948,33 +1147,28 @@ common_color_or_texture_type entry, along with a description of how transparency Collada Digital Asset Schema Release 1.5.0 Release Notes The element’s opaque attribute now allows, in addition to A_ONE and RGB_ZERO, the following values: -• A_ZERO (the default): Takes the transparency information from the color’s alpha channel, where the value 0.0 is opaque. -• RGB_ONE: Takes the transparency information from the color’s red, green, and blue channels, where the value 1.0 is opaque, -with each channel modulated independently. +• A_ZERO: Takes the transparency information from the color’s alpha channel, where the value 0.0 is opaque. +• RGB_ONE: Takes the transparency information from the color’s red, green, and blue channels, where the value 1.0 +* is opaque, with each channel modulated independently. +* When we update to a version of the dom using that schema we will need to modify the code below */ -osg::StateAttribute *daeReader::processTransparencySettings( domCommon_transparent_type *ctt, domCommon_float_or_param_type *pTransparency, osg::StateSet *ss ) +void daeReader::processTransparencySettings( domCommon_transparent_type *ctt, + domCommon_float_or_param_type *pTransparency, + osg::StateSet *ss, + osg::Material *material, + xsNCName diffuseTextureName ) { if (NULL == ctt && NULL == pTransparency) - return NULL; + return; if (ctt && ctt->getTexture() != NULL) { - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // The transparency functionality needs to be put in here as well - // but I suspect it will neeed a shader program to acheive it. - // Whenever someone gets round to this the default setting stuff in the - // color path below needs making common to both paths. This will involve some - // reorganisation of the code. - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - osg::StateAttribute *sa = NULL; - sa = processTexture( ctt->getTexture() ); - osg::BlendFunc *bf = new osg::BlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - ss->setAttribute( bf ); - ss->setMode( GL_BLEND, GL_TRUE ); - ss->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); - ss->setRenderBinDetails( 10, "DepthSortedBin" ); - return sa; + if (strcmp( ctt->getTexture()->getTexture(), diffuseTextureName)) + { + osg::notify( osg::WARN ) << "Currently no support for different textures in diffuse and transparent channels." << std::endl; + return; + } } // Fix up defaults acoording to 1.4.1 release notes @@ -1026,34 +1220,181 @@ osg::StateAttribute *daeReader::processTransparencySettings( domCommon_transpare } } - if (FX_OPAQUE_ENUM_A_ONE == Opaque) + if (NULL != ctt || NULL != pTransparency) { - if (Transparency * f4[3] > 0.99f) - // Material is really opaque so dont put it in the transparent bin - return NULL; - } - else - { - if ((Transparency * f4[0] < 0.01f) && - (Transparency * f4[1] < 0.01f) && - (Transparency * f4[2] < 0.01f) && - (Transparency * f4[3] < 0.01f)) - // Material is really opaque so dont put it in the transparent bin - return NULL; - } + int SourceBlendFactor; + int DestBlendFactor; + bool SwitchOnTheBlender = false; + if (m_StrictTransparency) + { + // Process transparent and transparency settings accroding to a strict interpretation of the spec + // See https://collada.org/public_forum/viewtopic.php?f=12&t=1210 + SwitchOnTheBlender = true; + switch(Opaque) + { + /* + case FX_OPAQUE_ENUM_RGB_ONE: + if (ctt->getTexture() != NULL) + { + SourceBlendFactor = GL_SRC_COLOR; + DestBlendFactor = GL_ONE_MINUS_SRC_COLOR; + } + else + { + SourceBlendFactor = GL_CONSTANT_COLOR; + DestBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; + } + break; + case FX_OPAQUE_ALPHA_ZERO: + if (ctt->getTexture() != NULL) + { + SourceBlendFactor = GL_ONE_MINUS_SRC_ALPHA; + DestBlendFactor = GL_SRC_ALPHA; + } + else + { + SourceBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; + DestBlendFactor = GL_CONSTANT_ALPHA; + } + break; + */ + case FX_OPAQUE_ENUM_RGB_ZERO: + if (ctt->getTexture() != NULL) + { + SourceBlendFactor = GL_ONE_MINUS_SRC_COLOR; + DestBlendFactor = GL_SRC_COLOR; + } + else + { + SourceBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; + DestBlendFactor = GL_CONSTANT_COLOR; + } + break; + default: + if (ctt->getTexture() != NULL) + { + SourceBlendFactor = GL_SRC_ALPHA; + DestBlendFactor = GL_ONE_MINUS_SRC_ALPHA; + } + else + { + SourceBlendFactor = GL_CONSTANT_ALPHA; + DestBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; + } + break; + } + } + else + { + // Jump through various hoops to accomodate the multiplicity of different ways + // that various people have interpreted the specification + // I assume that the presence of either a or a element + // means that the user may want some kind of alpha blending + bool HaveDiffuseTexture = false; + bool HaveTranslucentDiffuseTexture = false; + // Unfortunately isImageTransparent only works for A_ONE_OPAQUE + if ((NULL != ss) && + (HaveDiffuseTexture = (GL_TRUE == ss->getTextureMode(MAIN_TEXTURE_UNIT, GL_TEXTURE_2D))) && + (NULL != dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE))) && + (NULL != dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE))->getImage()) && + (dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE))->getImage()->isImageTranslucent())) + HaveTranslucentDiffuseTexture = true; + osg::Vec4 Diffuse; + if (material) + Diffuse = material->getDiffuse(osg::Material::FRONT_AND_BACK); - osg::BlendColor *bc = new osg::BlendColor(); - bc->setConstantColor(osg::Vec4( f4[0] * Transparency, f4[1] * Transparency, f4[2] * Transparency, f4[3] * Transparency )); - ss->setAttribute( bc ); - osg::BlendFunc *bf; - if (FX_OPAQUE_ENUM_A_ONE == Opaque) - bf = new osg::BlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); - else - bf = new osg::BlendFunc(GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_COLOR); - ss->setAttribute( bf ); - ss->setMode( GL_BLEND, GL_TRUE ); + // Determine whether or not to switch on the blender and which blending factors to use. + // I switch the blender on if the supplied (or default) and elements work out as non opaque, + // or if they work out opaque and I have a translucent texture in the MAIN_TEXTURE_UNIT or a non opaque value in the diffuse colour + switch(Opaque) + { + /* + case FX_OPAQUE_ENUM_RGB_ONE: + if ((Transparency * f4[0] > 0.99f) && + (Transparency * f4[1] > 0.99f) && + (Transparency * f4[2] > 0.99f)) + { + SourceBlendFactor = GL_SRC_COLOR; + DestBlendFactor = GL_ONE_MINUS_SRC_COLOR; + // It would be nice to check for a translucent texture here as well + if (!HaveDiffuseTexture && (Diffuse.r() < 0.99f) && (Diffuse.g() < 0.99f) && (Diffuse.b() < 0.99f)) + SwitchOnTheBlender = true; + } + else + { + SourceBlendFactor = GL_CONSTANT_COLOR; + DestBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; + SwitchOnTheBlender = true; + } + break; + case FX_OPAQUE_ALPHA_ZERO: + if (Transparency * f4[3] < 0.01f) + { + SourceBlendFactor = GL_ONE_MINUS_SRC_ALPHA; + DestBlendFactor = GL_SRC_ALPHA; + // It would be nice to check for a translucent texture here as well + if (Diffuse.a() > 0.01f) + SwitchOnTheBlender = true; + } + else + { + SourceBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; + DestBlendFactor = GL_CONSTANT_ALPHA; + SwitchOnTheBlender = true; + } + break; + */ + case FX_OPAQUE_ENUM_RGB_ZERO: + if ((Transparency * f4[0] < 0.01f) && + (Transparency * f4[1] < 0.01f) && + (Transparency * f4[2] < 0.01f)) + { + SourceBlendFactor = GL_ONE_MINUS_SRC_COLOR; + DestBlendFactor = GL_SRC_COLOR; + // It would be nice to check for a translucent texture here as well + // if (!HaveDiffuseTexture && (Diffuse.r() > 0.01f) && (Diffuse.g() > 0.01f) && (Diffuse.b() > 0.01f)) + // SwitchOnTheBlender = true; + } + else + { + SourceBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; + DestBlendFactor = GL_CONSTANT_COLOR; + SwitchOnTheBlender = true; + } + break; + default: + if (Transparency * f4[3] > 0.99f) + { + SourceBlendFactor = GL_SRC_ALPHA; + DestBlendFactor = GL_ONE_MINUS_SRC_ALPHA; + if (HaveTranslucentDiffuseTexture || (Diffuse.a() < 0.99f)) + SwitchOnTheBlender = true; + } + else + { + SourceBlendFactor = GL_CONSTANT_ALPHA; + DestBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; + SwitchOnTheBlender = true; + } + break; + } + } + if (SwitchOnTheBlender) + { + if ((SourceBlendFactor == GL_CONSTANT_COLOR) || + (SourceBlendFactor == GL_ONE_MINUS_CONSTANT_COLOR) || + (SourceBlendFactor == GL_CONSTANT_ALPHA) || + (SourceBlendFactor == GL_ONE_MINUS_CONSTANT_ALPHA)) + { + osg::BlendColor *bc = new osg::BlendColor(); + bc->setConstantColor(osg::Vec4( f4[0] * Transparency, f4[1] * Transparency, f4[2] * Transparency, f4[3] * Transparency )); + ss->setAttribute( bc ); + } + osg::BlendFunc *bf = new osg::BlendFunc(SourceBlendFactor, DestBlendFactor); + ss->setAttribute( bf ); + ss->setMode( GL_BLEND, osg::StateAttribute::ON ); - ss->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); - ss->setRenderBinDetails( 10, "DepthSortedBin" ); - return NULL; + ss->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); + } + } } diff --git a/src/osgPlugins/dae/daeReader.cpp b/src/osgPlugins/dae/daeReader.cpp index 990cbd3c3..82082b2c1 100644 --- a/src/osgPlugins/dae/daeReader.cpp +++ b/src/osgPlugins/dae/daeReader.cpp @@ -21,7 +21,7 @@ using namespace osgdae; -daeReader::daeReader(DAE *dae_) : +daeReader::daeReader(DAE *dae_, bool strictTransparency) : m_AssetUnitName("meter"), m_AssetUnitMeter(1.0), m_AssetUp_axis(UPAXISTYPE_Y_UP), @@ -30,7 +30,8 @@ daeReader::daeReader(DAE *dae_) : m_numlights(0), currentInstance_effect(NULL), currentEffect(NULL), - m_AuthoringTool(UNKNOWN) + m_AuthoringTool(UNKNOWN), + m_StrictTransparency(strictTransparency) { } diff --git a/src/osgPlugins/dae/daeReader.h b/src/osgPlugins/dae/daeReader.h index ea55a9f3e..7473d4be3 100644 --- a/src/osgPlugins/dae/daeReader.h +++ b/src/osgPlugins/dae/daeReader.h @@ -131,7 +131,7 @@ inline osg::Matrix parseMatrixString(const std::string& valueAsString) */ class daeReader { public: - daeReader(DAE *dae_); + daeReader(DAE *dae_, bool strictTransparency = false); virtual ~daeReader(); bool convert( const std::string &fileURI ); @@ -143,6 +143,14 @@ public: float m_AssetUnitMeter; domUpAxisType m_AssetUp_axis; + // Texture unit useage + enum + { + AMBIENT_OCCLUSION_UNIT = 0, + MAIN_TEXTURE_UNIT, + TRANSPARENCY_MAP_UNIT + }; + protected: //scene processing osg::Node* processVisualScene( domVisual_scene *scene ); @@ -163,6 +171,11 @@ protected: osg::Node* processOsgSequence(domTechnique* teq); //geometry processing + class ReaderGeometry : public osg::Geometry + { + public: + std::map _TexcoordSetMap; + }; osg::Geode* processInstanceGeometry( domInstance_geometry *ig ); osg::Geode* processGeometry( domGeometry *geo ); osg::Geode* processInstanceController( domInstance_controller *ictrl ); @@ -178,13 +191,13 @@ protected: void processPolylist(osg::Geode* geode, domPolylist *group, SourceMap &sources ); - void resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry *&geom, + void resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry *geom, SourceMap &sources, IndexMap &index_map ); void processP( domP *p, osg::Geometry *&geom, IndexMap &index_map, osg::DrawArrayLengths* dal/*GLenum mode*/ ); //material/effect processing - void processBindMaterial( domBind_material *bm, domGeometry *geom, osg::Geode *geode ); + void processBindMaterial( domBind_material *bm, domGeometry *geom, osg::Geode *geode, osg::Geode *cachedGeode ); void processMaterial(osg::StateSet *ss, domMaterial *mat ); void processEffect(osg::StateSet *ss, domEffect *effect ); void processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ); @@ -194,7 +207,11 @@ protected: domCommon_float_or_param_type *fop = NULL, osg::StateAttribute **sa = NULL, bool normalizeShininess=false); - osg::StateAttribute *processTransparencySettings( domCommon_transparent_type *ctt, domCommon_float_or_param_type *pTransparency, osg::StateSet *ss ); + void processTransparencySettings( domCommon_transparent_type *ctt, + domCommon_float_or_param_type *pTransparency, + osg::StateSet *ss, + osg::Material *material, + xsNCName diffuseTextureName ); bool GetFloat4Param(xsNCName Reference, domFloat4 &f4); bool GetFloatParam(xsNCName Reference, domFloat &f); @@ -232,6 +249,7 @@ protected: GOOGLE_SKETCHUP }; AuthoringTool m_AuthoringTool; + bool m_StrictTransparency; }; }