[4/5] d3dx9: Implement D3DXMESHOPT_ATTRSORT vertex reordering. (try 2)

Dylan Smith dylan.ah.smith at gmail.com
Mon May 9 05:31:14 CDT 2011


---
try 2:
- Use include/wine/list.h for linked list.
- Use separate heap allocations for separate arrays.
---
 dlls/d3dx9_36/mesh.c |  130 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 124 insertions(+), 6 deletions(-)

diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index eaa9763..45f5f70 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -32,6 +32,7 @@
 #include "d3dx9.h"
 #include "wine/debug.h"
 #include "wine/unicode.h"
+#include "wine/list.h"
 #include "d3dx9_36_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
@@ -665,6 +666,24 @@ static int attrib_entry_compare(const DWORD **a, const DWORD **b)
     return delta;
 }
 
+struct vertex_attrib_duplication {
+    DWORD attrib;
+    DWORD vertex_index;
+    union {
+        int orig_order;
+        struct list entry;
+    } u;
+};
+
+static int vertex_attrib_compare(const struct vertex_attrib_duplication *a,
+                                 const struct vertex_attrib_duplication *b)
+{
+    int delta = a->attrib - b->attrib;
+    if (delta) return delta;
+    delta = a->u.orig_order - b->u.orig_order;
+    return delta;
+}
+
 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
                                                     DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
 {
@@ -751,15 +770,114 @@ static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flag
         DWORD last_attribute;
         DWORD min_vertex, max_vertex;
 
+        hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
+        if (FAILED(hr)) goto cleanup;
+
         if (!(flags & D3DXMESHOPT_IGNOREVERTS))
         {
-            FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
-            hr = E_NOTIMPL;
-            goto cleanup;
-        }
+            /* Reorder vertices */
+            BOOL is_splitting = !(flags & D3DXMESHOPT_DONOTSPLIT);
+            DWORD max_entries = is_splitting ? This->numfaces * 3 : This->numvertices;
+            struct vertex_attrib_duplication *next_duplicate = NULL;
+            DWORD num_duplicates = 0;
+            struct list *heads = NULL; /* head of list for each vertex */
+            struct vertex_attrib_duplication *duplications;
+            DWORD *vertex_remap_ptr;
+
+            heads = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*heads));
+            if (!heads) {
+                hr = E_OUTOFMEMORY;
+                goto cleanup;
+            }
+            duplications = HeapAlloc(GetProcessHeap(), 0, max_entries * sizeof(*duplications));
+            if (!duplications) {
+                HeapFree(GetProcessHeap(), 0, heads);
+                hr = E_OUTOFMEMORY;
+                goto cleanup;
+            }
+            for (i = 0; i < This->numvertices; i++)
+                list_init(&heads[i]);
+            next_duplicate = duplications;
 
-        hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
-        if (FAILED(hr)) goto cleanup;
+            for (i = 0; i < This->numfaces * 3; i++)
+            {
+                DWORD vertex_index = dword_indices[i];
+                struct list *vertex_list = &heads[vertex_index];
+                struct vertex_attrib_duplication *dup_ptr;
+                if (list_empty(vertex_list)) {
+                    dup_ptr = next_duplicate++;
+                    dup_ptr->attrib = attrib_buffer[i / 3];
+                    dup_ptr->vertex_index = vertex_index;
+                    list_add_tail(vertex_list, &dup_ptr->u.entry);
+                } else if (is_splitting) {
+                    DWORD attrib = attrib_buffer[i / 3];
+                    BOOL found = FALSE;
+                    LIST_FOR_EACH_ENTRY(dup_ptr, vertex_list, struct vertex_attrib_duplication, u.entry)
+                    {
+                        if (dup_ptr->attrib == attrib) {
+                            found = TRUE;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        num_duplicates++;
+                        dup_ptr = next_duplicate++;
+                        dup_ptr->attrib = attrib;
+                        dup_ptr->vertex_index = vertex_index;
+                        list_add_tail(vertex_list, &dup_ptr->u.entry);
+                    }
+                }
+            }
+            new_num_vertices = next_duplicate - duplications;
+            /* Sorting must be a stable sort to match native behaviour.
+             * The orig_order field mimics the desired relative original order
+             * for use in vertex_attrib_compare. */
+            for (i = 0; i < new_num_vertices; i++)
+                duplications[i].u.orig_order = This->numvertices + i;
+            for (i = 0; i < This->numvertices; i++) {
+                if (!list_empty(&heads[i]))
+                    LIST_ENTRY(list_head(&heads[i]), struct vertex_attrib_duplication, u.entry)->u.orig_order = i;
+            }
+            qsort(duplications, new_num_vertices, sizeof(*duplications),
+                  (int(*)(const void *, const void *))vertex_attrib_compare);
+            /* reconstruct the list of duplications for each vertex index */
+            for (i = 0; i < This->numvertices; i++)
+                list_init(&heads[i]);
+            for (i = 0; i < new_num_vertices; i++)
+                list_add_tail(&heads[duplications[i].vertex_index], &duplications[i].u.entry);
+
+            /* convert indices */
+            for (i = 0; i < This->numfaces * 3; i++) {
+                struct list *vertex_list = &heads[dword_indices[i]];
+                struct vertex_attrib_duplication *dup_ptr;
+                DWORD attrib = attrib_buffer[i / 3];
+
+                LIST_FOR_EACH_ENTRY(dup_ptr, vertex_list, struct vertex_attrib_duplication, u.entry)
+                {
+                    if (attrib == dup_ptr->attrib)
+                        dword_indices[i] = dup_ptr - duplications;
+                }
+            }
+
+            HeapFree(GetProcessHeap(), 0, heads);
+
+            /* native behaviour is to allocate a buffer this big, even though using new_num_vertices
+             * would make more sense. */
+            new_vertex_buffer_size = This->numvertices + num_duplicates;
+            hr = D3DXCreateBuffer(new_vertex_buffer_size * sizeof(DWORD), &vertex_remap);
+            if (FAILED(hr)) {
+                HeapFree(GetProcessHeap(), 0, duplications);
+                goto cleanup;
+            }
+            /* create new->old vertex map */
+            vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
+            for (i = 0; i < new_num_vertices; i++)
+                *vertex_remap_ptr++ = duplications[i].vertex_index;
+            for (; i < This->numvertices + num_duplicates; i++)
+                *vertex_remap_ptr++ = -1;
+
+            HeapFree(GetProcessHeap(), 0, duplications);
+        }
 
         /* Reorder faces in order to sort the attribute table */
         face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*face_remap));
-- 
1.7.2.5




More information about the wine-patches mailing list