DGLE_RESULT DGLE_API CMesh::RecalculateTangentSpace() { if (!_pBuffer) return S_FALSE; TDrawDataDesc desc; _pBuffer->GetBufferDrawDataDesc(desc); if (desc.uiTextureVertexOffset == -1 || (desc.uiTangentOffset == -1 && desc.uiBinormalOffset != -1) || (desc.uiTangentOffset != -1 && desc.uiBinormalOffset == -1)) // confusing situation should never happen return E_ABORT; if (desc.uiNormalOffset == -1) RecalculateNormals(false); uint stride, verts_data_size, verts_count, idxs_data_size, idxs_count; _CopyMeshData(desc, stride, verts_data_size, verts_count, idxs_data_size, idxs_count); TDrawDataDesc desc_new(desc); bool do_delete_new = false; if (desc.uiTangentOffset == -1 && desc.uiBinormalOffset == -1) { do_delete_new = true; const uint new_verts_data_size = verts_data_size + verts_count * 6 * sizeof(float); desc_new.pData = new uint8[new_verts_data_size + idxs_data_size]; memcpy(desc_new.pData, desc.pData, verts_data_size); desc_new.uiTangentOffset = verts_data_size; desc_new.uiTangentStride = 0; desc_new.uiBinormalOffset = verts_data_size + verts_count * 3 * sizeof(float); desc_new.uiBinormalStride = 0; if (idxs_count != 0) { desc_new.pIndexBuffer = &desc_new.pData[new_verts_data_size]; memcpy(&desc_new.pData[new_verts_data_size], &desc.pData[verts_data_size], idxs_data_size); } memset(&desc_new.pData[verts_data_size], 0, new_verts_data_size - verts_data_size); } else { const uint t_stride = desc_new.uiTangentStride == 0 ? 3 * sizeof(float) : desc_new.uiTangentStride, b_stride = desc_new.uiBinormalStride == 0 ? 3 * sizeof(float) : desc_new.uiBinormalStride; for (uint i = 0; i < verts_count; ++i) { *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiTangentOffset + i * t_stride])) = TVector3(); *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiBinormalOffset + i * b_stride])) = TVector3(); } } const uint t_stride = desc_new.uiTangentStride == 0 ? 3 * sizeof(float) : desc_new.uiTangentStride, b_stride = desc_new.uiBinormalStride == 0 ? 3 * sizeof(float) : desc_new.uiBinormalStride, tex_stride = desc_new.uiTextureVertexStride == 0 ? 2 * sizeof(float) : desc_new.uiTextureVertexStride, v_stride = desc_new.uiVertexStride == 0 ? 3 * sizeof(float) : desc_new.uiVertexStride, n_stride = desc_new.uiNormalStride == 0 ? 3 * sizeof(float) : desc_new.uiNormalStride, count = idxs_count == 0 ? verts_count / 3 : idxs_count / 3; for(uint i = 0; i < count; ++i) { uint face[3]; if (idxs_count == 0) { face[0] = i * 3; face[1] = i * 3 + 1; face[2] = i * 3 + 2; } else if (desc_new.bIndexBuffer32) { face[0] = reinterpret_cast<uint *>(&desc_new.pIndexBuffer[i * 3 * sizeof(uint)])[0]; face[1] = reinterpret_cast<uint *>(&desc_new.pIndexBuffer[i * 3 * sizeof(uint)])[1]; face[2] = reinterpret_cast<uint *>(&desc_new.pIndexBuffer[i * 3 * sizeof(uint)])[2]; } else { face[0] = reinterpret_cast<uint16 *>(&desc_new.pIndexBuffer[i * 3 * sizeof(uint16)])[0]; face[1] = reinterpret_cast<uint16 *>(&desc_new.pIndexBuffer[i * 3 * sizeof(uint16)])[1]; face[2] = reinterpret_cast<uint16 *>(&desc_new.pIndexBuffer[i * 3 * sizeof(uint16)])[2]; } const TPoint3 * const v[3] = { reinterpret_cast<TPoint3 *>(&desc_new.pData[face[0] * v_stride]), reinterpret_cast<TPoint3 *>(&desc_new.pData[face[1] * v_stride]), reinterpret_cast<TPoint3 *>(&desc_new.pData[face[2] * v_stride])}; const TPoint2 * const t[3] = { reinterpret_cast<TPoint2 *>(&desc_new.pData[desc_new.uiTextureVertexOffset + face[0] * tex_stride]), reinterpret_cast<TPoint2 *>(&desc_new.pData[desc_new.uiTextureVertexOffset + face[1] * tex_stride]), reinterpret_cast<TPoint2 *>(&desc_new.pData[desc_new.uiTextureVertexOffset + face[2] * tex_stride])}; const TVector3 v1 = *v[1] - *v[0], v2 = *v[2] - *v[0]; const TVector2 v3 = *t[1] - *t[0], v4 = *t[2] - *t[0]; const float d = v3.Cross(v4); if (d == 0.f) continue; const float r = 1.f / d; const TVector3 sdir = TVector3(v4.y * v1.x - v4.x * v2.x, v4.y * v1.y - v4.x * v2.y, v4.y * v1.z - v4.x * v2.z) * r, tdir = TVector3(v3.x * v2.x - v3.y * v1.x, v3.x * v2.y - v3.y * v1.y, v3.x * v2.z - v3.y * v1.z) * r; *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiTangentOffset + face[0] * t_stride])) += sdir; *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiTangentOffset + face[1] * t_stride])) += sdir; *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiTangentOffset + face[2] * t_stride])) += sdir; *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiBinormalOffset + face[0] * b_stride])) += tdir; *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiBinormalOffset + face[1] * b_stride])) += tdir; *(reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiBinormalOffset + face[2] * b_stride])) += tdir; } for (uint i = 0; i < verts_count; ++i) { const TVector3 * const normal = reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiNormalOffset + i * n_stride]); TVector3 *tangent = reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiTangentOffset + i * t_stride]), *binormal = reinterpret_cast<TVector3 *>(&desc_new.pData[desc_new.uiBinormalOffset + i * b_stride]); float dot_1 = normal->Dot(*tangent), dot_2; *tangent = (*tangent - *normal * dot_1).Normalize(); dot_1 = normal->Dot(*tangent); dot_2 = tangent->Dot(*binormal); *binormal = (*binormal - *normal * dot_1 + *tangent * dot_2).Normalize(); } PARANOIC_CHECK_RES(_pBuffer->Reallocate(desc_new, verts_count, idxs_count, CRDM_TRIANGLES)); if (do_delete_new) delete[] desc_new.pData; delete[] desc.pData; return S_OK; }