From: Jannis Lübke <luebke.jannis@t-online.de> Some applications use this for hit-testing. Wine already largely provides the base for this since hit-testing a single triangle was already implemented, only hit-testing a mesh was missing. --- dlls/d3dx9_36/mesh.c | 193 +++++++++++++++++++++++++++++++++++-- dlls/d3dx9_36/tests/mesh.c | 98 +++++++++---------- 2 files changed, 236 insertions(+), 55 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index 462b6bfca7c..d67a5528f31 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -7650,16 +7650,196 @@ HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjac adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL); } +static HRESULT d3dx_intersect(ID3DXBaseMesh *mesh, BOOL filter_by_attribute, DWORD attrib_id, + const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir, BOOL *hit, DWORD *face_index, + float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits) +{ + D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]; + DWORD pos_offset = 0, vertex_size, num_faces, hit_count = 0; + struct dynamic_array hits = { 0 }; + D3DXINTERSECTINFO nearest_hit = { 0 }; + DWORD face_start = 0, face_end, i; + BOOL found_position = FALSE, indices_32bit; + ID3DXBuffer *buffer = NULL; + const BYTE *vertices = NULL; + const void *indices = NULL; + HRESULT hr; + + if (!mesh || !ray_pos || !ray_dir) + return D3DERR_INVALIDCALL; + + if (!hit) + return D3DERR_INVALIDCALL; + + if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration))) + return hr; + + for (i = 0; i < MAX_FVF_DECL_SIZE && declaration[i].Stream != 0xff; i++) + { + if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex + && declaration[i].Type == D3DDECLTYPE_FLOAT3) + { + pos_offset = declaration[i].Offset; + found_position = TRUE; + break; + } + } + + if (!found_position) + { + WARN("Mesh has no float3 position element.\n"); + return D3DERR_INVALIDCALL; + } + + num_faces = mesh->lpVtbl->GetNumFaces(mesh); + vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh); + indices_32bit = !!(mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT); + face_end = num_faces; + + if (filter_by_attribute) + { + D3DXATTRIBUTERANGE *attrib_table; + DWORD attrib_table_size = 0; + + if (FAILED(hr = mesh->lpVtbl->GetAttributeTable(mesh, NULL, &attrib_table_size))) + return hr; + + if (!attrib_table_size) + { + WARN("Mesh has no attribute table.\n"); + return D3DERR_INVALIDCALL; + } + + if (!(attrib_table = malloc(attrib_table_size * sizeof(*attrib_table)))) + return E_OUTOFMEMORY; + + if (SUCCEEDED(hr = mesh->lpVtbl->GetAttributeTable(mesh, attrib_table, &attrib_table_size))) + { + for (i = 0; i < attrib_table_size; i++) + { + if (attrib_table[i].AttribId != attrib_id) + continue; + + face_start = attrib_table[i].FaceStart; + face_end = min(num_faces, face_start + attrib_table[i].FaceCount); + break; + } + } + free(attrib_table); + + if (FAILED(hr)) + return hr; + + if (i == attrib_table_size) + { + *hit = FALSE; + return D3D_OK; + } + } + + if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void **)&vertices))) + goto done; + if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void **)&indices))) + goto done; + + for (i = face_start; i < face_end; i++) + { + const D3DXVECTOR3 *vertex[3]; + float face_u, face_v, face_dist; + unsigned int j; + + for (j = 0; j < 3; j++) + { + DWORD index = indices_32bit ? ((const DWORD *)indices)[i * 3 + j] + : ((const WORD *)indices)[i * 3 + j]; + + vertex[j] = (const D3DXVECTOR3 *)(vertices + index * vertex_size + pos_offset); + } + + if (!D3DXIntersectTri(vertex[0], vertex[1], vertex[2], ray_pos, ray_dir, &face_u, &face_v, &face_dist)) + continue; + + if (all_hits) + { + D3DXINTERSECTINFO *info; + + if (!reserve(&hits, hit_count + 1, sizeof(*info))) + { + hr = E_OUTOFMEMORY; + goto done; + } + info = &((D3DXINTERSECTINFO *)hits.items)[hit_count]; + info->FaceIndex = i; + info->U = face_u; + info->V = face_v; + info->Dist = face_dist; + } + + if (!hit_count || face_dist < nearest_hit.Dist) + { + nearest_hit.FaceIndex = i; + nearest_hit.U = face_u; + nearest_hit.V = face_v; + nearest_hit.Dist = face_dist; + } + + hit_count++; + } + + if (all_hits && hit_count) + { + if (FAILED(hr = D3DXCreateBuffer(hit_count * sizeof(D3DXINTERSECTINFO), &buffer))) + goto done; + memcpy(ID3DXBuffer_GetBufferPointer(buffer), hits.items, hit_count * sizeof(D3DXINTERSECTINFO)); + } + + *hit = !!hit_count; + if (count_of_hits) + *count_of_hits = hit_count; + + if (hit_count) + { + if (face_index) + *face_index = nearest_hit.FaceIndex; + if (u) + *u = nearest_hit.U; + if (v) + *v = nearest_hit.V; + if (distance) + *distance = nearest_hit.Dist; + } + + if (all_hits) + { + *all_hits = buffer; + buffer = NULL; + } + + hr = D3D_OK; + +done: + if (buffer) + ID3DXBuffer_Release(buffer); + if (indices) + mesh->lpVtbl->UnlockIndexBuffer(mesh); + if (vertices) + mesh->lpVtbl->UnlockVertexBuffer(mesh); + free(hits.items); + + return hr; +} + /************************************************************************* * D3DXIntersect (D3DX9_36.@) */ HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir, BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits) { - FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, " - "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits); + TRACE("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, " + "count_of_hits %p.\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits); - return E_NOTIMPL; + return d3dx_intersect(mesh, FALSE, 0, ray_pos, ray_dir, hit, face_index, u, v, distance, + all_hits, count_of_hits); } /************************************************************************* @@ -7669,11 +7849,12 @@ HRESULT WINAPI D3DXIntersectSubset(ID3DXBaseMesh *mesh, DWORD attrib_id, const D const D3DXVECTOR3 *ray_dir, BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits) { - FIXME("mesh %p, attrib_id %lu, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, " - "all_hits %p, count_of_hits %p stub!\n", mesh, attrib_id, ray_pos, ray_dir, hit, face_index, u, v, + TRACE("mesh %p, attrib_id %lu, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, " + "all_hits %p, count_of_hits %p.\n", mesh, attrib_id, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits); - return E_NOTIMPL; + return d3dx_intersect(mesh, TRUE, attrib_id, ray_pos, ray_dir, hit, face_index, u, v, distance, + all_hits, count_of_hits); } HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs, diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index d1510704558..38c3b10d775 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -12052,22 +12052,22 @@ static void test_intersect(void) ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect(NULL, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, NULL, &ray_dir, &hit, &face_index, &u, &v, &dist, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, NULL, &hit, &face_index, &u, &v, &dist, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, &ray_dir, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, &ray_dir, NULL, &face_index, NULL, NULL, NULL, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, &ray_dir, NULL, NULL, NULL, NULL, NULL, NULL, &count); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, &ray_dir, NULL, NULL, NULL, NULL, NULL, &all_hits, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, &ray_dir, &hit, NULL, NULL, NULL, NULL, NULL, NULL); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); hit = FALSE; face_index = 0xdeadbeef; @@ -12076,14 +12076,14 @@ static void test_intersect(void) all_hits = NULL; hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, &all_hits, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(hit, "Expected a hit.\n"); - todo_wine ok(!face_index, "Got unexpected face index %lu.\n", face_index); - todo_wine ok(compare(u, 0.25f), "Got unexpected u %.8e.\n", u); - todo_wine ok(compare(v, 0.25f), "Got unexpected v %.8e.\n", v); - todo_wine ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); - todo_wine ok(count == 2, "Got unexpected hit count %lu.\n", count); - todo_wine ok(!!all_hits, "Expected a hit buffer.\n"); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hit, "Expected a hit.\n"); + ok(!face_index, "Got unexpected face index %lu.\n", face_index); + ok(compare(u, 0.25f), "Got unexpected u %.8e.\n", u); + ok(compare(v, 0.25f), "Got unexpected v %.8e.\n", v); + ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); + ok(count == 2, "Got unexpected hit count %lu.\n", count); + ok(!!all_hits, "Expected a hit buffer.\n"); if (all_hits) { ok(ID3DXBuffer_GetBufferSize(all_hits) == 2 * sizeof(*hit_info), @@ -12106,11 +12106,11 @@ static void test_intersect(void) dist = -1.0f; hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos_second, &ray_dir, &hit, &face_index, &u, &v, &dist, NULL, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(hit, "Expected a hit.\n"); - todo_wine ok(face_index == 1, "Got unexpected face index %lu.\n", face_index); - todo_wine ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); - todo_wine ok(count == 2, "Got unexpected hit count %lu.\n", count); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hit, "Expected a hit.\n"); + ok(face_index == 1, "Got unexpected face index %lu.\n", face_index); + ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); + ok(count == 2, "Got unexpected hit count %lu.\n", count); hit = TRUE; face_index = 0xdeadbeef; @@ -12119,9 +12119,9 @@ static void test_intersect(void) all_hits = NULL; hr = D3DXIntersect((ID3DXBaseMesh *)mesh, &ray_pos_miss, &ray_dir, &hit, &face_index, &u, &v, &dist, &all_hits, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!hit, "Expected no hit.\n"); - todo_wine ok(!count, "Got unexpected hit count %lu.\n", count); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(!hit, "Expected no hit.\n"); + ok(!count, "Got unexpected hit count %lu.\n", count); ok(!all_hits, "Expected no hit buffer.\n"); ok(face_index == 0xdeadbeef, "Got unexpected face index %lu.\n", face_index); ok(u == -1.0f, "Got unexpected u %.8e.\n", u); @@ -12129,10 +12129,10 @@ static void test_intersect(void) ok(dist == -1.0f, "Got unexpected distance %.8e.\n", dist); hr = D3DXIntersectSubset(NULL, 0, &ray_pos, &ray_dir, &hit, NULL, NULL, NULL, NULL, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = D3DXIntersectSubset((ID3DXBaseMesh *)mesh, 0, &ray_pos, &ray_dir, &hit, NULL, NULL, NULL, NULL, NULL, NULL); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); hr = mesh->lpVtbl->OptimizeInplace(mesh, D3DXMESHOPT_ATTRSORT, NULL, NULL, NULL, NULL); ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); @@ -12148,14 +12148,14 @@ static void test_intersect(void) all_hits = NULL; hr = D3DXIntersectSubset((ID3DXBaseMesh *)mesh, 0, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, &all_hits, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(hit, "Expected a hit.\n"); - todo_wine ok(!face_index, "Got unexpected face index %lu.\n", face_index); - todo_wine ok(compare(u, 0.25f), "Got unexpected u %.8e.\n", u); - todo_wine ok(compare(v, 0.25f), "Got unexpected v %.8e.\n", v); - todo_wine ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); - todo_wine ok(count == 1, "Got unexpected hit count %lu.\n", count); - todo_wine ok(!!all_hits, "Expected a hit buffer.\n"); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hit, "Expected a hit.\n"); + ok(!face_index, "Got unexpected face index %lu.\n", face_index); + ok(compare(u, 0.25f), "Got unexpected u %.8e.\n", u); + ok(compare(v, 0.25f), "Got unexpected v %.8e.\n", v); + ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); + ok(count == 1, "Got unexpected hit count %lu.\n", count); + ok(!!all_hits, "Expected a hit buffer.\n"); if (all_hits) { ok(ID3DXBuffer_GetBufferSize(all_hits) == sizeof(*hit_info), @@ -12175,13 +12175,13 @@ static void test_intersect(void) u = v = -1.0f; hr = D3DXIntersectSubset((ID3DXBaseMesh *)mesh, 1, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, NULL, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(hit, "Expected a hit.\n"); - todo_wine ok(face_index == 2, "Got unexpected face index %lu.\n", face_index); - todo_wine ok(compare(u, 0.25f), "Got unexpected u %.8e.\n", u); - todo_wine ok(compare(v, 0.25f), "Got unexpected v %.8e.\n", v); - todo_wine ok(compare(dist, 2.0f), "Got unexpected distance %.8e.\n", dist); - todo_wine ok(count == 1, "Got unexpected hit count %lu.\n", count); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hit, "Expected a hit.\n"); + ok(face_index == 2, "Got unexpected face index %lu.\n", face_index); + ok(compare(u, 0.25f), "Got unexpected u %.8e.\n", u); + ok(compare(v, 0.25f), "Got unexpected v %.8e.\n", v); + ok(compare(dist, 2.0f), "Got unexpected distance %.8e.\n", dist); + ok(count == 1, "Got unexpected hit count %lu.\n", count); hit = TRUE; face_index = 0xdeadbeef; @@ -12190,8 +12190,8 @@ static void test_intersect(void) all_hits = NULL; hr = D3DXIntersectSubset((ID3DXBaseMesh *)mesh, 2, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, &all_hits, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!hit, "Expected no hit.\n"); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(!hit, "Expected no hit.\n"); ok(face_index == 0xdeadbeef, "Got unexpected face index %lu.\n", face_index); ok(u == -1.0f, "Got unexpected u %.8e.\n", u); ok(v == -1.0f, "Got unexpected v %.8e.\n", v); @@ -12223,11 +12223,11 @@ static void test_intersect(void) dist = -1.0f; hr = D3DXIntersectSubset((ID3DXBaseMesh *)mesh, 0, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, NULL, &count); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(hit, "Expected a hit.\n"); - todo_wine ok(!face_index, "Got unexpected face index %lu.\n", face_index); - todo_wine ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); - todo_wine ok(count == 1, "Got unexpected hit count %lu.\n", count); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hit, "Expected a hit.\n"); + ok(!face_index, "Got unexpected face index %lu.\n", face_index); + ok(compare(dist, 1.0f), "Got unexpected distance %.8e.\n", dist); + ok(count == 1, "Got unexpected hit count %lu.\n", count); mesh->lpVtbl->Release(mesh); winetest_pop_context(); @@ -12248,7 +12248,7 @@ static void test_intersect(void) count = 0xdeadbeef; hr = D3DXIntersect((ID3DXBaseMesh *)decl_mesh, &ray_pos, &ray_dir, &hit, &face_index, &u, &v, &dist, NULL, &count); - todo_wine ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#lx.\n", hr); decl_mesh->lpVtbl->Release(decl_mesh); winetest_pop_context(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11482