Matrix4 SplineBase::computeBasis() { // The standard Catmull-Rom spline basis (e.g., Watt & Watt p108) // is for [u^3 u^2 u^1 u^0] * B * [p[0] p[1] p[2] p[3]]^T. // We need a basis formed for: // // U * C * [2*p'[1] p[1] p[2] 2*p'[2]]^T // // U * C * [p2-p0 p1 p2 p3-p1]^T // // To make this transformation, compute the differences of columns in C: // For [p0 p1 p2 p3] Matrix4 basis = Matrix4( -1, 3, -3, 1, 2, -5, 4, -1, -1, 0, 1, 0, 0, 2, 0, 0) * 0.5f; // For [-p0 p1 p2 p3]^T basis.setColumn(0, -basis.column(0)); // For [-p0 p1 p2 p3-p1]^T basis.setColumn(1, basis.column(1) + basis.column(3)); // For [p2-p0 p1 p2 p3-p1]^T basis.setColumn(2, basis.column(2) - basis.column(0)); return basis; }
Matrix4 Matrix4::inverse() const { // Inverse = adjoint / determinant Matrix4 A = adjoint(); // Determinant is the dot product of the first row and the first row // of cofactors (i.e. the first col of the adjoint matrix) float det = A.column(0).dot(row(0)); return A * (1.0f / det); }
Matrix4 Matrix4::inverse() const { Matrix4 A = adjoint(); float det = A.column(0).dot(row(0)); return A * (1.0f / det); }
void ArticulatedModel::load3DS(const Specification& specification) { // During loading, we make no attempt to optimize the mesh. We leave that until the // Parts have been created. The vertex arrays are therefore much larger than they // need to be. Stopwatch timer; Parse3DS parseData; { BinaryInput bi(specification.filename, G3D_LITTLE_ENDIAN); timer.after(" open file"); parseData.parse(bi); timer.after(" parse"); } name = FilePath::base(specification.filename); const std::string& path = FilePath::parent(specification.filename); /* if (specification.stripMaterials) { stripMaterials(parseData); } if (specification.mergeMeshesByMaterial) { mergeGroupsAndMeshesByMaterial(parseData); }*/ for (int p = 0; p < parseData.objectArray.size(); ++p) { Parse3DS::Object& object = parseData.objectArray[p]; // Create a unique name for this part std::string name = object.name; int count = 0; while (this->part(name) != NULL) { ++count; name = object.name + format("_#%d", count); } // Create the new part // All 3DS parts are promoted to the root in the current implementation. Part* part = addPart(name); // Process geometry part->cpuVertexArray.vertex.resize(object.vertexArray.size()); part->cframe = object.keyframe.approxCoordinateFrame(); debugAssert(isFinite(part->cframe.rotation.determinant())); debugAssert(part->cframe.rotation.isOrthonormal()); if (! part->cframe.rotation.isRightHanded()) { // TODO: how will this impact other code? I think we can't just force it like this -- Morgan part->cframe.rotation.setColumn(0, -part->cframe.rotation.column(0)); } debugAssert(part->cframe.rotation.isRightHanded()); //debugPrintf("%s %d %d\n", object.name.c_str(), object.hierarchyIndex, object.nodeID); if (part->cpuVertexArray.vertex.size() > 0) { // Convert vertices to object space (there is no surface normal data at this point) Matrix4 netXForm = part->cframe.inverse().toMatrix4(); debugAssertM(netXForm.row(3) == Vector4(0,0,0,1), "3DS file loading requires that the last row of the xform matrix be 0, 0, 0, 1"); if (object.texCoordArray.size() > 0) { part->m_hasTexCoord0 = true; part->cpuVertexArray.hasTexCoord0 = true; } const Matrix3& S = netXForm.upper3x3(); const Vector3& T = netXForm.column(3).xyz(); for (int v = 0; v < part->cpuVertexArray.vertex.size(); ++v) { # ifdef G3D_DEBUG { const Vector3& vec = object.vertexArray[v]; debugAssert(vec.isFinite()); } # endif CPUVertexArray::Vertex& vertex = part->cpuVertexArray.vertex[v]; vertex.position = S * object.vertexArray[v] + T; vertex.tangent = Vector4::nan(); vertex.normal = Vector3::nan(); if (part->m_hasTexCoord0) { vertex.texCoord0 = object.texCoordArray[v]; } # ifdef G3D_DEBUG { const Vector3& vec = vertex.position; debugAssert(vec.isFinite()); } # endif } if (object.faceMatArray.size() == 0) { // Merge all geometry into one mesh since there are no materials Mesh* mesh = addMesh("mesh", part); mesh->cpuIndexArray = object.indexArray; debugAssert(mesh->cpuIndexArray.size() % 3 == 0); } else { for (int m = 0; m < object.faceMatArray.size(); ++m) { const Parse3DS::FaceMat& faceMat = object.faceMatArray[m]; if (faceMat.faceIndexArray.size() > 0) { Material::Ref mat; bool twoSided = false; const std::string& materialName = faceMat.materialName; if (parseData.materialNameToIndex.containsKey(materialName)) { int i = parseData.materialNameToIndex[materialName]; const Parse3DS::Material& material = parseData.materialArray[i]; //if (! materialSubstitution.get(material.texture1.filename, mat)) { const Material::Specification& spec = compute3DSMaterial(&material, path, specification); mat = Material::create(spec); //} twoSided = material.twoSided || mat->hasAlphaMask(); } else { mat = Material::create(); logPrintf("Referenced unknown material '%s'\n", materialName.c_str()); } Mesh* mesh = addMesh(materialName, part); debugAssert(isValidHeapPointer(mesh)); mesh->material = mat; mesh->twoSided = twoSided; // Construct an index array for this part for (int i = 0; i < faceMat.faceIndexArray.size(); ++i) { // 3*f is an index into object.indexArray int f = faceMat.faceIndexArray[i]; debugAssert(f >= 0); for (int v = 0; v < 3; ++v) { mesh->cpuIndexArray.append(object.indexArray[3 * f + v]); } } debugAssert(mesh->cpuIndexArray.size() > 0); debugAssert(mesh->cpuIndexArray.size() % 3 == 0); } } // for m } // if has materials } } timer.after(" convert"); }
Quaternion::Quaternion(const Matrix4 &src) { this->set(src.column(0).xyz(),src.column(1).xyz(),src.column(2).xyz()); }