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; }; }