struct mesh_head_t
{
char common_tag[8];
char module_tag[8];
__int32 version;
char description[64];
byte4 filemask;
byte4 length;
byte4 materail_offset;
byte4 animation_offset;
byte4 reserved[10];
byte4 mesh_count;
byte4 vertex_count;
byte4 face_count;
byte4 subset_count;
byte4 pos_offset;
byte4 normal_offset;
byte4 color1_offset;
byte4 tex1_offset;
byte4 tex2_offset;
byte4 tex3_offset;
byte4 index_buffer_offset;
byte4 attri_buffer_offset;
byte4 skin_info_offset;
byte4 lod_info_offset;
byte4 reserved_ex[20];
};
void mesh_media_t::process()
{
if(!filedata)
return;
mesh_head_t* head = (mesh_head_t*)filedata->GetBufferPointer();
if( head->filemask != 0x4D455348 )
{
clear();
return;
}
if( !head->pos_offset || !head->tex1_offset || !head->index_buffer_offset )
{
clear();
return;
}
skin_info = new mesh_skin_info_t;
skin_info->d3dsi = 0;
//non-skinned mesh
if( !head->skin_info_offset )
return;
//skinned mesh
byte* ptr = (byte*)filedata->GetBufferPointer() + head->skin_info_offset;
skin_info->bone_vec.resize(*(byte4*)ptr);
ptr += 4;
if( !skin_info->bone_vec.empty() )
{
//create d3dx skin info (may be larger than needed)
if(FAILED(D3DXCreateSkinInfoFVF(head->vertex_count, D3DFVF_XYZ|D3DFVF_TEX1, (byte4)skin_info->bone_vec.size(), &skin_info->d3dsi)))
{
SAFE_DELETE(skin_info);
clear();
return;
}
}
for( byte4 i = 0; i < skin_info->bone_vec.size(); i++ )
{
strcpy(skin_info->bone_vec[i].name, (char*)ptr);
ptr += 30;
skin_info->bone_vec[i].parent_name = (char*)ptr;
ptr += 30;
byte4 num_child = *(byte4*)ptr;
ptr += 4 + num_child * 30;
skin_info->bone_vec[i].offset_matrix = *(matrix*)ptr;
ptr += 2 * sizeof(matrix);
byte4 num_infl = *(byte4*)ptr;
ptr += 4;
if( num_infl > 0 )
{
skin_info->real_bone_index_vec.push_back(i);
skin_info->d3dsi->SetBoneInfluence((byte4)skin_info->real_bone_index_vec.size() - 1, num_infl, (byte4*)ptr, (float*)(ptr + 4 * num_infl));
ptr += num_infl * (sizeof(byte4) + sizeof(float));
}
}
if( skin_info->real_bone_index_vec.empty() )
{
//non-skinned in fact
SAFE_RELEASE(skin_info->d3dsi);
}
//compute derived bone members
//parent index
for( byte4 i = 0; i < skin_info->bone_vec.size(); i++ )
{
skin_info->bone_vec[i].parent_index = -1;
for( byte4 j = 0; j < skin_info->bone_vec.size(); j++ )
{
if( strcmp(skin_info->bone_vec[i].parent_name.c_str(), skin_info->bone_vec[j].name) == 0 )
{
skin_info->bone_vec[i].parent_index = j;
skin_info->bone_vec[i].parent_name.clear();
break;
}
}
}
//soft type
for( byte4 i = 0; i < skin_info->bone_vec.size(); i++ )
{
if( strncmp(skin_info->bone_vec[i].name, "FL_", 3) == 0 )
skin_info->bone_vec[i].soft_type = _bone_soft_type_soft_;
else if( strncmp(skin_info->bone_vec[i].name, "GL_", 3) == 0 )
skin_info->bone_vec[i].soft_type = _bone_soft_type_gravity_;
else if( strncmp(skin_info->bone_vec[i].name, "AL_", 3) == 0)
skin_info->bone_vec[i].soft_type = _bone_soft_type_adjsoft_;
else
skin_info->bone_vec[i].soft_type = _bone_soft_type_normal_;
}
//soft level
for( byte4 i = 0; i < skin_info->bone_vec.size(); i++ )
skin_info->bone_vec[i].soft_level = -1;
for( byte4 i = 0; i < skin_info->bone_vec.size(); i++ )
_compute_bone_soft_level_recursive(skin_info, i);
//inv-offset matrix & relative matrix
for( byte4 i = 0; i < skin_info->bone_vec.size(); i++ )
{
D3DXMatrixInverse(&skin_info->bone_vec[i].inv_offset_matrix, 0, &skin_info->bone_vec[i].offset_matrix);
if( skin_info->bone_vec[i].parent_index != -1 )
skin_info->bone_vec[i].relative_matrix = skin_info->bone_vec[i].inv_offset_matrix * skin_info->bone_vec[skin_info->bone_vec[i].parent_index].offset_matrix;
else
skin_info->bone_vec[i].relative_matrix = skin_info->bone_vec[i].inv_offset_matrix;
}
//end of computing derived bone members
//sockets
skin_info->socket_vec.resize(*(byte4*)ptr);
ptr += 4;
for( byte4 i = 0; i < skin_info->socket_vec.size(); i++ )
{
strcpy( skin_info->socket_vec[i].name, (char*)ptr );
ptr += 30;
skin_info->socket_vec[i].parent_bone_name = (char*)ptr;
ptr += 30;
skin_info->socket_vec[i].relative_matrix = *(matrix*)ptr;
ptr += sizeof(matrix);
}
//compute derived socket members
for( byte4 i = 0; i < skin_info->socket_vec.size(); i++ )
{
skin_info->socket_vec[i].parent_bone_index = -1;
for( byte4 j = 0; j < skin_info->bone_vec.size(); j++ )
{
if( strcmp(skin_info->socket_vec[i].parent_bone_name.c_str(), skin_info->bone_vec[j].name) == 0 )
{
skin_info->socket_vec[i].parent_bone_index = j;
skin_info->socket_vec[i].parent_bone_name.clear();
break;
}
}
}
//end of computing derived socket members
}
void mesh_media_t::produce()
{
if( !filedata || !skin_info )
return;
mesh_head_t* head = (mesh_head_t*)filedata->GetBufferPointer();
//create the original mesh
byte4 mesh_option = 0;
if( !skin_info->d3dsi && gpu()->get_creation_parameters()->vertex_processing_method != _gpu_vertex_processing_method_software_ )
mesh_option |= D3DXMESH_MANAGED; //non-skinned && non-soft-vp
else
mesh_option |= D3DXMESH_SYSTEMMEM; //skinned or soft-vp
if( head->vertex_count > 0x0000ffff )
mesh_option |= D3DXMESH_32BIT;
LPD3DXMESH d3dmesh = 0;
if(FAILED(D3DXCreateMeshFVF(head->face_count, head->vertex_count, mesh_option, D3DFVF_XYZ|D3DFVF_TEX1, gpu()->d3ddevice(), &d3dmesh)))
{
clear();
return;
}
//load data
//vertex buffer
float3* v = (float3*)((byte*)filedata->GetBufferPointer() + head->pos_offset);
float3* uv = (float3*)((byte*)filedata->GetBufferPointer() + head->tex1_offset);
xyz_uv_t* vb_v = 0;
d3dmesh->LockVertexBuffer(0, (void**)&vb_v);
for( byte4 i = 0; i < head->vertex_count; i++ )
{
vb_v[i].xyz = v[i];
vb_v[i].uv.x = uv[i].x;
vb_v[i].uv.y = uv[i].y;
}
d3dmesh->UnlockVertexBuffer();
//index buffer
byte4* f = (byte4*)((byte*)filedata->GetBufferPointer() + head->index_buffer_offset);
void* ib_f;
d3dmesh->LockIndexBuffer(0, (void**)&ib_f);
if( mesh_option & D3DXMESH_32BIT )
memcpy(ib_f, f, head->face_count * 3 * 4);
else
{
for( byte4 i = 0; i < head->face_count * 3; i++ )
{
((byte2*)ib_f)[i] = (byte2)f[i];
}
}
d3dmesh->UnlockIndexBuffer();
//attribute buffer
byte4* ab_ss = 0;
d3dmesh->LockAttributeBuffer(0, (byte4**)&ab_ss);
if( !head->attri_buffer_offset )
{
memset(ab_ss, 0, head->face_count * 4);
}
else
{
byte4* ss = (byte4*)((byte*)filedata->GetBufferPointer() + head->attri_buffer_offset);
memcpy(ab_ss, ss, head->face_count * 4);
}
d3dmesh->UnlockAttributeBuffer();
//end of creating original mesh
//generate geo info
geo_info = new mesh_geo_info_t;
//clear ptrs
geo_info->vb = 0;
geo_info->ib = 0;
geo_info->decl = 0;
geo_info->attri_table_buf = 0;
if( !skin_info->d3dsi ) //non-skinned
{
//optimize & generate the attribute table
byte4* adj = new byte4[head->face_count * 3];
d3dmesh->GenerateAdjacency(0, adj);
d3dmesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_VERTEXCACHE, adj, 0, 0, 0);
SAFE_DELETE_ARRAY(adj);
//retrieve datas (decl is unused and left as 0) (infl_count_vec is unused and left as empty)
d3dmesh->GetVertexBuffer(&geo_info->vb);
geo_info->stride = d3dmesh->GetNumBytesPerVertex();
d3dmesh->GetIndexBuffer(&geo_info->ib);
geo_info->fvf = d3dmesh->GetFVF();
d3dmesh->GetAttributeTable(0, &geo_info->subset_count);
D3DXCreateBuffer(geo_info->subset_count * sizeof(D3DXATTRIBUTERANGE), &geo_info->attri_table_buf);
d3dmesh->GetAttributeTable((D3DXATTRIBUTERANGE*)geo_info->attri_table_buf->GetBufferPointer(), &geo_info->subset_count);
geo_info->palette_count = 0;
geo_info->max_infl_count = 0;
}
else //skinned
{
//generate adjacency
byte4* adj = new byte4[head->face_count * 3];
d3dmesh->GenerateAdjacency(0, adj);
//generate blended mesh
LPD3DXMESH blended_mesh = 0;
if( primary_skinning_method == _skinning_method_nonindexed_ || skin_info->real_bone_index_vec.size() == 1 ) //non-indexed
{
//pal count
geo_info->palette_count = 0;
byte4 flags = D3DXMESHOPT_VERTEXCACHE;
if( gpu()->get_creation_parameters()->vertex_processing_method == _gpu_vertex_processing_method_software_ )
flags |= D3DXMESH_SYSTEMMEM;
else
flags |= D3DXMESH_MANAGED;
//convert
if( FAILED(skin_info->d3dsi->ConvertToBlendedMesh( d3dmesh,
flags,
adj,
0, 0, 0,
(byte4*)&geo_info->max_infl_count,
(byte4*)&geo_info->subset_count,
&geo_info->attri_table_buf,
&blended_mesh )) ||
(geo_info->max_infl_count > 4) )
{
SAFE_DELETE_ARRAY(adj);
SAFE_DELETE(geo_info);
SAFE_RELEASE(d3dmesh);
clear();
return;
}
if( (flags & D3DXMESH_MANAGED) && (geo_info->max_infl_count > max(1, gpu()->get_hardware_caps()->max_matrix_blend_count)) )
{
LPD3DXMESH mesh_tmp = 0;
blended_mesh->CloneMeshFVF( D3DXMESH_SOFTWAREPROCESSING | blended_mesh->GetOptions(),
blended_mesh->GetFVF(),
gpu()->d3ddevice(), &mesh_tmp );
blended_mesh->Release();
blended_mesh = mesh_tmp;
mesh_tmp = 0;
}
//infl vec
LPD3DXBONECOMBINATION attr_table = (LPD3DXBONECOMBINATION)(geo_info->attri_table_buf->GetBufferPointer());
geo_info->infl_count_vec.resize(geo_info->subset_count);
for( byte4 i = 0; i < geo_info->subset_count; i++ )
{
geo_info->infl_count_vec[i] = 0;
for( byte4 j = 0; j < geo_info->max_infl_count; j++ )
{
if( attr_table[i].BoneId[j] != -1 )
{
geo_info->infl_count_vec[i] = j + 1;
}
}
}
}
else //indexed
{
//pal count
geo_info->palette_count = min((byte4)skin_info->real_bone_index_vec.size(), indexed_skinning_max_pal_count);
byte4 flags = D3DXMESHOPT_VERTEXCACHE;
switch( gpu()->get_creation_parameters()->vertex_processing_method )
{
case _gpu_vertex_processing_method_software_:
case _gpu_vertex_processing_method_mixed_:
flags |= D3DXMESH_SYSTEMMEM; //if the hardware indexed skinning is avaiable, we won't use mixed or software vertex processing method
break;
default:
flags |= D3DXMESH_MANAGED;
}
//convert
if( FAILED(skin_info->d3dsi->ConvertToIndexedBlendedMesh( d3dmesh,
flags,
geo_info->palette_count,
adj,
0, 0, 0,
(byte4*)&geo_info->max_infl_count,
(byte4*)&geo_info->subset_count,
&geo_info->attri_table_buf,
&blended_mesh )) ||
geo_info->max_infl_count > 4 )
{
SAFE_DELETE_ARRAY(adj);
SAFE_DELETE(geo_info);
SAFE_RELEASE(d3dmesh);
clear();
return;
}
}
//retrieve datas
blended_mesh->GetVertexBuffer(&geo_info->vb);
geo_info->stride = blended_mesh->GetNumBytesPerVertex();
blended_mesh->GetIndexBuffer(&geo_info->ib);
//fvf
geo_info->fvf = blended_mesh->GetFVF();
//decl will be generated later
SAFE_DELETE_ARRAY(adj);
SAFE_RELEASE(blended_mesh);
}
SAFE_RELEASE(d3dmesh);
SAFE_RELEASE(skin_info->d3dsi);
SAFE_RELEASE(filedata);
}
void mesh_media_t::postprocess()
{
if( !geo_info )
return;
if( !geo_info->max_infl_count )
return;
if( !geo_info->palette_count )
return;
if( ( primary_skinning_method == _skinning_method_indexed_vs_ ) ||
( primary_skinning_method == _skinning_method_indexed_ff_ && geo_info->max_infl_count == 1 &&
( gpu()->get_creation_parameters()->vertex_processing_method == _gpu_vertex_processing_method_software_ ||
gpu()->get_creation_parameters()->vertex_processing_method == _gpu_vertex_processing_method_mixed_ )))
{
byte4 fvf = geo_info->fvf;
if( fvf & D3DFVF_LASTBETA_UBYTE4 ) //conpensate for the lack of UBYTE4 on Geforce3
{
fvf &= ~D3DFVF_LASTBETA_UBYTE4;
fvf |= D3DFVF_LASTBETA_D3DCOLOR;
}
gpu()->create_vertex_declaration(fvf, &geo_info->decl);
geo_info->fvf = 0;
if( !geo_info->decl )
{
clear();
return;
}
}
}
media_t* create_mesh(const char* filename, void* arg)
{
return new mesh_media_t(filename);
}