void Actor::doAttach( Actor* actor, const Ogre::String& slot, const Ogre::String& childSlot, const Ogre::Vector3& offsetPosition, const Ogre::Quaternion& offsetOrientation ) { Ogre::Vector3 offsetPositionMod = offsetPosition; Ogre::Quaternion offsetOrientationMod = offsetOrientation; if( actor == NULL ) Throw(NullPointerException, "Aktor "+mName+": Der anzufügende Aktor darf nicht NULL sein." ); if( actor->mParent != NULL ) Throw(NullPointerException, "Aktor "+mName+": Der Aktor ist bereits an einen anderen Aktor angefügt." ); // Verschiebung durch den Child-Slot berechnen // Ist es ein nicht Standard-Slot && Kontrolliert der Aktor ein Objekt && Ist dieses ein Mesh if( childSlot.compare(DEFAULT_SLOT_NAME) != 0 && actor->getControlledObject() != NULL && actor->getControlledObject()->isMeshObject() ) { Entity* ent = dynamic_cast<MeshObject*>(actor->getControlledObject())->getEntity(); // Braucht ein Skelett if( !ent->hasSkeleton() ) Throw(IllegalArgumentException, "Aktor "+mName+": Das kontrollierte MeshObject des ChildAktor hat kein Skeleton." ); // Der Slot muss existieren try { Bone* bone = ent->getSkeleton()->getBone( childSlot ); Vector3 vec = bone->_getDerivedPosition(); Quaternion quat = bone->_getDerivedOrientation(); // Durch den Bone ExtraOffset hinzufügen offsetOrientationMod = offsetOrientation * quat; offsetPositionMod = ( offsetOrientationMod * (-vec) ) + offsetPosition; } catch (Ogre::Exception) { Throw(IllegalArgumentException, "Aktor "+mName+": Der geforderte Slot '"+childSlot+"' am ChildAktor existiert nicht." ); } } // Das wirkliche Anfügen // Ist es ein nicht Standard-Slot && Kontrolliert der Aktor ein Objekt && Ist dieses ein Mesh if( slot.compare(DEFAULT_SLOT_NAME) != 0 && getControlledObject() != NULL && getControlledObject()->isMeshObject() ) { if( actor->getControlledObject() == NULL ) Throw(IllegalArgumentException, "Aktor "+mName+": Der zu befestigende Aktor darf bei SLOTs nicht leer sein." ); MovableObject* movObj = actor->getControlledObject()->getMovableObject(); Entity* ent = dynamic_cast<MeshObject*>(getControlledObject())->getEntity(); // Braucht ein Skelett if( !ent->hasSkeleton() ) Throw(IllegalArgumentException, "Aktor "+mName+": Das kontrollierte MeshObject hat kein Skeleton." ); // Der Slot muss existieren try { ent->getSkeleton()->getBone( slot ); } catch (Ogre::Exception) { Throw(IllegalArgumentException, "Aktor "+mName+": Der geforderte Slot '"+slot+"' existiert nicht." ); } // Am Bone befestigen ent->attachObjectToBone( slot, movObj, offsetOrientationMod, offsetPositionMod ); // Der Aktor wurde an einem Bone befestigt actor->mBone = ent->getSkeleton()->getBone( slot ); return; } // Wenn hier kein MeshObjekt dran ist, trotzdem irgendwie zusammenfügen else { actor->placeIntoNode( mSceneNode, offsetPositionMod, offsetOrientationMod ); // Der Aktor wurde nicht an einem Bone befestigt actor->mBone = 0; return; } }
void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bounds, Transformation original, std::vector<std::string> boneSequence) { assert(shape != NULL); bool saveTheShape = inTheSkeletonTree; // Interpret flags bool hidden = (flags & 0x01) != 0; // Not displayed bool collide = (flags & 0x02) != 0; // Use mesh for collision bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision // Bounding box collision isn't implemented, always use mesh for now. if (bbcollide) { collide = true; bbcollide = false; } // If the object was marked "NCO" earlier, it shouldn't collide with // anything. if (flags & 0x800) { collide = false; bbcollide = false; } if (!collide && !bbcollide && hidden) // This mesh apparently isn't being used for anything, so don't // bother setting it up. return; // Material name for this submesh, if any String material; // Skip the entire material phase for hidden nodes if (!hidden) { // These are set below if present NiTexturingProperty *t = NULL; NiMaterialProperty *m = NULL; NiAlphaProperty *a = NULL; // Scan the property list for material information PropertyList &list = shape->props; int n = list.length(); for (int i=0; i<n; i++) { // Entries may be empty if (!list.has(i)) continue; Property *pr = &list[i]; if (pr->recType == RC_NiTexturingProperty) t = (NiTexturingProperty*)pr; else if (pr->recType == RC_NiMaterialProperty) m = (NiMaterialProperty*)pr; else if (pr->recType == RC_NiAlphaProperty) a = (NiAlphaProperty*)pr; } // Texture String texName; if (t && t->textures[0].inUse) { NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { SString tname = st->filename; /* findRealTexture checks if the file actually exists. If it doesn't, and the name ends in .tga, it will try replacing the extension with .dds instead and search for that. Bethesda at some at some point converted all their BSA textures from tga to dds for increased load speed, but all texture file name references were kept as .tga. The function replaces the name in place (that's why we cast away the const modifier), but this is no problem since all the nif data is stored in a local throwaway buffer. */ texName = "textures\\" + tname.toString(); findRealTexture(texName); } else warn("Found internal texture, ignoring."); } // Alpha modifiers int alphaFlags = -1; ubyte alphaTest = 0; if (a) { alphaFlags = a->flags; alphaTest = a->data->threshold; } // Material if (m || !texName.empty()) { // If we're here, then this mesh has a material. Thus we // need to calculate a snappy material name. It should // contain the mesh name (mesh->getName()) but also has to // be unique. One mesh may use many materials. material = getUniqueName(mesh->getName()); if (m) { // Use NiMaterialProperty data to create the data const S_MaterialProperty *d = m->data; std::multimap<std::string,std::string>::iterator itr = MaterialMap.find(texName); std::multimap<std::string,std::string>::iterator lastElement; lastElement = MaterialMap.upper_bound(texName); if (itr != MaterialMap.end()) { for ( ; itr != lastElement; ++itr) { //std::cout << "OK!"; //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); material = itr->second; //if( mat->getA } } else { //std::cout << "new"; createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, d->glossiness, d->alpha, alphaFlags, alphaTest, texName); MaterialMap.insert(std::make_pair(texName,material)); } } else { // We only have a texture name. Create a default // material for it. Vector zero, one; for (int i=0; i<3;i++) { zero.array[i] = 0.0; one.array[i] = 1.0; } createMaterial(material, one, one, zero, zero, 0.0, 1.0, alphaFlags, alphaTest, texName); } } } // End of material block, if(!hidden) ... /* Do in-place transformation of all the vertices and normals. This is pretty messy stuff, but we need it to make the sub-meshes appear in the correct place. Neither Ogre nor Bullet support nested levels of sub-meshes with transformations applied to each level. */ NiTriShapeData *data = shape->data.getPtr(); int numVerts = data->vertices.length / 3; float *ptr = (float*)data->vertices.ptr; float *optr = ptr; std::list<VertexBoneAssignment> vertexBoneAssignments; Nif::NiTriShapeCopy copy = shape->clone(); if(!shape->controller.empty()) { Nif::Controller* cont = shape->controller.getPtr(); if(cont->recType == RC_NiGeomMorpherController) { Nif::NiGeomMorpherController* morph = dynamic_cast<Nif::NiGeomMorpherController*> (cont); copy.morph = morph->data.get(); copy.morph.setStartTime(morph->timeStart); copy.morph.setStopTime(morph->timeStop); saveTheShape = true; } } //use niskindata for the position of vertices. if (!shape->skin.empty()) { // vector that stores if the position of a vertex is absolute std::vector<bool> vertexPosAbsolut(numVerts,false); std::vector<Ogre::Vector3> vertexPosOriginal(numVerts, Ogre::Vector3::ZERO); std::vector<Ogre::Vector3> vertexNormalOriginal(numVerts, Ogre::Vector3::ZERO); float *ptrNormals = (float*)data->normals.ptr; //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] //the first one contains a link to the bone, the second vertex transformation //relative to the bone int boneIndex = 0; Bone *bonePtr; Vector3 vecPos; Quaternion vecRot; std::vector<NiSkinData::BoneInfo> boneList = shape->skin->data->bones; /* Iterate through the boneList which contains what vertices are linked to the bone (it->weights array) and at what position (it->trafo) That position is added to every vertex. */ for (std::vector<NiSkinData::BoneInfo>::iterator it = boneList.begin(); it != boneList.end(); it++) { if(mSkel.isNull()) { std::cout << "No skeleton for :" << shape->skin->bones[boneIndex].name.toString() << std::endl; break; } //get the bone from bones array of skindata if(!mSkel->hasBone(shape->skin->bones[boneIndex].name.toString())) std::cout << "We don't have this bone"; bonePtr = mSkel->getBone(shape->skin->bones[boneIndex].name.toString()); // final_vector = old_vector + old_rotation*new_vector*old_scale Nif::NiSkinData::BoneInfoCopy boneinfocopy; boneinfocopy.trafo.rotation = convertRotation(it->trafo->rotation); boneinfocopy.trafo.trans = convertVector3(it->trafo->trans); boneinfocopy.bonename = shape->skin->bones[boneIndex].name.toString(); boneinfocopy.bonehandle = bonePtr->getHandle(); copy.boneinfo.push_back(boneinfocopy); for (unsigned int i=0; i<it->weights.length; i++) { vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans); vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation); unsigned int verIndex = (it->weights.ptr + i)->vertex; //boneinfo.weights.push_back(*(it->weights.ptr + i)); Nif::NiSkinData::IndividualWeight ind; ind.weight = (it->weights.ptr + i)->weight; ind.boneinfocopyindex = copy.boneinfo.size() - 1; if(copy.vertsToWeights.find(verIndex) == copy.vertsToWeights.end()) { std::vector<Nif::NiSkinData::IndividualWeight> blank; blank.push_back(ind); copy.vertsToWeights[verIndex] = blank; } else { copy.vertsToWeights[verIndex].push_back(ind); } //Check if the vertex is relativ, FIXME: Is there a better solution? if (vertexPosAbsolut[verIndex] == false) { //apply transformation to the vertices Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3); absVertPos = absVertPos * (it->weights.ptr + i)->weight; vertexPosOriginal[verIndex] = Vector3(ptr + verIndex *3); mBoundingBox.merge(absVertPos); //convert it back to float * for (int j=0; j<3; j++) (ptr + verIndex*3)[j] = absVertPos[j]; //apply rotation to the normals (not every vertex has a normal) //FIXME: I guessed that vertex[i] = normal[i], is that true? if (verIndex < data->normals.length) { Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos * (it->weights.ptr + i)->weight; vertexNormalOriginal[verIndex] = Vector3(ptrNormals + verIndex *3); for (int j=0; j<3; j++) (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; } vertexPosAbsolut[verIndex] = true; } else { Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; absVertPos = absVertPos * (it->weights.ptr + i)->weight; Vector3 old = Vector3(ptr + verIndex *3); absVertPos = absVertPos + old; mBoundingBox.merge(absVertPos); //convert it back to float * for (int j=0; j<3; j++) (ptr + verIndex*3)[j] = absVertPos[j]; //apply rotation to the normals (not every vertex has a normal) //FIXME: I guessed that vertex[i] = normal[i], is that true? if (verIndex < data->normals.length) { Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; absNormalsPos = absNormalsPos * (it->weights.ptr + i)->weight; Vector3 oldNormal = Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos + oldNormal; for (int j=0; j<3; j++) (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; } } VertexBoneAssignment vba; vba.boneIndex = bonePtr->getHandle(); vba.vertexIndex = verIndex; vba.weight = (it->weights.ptr + i)->weight; vertexBoneAssignments.push_back(vba); } boneIndex++; } } else { copy.boneSequence = boneSequence; // Rotate, scale and translate all the vertices, const Matrix &rot = shape->trafo->rotation; const Vector &pos = shape->trafo->pos; float scale = shape->trafo->scale; copy.trafo.trans = convertVector3(original.pos); copy.trafo.rotation = convertRotation(original.rotation); copy.trafo.scale = original.scale; //We don't use velocity for anything yet, so it does not need to be saved // Computes C = B + AxC*scale for (int i=0; i<numVerts; i++) { vectorMulAdd(rot, pos, ptr, scale); Ogre::Vector3 absVertPos = Ogre::Vector3(*(ptr + 3 * i), *(ptr + 3 * i + 1), *(ptr + 3 * i + 2)); mBoundingBox.merge(absVertPos); ptr += 3; } // Remember to rotate all the vertex normals as well if (data->normals.length) { ptr = (float*)data->normals.ptr; for (int i=0; i<numVerts; i++) { vectorMul(rot, ptr); ptr += 3; } } if(!mSkel.isNull() ){ int boneIndex; boneIndex = mSkel->getNumBones() - 1; for(int i = 0; i < numVerts; i++){ VertexBoneAssignment vba; vba.boneIndex = boneIndex; vba.vertexIndex = i; vba.weight = 1; vertexBoneAssignments.push_back(vba); } } } if (!hidden) { // Add this vertex set to the bounding box bounds.add(optr, numVerts); if(saveTheShape) shapes.push_back(copy); // Create the submesh createOgreSubMesh(shape, material, vertexBoneAssignments); } }