[1/5] d3dx9: Add support for loading materials from X files.

Dylan Smith dylan.ah.smith at gmail.com
Tue May 24 15:09:58 CDT 2011


---
 dlls/d3dx9_36/mesh.c |  249 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 245 insertions(+), 4 deletions(-)

diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index 48ae20e..fa32797 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -1829,6 +1829,10 @@ struct mesh_data {
     DWORD num_normals;
     D3DXVECTOR3 *normals;
     DWORD *normal_indices;
+
+    DWORD num_materials;
+    D3DXMATERIAL *materials;
+    DWORD *material_indices;
 };
 
 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
@@ -1862,6 +1866,192 @@ static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **chi
     return hr;
 }
 
+static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
+{
+    HRESULT hr;
+    DWORD data_size;
+    BYTE *data;
+    char *filename_in;
+    char *filename = NULL;
+
+    /* template TextureFilename {
+     *     STRING filename;
+     * }
+     */
+
+    HeapFree(GetProcessHeap(), 0, *filename_out);
+    *filename_out = NULL;
+
+    hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
+    if (FAILED(hr)) return hr;
+
+    if (data_size < sizeof(LPSTR)) {
+        WARN("truncated data (%u bytes)\n", data_size);
+        return E_FAIL;
+    }
+    filename_in = *(LPSTR*)data;
+
+    filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
+    if (!filename) return E_OUTOFMEMORY;
+
+    strcpy(filename, filename_in);
+    *filename_out = filename;
+
+    return D3D_OK;
+}
+
+static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
+{
+    HRESULT hr;
+    DWORD data_size;
+    BYTE *data;
+    const GUID *type;
+    IDirectXFileData *child;
+
+    material->pTextureFilename = NULL;
+
+    hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
+    if (FAILED(hr)) return hr;
+
+    /*
+     * template ColorRGBA {
+     *     FLOAT red;
+     *     FLOAT green;
+     *     FLOAT blue;
+     *     FLOAT alpha;
+     * }
+     * template ColorRGB {
+     *     FLOAT red;
+     *     FLOAT green;
+     *     FLOAT blue;
+     * }
+     * template Material {
+     *     ColorRGBA faceColor;
+     *     FLOAT power;
+     *     ColorRGB specularColor;
+     *     ColorRGB emissiveColor;
+     *     [ ... ]
+     * }
+     */
+    if (data_size != sizeof(FLOAT) * 11) {
+        WARN("incorrect data size (%u bytes)\n", data_size);
+        return E_FAIL;
+    }
+
+    memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
+    data += sizeof(D3DCOLORVALUE);
+    material->MatD3D.Power = *(FLOAT*)data;
+    data += sizeof(FLOAT);
+    memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
+    material->MatD3D.Specular.a = 1.0f;
+    data += 3 * sizeof(FLOAT);
+    memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
+    material->MatD3D.Emissive.a = 1.0f;
+    material->MatD3D.Ambient.r = 0.0f;
+    material->MatD3D.Ambient.g = 0.0f;
+    material->MatD3D.Ambient.b = 0.0f;
+    material->MatD3D.Ambient.a = 1.0f;
+
+    while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
+    {
+        if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
+            hr = parse_texture_filename(child, &material->pTextureFilename);
+            if (FAILED(hr)) break;
+        }
+    }
+    return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
+}
+
+static void destroy_materials(struct mesh_data *mesh)
+{
+    int i;
+    for (i = 0; i < mesh->num_materials; i++)
+        HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
+    HeapFree(GetProcessHeap(), 0, mesh->materials);
+    HeapFree(GetProcessHeap(), 0, mesh->material_indices);
+    mesh->num_materials = 0;
+    mesh->materials = NULL;
+    mesh->material_indices = NULL;
+}
+
+static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
+{
+    HRESULT hr;
+    DWORD data_size;
+    DWORD *data, *in_ptr;
+    const GUID *type;
+    IDirectXFileData *child;
+    DWORD num_materials;
+    int i;
+
+    destroy_materials(mesh);
+
+    hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
+    if (FAILED(hr)) return hr;
+
+    /* template MeshMaterialList {
+     *     DWORD nMaterials;
+     *     DWORD nFaceIndexes;
+     *     array DWORD faceIndexes[nFaceIndexes];
+     *     [ Material ]
+     * }
+     */
+
+    in_ptr = data;
+
+    if (data_size < sizeof(DWORD))
+        goto truncated_data_error;
+    num_materials = *in_ptr++;
+    if (!num_materials)
+        return D3D_OK;
+
+    if (data_size < 2 * sizeof(DWORD))
+        goto truncated_data_error;
+    if (*in_ptr++ != mesh->num_poly_faces) {
+        WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
+             *(in_ptr - 1), mesh->num_poly_faces);
+        return E_FAIL;
+    }
+    if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
+        goto truncated_data_error;
+    for (i = 0; i < mesh->num_poly_faces; i++) {
+        if (*in_ptr++ >= num_materials) {
+            WARN("face %u: reference to undefined material %u (only %u materials)\n",
+                 i, *(in_ptr - 1), num_materials);
+            return E_FAIL;
+        }
+    }
+
+    mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
+    mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
+    if (!mesh->materials || !mesh->material_indices)
+        return E_OUTOFMEMORY;
+    memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
+
+    while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
+    {
+        if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
+            if (mesh->num_materials >= num_materials) {
+                WARN("more materials defined than declared\n");
+                return E_FAIL;
+            }
+            hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
+            if (FAILED(hr)) break;
+        }
+    }
+    if (hr != DXFILEERR_NOMOREOBJECTS)
+        return hr;
+    if (num_materials != mesh->num_materials) {
+        WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
+        return E_FAIL;
+    }
+
+    return D3D_OK;
+truncated_data_error:
+    WARN("truncated data (%u bytes)\n", data_size);
+    return E_FAIL;
+}
+
 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
 {
     HRESULT hr;
@@ -2059,8 +2249,7 @@ static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_dat
         } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
                    (provide_flags & PROVIDE_MATERIALS))
         {
-            FIXME("Mesh material list loading not implemented.\n");
-            hr = E_NOTIMPL;
+            hr = parse_material_list(child, mesh_data);
         } else if (provide_flags & PROVIDE_SKININFO) {
             if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
                 FIXME("Skin mesh loading not implemented.\n");
@@ -2095,6 +2284,7 @@ static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
     DWORD total_vertices;
     ID3DXMesh *d3dxmesh = NULL;
     ID3DXBuffer *adjacency = NULL;
+    ID3DXBuffer *materials = NULL;
     struct vertex_duplication {
         DWORD normal_index;
         struct list entry;
@@ -2226,6 +2416,55 @@ static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
 #undef FILL_INDEX_BUFFER
     d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
 
+    if (mesh_data.material_indices) {
+        DWORD *attrib_buffer = NULL;
+        hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, D3DLOCK_DISCARD, &attrib_buffer);
+        if (FAILED(hr)) goto cleanup;
+        for (i = 0; i < mesh_data.num_poly_faces; i++)
+        {
+            DWORD count = mesh_data.num_tri_per_face[i];
+            while (count--)
+                *attrib_buffer++ = mesh_data.material_indices[i];
+        }
+        d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
+
+        hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
+                D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
+                NULL, NULL, NULL, NULL);
+        if (FAILED(hr)) goto cleanup;
+    }
+
+    if (mesh_data.num_materials && materials_out) {
+        DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
+        char *strings_out_ptr;
+        D3DXMATERIAL *materials_ptr;
+
+        for (i = 0; i < mesh_data.num_materials; i++) {
+            if (mesh_data.materials[i].pTextureFilename)
+                buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
+        }
+
+        hr = D3DXCreateBuffer(buffer_size, &materials);
+        if (FAILED(hr)) goto cleanup;
+
+        materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
+        memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
+        strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
+        for (i = 0; i < mesh_data.num_materials; i++) {
+            if (materials_ptr[i].pTextureFilename) {
+                strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
+                materials_ptr[i].pTextureFilename = strings_out_ptr;
+                strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
+            }
+        }
+    }
+
+    if (mesh_data.num_materials && effects_out) {
+        FIXME("Effect instance generation not supported.\n");
+        hr = E_NOTIMPL;
+        goto cleanup;
+    }
+
     if (adjacency_out) {
         hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
         if (FAILED(hr)) goto cleanup;
@@ -2235,8 +2474,8 @@ static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
 
     *mesh_out = d3dxmesh;
     if (adjacency_out) *adjacency_out = adjacency;
-    if (num_materials_out) *num_materials_out = 0;
-    if (materials_out) *materials_out = NULL;
+    if (num_materials_out) *num_materials_out = mesh_data.num_materials;
+    if (materials_out) *materials_out = materials;
     if (effects_out) *effects_out = NULL;
     if (skin_info_out) *skin_info_out = NULL;
 
@@ -2245,12 +2484,14 @@ cleanup:
     if (FAILED(hr)) {
         if (d3dxmesh) IUnknown_Release(d3dxmesh);
         if (adjacency) ID3DXBuffer_Release(adjacency);
+        if (materials) ID3DXBuffer_Release(materials);
     }
     HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
     HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
     HeapFree(GetProcessHeap(), 0, mesh_data.indices);
     HeapFree(GetProcessHeap(), 0, mesh_data.normals);
     HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
+    destroy_materials(&mesh_data);
     HeapFree(GetProcessHeap(), 0, duplications);
     return hr;
 }
-- 
1.7.4.1




More information about the wine-patches mailing list