Tôi gặp một số vấn đề khi tạo mô hình COLLADA đã tải. Tôi đã viết trình phân tích cú pháp của riêng mình và bây giờ tôi cũng muốn viết thường trình vẽ của riêng mình. Vấn đề ist, rằng ngay sau khi tôi kích hoạt các hình ảnh động trên mô hình của tôi, bàn tay, chân và đầu được kéo dài ra khỏi nguồn gốc của mô hình. (Trình tải được thực hiện dựa trên hướng dẫn tại đây: COLLADA Tutorial)Các vấn đề về hoạt hình COLLADA Model
Điều đầu tiên tôi làm trong chức năng vẽ của mô hình là thiết lập ma trận khớp (không phải là ma trận thế giới!) Với các mục tiêu đã cho từ khối đọc, Nếu tôi ví dụ đọc một kênh như:
<channel source="#some_sampler" target="some_joint/transform(3)(2)"/>
tôi sẽ thay đổi thành phần ma trận (3) (2) từ jointMatrix của doanh với sid = "chuyển hóa" trong bước đầu tiên này:
if(mCurrentAnimations_.size() > 0) {
unsigned currentFrame = GEAR::Root::getSingleton().getFrameEvent().frame;
bool updateTime = false;
if(currentFrame != mLastFrameUpdate_) {
if(timeSinceLastFrame < 1.0f)
updateTime = true;
mLastFrameUpdate_ = currentFrame;
}
/****************************************************
* If we have an active animation, *
* we animate it in each of it's defined channels *
***************************************************/
std::list<DAEAnimation*>::iterator it = mCurrentAnimations_.begin();
while(it != mCurrentAnimations_.end()) {
for(int c = 0; c < (*it)->animation->channels.size(); ++c) {
// update the time of the channelanimation if requested
if(updateTime) {
(*it)->channelStates[c].elapsedTime += timeSinceLastFrame;
}
GEAR::COLLADA::Channel* channel = (*it)->animation->channels[c];
// read the two indices depending on the time we're
int firstKeyframeTimeIndex = 0;
int secondKeyframeTimeIndex = 0;
for(int i = 0; i < channel->sampler->inputSource->mFloatArray_->mCount_; ++i) {
float time = channel->sampler->inputSource->mFloatArray_->mFloats_[i];
if(firstKeyframeTimeIndex == secondKeyframeTimeIndex && time > (*it)->channelStates[c].elapsedTime && i > 0) {
firstKeyframeTimeIndex = i-1;
secondKeyframeTimeIndex = i;
break;
}
if(firstKeyframeTimeIndex == secondKeyframeTimeIndex && i == channel->sampler->inputSource->mFloatArray_->mCount_-1) {
(*it)->channelStates[c].elapsedTime = 0.0f;
firstKeyframeTimeIndex = i;
secondKeyframeTimeIndex = 0;
break;
}
}
// look what kind of TargetAccessor we have
if(channel->targetAccessor != NULL && channel->targetAccessor->type == GEAR::MATRIX_ACCESSOR) {
// ok we have to read 1 value for first and second index
float firstValue = channel->sampler->outputSource->mFloatArray_->mFloats_[firstKeyframeTimeIndex];
float secondValue = channel->sampler->outputSource->mFloatArray_->mFloats_[secondKeyframeTimeIndex];
float firstTime = channel->sampler->inputSource->mFloatArray_->mFloats_[firstKeyframeTimeIndex];
float secondTime = channel->sampler->inputSource->mFloatArray_->mFloats_[secondKeyframeTimeIndex];
float interpolateValue = 1.0f/(secondTime - firstTime) * (secondTime - (*it)->channelStates[c].elapsedTime);
// now we calculate an linear interpolated value
float value = (secondValue*interpolateValue) + (firstValue*(1.0-interpolateValue));
// now we have to write this value to the Joint's Matrix
int entry = ((COLLADA::MatrixTargetAccessor*)channel->targetAccessor)->firstAccessor*4+((COLLADA::MatrixTargetAccessor*)channel->targetAccessor)->secondAccessor;
channel->targetJoint->matrix->jointSpaceMatrix.entries[entry] = channel->targetJoint->matrix->matrix.entries[entry] + value;
}
}
++it;
}
}
Sau khi tham gia tMatrices được sửa đổi theo tất cả các kênh, tôi tính toán lại worldMatrices của doanh bằng cách gọi hàm sau vào thư mục gốc phần:
void
COLLADA::Joint::recalcWorldSpaceTransMat() {
GEAR::Mat4 parentMat;
if(parent != NULL)
parentMat = parent->worldSpaceTransformationMatrix;
// @todo Here we have to test against NULL!
if(matrix != NULL)
this->worldSpaceTransformationMatrix = parentMat * matrix->jointSpaceMatrix;
else {
this->worldSpaceTransformationMatrix = parentMat;
}
//std::cout << "Joint " << sid << " recalculated\n";
for(int i = 0; i < mChildJoints_.size(); ++i)
mChildJoints_[i]->recalcWorldSpaceTransMat();
}
Bây giờ tất cả mọi thứ nên được sẵn sàng để vẽ mô hình của tôi chiều rộng phần cuối cùng sau chức năng vẽ của tôi:
for(int i = 0; i < mSubMeshes_.size(); ++i) {
for(int k = 0; k < mSubMeshes_[i]->mSubMeshes_.size(); ++k) {
// first we animate it
GEAR::DAESubMesh* submesh = mSubMeshes_[i]->mSubMeshes_[k];
submesh->buffer->lock(true);
{
for(unsigned v = 0; v < submesh->buffer->getNumVertices(); ++v) {
// get the array of joints, which influence the current vertex
DAEVertexInfo* vertexInfo = submesh->vertexInfo[v];
GEAR::Vec3 vertex; // do not init the vertex with any value!
float totalWeight = 0.0f;
for(int j = 0; j < vertexInfo->joints.size(); ++j) {
Mat4& invBindPoseMatrix = vertexInfo->joints[j]->joint->invBindPoseMatrix;
Mat4& transMat = vertexInfo->joints[j]->joint->worldSpaceTransformationMatrix;
totalWeight += vertexInfo->joints[j]->weight;
vertex += (transMat*invBindPoseMatrix*(submesh->skin->bindShapeMatrix*vertexInfo->vertex))*vertexInfo->joints[j]->weight;
}
if(totalWeight != 1.0f) {
float normalizedWeight = 1.0f/totalWeight;
vertex *= normalizedWeight;
}
submesh->buffer->bufferVertexPos(v, vertex);
}
}
submesh->buffer->unlock();
mSubMeshes_[i]->mSubMeshes_[k]->buffer->draw(GEAR::TRIANGLES, 0, mSubMeshes_[i]->mSubMeshes_[k]->buffer->getNumVertices());
}
}
Bây giờ vấn đề đặt ra là, mà đầu ra trông giống như sau:
tôi chắc chắn phải có thói quen nạp dữ liệu thực hiện đúng, bởi vì các hình ảnh động chung của người đàn ông đi bộ là có thể nhìn thấy, nhưng lưới bị biến dạng:
Như tôi đã nói, khi tôi bỏ ghi chú dòng:
channel->targetJoint->matrix->jointSpaceMatrix.entries[entry] = channel->targetJoint->matrix->matrix.entries[entry] + value;
Các hình ảnh động bị vô hiệu hóa và mô hình được hiển thị trong đó là tư thế tiêu chuẩn:
Bây giờ ngoài khi tôi thêm một bình thường đến 3 cột đầu tiên của jointMatrices như thế này trước khi tôi tính toán lại worldMatrix của doanh:
GEAR::Vec3 row1(matrix->jointSpaceMatrix.entries[0], matrix->jointSpaceMatrix.entries[1], matrix->jointSpaceMatrix.entries[2]);
row1.normalize();
matrix->jointSpaceMatrix.entries[0] = row1.x;
matrix->jointSpaceMatrix.entries[1] = row1.y;
matrix->jointSpaceMatrix.entries[2] = row1.z;
GEAR::Vec3 row2(matrix->jointSpaceMatrix.entries[4], matrix->jointSpaceMatrix.entries[5], matrix->jointSpaceMatrix.entries[6]);
row2.normalize();
matrix->jointSpaceMatrix.entries[4] = row2.x;
matrix->jointSpaceMatrix.entries[5] = row2.y;
matrix->jointSpaceMatrix.entries[6] = row2.z;
GEAR::Vec3 row3(matrix->jointSpaceMatrix.entries[8], matrix->jointSpaceMatrix.entries[9], matrix->jointSpaceMatrix.entries[10]);
row3.normalize();
matrix->jointSpaceMatrix.entries[8] = row3.x;
matrix->jointSpaceMatrix.entries[9] = row3.y;
matrix->jointSpaceMatrix.entries[10] = row3.z;
Sự cố vẫn tồn tại, nhưng lần này ở đầu ra khác. Người đàn ông bây giờ trông giống như một người ngoài hành tinh: D, nhưng điều này làm giảm tỷ lệ:
Tôi không chính xác bây giờ, cho dù tôi đã thực hiện bình thường đúng cách. Sự bình thường này có thực sự cần thiết không? Nó không được mô tả trong hướng dẫn và tôi cũng không thể tìm thấy bất cứ điều gì liên quan.
Sau khi tất cả tôi đã được xem xét việc thực hiện nội suy trong mã từ trang hướng dẫn. VÀ: Chúng không sử dụng bất kỳ quaternion nào để intrpolate ma trận lỗ. Những gì họ làm là như sau (mà không làm việc cho tôi):
Mat4 temp;
for (int i = 0; i < 16; ++i)
temp.entries[i] = interpolatef(matrix->jointSpaceMatrixStart.entries[i],matrix->jointSpaceMatrixFinish.entries[i],matrix->delta);
Vec3 forward,up,right,translation;
forward = Vec3(temp.entries[8], temp.entries[9], temp.entries[10]);
up= Vec3(temp.entries[4], temp.entries[5], temp.entries[6]);
right = Vec3(temp.entries[0], temp.entries[1], temp.entries[2]);
forward.normalize();
up.normalize();
right.normalize();
temp.entries[8] = forward.x; temp.entries[9] = forward.y; temp.entries[10] = forward.z;
temp.entries[4] = up.x; temp.entries[5] = up.y; temp.entries[6] = up.z;
temp.entries[0] = right.x; temp.entries[1] = right.y; temp.entries[2] = right.z;
matrix->jointSpaceMatrix = GEAR::Mat4(temp);
Sau đó, tôi sử dụng quaternion trong cách tiếp cận khác như vậy (cũng không hoạt động đối với tôi):
// wat we need for interpolation: rotMatStart, rotMatFinish, delta
// create rotation matrices from our 2 given matrices
GEAR::Mat4 rotMatStart = matrix->jointSpaceMatrixStart;
rotMatStart.setTranslationPart(GEAR::VEC3_ZERO);
GEAR::Mat4 rotMatFinish = matrix->jointSpaceMatrixFinish;
rotMatFinish.setTranslationPart(GEAR::VEC3_ZERO);
rotMatStart.transpose();
rotMatFinish.transpose();
// create Quaternions, which represent these 2 matrices
float w = GEAR::Tools::sqr(1.0 + rotMatStart.entries[0] + rotMatStart.entries[5] + rotMatStart.entries[10])/2.0;
float w4 = (4.0 * w);
float x = (rotMatStart.entries[6] - rotMatStart.entries[9])/w4 ;
float y = (rotMatStart.entries[8] - rotMatStart.entries[2])/w4 ;
float z = (rotMatStart.entries[1] - rotMatStart.entries[4])/w4 ;
GEAR::Quaternion rotQuadStart(x, y, z, w);
rotQuadStart.normalize();
w = GEAR::Tools::sqr(1.0 + rotMatFinish.entries[0] + rotMatFinish.entries[5] + rotMatFinish.entries[10])/2.0;
w4 = (4.0 * w);
x = (rotMatFinish.entries[6] - rotMatFinish.entries[9])/w4 ;
y = (rotMatFinish.entries[8] - rotMatFinish.entries[2])/w4 ;
z = (rotMatFinish.entries[1] - rotMatFinish.entries[4])/w4 ;
GEAR::Quaternion rotQuadFinish(x, y, z, w);
rotQuadFinish.normalize();
// create the interpolated rotation matrix
GEAR::Quaternion slerpedRotQuat = slerp(rotQuadStart, rotQuadFinish, matrix->delta);
slerpedRotQuat.normalize();
GEAR::Mat4 rotMat;
slerpedRotQuat.createMatrix(rotMat);
// interpolate the translation part
GEAR::Vec3 transVecStart(0.0,0.0,0.0);
matrix->jointSpaceMatrixStart.getTranslatedVector3D(transVecStart);
GEAR::Vec3 transVecFinish(0.0,0.0,0.0);
matrix->jointSpaceMatrixFinish.getTranslatedVector3D(transVecFinish);
GEAR::Mat4 transMat;
transMat.setTranslation(transVecFinish*matrix->delta + (transVecStart*(1.0f-matrix->delta)));
// now write the resulting Matrix back to the Joint
matrix->jointSpaceMatrix = transMat * rotMat;
Nó cũng sẽ không làm việc cho tôi. Không có gì có vẻ hiệu quả. Tôi thực sự không có ý tưởng gì đang xảy ra với điều này.
Bây giờ sau 2 ngày, tôi nhận nó làm việc nhờ vào câu trả lời của datenwolf
Tôi muốn thông báo tất cả làm thế nào tôi nhận nó làm việc. Bây giờ tất cả mọi thứ có vẻ rõ ràng và nó chỉ là một bước nhỏ tất cả các thời gian. Bây giờ chúng ta bắt đầu với phần hoạt hình. Tôi lặp qua tất cả các kênh và tiết kiệm thời gian bắt đầu và các giá trị kết thúc cũng như giá trị đồng bằng nội suy trong phạm vi 0,0 1,0 đến doanh, kênh sinh động:
if(mCurrentAnimations_.size() > 0) {
unsigned currentFrame = GEAR::Root::getSingleton().getFrameEvent().frame;
bool updateTime = false;
if(currentFrame != mLastFrameUpdate_) {
if(timeSinceLastFrame < 1.0f)
updateTime = true;
mLastFrameUpdate_ = currentFrame;
}
/****************************************************
* If we have an active animation, *
* we animate it in each of it's defined channels *
***************************************************/
std::list<DAEAnimation*>::iterator it = mCurrentAnimations_.begin();
while(it != mCurrentAnimations_.end()) {
for(int c = 0; c < (*it)->animation->channels.size(); ++c) {
// update the time of the channelanimation if requested
if(updateTime) {
(*it)->channelStates[c].elapsedTime += timeSinceLastFrame;
}
GEAR::COLLADA::Channel* channel = (*it)->animation->channels[c];
// read the two indices depending on the time we're
int firstIndex = 0;
int secondIndex = 1;
for(int i = 0; i < channel->sampler->inputSource->mFloatArray_->mCount_; ++i) {
float time = channel->sampler->inputSource->mFloatArray_->mFloats_[i];
if(time > (*it)->channelStates[c].elapsedTime) {
firstIndex = i-1;
secondIndex = i;
if(firstIndex == -1) // set to last frame
firstIndex = channel->sampler->inputSource->mFloatArray_->mCount_ - 1;
break;
}
else if(i == channel->sampler->inputSource->mFloatArray_->mCount_ - 1) {
(*it)->channelStates[c].elapsedTime -= channel->sampler->inputSource->mFloatArray_->mFloats_[i];
firstIndex = 0;
secondIndex = 1;
break;
}
}
// look what kind of TargetAccessor we have
if(channel->targetAccessor != NULL && channel->targetAccessor->type == GEAR::MATRIX_ACCESSOR) {
/************************************************************************
* Matrix accessors, which are read from a COLLADA <channel> block *
* will always target one matrix component they animate. *
* Such accessors are for example: *
* <channel source"#someSource" target="someJoint/transform(0)(2)"/> *
* *
* @TODO: *
* In a pre processing step, we have to group all channels, which *
* operate on the same joint. In order to accelerate the processing of *
* grouped channels, we have to expand the number of keyframes of all *
* channels to the maximum of all channels. *
************************************************************************/
unsigned entry = ((COLLADA::MatrixTargetAccessor*)channel->targetAccessor)->index;
float firstTime = channel->sampler->inputSource->mFloatArray_->mFloats_[firstIndex];
float secondTime = channel->sampler->inputSource->mFloatArray_->mFloats_[secondIndex];
// in case of matrix accessor, we write the startMatrix and the endMatrix to the Joints accessor, who finally will do the animation interpolation
channel->targetJoint->matrix->interpolationRequired = true;
// write out the start and end value to the jointSpaceMatrix
// this matrix will later be interpolated
channel->targetJoint->matrix->jointSpaceMatrixStart.entries[entry] = channel->sampler->outputSource->mFloatArray_->mFloats_[firstIndex];
channel->targetJoint->matrix->jointSpaceMatrixFinish.entries[entry] = channel->sampler->outputSource->mFloatArray_->mFloats_[secondIndex];
// the delta value is in the range [0.0,1.0]
channel->targetJoint->matrix->delta = 1.0f/(secondTime - firstTime) * (secondTime - (*it)->channelStates[c].elapsedTime);
}
}
++it;
}
}
Như bạn có thể thấy, đây là không suy gì cả. Chúng tôi chỉ đơn giản là bộ nhớ đệm các giá trị bắt đầu và kết thúc và một đồng bằng cho tất cả các khớp hoạt hình (và chúng tôi cũng thiết lập một lá cờ trên mỗi khớp đã sửa đổi)
Bây giờ sau khi tất cả các hoạt ảnh được thực hiện, chúng tôi gọi hàm nội suy() trên tất cả khớp gốc:
for(int i = 0; i < mSourceModel_->mVisualSceneLibrary_.mVisualScenes_.size(); ++i) {
for(int v = 0; v < mSourceModel_->mVisualSceneLibrary_.mVisualScenes_[i]->mSkeleton_.size(); ++v) {
if(mSourceModel_->mVisualSceneLibrary_.mVisualScenes_[i]->mSkeleton_[v]->mRootJoint_ != NULL) {
/************************************************************************************
* Now we have constructed all jointSpaceMatrixces for the start and the end and *
* we're ready to interpolate them and to also recalculate the joint's *
* worldSpaceMatrix. *
***********************************************************************************/
mSourceModel_->mVisualSceneLibrary_.mVisualScenes_[i]->mSkeleton_[v]->mRootJoint_->interpolateMatrices();
}
}
}
Điều này không mới, nhưng phần thú vị bây giờ là triển khai nội suy. Không có gì qith Quaternions tại tất cả:
void COLLADA::Joint::interpolateMatrices() {
if(matrix != NULL && matrix->interpolationRequired) {
for (unsigned i = 0; i < 16; ++i)
matrix->jointSpaceMatrix.entries[i] = interpolatef(matrix->jointSpaceMatrixStart.entries[i],matrix->jointSpaceMatrixFinish.entries[i],matrix->delta);
Vec3 forward,up,right,translation;
forward = Vec3(matrix->jointSpaceMatrix.entries[8], matrix->jointSpaceMatrix.entries[9], matrix->jointSpaceMatrix.entries[10]);
up= Vec3(matrix->jointSpaceMatrix.entries[4], matrix->jointSpaceMatrix.entries[5], matrix->jointSpaceMatrix.entries[6]);
right = Vec3(matrix->jointSpaceMatrix.entries[0], matrix->jointSpaceMatrix.entries[1], matrix->jointSpaceMatrix.entries[2]);
forward.normalize();
up.normalize();
right.normalize();
matrix->jointSpaceMatrix.entries[8] = forward.x; matrix->jointSpaceMatrix.entries[9] = forward.y; matrix->jointSpaceMatrix.entries[10] = forward.z;
matrix->jointSpaceMatrix.entries[4] = up.x; matrix->jointSpaceMatrix.entries[5] = up.y; matrix->jointSpaceMatrix.entries[6] = up.z;
matrix->jointSpaceMatrix.entries[0] = right.x; matrix->jointSpaceMatrix.entries[1] = right.y; matrix->jointSpaceMatrix.entries[2] = right.z;
matrix->jointSpaceMatrix.entries[15] = 1.0f; // this component is always 1.0! In some files, this is exported the wrong way, which causes bugs!
}
/********************************************************
* After the interpolation is finished, *
* we have to recalculate the joint's worldSpaceMatrix. *
********************************************************/
GEAR::Mat4 parentMat;
if(parent != NULL)
parentMat = parent->worldSpaceTransformationMatrix;
if(matrix != NULL)
worldSpaceTransformationMatrix = (parentMat * matrix->jointSpaceMatrix);
else
worldSpaceTransformationMatrix = parentMat;
skinningMatrix = worldSpaceTransformationMatrix*invBindPoseMatrix;
// also interpolate and recalculate all childs
for(unsigned k = 0; k < mChildJoints_.size(); ++k)
mChildJoints_[k]->interpolateMatrices();
}
Như bạn có thể thấy chúng tôi chỉ đơn giản là intrpolate tất cả các giá trị của ma trận và sau đó chúng ta chuẩn hóa 3 cột trên của ma trận. Sau đó, chúng tôi tính toán lại ngay lập tức worldSpaceMatrix cho phần khớp đó, cũng như ma trận tạo lớp hoàn chỉnh để lưu hiệu suất. Bây giờ chúng ta gần như hoàn chỉnh với tất cả. Điều cuối cùng cần làm là để thực sự animate đỉnh và sau đó để vẽ lưới:
for(int i = 0; i < mSubMeshes_.size(); ++i) {
for(int k = 0; k < mSubMeshes_[i]->mSubMeshes_.size(); ++k) {
// first we animate it
GEAR::DAESubMesh* submesh = mSubMeshes_[i]->mSubMeshes_[k];
submesh->buffer->lock(true);
{
for(unsigned v = 0; v < submesh->buffer->getNumVertices(); ++v) {
// get the array of joints, which influence the current vertex
DAEVertexInfo* vertexInfo = submesh->vertexInfo[v];
GEAR::Vec3 vertex; // do not init the vertex with any value!
float totalWeight = 0.0f;
for(int j = 0; j < vertexInfo->joints.size(); ++j) {
totalWeight += vertexInfo->joints[j]->weight;
vertex += ((vertexInfo->joints[j]->joint->skinningMatrix*(vertexInfo->vertex))*vertexInfo->joints[j]->weight);
}
// since it isn't guaranteed that the total weight is exactly 1.0, we have no normalize it
// @todo this should be moved to the parser
if(totalWeight != 1.0f) {
float normalizedWeight = 1.0f/totalWeight;
vertex *= normalizedWeight;
}
submesh->buffer->bufferVertexPos(v, vertex);
}
}
submesh->buffer->unlock();
mSubMeshes_[i]->mSubMeshes_[k]->buffer->draw(GEAR::TRIANGLES, 0, mSubMeshes_[i]->mSubMeshes_[k]->buffer->getNumVertices());
}
}
Tất cả trong tất cả đó là gần giống như đoạn code tôi bắt đầu với. Nhưng bây giờ mọi thứ rõ ràng hơn nhiều đối với tôi và tôi cũng có thể bắt đầu hỗ trợ < dịch >, <xoay> và < quy mô > hoạt ảnh. Hãy nhìn vào thực hiện của tôi tại gear3d.de (tải về thân cây SVN)
Tôi hy vọng điều này sẽ giúp một số người ngoài kia thực hiện giải pháp riêng của họ về chủ đề tuyệt vời này :)
+1; một câu hỏi tốt bằng văn bản với mã thiết yếu nhất định và mô tả chính xác về sai và kết quả chính xác mong đợi. – datenwolf