bool GeoAttributeCopier::createHandles(const attrib_copy& copy, GEO_AttributeHandle& hSrc, GEO_AttributeHandle& hDest) { // find source attrib hSrc = copy.m_srcGeo->getAttribute(copy.m_srcDict, copy.m_srcName.c_str()); if(!hSrc.isAttributeValid()) return false; const char* destName_ = copy.m_destName.c_str(); const GB_Attribute* srcAttrib = hSrc.getAttribute(); assert(srcAttrib); // create attrib if it doesn't exist, or does but data type doesn't match bool createAttrib = true; GEO_AttributeHandle hExist = m_destGeo.getAttribute(copy.m_destDict, destName_); if(hExist.isAttributeValid()) { const GB_Attribute* destAttrib = hExist.getAttribute(); assert(destAttrib); createAttrib = (destAttrib->getType() != srcAttrib->getType()); } if(createAttrib) { GB_AttributeRef aref = m_destGeo.addAttribute(destName_, srcAttrib->getSize(), srcAttrib->getType(), srcAttrib->getDefault(), copy.m_destDict); if(!aref.isValid()) return false; } hDest = m_destGeo.getAttribute(copy.m_destDict, destName_); hDest.setSourceMap(hSrc); return true; }
FromHoudiniGeometryConverter::Convertability FromHoudiniPolygonsConverter::canConvert( const GU_Detail *geo ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); for ( GA_Iterator it=geo->getPrimitiveRange().begin(); !it.atEnd(); ++it ) { const GA_Primitive *prim = primitives.get( it.getOffset() ); if ( prim->getTypeId() != GEO_PRIMPOLY ) { return Inapplicable; } } // is there a single named shape? const GEO_AttributeHandle attrHandle = geo->getPrimAttribute( "name" ); if ( attrHandle.isAttributeValid() ) { const GA_ROAttributeRef attrRef( attrHandle.getAttribute() ); if ( geo->getUniqueValueCount( attrRef ) < 2 ) { return Ideal; } } return Suitable; }
FromHoudiniGeometryConverter::Convertability FromHoudiniCurvesConverter::canConvert( const GU_Detail *geo ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); unsigned numPrims = geo->getNumPrimitives(); GA_Iterator firstPrim = geo->getPrimitiveRange().begin(); if ( !numPrims || !compatiblePrimitive( primitives.get( firstPrim.getOffset() )->getTypeId() ) ) { return Inapplicable; } const GEO_Curve *firstCurve = (const GEO_Curve*)primitives.get( firstPrim.getOffset() ); bool periodic = firstCurve->isClosed(); unsigned order = firstCurve->getOrder(); for ( GA_Iterator it=firstPrim; !it.atEnd(); ++it ) { const GA_Primitive *prim = primitives.get( it.getOffset() ); if ( !compatiblePrimitive( prim->getTypeId() ) ) { return Inapplicable; } const GEO_Curve *curve = (const GEO_Curve*)prim; if ( curve->getOrder() != order ) { return Inapplicable; } if ( curve->isClosed() != periodic ) { return Inapplicable; } } // is there a single named shape? const GEO_AttributeHandle attrHandle = geo->getPrimAttribute( "name" ); if ( attrHandle.isAttributeValid() ) { const GA_ROAttributeRef attrRef( attrHandle.getAttribute() ); if ( geo->getUniqueValueCount( attrRef ) < 2 ) { return Ideal; } } return Suitable; }
void IECoreMantra::ProceduralPrimitive::addVisibleRenderable( VisibleRenderablePtr renderable ) { ToHoudiniGeometryConverterPtr converter = ToHoudiniGeometryConverter::create( renderable ); if( !converter ) { msg( Msg::Warning, "ProceduralPrimitive::addVisibleRenderable", "converter could not be found" ); return; } GU_Detail *gdp = allocateGeometry(); GU_DetailHandle handle; handle.allocateAndSet( (GU_Detail*)gdp, false ); bool converted = converter->convert( handle ); if ( !converted ) { msg( Msg::Warning, "ProceduralPrimitive::addVisibleRenderable", "converter failed" ); return; } /// \todo ToHoudiniGeometryConverter does not create a Houdini style uv attribute. /// We make one from s and t. This code should probably live in a converter or in an Op that /// remaps IECore conventions to common Houdini ones. MeshPrimitivePtr mesh = runTimeCast<MeshPrimitive> (renderable); if ( mesh ) { gdp->addTextureAttribute( GA_ATTRIB_VERTEX ); GEO_AttributeHandle auv = gdp->getAttribute( GA_ATTRIB_VERTEX, "uv" ); GEO_AttributeHandle as = gdp->getAttribute( GA_ATTRIB_VERTEX, "s" ); GEO_AttributeHandle at = gdp->getAttribute( GA_ATTRIB_VERTEX, "t" ); if ( auv.isAttributeValid() && as.isAttributeValid() && at.isAttributeValid() ) { GA_GBPrimitiveIterator it( *gdp ); GA_Primitive *p = it.getPrimitive(); while ( p ) { for (int i = 0; i < p->getVertexCount(); ++i) { GA_Offset v = p->getVertexOffset(i); as.setVertex(v); at.setVertex(v); auv.setVertex(v); auv.setF( as.getF(0), 0 ); auv.setF( ((at.getF(0) * -1.0f) + 1.0f), 1 ); // wat, t arrives upside down for some reason. auv.setF( 0.0f, 2 ); } ++it; p = it.getPrimitive(); } } } if ( m_renderer->m_motionType == RendererImplementation::Geometry ) { msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:Geometry" ); if ( !m_renderer->m_motionTimes.empty() ) { if ( (size_t)m_renderer->m_motionSize == m_renderer->m_motionTimes.size() ) { openGeometryObject(); } addGeometry(gdp, m_renderer->m_motionTimes.front()); m_renderer->m_motionTimes.pop_front(); if ( m_renderer->m_motionTimes.empty() ) { applySettings(); closeObject(); } } } else if ( m_renderer->m_motionType == RendererImplementation::ConcatTransform || m_renderer->m_motionType == RendererImplementation::SetTransform ) { // It isn't clear that this will give correct results. // ConcatTransform may need to interpolate transform snapshots. msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:Transform" ); openGeometryObject(); addGeometry(gdp, 0.0f); while ( !m_renderer->m_motionTimes.empty() ) { setPreTransform( convert< UT_Matrix4T<float> >(m_renderer->m_motionTransforms.front()), m_renderer->m_motionTimes.front() ); m_renderer->m_motionTimes.pop_front(); m_renderer->m_motionTransforms.pop_front(); } applySettings(); closeObject(); m_renderer->m_motionType = RendererImplementation::Unknown; } else if ( m_renderer->m_motionType == RendererImplementation::Velocity ) { msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:Velocity" ); openGeometryObject(); addGeometry(gdp, 0.0f); addVelocityBlurGeometry(gdp, m_preBlur, m_postBlur); applySettings(); closeObject(); m_renderer->m_motionType = RendererImplementation::Unknown; } else { msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:None" ); openGeometryObject(); addGeometry( gdp, 0.0f ); setPreTransform( convert< UT_Matrix4T<float> >(m_renderer->m_transformStack.top()), 0.0f); applySettings(); closeObject(); } }
Geo2Emp::ErrorCode Geo2Emp::loadMeshShape( const Nb::Body* pBody ) { if (!_gdp) { //If we don't have a GDP for writing data into Houdini, return error. return EC_NULL_WRITE_GDP; } LogInfo() << "=============== Loading mesh shape ===============" << std::endl; const Nb::TriangleShape* pShape; pShape = pBody->queryConstTriangleShape(); if (!pShape) { //NULL mesh shape, so return right now. return EC_NO_TRIANGLE_SHAPE; } //Get access to shapes we need to loading a mesh (point and triangle) const Nb::TriangleShape& triShape = pBody->constTriangleShape(); const Nb::PointShape& ptShape = pBody->constPointShape(); //Retrieve the number of points and channels int64_t size = ptShape.size(); int channelCount = ptShape.channelCount(); //Attribute Lookup Tables std::vector< GEO_AttributeHandle > attribLut; std::vector<GeoAttributeInfo> attribInfo; //houdini types for the naiad channels. GeoAttributeInfo* pInfo; GEO_AttributeHandle attr; std::string attrName; float zero3f[3] = {0,0,0}; float zero1f = 0; int zero3i[3] = {0,0,0}; int zero1i = 0; const void* data; attribLut.clear(); attribInfo.clear(); attribLut.resize( channelCount ); attribInfo.resize( channelCount ); //Prepare for a blind copy of Naiad channels to Houdini attributes. //Iterate over the channels and create the corresponding attributes in the GDP for (int i = 0; i < channelCount; i++) { //std::cout << "channel: " << i << std::endl; const Nb::ChannelCowPtr& chan = ptShape.channel(i); if ( _empAttribMangle.find( chan->name() ) != _empAttribMangle.end() ) attrName = _empAttribMangle[ chan->name() ]; else attrName = chan->name(); LogDebug() << "Processing EMP Channel: " << chan->name() << "; mangled: " << attrName << std::endl; //Determine the attribute type, and store it pInfo = &(attribInfo[ i ]); pInfo->supported = false; pInfo->size = 0; pInfo->use64 = false; switch ( chan->type() ) { case Nb::ValueBase::IntType: LogDebug() << "IntType " << std::endl; pInfo->type = GB_ATTRIB_INT; pInfo->entries = 1; pInfo->size = sizeof(int); pInfo->supported = true; data = &zero1i; break; case Nb::ValueBase::Int64Type: //NOTE: This might need to be handled differently ... just remember this 'hack' pInfo->type = GB_ATTRIB_INT; pInfo->entries = 1; pInfo->size = sizeof(int); pInfo->supported = true; data = &zero1i; break; case Nb::ValueBase::FloatType: LogDebug() << "FloatType " << std::endl; pInfo->type = GB_ATTRIB_FLOAT; pInfo->size = sizeof(float); pInfo->entries = 1; pInfo->supported = true; data = &zero1f; break; case Nb::ValueBase::Vec3fType: LogDebug() << "Vec3fType " << std::endl; pInfo->type = GB_ATTRIB_VECTOR; pInfo->size = sizeof(float); pInfo->entries = 3; pInfo->supported = true; data = &zero3f; break; default: pInfo->supported = false; break; } //If the attribute is not supported, then continue with the next one. if (!pInfo->supported) { LogVerbose() << "Unsupported attribute. Skipping:" << attrName << std::endl; continue; } //Check whether the attribute exists or not attr = _gdp->getPointAttribute( attrName.c_str() ); if ( !attr.isAttributeValid() ) { LogDebug() << "Creating attribute in GDP:" << attrName << std::endl; _gdp->addPointAttrib( attrName.c_str(), pInfo->size * pInfo->entries, pInfo->type, data); attr = _gdp->getPointAttribute( attrName.c_str() ); } //Put the attribute handle in a Lut for easy access later (during transfer) attribLut[i] = attr; } //Get index buffer from the triangle shape const Nb::Buffer3i& bufIndex ( triShape.constBuffer3i("index") ); int64_t indexBufSize = bufIndex.size(); //Before we start adding points to the GDP, just record how many points are already there. int initialPointCount = _gdp->points().entries(); GEO_Point *ppt; //Now, copy all the points into the GDP. for (int ptNum = 0; ptNum < ptShape.size(); ptNum ++) { ppt = _gdp->appendPoint(); //Iterate over the channels in the point shape and copy the data for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { pInfo = &(attribInfo[ channelIndex ]); //If the attribute is not supported then skip it if (!pInfo->supported) { continue; } attribLut[ channelIndex ].setElement( ppt ); //Transfer supported attributes (this includes the point Position) switch ( pInfo->type ) { case GB_ATTRIB_INT: { const Nb::Buffer1i& channelData = ptShape.constBuffer1i(channelIndex); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setI( channelData(ptNum) ); } break; case GB_ATTRIB_FLOAT: { //TODO: Handle more that 1 entry here, if we ever get something like that ... although I doubt it would happen. const Nb::Buffer1f& channelData( ptShape.constBuffer1f(channelIndex) ); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setF( channelData(ptNum) ); } break; case GB_ATTRIB_VECTOR: { const Nb::Buffer3f& channelData = ptShape.constBuffer3f(channelIndex); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setV3( UT_Vector3( channelData(ptNum)[0], channelData(ptNum)[1], channelData(ptNum)[2] ) ); } break; default: //not yet implemented. continue; break; } } } //Now that all the points are in the GDP, build the triangles GU_PrimPoly *pPrim; for (int tri = 0; tri < indexBufSize; tri++) { pPrim = GU_PrimPoly::build(_gdp, 3, GU_POLY_CLOSED, 0); //Build a closed poly with 3 points, but don't add them to the GDP. //Set the three vertices of the triangle for (int i = 0; i < 3; i++ ) { pPrim->setVertex(i, _gdp->points()[ initialPointCount + bufIndex(tri)[i] ] ); } } return EC_SUCCESS; }
Geo2Emp::ErrorCode Geo2Emp::loadParticleShape( const Nb::Body* pBody ) { if (!_gdp) { //If we don't have a GDP for writing data into Houdini, return error. return EC_NULL_WRITE_GDP; } const Nb::ParticleShape* pShape; LogInfo() << "=============== Loading particle shape ===============" << std::endl; pShape = pBody->queryConstParticleShape(); if (!pShape) { //std::cout << "Received NULL particle shape!" << std::endl; return EC_NO_PARTICLE_SHAPE; } //Attribute Lookup Tables std::vector< GEO_AttributeHandle > attribLut; std::vector<GeoAttributeInfo> attribInfo; //houdini types for the naiad channels. GeoAttributeInfo* pInfo; std::string attrName; //We have a valid particle shape. Start copying particle data over to the GDP int64_t size = pShape->size(); int channelCount = pShape->channelCount(); GEO_Point *ppt; GEO_AttributeHandle attr; GU_PrimParticle *pParticle; std::vector<std::string> houdiniNames; //houdini names that correspond to naiad channels. std::vector<GB_AttribType> houdiniTypes; //houdini types for the naiad channels. //Default values for attributes float zero3f[3] = {0,0,0}; float zero1f = 0; //int zero3i[3] = {0,0,0}; int zero1i = 0; const void* data; LogVerbose() << "Particle shape size: " << size << std::endl; LogVerbose() << "Particle shape channel count: " << channelCount << std::endl; LogVerbose() << "Building particle primitive..."; pParticle = GU_PrimParticle::build(_gdp, 0); LogVerbose() << "done." << std::endl; attribLut.clear(); attribInfo.clear(); attribLut.resize( channelCount ); attribInfo.resize( channelCount ); //Prepare for a blind copy of Naiad channels to Houdini attributes. //Iterate over the channels and create the corresponding attributes in the GDP for (int i = 0; i < channelCount; i++) { //std::cout << "channel: " << i << std::endl; const Nb::ChannelCowPtr& chan = pShape->channel(i); if ( _empAttribMangle.find( chan->name() ) != _empAttribMangle.end() ) attrName = _empAttribMangle[ chan->name() ]; else attrName = chan->name(); LogDebug() << "Processing EMP Channel: " << chan->name() << "; mangled: " << attrName << std::endl; //Determine the attribute type, and store it pInfo = &(attribInfo[ i ]); pInfo->supported = false; pInfo->size = 0; pInfo->use64 = false; if (attrName.compare("P") == 0) //Don't treat position as an attribute. This needs to be handled separately. continue; switch ( chan->type() ) { case Nb::ValueBase::IntType: pInfo->type = GB_ATTRIB_INT; pInfo->entries = 1; pInfo->size = sizeof(int); pInfo->supported = true; data = &zero1i; break; case Nb::ValueBase::Int64Type: //NOTE: This might need to be handled differently ... just remember this 'hack' pInfo->type = GB_ATTRIB_INT; pInfo->entries = 1; pInfo->size = sizeof(int); pInfo->supported = true; pInfo->use64 = true; data = &zero1i; break; case Nb::ValueBase::FloatType: pInfo->type = GB_ATTRIB_FLOAT; pInfo->size = sizeof(float); pInfo->entries = 1; pInfo->supported = true; data = &zero1f; break; case Nb::ValueBase::Vec3fType: pInfo->type = GB_ATTRIB_VECTOR; pInfo->size = sizeof(float); pInfo->entries = 3; pInfo->supported = true; data = &zero3f; break; default: pInfo->supported = false; break; } //If the attribute is not supported, then continue with the next one. if (!pInfo->supported) { LogVerbose() << "Unsupported attribute. Skipping:" << attrName << std::endl; continue; } //Check whether the attribute exists or not attr = _gdp->getPointAttribute( attrName.c_str() ); if ( !attr.isAttributeValid() ) { LogVerbose() << "Creating attribute in GDP:" << attrName << std::endl; LogDebug() << "Name: " << attrName << std::endl << "Entries: " << pInfo->entries << std::endl << "Size: " << pInfo->size << std::endl; _gdp->addPointAttrib( attrName.c_str(), pInfo->size * pInfo->entries, pInfo->type, data); attr = _gdp->getPointAttribute( attrName.c_str() ); } //Put the attribute handle in a Lut for easy access later. attribLut[i] = attr; } //The channel values for particle shapes are stored in blocks/tiles. unsigned int absPtNum = 0; //Get the block array for the positions channel const em::block3_array3f& positionBlocks( pShape->constBlocks3f("position") ); unsigned int numBlocks = positionBlocks.block_count(); unsigned int bsize; float dec_accumulator = 0.0f; float dec = (1.0f - (_decimation)/100.0f); LogInfo() << "Keeping decimation value: " << dec << std::endl; for (int blockIndex = 0; blockIndex < numBlocks; blockIndex ++) { //if (ptNum % 100 == 0) //Get a single block from the position blocks const em::block3vec3f& posBlock = positionBlocks(blockIndex); if ( blockIndex % 100 == 0 ) LogDebug() << "Block: " << blockIndex << "/" << numBlocks << std::endl; //Iterate over all the points/particles in the position block bsize = posBlock.size(); //Only process the point if the decimation accumulator is greater than one. for (int ptNum = 0; ptNum < bsize; ptNum++, absPtNum++) { dec_accumulator += dec; if (dec_accumulator < 1.0f) //Skip this point continue; //Process this point, remove the dec_accumulator rollover. dec_accumulator -= (int) dec_accumulator; ppt = _gdp->appendPoint(); pParticle->appendParticle(ppt); ppt->setPos( UT_Vector3( posBlock(ptNum)[0], posBlock(ptNum)[1], posBlock(ptNum)[2] ) ); //Loop over the channels and add the attributes //This loop needs to be as fast as possible. for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { pInfo = &(attribInfo[ channelIndex ]); //If the attribute is not supported then skip it if (!pInfo->supported) { continue; } attribLut[ channelIndex ].setElement( ppt ); switch ( pInfo->type ) { case GB_ATTRIB_INT: { if (pInfo->use64) { const em::block3i64& channelData( pShape->constBlocks1i64(channelIndex)(blockIndex) ); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setI( channelData(ptNum) ); } else { const em::block3i& channelData( pShape->constBlocks1i(channelIndex)(blockIndex) ); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setI( channelData(ptNum) ); } } break; case GB_ATTRIB_FLOAT: { //TODO: Handle more that 1 entry here, if we ever get something like that ... although I doubt it would happen. const em::block3f& channelData( pShape->constBlocks1f(channelIndex)(blockIndex) ); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setF( channelData(ptNum) ); } break; case GB_ATTRIB_VECTOR: { const em::block3vec3f& channelData( pShape->constBlocks3f(channelIndex)(blockIndex) ); //Get the Houdini point attribute using the name list we built earlier. attribLut[channelIndex].setV3( UT_Vector3( channelData(ptNum)[0], channelData(ptNum)[1], channelData(ptNum)[2] ) ); } break; default: //not yet implemented. continue; break; } } } } //std::cout << "all done. " << std::endl; return EC_SUCCESS; }
FromHoudiniGeometryConverter::Convertability FromHoudiniGroupConverter::canConvert( const GU_Detail *geo ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); // are there multiple primitives? unsigned numPrims = geo->getNumPrimitives(); if ( numPrims < 2 ) { return Admissible; } // are there mixed primitive types? GA_Iterator firstPrim = geo->getPrimitiveRange().begin(); GA_PrimitiveTypeId firstPrimId = primitives.get( firstPrim.getOffset() )->getTypeId(); for ( GA_Iterator it=firstPrim; !it.atEnd(); ++it ) { if ( primitives.get( it.getOffset() )->getTypeId() != firstPrimId ) { return Ideal; } } // are there multiple named shapes? const GEO_AttributeHandle attrHandle = geo->getPrimAttribute( "name" ); if ( attrHandle.isAttributeValid() ) { const GA_ROAttributeRef attrRef( attrHandle.getAttribute() ); if ( geo->getUniqueValueCount( attrRef ) > 1 ) { return Ideal; } } // are the primitives split into groups? UT_PtrArray<const GA_ElementGroup*> primGroups; geo->getElementGroupList( GA_ATTRIB_PRIMITIVE, primGroups ); if ( primGroups.isEmpty() ) { return Admissible; } bool externalGroups = false; for ( unsigned i=0; i < primGroups.entries(); ++i ) { const GA_ElementGroup *group = primGroups[i]; if ( group->getInternal() ) { continue; } if ( group->entries() == numPrims ) { return Admissible; } externalGroups = true; } if ( externalGroups ) { return Ideal; } return Admissible; }
void Geo2Emp::transferParticlePointAttribs(int numAttribs, GEO_AttributeHandleList& attribList, std::map<int, AttributeInfo>& attrLut, Nb::ParticleShape& shape, const GEO_Point* ppt) { GEO_AttributeHandle* pAttr; AttributeInfo* pAttrInfo; for (int i = 0; i < numAttribs; i++) { pAttr = attribList[i]; pAttrInfo = &( attrLut[i] ); if (!pAttrInfo->supported) //Skip unsupported attributes continue; pAttr->setElement( ppt ); if (! pAttr->isAttributeValid() ) { LogDebug() << "Invalid attribute handle on supported attribute!! [" << pAttr->getName() << "]" << std::endl; } LogDebug() << "Transferring attribute: " << pAttr->getName() << std::endl; switch ( pAttrInfo->type ) { case GB_ATTRIB_FLOAT: //LogDebug() << "Transfer Float" << pAttrInfo->entries << "[" << pAttr->getName() << "]" << std::endl; switch ( pAttrInfo->entries ) { case 1: { //LogDebug() << "Float1: " << pAttr->getV3() << std::endl; //Get the channel from the point shape em::block3_array1f& vecData = shape.mutableBlocks1f( pAttrInfo->empIndex ); //Write the data into the buffer vecData(0).push_back( pAttr->getF() ); } break; case 3: { LogDebug() << "Float3: " << pAttr->getV3() << std::endl; //Get the channel from the particle shape em::block3_array3f& vecData = shape.mutableBlocks3f( pAttrInfo->empIndex ); //Write the data into the buffer vecData(0).push_back( em::vec3f( pAttr->getF(0), pAttr->getF(1), pAttr->getF(2) ) ); } break; } break; case GB_ATTRIB_INT: LogDebug() << "Int " << pAttrInfo->entries << std::endl; switch ( pAttrInfo->entries ) { case 1: { //LogDebug() << "Float1: " << pAttr->getV3() << std::endl; //Get the channel from the point shape em::block3_array1i& vecData = shape.mutableBlocks1i( pAttrInfo->empIndex ); //Write the data into the buffer vecData(0).push_back( pAttr->getI() ); } break; case 3: { LogDebug() << "Int3: " << std::endl; //Get the channel from the particle shape em::block3_array3i& vecData = shape.mutableBlocks3i( pAttrInfo->empIndex ); //Write the data into the buffer vecData(0).push_back( em::vec3i( pAttr->getI(0), pAttr->getI(1), pAttr->getI(2) ) ); } break; } break; case GB_ATTRIB_VECTOR: { //LogDebug() << "Transfer Vector3 [" << pAttr->getName() << "] " << pAttr->getF(0) << "," << pAttr->getF(1) << "," << pAttr->getF(2)<< std::endl; //If we have a vector, we need to invert it (reverse winding). em::block3_array3f& vecData = shape.mutableBlocks3f( pAttrInfo->empIndex ); //Write the data into the buffer vecData(0).push_back( em::vec3f( pAttr->getF(0), pAttr->getF(1), pAttr->getF(2) ) ); break; } case GB_ATTRIB_MIXED: case GB_ATTRIB_INDEX: default: //Unsupported attribute, so give it a skip. LogDebug() << " !!!!! SHOULDNT GET THIS !!!! Unsupported attribute type for blind copy [" << pAttrInfo->type << "]" << std::endl; continue; break; } } }