Node *
ColladaVisualScene::createInstance(ColladaInstanceElement *colInstElem)
{
    OSG_COLLADA_LOG(("ColladaVisualScene::createInstance\n"));

    ColladaInstanceVisualSceneRefPtr colInstVisScene =
        dynamic_cast<ColladaInstanceVisualScene *>(colInstElem);

    domVisual_sceneRef   visScene = getDOMElementAs<domVisual_scene>();
    const domNode_Array &nodes    = visScene->getNode_array         ();

    for(UInt32 i = 0; i < nodes.getCount(); ++i)
    {
        ColladaNodeRefPtr colNode = getUserDataAs<ColladaNode>(nodes[i]);

        if(colNode == NULL)
        {
            colNode = dynamic_pointer_cast<ColladaNode>(
                ColladaElementFactory::the()->create(nodes[i], getGlobal()));

            colNode->setVisualScene(this);
            colNode->read();
        }

        if(nodes.getCount() > 1)
        {
            if(_RootN == NULL)
            {
                GroupUnrecPtr group = Group::create();
                _RootN = makeNodeFor(group);
            }

            Node *childN = colNode->getTopNode();

            if(childN->getParent() != NULL)
            {
                SWARNING << "ColladaVisualScene::createInstance: <node> [" << i
                         << "] already has a parent." << std::endl;
            }

            _RootN->addChild(childN);
        }
        else
        {
            Node *childN = colNode->getTopNode();

            if(childN->getParent() != NULL)
            {
                SWARNING << "ColladaVisualScene::createInstance: <node> [" << i
                         << "] already has a parent." << std::endl;
            }

            _RootN = childN;
        }
    }

    editInstStore().push_back(_RootN);

    return _RootN;
}
Node *
ColladaNode::createInstance(ColladaInstanceElement *colInstElem)
{
    OSG_COLLADA_LOG(("ColladaNode::createInstance\n"));

    NodeUnrecPtr retVal = NULL;

   // if(getInstStore().empty() == true)
   // {
   //     retVal = _topN;
   // }
   // else
   // {
        retVal = cloneTree(_topN);
   // }

    editInstStore().push_back(retVal);

    return retVal;
}
Material *
ColladaMaterial::createInstance(ColladaInstInfo *colInstInfo)
{
    OSG_COLLADA_LOG(("ColladaMaterial::createInstance\n"));

    MaterialUnrecPtr            retVal        = NULL;
    domMaterialRef              material      = getDOMElementAs<domMaterial>();
    domInstance_effectRef       instEffect    = material->getInstance_effect();
    ColladaInstanceEffectRefPtr colInstEffect =
        getUserDataAs<ColladaInstanceEffect>(instEffect);

    if(getInstStore().empty() == true)
    {
        OSG_ASSERT(colInstEffect                  != NULL);
        OSG_ASSERT(colInstEffect->getTargetElem() != NULL);

        ColladaInstInfoRefPtr colEffectInstInfo =
            ColladaEffect::ColladaEffectInstInfo::create(this, colInstEffect);

        retVal = colInstEffect->getTargetElem()->createInstance(
            colEffectInstInfo);

        if(getGlobal()->getOptions()->getCreateNameAttachments() == true &&
           material->getName()                                   != NULL   )
        {
            setName(retVal, material->getName());
        }

        editInstStore().push_back(retVal);
    }
    else
    {
        retVal = dynamic_pointer_cast<Material>(getInstStore()[0]);

        getGlobal()->getStatCollector()->getElem(
            ColladaGlobal::statNMaterialUsed)->inc();
    }

    return retVal;
}
Node *
ColladaNode::createInstanceJoint(ColladaInstInfo *colInstInfo, domNode *node)
{
    NodeUnrecPtr retVal    = NULL;
    bool         startSkel = false;

    // if there is a ColladaInstanceElement someone tried to use <instance_node>
    // with this joint as target - this is currently not supported
    if(colInstInfo->getColInst() != NULL)
    {
        SWARNING << "ColladaNode::createInstanceJoint: <instance_node> with "
                 << "target <node> of type JOINT not supported." << std::endl;
        return retVal;
    }

    NodeLoaderState *state =
        getGlobal()->getLoaderStateAs<NodeLoaderState>(_loaderStateName);
    OSG_ASSERT(state != NULL);

    state->pushNodePath(node->getId() != NULL ? node->getId() : "");
    state->dumpNodePath();

    InstData instData;
    instData._nodePath = state->getNodePath();
    instData._skel     = state->getSkeleton();

    if(instData._skel == NULL)
    {
        startSkel      = true;
        instData._skel = Skeleton::create();

        state->setSkeleton(instData._skel);
        state->setJointId (0             );

        OSG_COLLADA_LOG(("ColladaNode::createInstanceJoint: id [%s] "
                         "root joint\n", node->getId()));
    }
    else
    {
        state->setJointId(state->getJointId() + 1);

        OSG_COLLADA_LOG(("ColladaNode::createInstanceJoint: id [%s] "
                         "joint [%d]\n", node->getId(), state->getJointId()));
    }

    const daeElementRefArray &contents = node->getContents();

    for(UInt32 i = 0; i < contents.getCount(); ++i)
    {
        switch(contents[i]->getElementType())
        {
        case COLLADA_TYPE::LOOKAT:
            handleLookAt(daeSafeCast<domLookat>(contents[i]), instData);
            break;

        case COLLADA_TYPE::MATRIX:
            handleMatrix(daeSafeCast<domMatrix>(contents[i]), instData);
            break;

        case COLLADA_TYPE::ROTATE:
            handleRotate(daeSafeCast<domRotate>(contents[i]), instData);
            break;

        case COLLADA_TYPE::SCALE:
            handleScale(daeSafeCast<domScale>(contents[i]), instData);
            break;

        case COLLADA_TYPE::SKEW:
            handleSkew(daeSafeCast<domSkew>(contents[i]), instData);
            break;

        case COLLADA_TYPE::TRANSLATE:
            handleTranslate(daeSafeCast<domTranslate>(contents[i]), instData);
            break;
        }
    }

    // assert top and bottom are both set or both unset
    OSG_ASSERT((instData._topN != NULL && instData._bottomN != NULL) ||
               (instData._topN == NULL && instData._bottomN == NULL)   );

    if(instData._topN == NULL && instData._bottomN == NULL)
    {
        // no xforms were created, make a SkeletonJoint for this <node>

        SkeletonJointUnrecPtr joint = SkeletonJoint::create();

        joint->setJointId(state->getJointId());

        instData._topN    = makeNodeFor(joint);
        instData._bottomN = instData._topN;

        if(getGlobal()->getOptions()->getCreateNameAttachments() == true &&
           node->getName()                                       != NULL   )
        {
            setName(instData._topN, node->getName());
        }
    }
    else if(getGlobal()->getOptions()->getMergeTransforms() == false)
    {
        // when not merging transforms add SkeletonJoint core now

        SkeletonJointUnrecPtr joint  = SkeletonJoint::create();
        NodeUnrecPtr          jointN = makeNodeFor(joint);

        joint->setJointId(state->getJointId());

        instData._bottomN->addChild(jointN);
        instData._bottomN = jointN;
    }

    if(startSkel == true)
    {
        // add a transform for the world matrix up to this node to put
        // the Skeleton in the correct coordinate system

        TransformUnrecPtr xform  = Transform::create();
        NodeUnrecPtr      xformN = makeNodeFor(xform);

        xform->setMatrix(state->getWorldMatrix());

        xformN->addChild(instData._topN);
        instData._topN = xformN;

        if(getGlobal()->getOptions()->getCreateNameAttachments() == true)
            setName(xformN, "SkeletonWorldMatrix");
    }

    // update world matrix before we instantiate child nodes, etc.
    state->pushMatrix(instData._localMatrix);

    // add <node> child elements
    const domNode_Array &nodes = node->getNode_array();

    for(UInt32 i = 0; i < nodes.getCount(); ++i)
        handleNode(nodes[i], instData);

    // add <instance_node> child elements
    const domInstance_node_Array &instNodes =
        node->getInstance_node_array();

    for(UInt32 i = 0; i < instNodes.getCount(); ++i)
        handleInstanceNode(instNodes[i], instData);

    // we don't handle other <instance_*> tags here, it does not
    // make sense to have them inside a skeleton

    editInstStore().push_back(instData._topN);
    _instDataStore .push_back(instData      );
    retVal = instData._topN;

    if(startSkel == true)
    {
        instData._skel->pushToRoots(instData._topN);

        state->setSkeleton(NULL);
        state->setJointId (SkeletonJoint::INVALID_JOINT_ID);
    }

    state->popMatrix  ();
    state->popNodePath();

    return retVal;
}
Node *
ColladaNode::createInstanceNode(ColladaInstInfo *colInstInfo, domNode *node)
{
    OSG_COLLADA_LOG(("ColladaNode::createInstanceNode id [%s]\n",
                     node->getId()));

    NodeLoaderState *state =
        getGlobal()->getLoaderStateAs<NodeLoaderState>(_loaderStateName);
    OSG_ASSERT(state != NULL);

    state->pushNodePath(node->getId() != NULL ? node->getId() : "");
    state->dumpNodePath();

    NodeUnrecPtr retVal = NULL;
    InstData     instData;

    instData._nodePath = state->getNodePath();
    const daeElementRefArray &contents = node->getContents();

    // read "transform" child elements in the order
    // they occur in the document
    for(UInt32 i = 0; i < contents.getCount(); ++i)
    {
        switch(contents[i]->getElementType())
        {
        case COLLADA_TYPE::LOOKAT:
            handleLookAt(daeSafeCast<domLookat>(contents[i]), instData);
            break;

        case COLLADA_TYPE::MATRIX:
            handleMatrix(daeSafeCast<domMatrix>(contents[i]), instData);
            break;

        case COLLADA_TYPE::ROTATE:
            handleRotate(daeSafeCast<domRotate>(contents[i]), instData);
            break;

        case COLLADA_TYPE::SCALE:
            handleScale(daeSafeCast<domScale>(contents[i]), instData);
            break;

        case COLLADA_TYPE::SKEW:
            handleSkew(daeSafeCast<domSkew>(contents[i]), instData);
            break;

        case COLLADA_TYPE::TRANSLATE:
            handleTranslate(daeSafeCast<domTranslate>(contents[i]), instData);
            break;
        }
    }

    // assert top and bottom are both set or both unset
    OSG_ASSERT((instData._topN != NULL && instData._bottomN != NULL) ||
               (instData._topN == NULL && instData._bottomN == NULL)   );

    // if no xforms were created make a group for this <node>
    if(instData._topN == NULL && instData._bottomN == NULL)
    {
        instData._topN    = makeCoredNode<Group>();
        instData._bottomN = instData._topN;

        if(getGlobal()->getOptions()->getCreateNameAttachments() == true &&
           node->getName()                                       != NULL   )
        {
            setName(instData._topN, node->getName());
        }
    }

    // update world matrix before we instantiate child nodes, etc.
    state->pushMatrix(instData._localMatrix);

    // add <node> child elements
    const domNode_Array &nodes = node->getNode_array();

    for(UInt32 i = 0; i < nodes.getCount(); ++i)
        handleNode(nodes[i], instData);

    // add <instance_node> child elements
    const domInstance_node_Array &instNodes =
        node->getInstance_node_array();

    for(UInt32 i = 0; i < instNodes.getCount(); ++i)
        handleInstanceNode(instNodes[i], instData);

    // add <instance_light> child elements
    const domInstance_light_Array &instLights =
        node->getInstance_light_array();

    for(UInt32 i = 0; i < instLights.getCount(); ++i)
        handleInstanceLight(instLights[i], instData);

    // add <instance_geometry> child elements
    const domInstance_geometry_Array &instGeos =
        node->getInstance_geometry_array();

    for(UInt32 i = 0; i < instGeos.getCount(); ++i)
        handleInstanceGeometry(instGeos[i], instData);

    // add <instance_controller> child elemnts
    const domInstance_controller_Array &instControllers =
        node->getInstance_controller_array();

    for(UInt32 i = 0; i < instControllers.getCount(); ++i)
        handleInstanceController(instControllers[i], instData);

    editInstStore().push_back(instData._topN);
    _instDataStore .push_back(instData      );
    retVal = instData._topN;

    state->popMatrix  ();
    state->popNodePath();

    return retVal;
}
Node *
ColladaGeometry::createInstance(ColladaInstInfo *colInstInfo)
{
    typedef ColladaInstanceGeometry::MaterialMap        MaterialMap;
    typedef ColladaInstanceGeometry::MaterialMapConstIt MaterialMapConstIt;

    domGeometryRef geometry = getDOMElementAs<domGeometry>();
    NodeUnrecPtr   groupN   = makeCoredNode<Group>();

    OSG_COLLADA_LOG(("ColladaGeometry::createInstance id [%s]\n",
                     geometry->getId()));

    if(getGlobal()->getOptions()->getCreateNameAttachments() == true &&
       geometry->getName()                                   != NULL   )
    {
        setName(groupN, geometry->getName());
    }

    ColladaInstanceGeometryRefPtr  colInstGeo =
        dynamic_cast<ColladaInstanceGeometry *>(colInstInfo->getColInst());
    const MaterialMap             &matMap     =
        colInstGeo->getMaterialMap();

    // iterate over all parts of geometry
    GeoStoreIt gsIt   = _geoStore.begin();
    GeoStoreIt gsEnd  = _geoStore.end  ();

    for(; gsIt != gsEnd; ++gsIt)
    {
        OSG_ASSERT(gsIt->_propStore.size() == gsIt->_indexStore.size());

        // find the material associated with the geometry's material symbol
        MaterialMapConstIt mmIt       = matMap.find(gsIt->_matSymbol);
        std::string        matTarget;

        if(mmIt != matMap.end())
        {
            matTarget = mmIt->second->getTarget();
        }

        // check if the geometry was already used with that material

        GeometryUnrecPtr   geo      = NULL;
        InstanceMapConstIt instIt   = gsIt->_instMap.find(matTarget);

        if(instIt != gsIt->_instMap.end())
        {
            // reuse geometry

            geo = dynamic_pointer_cast<Geometry>(
                getInstStore()[instIt->second]);

            getGlobal()->getStatCollector()->getElem(
                ColladaGlobal::statNGeometryUsed)->inc();
        }
        else
        {
            // create new geometry

            geo = Geometry::create();

            getGlobal()->getStatCollector()->getElem(
                ColladaGlobal::statNGeometryCreated)->inc();

            geo->setLengths(gsIt->_lengths);
            geo->setTypes  (gsIt->_types  );

            handleBindMaterial(*gsIt, geo, colInstGeo);

            // record the instantiation of the geometry with the
            // material for reuse
            gsIt->_instMap.insert(
                InstanceMap::value_type(matTarget, getInstStore().size()));

            editInstStore().push_back(geo);
        }

        NodeUnrecPtr geoN = makeNodeFor(geo);

        groupN->addChild(geoN);
    }

    // store the generated group node
    editInstStore().push_back(groupN);

    return groupN;
}
Node *
ColladaLight::createInstanceCommon(
    ColladaLightInstInfo *colInstInfo, domLight::domTechnique_common *tech)
{
    domLightRef light = getDOMElementAs<domLight>();

    OSG_COLLADA_LOG(("ColladaLight::createInstanceCommon id [%s]\n",
                     light->getId()));

    NodeUnrecPtr                                     lightN      = NULL;

    domLight::domTechnique_common::domAmbientRef     ambient     =
        tech->getAmbient();
    domLight::domTechnique_common::domDirectionalRef directional =
        tech->getDirectional();
    domLight::domTechnique_common::domPointRef       point       =
        tech->getPoint();
    domLight::domTechnique_common::domSpotRef        spot        =
        tech->getSpot();

    if(ambient != NULL)
    {
        Color4f lightColor(ambient->getColor()->getValue()[0],
                           ambient->getColor()->getValue()[1],
                           ambient->getColor()->getValue()[2], 1.f);

        LightLoaderState *state =
            getGlobal()->getLoaderStateAs<LightLoaderState>(_loaderStateName);
        OSG_ASSERT(state != NULL);

        LightModelChunkUnrecPtr lmChunk = state->getLightModelChunk();

        if(state->getLightModelChunk() == NULL)
        {
            lmChunk = LightModelChunk::create();
            lmChunk->setAmbient(Color4f(0.f, 0.f, 0.f, 1.f));

            state->setLightModelChunk(lmChunk);

            // only place one instance into the inst queue, it will
            // add the LightModelChunk with the accumulated ambient
            // for the scene
            ColladaInstInfoRefPtr ambientInstInfo =
                ColladaLightAmbientInstInfo::create(
                    colInstInfo->getColInstParent(),
                    dynamic_cast<ColladaInstanceLight *>(
                        colInstInfo->getColInst())        );

            getGlobal()->editInstQueue().push_back(ambientInstInfo);
        }

        lightColor[0] = 
            osgClamp(0.f, lmChunk->getAmbient()[0] + lightColor[0], 1.f);
        lightColor[1] =
            osgClamp(0.f, lmChunk->getAmbient()[1] + lightColor[1], 1.f);
        lightColor[2] =
            osgClamp(0.f, lmChunk->getAmbient()[2] + lightColor[0], 1.f);

        lmChunk->setAmbient(lightColor);
    }

    if(directional != NULL)
    {
        Color4f lightColor(directional->getColor()->getValue()[0],
                           directional->getColor()->getValue()[1],
                           directional->getColor()->getValue()[2], 1.f);

        DirectionalLightUnrecPtr dl = DirectionalLight::create();
        lightN                      = makeNodeFor(dl);

        dl->setBeacon  (colInstInfo->getBeacon());
        dl->setDiffuse (lightColor              );
        dl->setSpecular(lightColor              );
    }

    if(point != NULL)
    {
        Color4f lightColor(point->getColor()->getValue()[0],
                           point->getColor()->getValue()[1],
                           point->getColor()->getValue()[2], 1.f);

        PointLightUnrecPtr pl = PointLight::create();
        lightN                = makeNodeFor(pl);

        Real32 constAtt = 1.f;
        Real32 linAtt   = 0.f;
        Real32 quadAtt  = 0.f;

        if(point->getConstant_attenuation() != NULL)
            constAtt = point->getConstant_attenuation()->getValue();

        if(point->getLinear_attenuation() != NULL)
            linAtt   = point->getLinear_attenuation()->getValue();

        if(point->getQuadratic_attenuation() != NULL)
            quadAtt  = point->getQuadratic_attenuation()->getValue();

        pl->setBeacon              (colInstInfo->getBeacon());
        pl->setDiffuse             (lightColor              );
        pl->setSpecular            (lightColor              );
        pl->setConstantAttenuation (constAtt                );
        pl->setLinearAttenuation   (linAtt                  );
        pl->setQuadraticAttenuation(quadAtt                 );
    }

    if(spot != NULL)
    {
        Color4f lightColor(spot->getColor()->getValue()[0],
                           spot->getColor()->getValue()[1],
                           spot->getColor()->getValue()[2], 1.f);

        SpotLightUnrecPtr sl = SpotLight::create();
        lightN               = makeNodeFor(sl);

        Real32 constAtt =   1.f;
        Real32 linAtt   =   0.f;
        Real32 quadAtt  =   0.f;
        Real32 cutOff   = 180.f;
        Real32 exponent =   0.f;

        if(spot->getConstant_attenuation() != NULL)
            constAtt = spot->getConstant_attenuation()->getValue();

        if(spot->getLinear_attenuation() != NULL)
            linAtt   = spot->getLinear_attenuation()->getValue();

        if(spot->getQuadratic_attenuation() != NULL)
            quadAtt  = spot->getQuadratic_attenuation()->getValue();

        if(spot->getFalloff_angle() != NULL)
            cutOff   = spot->getFalloff_angle()->getValue();

        if(spot->getFalloff_exponent() != NULL)
            exponent = spot->getFalloff_exponent()->getValue();

        sl->setBeacon              (colInstInfo->getBeacon());
        sl->setDiffuse             (lightColor              );
        sl->setSpecular            (lightColor              );
        sl->setConstantAttenuation (constAtt                );
        sl->setLinearAttenuation   (linAtt                  );
        sl->setQuadraticAttenuation(quadAtt                 );
        sl->setSpotCutOff          (osgDegree2Rad(cutOff)   );
        sl->setSpotExponent        (exponent                );
    }

    if(lightN != NULL)
    {
        editInstStore().push_back(lightN);

        if(getGlobal()->getOptions()->getCreateNameAttachments() == true &&
           light->getName()                                      != NULL   )
        {
            setName(lightN, light->getName());
        }
    }

    return lightN;
}