Files
FlaxEngine/Source/Engine/Graphics/Models/ModelData.cpp

433 lines
13 KiB
C++

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "ModelData.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/BoundingSphere.h"
#include "Engine/Animations/CurveSerialization.h"
#include "Engine/Serialization/WriteStream.h"
void MeshData::Clear()
{
MaterialSlotIndex = 0;
NodeIndex = 0;
Positions.Clear();
Indices.Clear();
UVs.Clear();
Normals.Clear();
Tangents.Clear();
BitangentSigns.Clear();
LightmapUVs.Clear();
Colors.Clear();
BlendIndices.Clear();
BlendWeights.Clear();
BlendShapes.Clear();
}
void MeshData::EnsureCapacity(int32 vertices, int32 indices, bool preserveContents, bool withColors, bool withSkin)
{
Positions.EnsureCapacity(vertices, preserveContents);
Indices.EnsureCapacity(indices, preserveContents);
UVs.EnsureCapacity(vertices, preserveContents);
Normals.EnsureCapacity(vertices, preserveContents);
Tangents.EnsureCapacity(vertices, preserveContents);
LightmapUVs.EnsureCapacity(vertices, preserveContents);
Colors.EnsureCapacity(withColors ? vertices : 0, preserveContents);
BlendIndices.EnsureCapacity(withSkin ? vertices : 0, preserveContents);
BlendWeights.EnsureCapacity(withSkin ? vertices : 0, preserveContents);
}
void MeshData::SwapBuffers(MeshData& other)
{
Positions.Swap(other.Positions);
Indices.Swap(other.Indices);
UVs.Swap(other.UVs);
Normals.Swap(other.Normals);
Tangents.Swap(other.Tangents);
BitangentSigns.Swap(other.BitangentSigns);
LightmapUVs.Swap(other.LightmapUVs);
Colors.Swap(other.Colors);
BlendIndices.Swap(other.BlendIndices);
BlendWeights.Swap(other.BlendWeights);
BlendShapes.Swap(other.BlendShapes);
}
void MeshData::Release()
{
MaterialSlotIndex = 0;
Positions.Resize(0);
Indices.Resize(0);
UVs.Resize(0);
Normals.Resize(0);
Tangents.Resize(0);
BitangentSigns.Resize(0);
LightmapUVs.Resize(0);
Colors.Resize(0);
BlendIndices.Resize(0);
BlendWeights.Resize(0);
BlendShapes.Resize(0);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void MeshData::InitFromModelVertices(ModelVertex19* vertices, uint32 verticesCount)
{
Positions.Resize(verticesCount, false);
UVs.Resize(verticesCount, false);
Normals.Resize(verticesCount, false);
Tangents.Resize(verticesCount, false);
BitangentSigns.Resize(0);
LightmapUVs.Resize(verticesCount, false);
Colors.Resize(0);
BlendIndices.Resize(0);
BlendWeights.Resize(0);
BlendShapes.Resize(0);
for (uint32 i = 0; i < verticesCount; i++)
{
Positions[i] = vertices->Position;
UVs[i] = vertices->TexCoord.ToFloat2();
Normals[i] = vertices->Normal.ToFloat3() * 2.0f - 1.0f;
Tangents[i] = vertices->Tangent.ToFloat3() * 2.0f - 1.0f;
LightmapUVs[i] = vertices->LightmapUVs.ToFloat2();
Colors[i] = Color(vertices->Color);
vertices++;
}
}
void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb1, uint32 verticesCount)
{
Positions.Resize(verticesCount, false);
UVs.Resize(verticesCount, false);
Normals.Resize(verticesCount, false);
Tangents.Resize(verticesCount, false);
BitangentSigns.Resize(0);
LightmapUVs.Resize(verticesCount, false);
Colors.Resize(0);
BlendIndices.Resize(0);
BlendWeights.Resize(0);
BlendShapes.Resize(0);
for (uint32 i = 0; i < verticesCount; i++)
{
Positions[i] = vb0->Position;
UVs[i] = vb1->TexCoord.ToFloat2();
Normals[i] = vb1->Normal.ToFloat3() * 2.0f - 1.0f;
Tangents[i] = vb1->Tangent.ToFloat3() * 2.0f - 1.0f;
LightmapUVs[i] = vb1->LightmapUVs.ToFloat2();
vb0++;
vb1++;
}
}
void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb1, VB2ElementType18* vb2, uint32 verticesCount)
{
Positions.Resize(verticesCount, false);
UVs.Resize(verticesCount, false);
Normals.Resize(verticesCount, false);
Tangents.Resize(verticesCount, false);
BitangentSigns.Resize(0);
LightmapUVs.Resize(verticesCount, false);
if (vb2)
{
Colors.Resize(verticesCount, false);
}
else
{
Colors.Resize(0);
}
BlendIndices.Resize(0);
BlendWeights.Resize(0);
BlendShapes.Resize(0);
for (uint32 i = 0; i < verticesCount; i++)
{
Positions[i] = vb0->Position;
UVs[i] = vb1->TexCoord.ToFloat2();
Normals[i] = vb1->Normal.ToFloat3() * 2.0f - 1.0f;
Tangents[i] = vb1->Tangent.ToFloat3() * 2.0f - 1.0f;
LightmapUVs[i] = vb1->LightmapUVs.ToFloat2();
if (vb2)
{
Colors[i] = Color(vb2->Color);
vb2++;
}
vb0++;
vb1++;
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void MeshData::SetIndexBuffer(void* data, uint32 indicesCount)
{
bool use16BitIndexBuffer = indicesCount <= MAX_uint16;
Indices.Resize(indicesCount, false);
if (use16BitIndexBuffer)
{
auto ib16 = static_cast<uint16*>(data);
for (uint32 a = 0; a < indicesCount; a++)
Indices[a] = *ib16++;
}
else
{
auto ib32 = static_cast<uint32*>(data);
for (uint32 a = 0; a < indicesCount; a++)
Indices[a] = *ib32++;
}
}
void MeshData::CalculateBox(BoundingBox& result) const
{
if (Positions.HasItems())
BoundingBox::FromPoints(Positions.Get(), Positions.Count(), result);
else
result = BoundingBox::Zero;
}
void MeshData::CalculateSphere(BoundingSphere& result) const
{
if (Positions.HasItems())
BoundingSphere::FromPoints(Positions.Get(), Positions.Count(), result);
else
result = BoundingSphere::Empty;
}
void MeshData::CalculateBounds(BoundingBox& box, BoundingSphere& sphere) const
{
if (Positions.HasItems())
{
// Merged code of BoundingBox::FromPoints and BoundingSphere::FromPoints within a single loop
const Float3* points = Positions.Get();
const int32 pointsCount = Positions.Count();
Float3 min = points[0];
Float3 max = min;
Float3 center = min;
for (int32 i = 1; i < pointsCount; i++)
Float3::Add(points[i], center, center);
center /= (float)pointsCount;
float radiusSq = Float3::DistanceSquared(center, min);
for (int32 i = 1; i < pointsCount; i++)
{
Float3::Min(min, points[i], min);
Float3::Max(max, points[i], max);
const float distance = Float3::DistanceSquared(center, points[i]);
if (distance > radiusSq)
radiusSq = distance;
}
box = BoundingBox(min, max);
sphere = BoundingSphere(center, Math::Sqrt(radiusSq));
}
else
{
box = BoundingBox::Zero;
sphere = BoundingSphere::Empty;
}
}
void MeshData::TransformBuffer(const Matrix& matrix)
{
// Compute matrix inverse transpose
Matrix inverseTransposeMatrix;
Matrix::Invert(matrix, inverseTransposeMatrix);
Matrix::Transpose(inverseTransposeMatrix, inverseTransposeMatrix);
// Transform blend shapes
for (auto& blendShape : BlendShapes)
{
const auto vv = blendShape.Vertices.Get();
for (int32 i = 0; i < blendShape.Vertices.Count(); i++)
{
auto& v = vv[i];
Float3 p = Positions[v.VertexIndex];
Float3 vp = p + v.PositionDelta;
Float3::Transform(vp, matrix, vp);
Float3::Transform(p, matrix, p);
v.PositionDelta = vp - p;
Float3 n = Normals[v.VertexIndex];
Float3 vn = n + v.NormalDelta;
vn.Normalize();
Float3::TransformNormal(vn, inverseTransposeMatrix, vn);
vn.Normalize();
Float3::TransformNormal(n, inverseTransposeMatrix, n);
n.Normalize();
v.NormalDelta = vn - n;
}
}
// Transform positions
const auto pp = Positions.Get();
for (int32 i = 0; i < Positions.Count(); i++)
{
auto& p = pp[i];
Float3::Transform(p, matrix, p);
}
// Transform normals and tangents
const auto nn = Normals.Get();
for (int32 i = 0; i < Normals.Count(); i++)
{
auto& n = nn[i];
Float3::TransformNormal(n, inverseTransposeMatrix, n);
n.Normalize();
}
const auto tt = Tangents.Get();
for (int32 i = 0; i < Tangents.Count(); i++)
{
auto& t = tt[i];
Float3::TransformNormal(t, inverseTransposeMatrix, t);
t.Normalize();
}
}
void MeshData::NormalizeBlendWeights()
{
ASSERT(Positions.Count() == BlendWeights.Count());
for (int32 i = 0; i < Positions.Count(); i++)
{
Float4& weights = BlendWeights.Get()[i];
const float sum = weights.SumValues();
const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f;
weights *= invSum;
}
}
void MeshData::Merge(MeshData& other)
{
// Merge index buffer (and remap indices)
const uint32 vertexIndexOffset = Positions.Count();
const int32 indicesStart = Indices.Count();
const int32 indicesEnd = indicesStart + other.Indices.Count();
Indices.Add(other.Indices);
for (int32 i = indicesStart; i < indicesEnd; i++)
{
Indices[i] += vertexIndexOffset;
}
// Merge vertex buffer
#define MERGE(item, defautValue) \
if (item.HasItems() && other.item.HasItems()) \
item.Add(other.item); \
else if (item.HasItems() && !other.item.HasItems()) \
for (int32 i = 0; i < other.Positions.Count(); i++) item.Add(defautValue); \
else if (!item.HasItems() && other.item.HasItems()) \
for (int32 i = 0; i < Positions.Count(); i++) item.Add(defautValue)
MERGE(Positions, Float3::Zero);
MERGE(UVs, Float2::Zero);
MERGE(Normals, Float3::Forward);
MERGE(Tangents, Float3::Right);
MERGE(BitangentSigns, 1.0f);
MERGE(LightmapUVs, Float2::Zero);
MERGE(Colors, Color::Black);
MERGE(BlendIndices, Int4::Zero);
MERGE(BlendWeights, Float4::Zero);
#undef MERGE
// Merge blend shapes
for (auto& otherBlendShape : other.BlendShapes)
{
BlendShape* blendShape = nullptr;
for (int32 i = 0; i < BlendShapes.Count(); i++)
{
if (BlendShapes[i].Name == otherBlendShape.Name)
{
blendShape = &BlendShapes[i];
break;
}
}
if (!blendShape)
{
blendShape = &BlendShapes.AddOne();
blendShape->Name = otherBlendShape.Name;
blendShape->Weight = otherBlendShape.Weight;
}
const int32 startIndex = blendShape->Vertices.Count();
blendShape->Vertices.Add(otherBlendShape.Vertices);
for (int32 i = startIndex; i < blendShape->Vertices.Count(); i++)
{
blendShape->Vertices[i].VertexIndex += vertexIndexOffset;
}
}
}
bool MaterialSlotEntry::UsesProperties() const
{
return Diffuse.Color != Color::White ||
Diffuse.TextureIndex != -1 ||
Emissive.Color != Color::Transparent ||
Emissive.TextureIndex != -1 ||
!Math::IsOne(Opacity.Value) ||
Opacity.TextureIndex != -1 ||
Math::NotNearEqual(Roughness.Value, 0.5f) ||
Roughness.TextureIndex != -1 ||
Normals.TextureIndex != -1;
}
float MaterialSlotEntry::ShininessToRoughness(float shininess)
{
// https://github.com/assimp/assimp/issues/4573
const float a = -1.0f;
const float b = 2.0f;
const float c = (shininess / 100) - 1;
const float d = b * b - (4 * a * c);
return (-b + Math::Sqrt(d)) / (2 * a);
}
ModelLodData::~ModelLodData()
{
Meshes.ClearDelete();
}
BoundingBox ModelLodData::GetBox() const
{
if (Meshes.IsEmpty())
return BoundingBox::Empty;
BoundingBox bounds;
Meshes[0]->CalculateBox(bounds);
for (int32 i = 1; i < Meshes.Count(); i++)
{
BoundingBox b;
Meshes[i]->CalculateBox(b);
BoundingBox::Merge(bounds, b, bounds);
}
return bounds;
}
void ModelData::CalculateLODsScreenSizes()
{
const float autoComputeLodPowerBase = 0.5f;
const int32 lodCount = LODs.Count();
for (int32 lodIndex = 0; lodIndex < lodCount; lodIndex++)
{
auto& lod = LODs[lodIndex];
if (lodIndex == 0)
{
lod.ScreenSize = 1.0f;
}
else
{
lod.ScreenSize = Math::Pow(autoComputeLodPowerBase, (float)lodIndex);
}
}
MinScreenSize = 0.01f;
}
void ModelData::TransformBuffer(const Matrix& matrix)
{
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
{
auto& lod = LODs[lodIndex];
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
{
lod.Meshes[meshIndex]->TransformBuffer(matrix);
}
}
}