void AssimpModelImporter::ExtractJointsAndWeights(aiMesh* mesh, vector<Vector4i>& joints, vector<Vector4f>& weights, vector<ModelPartBone>& bones) const { vector<vector<VertexInfluence>> vertices; vertices.resize(mesh->mNumVertices); for(U32 i = 0; i < mesh->mNumBones; ++i) { aiBone* bone = mesh->mBones[i]; ModelPartBone modelPartBone(bone->mName.C_Str(), ToMatrix(bone->mOffsetMatrix)); bones.push_back(modelPartBone); for(U32 j = 0; j < bone->mNumWeights; ++j) { aiVertexWeight vertexWeight = bone->mWeights[j]; U32 vertexIndex = vertexWeight.mVertexId; VertexInfluence vertexInfluence; vertexInfluence.weight = vertexWeight.mWeight; vertexInfluence.boneIndex = i; vertices[vertexIndex].push_back(vertexInfluence); } } // When exporting program assigned more then 4 joints (engine limit) to single vertex we exclude the least // influential ones and add removed weights to 4th vertex; This is not ideal solution - we should always // prefer exporter with limited number of joints per vertex due to possible discrepancy in exported and // imported animation. for(auto it = begin(vertices); it != end(vertices); ++it) { vector<VertexInfluence>& influences = *it; sort(influences.begin(), influences.end(), [](const VertexInfluence& a, const VertexInfluence& b) -> bool { return a.weight > b.weight; }); if (influences.size() > 4) { influences.erase(begin(influences) + 4, end(influences)); _ASSERT(4 == influences.size()); F32 sumOfThreeWeights = influences[0].weight + influences[1].weight + influences[2].weight; influences[3].weight = 1.0f - sumOfThreeWeights; } } joints.resize(mesh->mNumVertices, Vector4i(0)); weights.resize(mesh->mNumVertices, Vector4f(0.0f)); for(U32 i = 0; i < mesh->mNumVertices; ++i) { for(U32 j = 0; j < vertices[i].size(); ++j) { joints[i].v[j] = vertices[i][j].boneIndex; weights[i].v[j] = vertices[i][j].weight; } } }
void ToViewProjectionMatrix(float m[16], float fov, float aspect, float znear, float zfar) { float w = 1.0f/tan(fov/2.0f); float h = w * aspect; float z1 = zfar / (znear-zfar); float z2 = z1 * znear; ToMatrix(m); *((Vec4*)m + 3) = -*((Vec4*)m + 2); *((Vec4*)m) *= w; *((Vec4*)m + 1) *= h; *((Vec4*)m + 2) *= z1; m[11] += z2; }
void RotatedContentBuffer::DrawTo(ThebesLayer* aLayer, gfxContext* aTarget, float aOpacity, gfxASurface* aMask, const gfxMatrix* aMaskTransform) { if (!EnsureBuffer()) { return; } RefPtr<DrawTarget> dt = aTarget->GetDrawTarget(); MOZ_ASSERT(dt, "Did you pass a non-Azure gfxContext?"); bool clipped = false; // If the entire buffer is valid, we can just draw the whole thing, // no need to clip. But we'll still clip if clipping is cheap --- // that might let us copy a smaller region of the buffer. // Also clip to the visible region if we're told to. if (!aLayer->GetValidRegion().Contains(BufferRect()) || (ToData(aLayer)->GetClipToVisibleRegion() && !aLayer->GetVisibleRegion().Contains(BufferRect())) || IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) { // We don't want to draw invalid stuff, so we need to clip. Might as // well clip to the smallest area possible --- the visible region. // Bug 599189 if there is a non-integer-translation transform in aTarget, // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong // and may cause gray lines. gfxUtils::ClipToRegionSnapped(dt, aLayer->GetEffectiveVisibleRegion()); clipped = true; } RefPtr<gfx::SourceSurface> mask; if (aMask) { mask = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aMask); } Matrix maskTransform; if (aMaskTransform) { maskTransform = ToMatrix(*aMaskTransform); } CompositionOp op = CompositionOpForOp(aTarget->CurrentOperator()); DrawBufferWithRotation(dt, BUFFER_BLACK, aOpacity, op, mask, &maskTransform); if (clipped) { dt->PopClip(); } }
void AssimpModelImporter::CreateModelGraph(aiNode* node, I32 parent, vector<ModelNode>& modelNodes) const { _ASSERT(node); ModelNode modelNode(node->mName.C_Str(), ToMatrix(node->mTransformation), parent); for(U32 i = 0; i < node->mNumMeshes; ++i) { U32 meshIndex = node->mMeshes[i]; modelNode.modelParts.push_back(meshIndex); } modelNodes.push_back(modelNode); I32 currentNodeIndex = modelNodes.size() - 1; if (-1 != parent) { modelNodes[parent].children.push_back(currentNodeIndex); } for(U32 i = 0; i < node->mNumChildren; ++i) { CreateModelGraph(node->mChildren[i], currentNodeIndex, modelNodes); } }
void Quat::ToEuler( Real& x, Real& y, Real& z, EulerOrder eulerOrder ) const { // use matrix based method Real m[3][3]; // convert to matrix ToMatrix( m ); switch( eulerOrder ) { case EULER_ORDER_XYZ: Restrain( m[0][2], Real( -1 ), Real( 1 ) ); y = asin( m[0][2] ); x = atan2( -m[1][2], m[2][2] ); z = atan2( -m[0][1], m[0][0] ); break; case EULER_ORDER_ZYX: Restrain( m[2][0], Real( -1 ), Real( 1 ) ); y = asin( -m[2][0] ); z = atan2( m[1][0], m[0][0] ); x = atan2( m[2][1], m[2][2] ); break; case EULER_ORDER_YXZ: Restrain( m[1][2], Real( -1 ), Real( 1 ) ); x = asin( -m[1][2] ); y = atan2( m[0][2], m[2][2] ); z = atan2( m[1][0], m[1][1] ); break; default: throw std::logic_error( "Euler order not supported" ); break; } }
/** original mesh file format. char[3] "MSH" uint8_t mesh count. reserved (2 byte) uint32_t vbo offset by the top of file(32bit alignment). uint32_t vbo byte size(32bit alignment). uint32_t ibo byte size(32bit alignment). [ uint8_t mesh name length. char[mesh name length] mesh name(without zero ternmination). uint8_t material count. padding (4 - (length + 2) % 4) % 4 byte. [ uint32_t ibo offset. uint16_t ibo size(this is the polygon counts, so actual ibo size is 3 times). uint8_t red uint8_t green uint8_t blue uint8_t alpha uint8_t metallic uint8_t roughness ] x (ibo count) ] x (mesh count) uint8_t albedo texture name length. char[albedo texture name length] albedo texture name(without zero ternmination). uint8_t normal texture name length. char[normal texture name length] normal texture name(without zero ternmination). padding (4 - (texture name block size % 4) % 4 byte. vbo vbo data. ibo ibo data. padding (4 - (ibo byte size % 4) % 4 byte. uint16_t bone count. uint16_t animation count. [ RotTrans rotation and translation for the bind pose. int32_t parent bone index. ] x (bone count) [ uint8_t animation name length. char[24] animation name. bool loop flag uint16_t key frame count. float total time. [ float time. [ RotTrans rotation and translation. ] x (bone count) ] x (key frame count) ] x (animation count) */ ImportMeshResult ImportMesh(const RawBuffer& data, GLuint& vbo, GLintptr& vboEnd, GLuint& ibo, GLintptr& iboEnd) { const uint8_t* p = &data[0]; const uint8_t* pEnd = p + data.size(); if (p[0] != 'M' || p[1] != 'S' || p[2] != 'H') { return ImportMeshResult(ImportMeshResult::Result::invalidHeader); } p += 3; const int count = *p; p += 1; /*const uint32_t vboOffset = GetValue(p, 4);*/ p += 4; const uint32_t vboByteSize = GetValue(p, 4); p += 4; const uint32_t iboByteSize = GetValue(p, 4); p += 4; if (p >= pEnd) { return ImportMeshResult(ImportMeshResult::Result::noData); } GLuint iboBaseOffset = iboEnd; ImportMeshResult result(ImportMeshResult::Result::success); result.meshes.reserve(count); for (int i = 0; i < count; ++i) { Mesh m; const uint32_t nameLength = *p++; m.id.assign(p, p + nameLength); p += nameLength; const size_t materialCount = *p++; p += (4 - (nameLength + 2) % 4) % 4; m.materialList.resize(materialCount); for (auto& e : m.materialList) { e.iboOffset = iboBaseOffset; p += 4; e.iboSize = GetValue(p, 2); p += 2; e.material.color.r = *p++; e.material.color.g = *p++; e.material.color.b = *p++; e.material.color.a = *p++; e.material.metallic.Set(static_cast<float>(*p++) / 255.0f); e.material.roughness.Set(static_cast<float>(*p++) / 255.0f); iboBaseOffset += e.iboSize * sizeof(GLushort); } result.meshes.push_back(m); if (p >= pEnd) { return ImportMeshResult(ImportMeshResult::Result::invalidMeshInfo); } } glBufferSubData(GL_ARRAY_BUFFER, vboEnd, vboByteSize, p); p += vboByteSize; if (p >= pEnd) { return ImportMeshResult(ImportMeshResult::Result::invalidVBO); } std::vector<GLushort> indices; indices.reserve(iboByteSize / sizeof(GLushort)); const uint32_t offsetTmp = vboEnd / sizeof(Vertex); if (offsetTmp > 0xffff) { return ImportMeshResult(ImportMeshResult::Result::indexOverflow); } const GLushort offset = static_cast<GLushort>(offsetTmp); for (uint32_t i = 0; i < iboByteSize; i += sizeof(GLushort)) { indices.push_back(*reinterpret_cast<const GLushort*>(p) + offset); p += sizeof(GLushort); if (p >= pEnd) { return ImportMeshResult(ImportMeshResult::Result::invalidIBO); } } glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, iboEnd, iboByteSize, &indices[0]); vboEnd += vboByteSize; iboEnd += iboByteSize; p += (4 - (reinterpret_cast<intptr_t>(p) % 4)) % 4; if (p >= pEnd) { return result; } const uint32_t boneCount = GetValue(p, 2); p += 2; const uint32_t animationCount = GetValue(p, 2); p += 2; if (boneCount) { JointList joints; joints.resize(boneCount); std::vector<std::vector<int>> parentIndexList; parentIndexList.resize(boneCount); for (uint32_t i = 0; i < boneCount; ++i) { if (p >= pEnd) { return ImportMeshResult(ImportMeshResult::Result::invalidJointInfo); } Joint& e = joints[i]; e.invBindPose.rot.x = GetFloat(p); e.invBindPose.rot.y = GetFloat(p); e.invBindPose.rot.z = GetFloat(p); e.invBindPose.rot.w = GetFloat(p); e.invBindPose.rot.Normalize(); e.invBindPose.trans.x = GetFloat(p); e.invBindPose.trans.y = GetFloat(p); e.invBindPose.trans.z = GetFloat(p); #if 0 const Matrix4x3 m43 = ToMatrix(e.invBindPose.rot); Matrix4x4 m44; m44.SetVector(0, m43.GetVector(0)); m44.SetVector(1, m43.GetVector(1)); m44.SetVector(2, m43.GetVector(2)); m44.SetVector(3, e.invBindPose.trans); m44.Inverse(); Vector3F scale; m44.Decompose(&e.initialPose.rot, &scale, &e.initialPose.trans); #else e.initialPose.rot = e.invBindPose.rot.Inverse(); e.initialPose.trans = e.initialPose.rot.Apply(-e.invBindPose.trans); #endif e.offChild = 0; e.offSibling = 0; const uint32_t parentIndex = GetValue(p, 4); p += 4; if (parentIndex != 0xffffffff) { parentIndexList[parentIndex].push_back(i); } } for (uint32_t i = 0; i < boneCount; ++i) { const auto& e = parentIndexList[i]; if (!e.empty()) { int current = e[0]; joints[i].offChild = current - i; Joint* pJoint = &joints[current]; for (auto itr = e.begin() + 1; itr != e.end(); ++itr) { const int sibling = *itr; pJoint->offSibling = sibling - current; pJoint = &joints[sibling]; current = sibling; } } } for (auto& e : result.meshes) { e.jointList = joints; } } if (animationCount) { LOGI("ImportMesh - Read animation:"); result.animations.reserve(animationCount); for (uint32_t i = 0; i < animationCount; ++i) { Animation anm; const uint32_t nameLength = GetValue(p++, 1); char name[24]; for (int i = 0; i < 24; ++i) { name[i] = static_cast<char>(GetValue(p++, 1)); } anm.id.assign(name, name + nameLength); anm.data.resize(boneCount); for (uint32_t bone = 0; bone < boneCount; ++bone) { anm.data[bone].first = bone; } anm.loopFlag = static_cast<bool>(GetValue(p++, 1) != 0); const uint32_t keyframeCount = GetValue(p, 2); p += 2; anm.totalTime = GetFloat(p); LOGI("%s: %fsec", anm.id.c_str(), anm.totalTime); for (uint32_t keyframe = 0; keyframe < keyframeCount; ++keyframe) { const float time = GetFloat(p); #ifdef DEBUG_LOG_VERBOSE LOGI("time=%f", time); #endif // DEBUG_LOG_VERBOSE for (uint32_t bone = 0; bone < boneCount; ++bone) { if (p >= pEnd) { return ImportMeshResult(ImportMeshResult::Result::invalidAnimationInfo); } Animation::Element elem; elem.time = time; elem.pose.rot.x = GetFloat(p); elem.pose.rot.y = GetFloat(p); elem.pose.rot.z = GetFloat(p); elem.pose.rot.w = GetFloat(p); elem.pose.rot.Normalize(); elem.pose.trans.x = GetFloat(p); elem.pose.trans.y = GetFloat(p); elem.pose.trans.z = GetFloat(p); anm.data[bone].second.push_back(elem); #ifdef DEBUG_LOG_VERBOSE LOGI("%02d:(%+1.3f, %+1.3f, %+1.3f, %+1.3f) (%+1.3f, %+1.3f, %+1.3f)", bone, elem.pose.rot.w, elem.pose.rot.x, elem.pose.rot.y, elem.pose.rot.z, elem.pose.trans.x, elem.pose.trans.y, elem.pose.trans.z); #endif // DEBUG_LOG_VERBOSE } } result.animations.push_back(anm); } } return result; }