1583 lines
45 KiB
C++
1583 lines
45 KiB
C++
#include "wiSceneSystem.h"
|
|
#include "wiMath.h"
|
|
#include "wiTextureHelper.h"
|
|
#include "wiResourceManager.h"
|
|
|
|
using namespace wiECS;
|
|
using namespace wiGraphicsTypes;
|
|
|
|
namespace wiSceneSystem
|
|
{
|
|
|
|
void TransformComponent::UpdateTransform()
|
|
{
|
|
if (dirty)
|
|
{
|
|
dirty = false;
|
|
|
|
XMVECTOR S_local = XMLoadFloat3(&scale_local);
|
|
XMVECTOR R_local = XMLoadFloat4(&rotation_local);
|
|
XMVECTOR T_local = XMLoadFloat3(&translation_local);
|
|
XMMATRIX W =
|
|
XMMatrixScalingFromVector(S_local) *
|
|
XMMatrixRotationQuaternion(R_local) *
|
|
XMMatrixTranslationFromVector(T_local);
|
|
|
|
scale = scale_local;
|
|
rotation = rotation_local;
|
|
translation = translation_local;
|
|
|
|
world_prev = world;
|
|
XMStoreFloat4x4(&world, W);
|
|
}
|
|
}
|
|
void TransformComponent::UpdateParentedTransform(const TransformComponent& parent, const XMFLOAT4X4& inverseParentBindMatrix)
|
|
{
|
|
XMVECTOR S_local = XMLoadFloat3(&scale_local);
|
|
XMVECTOR R_local = XMLoadFloat4(&rotation_local);
|
|
XMVECTOR T_local = XMLoadFloat3(&translation_local);
|
|
XMMATRIX W =
|
|
XMMatrixScalingFromVector(S_local) *
|
|
XMMatrixRotationQuaternion(R_local) *
|
|
XMMatrixTranslationFromVector(T_local);
|
|
|
|
XMMATRIX W_parent = XMLoadFloat4x4(&parent.world);
|
|
XMMATRIX B = XMLoadFloat4x4(&inverseParentBindMatrix);
|
|
W = W * B * W_parent;
|
|
|
|
XMVECTOR S, R, T;
|
|
XMMatrixDecompose(&S, &R, &T, W);
|
|
XMStoreFloat3(&scale, S);
|
|
XMStoreFloat4(&rotation, R);
|
|
XMStoreFloat3(&translation, T);
|
|
|
|
world_prev = world;
|
|
XMStoreFloat4x4(&world, W);
|
|
}
|
|
void TransformComponent::ApplyTransform()
|
|
{
|
|
dirty = true;
|
|
|
|
scale_local = scale;
|
|
rotation_local = rotation;
|
|
translation_local = translation;
|
|
}
|
|
void TransformComponent::ClearTransform()
|
|
{
|
|
dirty = true;
|
|
scale_local = XMFLOAT3(1, 1, 1);
|
|
rotation_local = XMFLOAT4(0, 0, 0, 1);
|
|
translation_local = XMFLOAT3(0, 0, 0);
|
|
}
|
|
void TransformComponent::Translate(const XMFLOAT3& value)
|
|
{
|
|
dirty = true;
|
|
translation_local.x += value.x;
|
|
translation_local.y += value.y;
|
|
translation_local.z += value.z;
|
|
}
|
|
void TransformComponent::RotateRollPitchYaw(const XMFLOAT3& value)
|
|
{
|
|
dirty = true;
|
|
|
|
// This needs to be handled a bit differently
|
|
XMVECTOR quat = XMLoadFloat4(&rotation_local);
|
|
XMVECTOR x = XMQuaternionRotationRollPitchYaw(value.x, 0, 0);
|
|
XMVECTOR y = XMQuaternionRotationRollPitchYaw(0, value.y, 0);
|
|
XMVECTOR z = XMQuaternionRotationRollPitchYaw(0, 0, value.z);
|
|
|
|
quat = XMQuaternionMultiply(x, quat);
|
|
quat = XMQuaternionMultiply(quat, y);
|
|
quat = XMQuaternionMultiply(z, quat);
|
|
quat = XMQuaternionNormalize(quat);
|
|
|
|
XMStoreFloat4(&rotation_local, quat);
|
|
}
|
|
void TransformComponent::Rotate(const XMFLOAT4& quaternion)
|
|
{
|
|
dirty = true;
|
|
|
|
XMVECTOR result = XMQuaternionMultiply(XMLoadFloat4(&rotation_local), XMLoadFloat4(&quaternion));
|
|
result = XMQuaternionNormalize(result);
|
|
XMStoreFloat4(&rotation_local, result);
|
|
}
|
|
void TransformComponent::Scale(const XMFLOAT3& value)
|
|
{
|
|
dirty = true;
|
|
|
|
scale_local.x *= value.x;
|
|
scale_local.y *= value.y;
|
|
scale_local.z *= value.z;
|
|
}
|
|
void TransformComponent::MatrixTransform(const XMFLOAT4X4& matrix)
|
|
{
|
|
MatrixTransform(XMLoadFloat4x4(&matrix));
|
|
}
|
|
void TransformComponent::MatrixTransform(const XMMATRIX& matrix)
|
|
{
|
|
dirty = true;
|
|
|
|
XMVECTOR S;
|
|
XMVECTOR R;
|
|
XMVECTOR T;
|
|
XMMatrixDecompose(&S, &R, &T, matrix);
|
|
|
|
S = XMLoadFloat3(&scale_local) * S;
|
|
R = XMQuaternionMultiply(XMLoadFloat4(&rotation_local), R);
|
|
T = XMLoadFloat3(&translation_local) + T;
|
|
|
|
XMStoreFloat3(&scale_local, S);
|
|
XMStoreFloat4(&rotation_local, R);
|
|
XMStoreFloat3(&translation_local, T);
|
|
}
|
|
void TransformComponent::Lerp(const TransformComponent& a, const TransformComponent& b, float t)
|
|
{
|
|
dirty = true;
|
|
|
|
XMVECTOR aS = XMLoadFloat3(&a.scale);
|
|
XMVECTOR aR = XMLoadFloat4(&a.rotation);
|
|
XMVECTOR aT = XMLoadFloat3(&a.translation);
|
|
|
|
XMVECTOR bS = XMLoadFloat3(&b.scale);
|
|
XMVECTOR bR = XMLoadFloat4(&b.rotation);
|
|
XMVECTOR bT = XMLoadFloat3(&b.translation);
|
|
|
|
XMVECTOR S = XMVectorLerp(aS, bS, t);
|
|
XMVECTOR R = XMQuaternionSlerp(aR, bR, t);
|
|
XMVECTOR T = XMVectorLerp(aT, bT, t);
|
|
|
|
XMStoreFloat3(&scale_local, S);
|
|
XMStoreFloat4(&rotation_local, R);
|
|
XMStoreFloat3(&translation_local, T);
|
|
}
|
|
void TransformComponent::CatmullRom(const TransformComponent& a, const TransformComponent& b, const TransformComponent& c, const TransformComponent& d, float t)
|
|
{
|
|
dirty = true;
|
|
|
|
XMVECTOR aS = XMLoadFloat3(&a.scale);
|
|
XMVECTOR aR = XMLoadFloat4(&a.rotation);
|
|
XMVECTOR aT = XMLoadFloat3(&a.translation);
|
|
|
|
XMVECTOR bS = XMLoadFloat3(&b.scale);
|
|
XMVECTOR bR = XMLoadFloat4(&b.rotation);
|
|
XMVECTOR bT = XMLoadFloat3(&b.translation);
|
|
|
|
XMVECTOR cS = XMLoadFloat3(&c.scale);
|
|
XMVECTOR cR = XMLoadFloat4(&c.rotation);
|
|
XMVECTOR cT = XMLoadFloat3(&c.translation);
|
|
|
|
XMVECTOR dS = XMLoadFloat3(&d.scale);
|
|
XMVECTOR dR = XMLoadFloat4(&d.rotation);
|
|
XMVECTOR dT = XMLoadFloat3(&d.translation);
|
|
|
|
XMVECTOR T = XMVectorCatmullRom(aT, bT, cT, dT, t);
|
|
|
|
// Catmull-rom has issues with full rotation for quaternions (todo):
|
|
XMVECTOR R = XMVectorCatmullRom(aR, bR, cR, dR, t);
|
|
R = XMQuaternionNormalize(R);
|
|
|
|
XMVECTOR S = XMVectorCatmullRom(aS, bS, cS, dS, t);
|
|
|
|
XMStoreFloat3(&translation_local, T);
|
|
XMStoreFloat4(&rotation_local, R);
|
|
XMStoreFloat3(&scale_local, S);
|
|
}
|
|
|
|
Texture2D* MaterialComponent::GetBaseColorMap() const
|
|
{
|
|
if (baseColorMap != nullptr)
|
|
{
|
|
return baseColorMap;
|
|
}
|
|
return wiTextureHelper::getInstance()->getWhite();
|
|
}
|
|
Texture2D* MaterialComponent::GetNormalMap() const
|
|
{
|
|
return normalMap;
|
|
//if (normalMap != nullptr)
|
|
//{
|
|
// return normalMap;
|
|
//}
|
|
//return wiTextureHelper::getInstance()->getNormalMapDefault();
|
|
}
|
|
Texture2D* MaterialComponent::GetSurfaceMap() const
|
|
{
|
|
if (surfaceMap != nullptr)
|
|
{
|
|
return surfaceMap;
|
|
}
|
|
return wiTextureHelper::getInstance()->getWhite();
|
|
}
|
|
Texture2D* MaterialComponent::GetDisplacementMap() const
|
|
{
|
|
if (displacementMap != nullptr)
|
|
{
|
|
return displacementMap;
|
|
}
|
|
return wiTextureHelper::getInstance()->getWhite();
|
|
}
|
|
|
|
void MeshComponent::CreateRenderData()
|
|
{
|
|
// First, assemble vertex, index arrays:
|
|
|
|
// In case of recreate, delete data first:
|
|
vertices_POS.clear();
|
|
vertices_TEX.clear();
|
|
vertices_BON.clear();
|
|
|
|
// De-interleave vertex arrays:
|
|
vertices_POS.resize(vertices_FULL.size());
|
|
vertices_TEX.resize(vertices_FULL.size());
|
|
// do not resize vertices_BON just yet, not every mesh will need bone vertex data!
|
|
for (size_t i = 0; i < vertices_FULL.size(); ++i)
|
|
{
|
|
// Normalize normals:
|
|
float alpha = vertices_FULL[i].nor.w;
|
|
XMVECTOR nor = XMLoadFloat4(&vertices_FULL[i].nor);
|
|
nor = XMVector3Normalize(nor);
|
|
XMStoreFloat4(&vertices_FULL[i].nor, nor);
|
|
vertices_FULL[i].nor.w = alpha;
|
|
|
|
// Normalize bone weights:
|
|
XMFLOAT4& wei = vertices_FULL[i].wei;
|
|
float len = wei.x + wei.y + wei.z + wei.w;
|
|
if (len > 0)
|
|
{
|
|
wei.x /= len;
|
|
wei.y /= len;
|
|
wei.z /= len;
|
|
wei.w /= len;
|
|
|
|
if (vertices_BON.empty())
|
|
{
|
|
// Allocate full bone vertex data when we find a correct bone weight.
|
|
vertices_BON.resize(vertices_FULL.size());
|
|
}
|
|
vertices_BON[i].FromFULL(vertices_FULL[i]);
|
|
}
|
|
|
|
// Split and type conversion:
|
|
vertices_POS[i].FromFULL(vertices_FULL[i]);
|
|
vertices_TEX[i].FromFULL(vertices_FULL[i]);
|
|
}
|
|
|
|
// Save original vertices. This will be input for CPU skinning / soft bodies
|
|
vertices_Transformed_POS = vertices_POS;
|
|
vertices_Transformed_PRE = vertices_POS; // pre <- pos!! (previous positions will have the current positions initially)
|
|
|
|
// Map subset indices:
|
|
for (auto& subset : subsets)
|
|
{
|
|
subset.subsetIndices.clear();
|
|
}
|
|
for (size_t i = 0; i < indices.size(); ++i)
|
|
{
|
|
uint32_t index = indices[i];
|
|
const XMFLOAT4& tex = vertices_FULL[index].tex;
|
|
unsigned int materialIndex = (unsigned int)floor(tex.z);
|
|
|
|
assert((materialIndex < (unsigned int)subsets.size()) && "Bad subset index!");
|
|
|
|
MeshSubset& subset = subsets[materialIndex];
|
|
subset.subsetIndices.push_back(index);
|
|
|
|
if (index >= 65536)
|
|
{
|
|
indexFormat = INDEXFORMAT_32BIT;
|
|
}
|
|
}
|
|
|
|
|
|
//// Goal positions, normals are controlling blending between animation and physics states for soft body rendering:
|
|
//goalPositions.clear();
|
|
//goalNormals.clear();
|
|
//if (goalVG >= 0)
|
|
//{
|
|
// goalPositions.resize(vertexGroups[goalVG].vertices.size());
|
|
// goalNormals.resize(vertexGroups[goalVG].vertices.size());
|
|
//}
|
|
|
|
|
|
//// Mapping render vertices to physics vertex representation:
|
|
//// the physics vertices contain unique position, not duplicated by texcoord or normals
|
|
//// this way we can map several renderable vertices to one physics vertex
|
|
//// but the mapping function will actually be indexed by renderable vertex index for efficient retrieval.
|
|
//if (!physicsverts.empty() && physicalmapGP.empty())
|
|
//{
|
|
// for (size_t i = 0; i < vertices_POS.size(); ++i)
|
|
// {
|
|
// for (size_t j = 0; j < physicsverts.size(); ++j)
|
|
// {
|
|
// if (fabs(vertices_POS[i].pos.x - physicsverts[j].x) < FLT_EPSILON
|
|
// && fabs(vertices_POS[i].pos.y - physicsverts[j].y) < FLT_EPSILON
|
|
// && fabs(vertices_POS[i].pos.z - physicsverts[j].z) < FLT_EPSILON
|
|
// )
|
|
// {
|
|
// physicalmapGP.push_back(static_cast<int>(j));
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
|
|
|
|
// Create actual GPU data:
|
|
|
|
GPUBufferDesc bd;
|
|
SubresourceData InitData;
|
|
|
|
HRESULT hr;
|
|
|
|
//if (!IsDynamicVB())
|
|
{
|
|
ZeroMemory(&bd, sizeof(bd));
|
|
bd.Usage = USAGE_IMMUTABLE;
|
|
bd.CPUAccessFlags = 0;
|
|
bd.BindFlags = BIND_VERTEX_BUFFER | BIND_SHADER_RESOURCE;
|
|
bd.MiscFlags = RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
|
|
ZeroMemory(&InitData, sizeof(InitData));
|
|
|
|
InitData.pSysMem = vertices_POS.data();
|
|
bd.ByteWidth = (UINT)(sizeof(Vertex_POS) * vertices_POS.size());
|
|
vertexBuffer_POS.reset(new GPUBuffer);
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&bd, &InitData, vertexBuffer_POS.get());
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
if (!vertices_BON.empty())
|
|
{
|
|
ZeroMemory(&bd, sizeof(bd));
|
|
bd.Usage = USAGE_IMMUTABLE;
|
|
bd.BindFlags = BIND_SHADER_RESOURCE;
|
|
bd.CPUAccessFlags = 0;
|
|
bd.MiscFlags = RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
|
|
|
|
InitData.pSysMem = vertices_BON.data();
|
|
bd.ByteWidth = (UINT)(sizeof(Vertex_BON) * vertices_BON.size());
|
|
vertexBuffer_BON.reset(new GPUBuffer);
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&bd, &InitData, vertexBuffer_BON.get());
|
|
assert(SUCCEEDED(hr));
|
|
|
|
ZeroMemory(&bd, sizeof(bd));
|
|
bd.Usage = USAGE_DEFAULT;
|
|
bd.BindFlags = BIND_VERTEX_BUFFER | BIND_UNORDERED_ACCESS;
|
|
bd.CPUAccessFlags = 0;
|
|
bd.MiscFlags = RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
|
|
|
|
bd.ByteWidth = (UINT)(sizeof(Vertex_POS) * vertices_POS.size());
|
|
streamoutBuffer_POS.reset(new GPUBuffer);
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, streamoutBuffer_POS.get());
|
|
assert(SUCCEEDED(hr));
|
|
|
|
bd.ByteWidth = (UINT)(sizeof(Vertex_POS) * vertices_POS.size());
|
|
streamoutBuffer_PRE.reset(new GPUBuffer);
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, streamoutBuffer_PRE.get());
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
// texture coordinate buffers are always static:
|
|
ZeroMemory(&bd, sizeof(bd));
|
|
bd.Usage = USAGE_IMMUTABLE;
|
|
bd.CPUAccessFlags = 0;
|
|
bd.BindFlags = BIND_VERTEX_BUFFER | BIND_SHADER_RESOURCE;
|
|
bd.MiscFlags = 0;
|
|
bd.StructureByteStride = sizeof(Vertex_TEX);
|
|
bd.ByteWidth = (UINT)(bd.StructureByteStride * vertices_TEX.size());
|
|
bd.Format = Vertex_TEX::FORMAT;
|
|
InitData.pSysMem = vertices_TEX.data();
|
|
vertexBuffer_TEX.reset(new GPUBuffer);
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&bd, &InitData, vertexBuffer_TEX.get());
|
|
assert(SUCCEEDED(hr));
|
|
|
|
|
|
// Remap index buffer to be continuous across subsets and create gpu buffer data:
|
|
uint32_t counter = 0;
|
|
uint8_t stride;
|
|
void* gpuIndexData;
|
|
if (GetIndexFormat() == INDEXFORMAT_16BIT)
|
|
{
|
|
gpuIndexData = new uint16_t[indices.size()];
|
|
stride = sizeof(uint16_t);
|
|
}
|
|
else
|
|
{
|
|
gpuIndexData = new uint32_t[indices.size()];
|
|
stride = sizeof(uint32_t);
|
|
}
|
|
|
|
for (MeshSubset& subset : subsets)
|
|
{
|
|
if (subset.subsetIndices.empty())
|
|
{
|
|
continue;
|
|
}
|
|
subset.indexBufferOffset = counter;
|
|
|
|
switch (GetIndexFormat())
|
|
{
|
|
case INDEXFORMAT_16BIT:
|
|
for (auto& x : subset.subsetIndices)
|
|
{
|
|
static_cast<uint16_t*>(gpuIndexData)[counter] = static_cast<uint16_t>(x);
|
|
counter++;
|
|
}
|
|
break;
|
|
default:
|
|
for (auto& x : subset.subsetIndices)
|
|
{
|
|
static_cast<uint32_t*>(gpuIndexData)[counter] = static_cast<uint32_t>(x);
|
|
counter++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&bd, sizeof(bd));
|
|
bd.Usage = USAGE_IMMUTABLE;
|
|
bd.CPUAccessFlags = 0;
|
|
bd.BindFlags = BIND_INDEX_BUFFER | BIND_SHADER_RESOURCE;
|
|
bd.MiscFlags = 0;
|
|
bd.StructureByteStride = stride;
|
|
bd.Format = GetIndexFormat() == INDEXFORMAT_16BIT ? FORMAT_R16_UINT : FORMAT_R32_UINT;
|
|
InitData.pSysMem = gpuIndexData;
|
|
bd.ByteWidth = (UINT)(stride * indices.size());
|
|
indexBuffer.reset(new GPUBuffer);
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&bd, &InitData, indexBuffer.get());
|
|
assert(SUCCEEDED(hr));
|
|
|
|
SAFE_DELETE_ARRAY(gpuIndexData);
|
|
|
|
}
|
|
void MeshComponent::ComputeNormals(bool smooth)
|
|
{
|
|
// Start recalculating normals:
|
|
|
|
if (smooth)
|
|
{
|
|
// Compute smooth surface normals:
|
|
|
|
// 1.) Zero normals, they will be averaged later
|
|
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
|
|
{
|
|
vertices_FULL[i].nor = XMFLOAT4(0, 0, 0, 0);
|
|
}
|
|
|
|
// 2.) Find identical vertices by POSITION, accumulate face normals
|
|
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
|
|
{
|
|
Vertex_FULL& v_search = vertices_FULL[i];
|
|
|
|
for (size_t ind = 0; ind < indices.size() / 3; ++ind)
|
|
{
|
|
uint32_t i0 = indices[ind * 3 + 0];
|
|
uint32_t i1 = indices[ind * 3 + 1];
|
|
uint32_t i2 = indices[ind * 3 + 2];
|
|
|
|
Vertex_FULL& v0 = vertices_FULL[i0];
|
|
Vertex_FULL& v1 = vertices_FULL[i1];
|
|
Vertex_FULL& v2 = vertices_FULL[i2];
|
|
|
|
bool match_pos0 =
|
|
fabs(v_search.pos.x - v0.pos.x) < FLT_EPSILON &&
|
|
fabs(v_search.pos.y - v0.pos.y) < FLT_EPSILON &&
|
|
fabs(v_search.pos.z - v0.pos.z) < FLT_EPSILON;
|
|
|
|
bool match_pos1 =
|
|
fabs(v_search.pos.x - v1.pos.x) < FLT_EPSILON &&
|
|
fabs(v_search.pos.y - v1.pos.y) < FLT_EPSILON &&
|
|
fabs(v_search.pos.z - v1.pos.z) < FLT_EPSILON;
|
|
|
|
bool match_pos2 =
|
|
fabs(v_search.pos.x - v2.pos.x) < FLT_EPSILON &&
|
|
fabs(v_search.pos.y - v2.pos.y) < FLT_EPSILON &&
|
|
fabs(v_search.pos.z - v2.pos.z) < FLT_EPSILON;
|
|
|
|
if (match_pos0 || match_pos1 || match_pos2)
|
|
{
|
|
XMVECTOR U = XMLoadFloat4(&v2.pos) - XMLoadFloat4(&v0.pos);
|
|
XMVECTOR V = XMLoadFloat4(&v1.pos) - XMLoadFloat4(&v0.pos);
|
|
|
|
XMVECTOR N = XMVector3Cross(U, V);
|
|
N = XMVector3Normalize(N);
|
|
|
|
XMFLOAT3 normal;
|
|
XMStoreFloat3(&normal, N);
|
|
|
|
v_search.nor.x += normal.x;
|
|
v_search.nor.y += normal.y;
|
|
v_search.nor.z += normal.z;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// 3.) Find unique vertices by POSITION and TEXCOORD and MATERIAL and remove duplicates
|
|
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
|
|
{
|
|
const Vertex_FULL& v0 = vertices_FULL[i];
|
|
|
|
for (size_t j = i + 1; j < vertices_FULL.size(); j++)
|
|
{
|
|
const Vertex_FULL& v1 = vertices_FULL[j];
|
|
|
|
bool unique_pos =
|
|
fabs(v0.pos.x - v1.pos.x) < FLT_EPSILON &&
|
|
fabs(v0.pos.y - v1.pos.y) < FLT_EPSILON &&
|
|
fabs(v0.pos.z - v1.pos.z) < FLT_EPSILON;
|
|
|
|
bool unique_tex =
|
|
fabs(v0.tex.x - v1.tex.x) < FLT_EPSILON &&
|
|
fabs(v0.tex.y - v1.tex.y) < FLT_EPSILON &&
|
|
(int)v0.tex.z == (int)v1.tex.z;
|
|
|
|
if (unique_pos && unique_tex)
|
|
{
|
|
for (size_t ind = 0; ind < indices.size(); ++ind)
|
|
{
|
|
if (indices[ind] == j)
|
|
{
|
|
indices[ind] = static_cast<uint32_t>(i);
|
|
}
|
|
else if (indices[ind] > j && indices[ind] > 0)
|
|
{
|
|
indices[ind]--;
|
|
}
|
|
}
|
|
|
|
vertices_FULL.erase(vertices_FULL.begin() + j);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Compute hard surface normals:
|
|
|
|
std::vector<uint32_t> newIndexBuffer;
|
|
std::vector<Vertex_FULL> newVertexBuffer;
|
|
|
|
for (size_t face = 0; face < indices.size() / 3; face++)
|
|
{
|
|
uint32_t i0 = indices[face * 3 + 0];
|
|
uint32_t i1 = indices[face * 3 + 1];
|
|
uint32_t i2 = indices[face * 3 + 2];
|
|
|
|
Vertex_FULL& v0 = vertices_FULL[i0];
|
|
Vertex_FULL& v1 = vertices_FULL[i1];
|
|
Vertex_FULL& v2 = vertices_FULL[i2];
|
|
|
|
XMVECTOR U = XMLoadFloat4(&v2.pos) - XMLoadFloat4(&v0.pos);
|
|
XMVECTOR V = XMLoadFloat4(&v1.pos) - XMLoadFloat4(&v0.pos);
|
|
|
|
XMVECTOR N = XMVector3Cross(U, V);
|
|
N = XMVector3Normalize(N);
|
|
|
|
XMFLOAT3 normal;
|
|
XMStoreFloat3(&normal, N);
|
|
|
|
v0.nor.x = normal.x;
|
|
v0.nor.y = normal.y;
|
|
v0.nor.z = normal.z;
|
|
|
|
v1.nor.x = normal.x;
|
|
v1.nor.y = normal.y;
|
|
v1.nor.z = normal.z;
|
|
|
|
v2.nor.x = normal.x;
|
|
v2.nor.y = normal.y;
|
|
v2.nor.z = normal.z;
|
|
|
|
newVertexBuffer.push_back(v0);
|
|
newVertexBuffer.push_back(v1);
|
|
newVertexBuffer.push_back(v2);
|
|
|
|
newIndexBuffer.push_back(static_cast<uint32_t>(newIndexBuffer.size()));
|
|
newIndexBuffer.push_back(static_cast<uint32_t>(newIndexBuffer.size()));
|
|
newIndexBuffer.push_back(static_cast<uint32_t>(newIndexBuffer.size()));
|
|
}
|
|
|
|
// For hard surface normals, we created a new mesh in the previous loop through faces, so swap data:
|
|
vertices_FULL = newVertexBuffer;
|
|
indices = newIndexBuffer;
|
|
}
|
|
|
|
CreateRenderData();
|
|
}
|
|
void MeshComponent::FlipCulling()
|
|
{
|
|
for (size_t face = 0; face < indices.size() / 3; face++)
|
|
{
|
|
uint32_t i0 = indices[face * 3 + 0];
|
|
uint32_t i1 = indices[face * 3 + 1];
|
|
uint32_t i2 = indices[face * 3 + 2];
|
|
|
|
indices[face * 3 + 0] = i0;
|
|
indices[face * 3 + 1] = i2;
|
|
indices[face * 3 + 2] = i1;
|
|
}
|
|
|
|
CreateRenderData();
|
|
}
|
|
void MeshComponent::FlipNormals()
|
|
{
|
|
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
|
|
{
|
|
Vertex_FULL& v0 = vertices_FULL[i];
|
|
|
|
v0.nor.x *= -1;
|
|
v0.nor.y *= -1;
|
|
v0.nor.z *= -1;
|
|
}
|
|
|
|
CreateRenderData();
|
|
}
|
|
|
|
|
|
|
|
void CameraComponent::CreatePerspective(float newWidth, float newHeight, float newNear, float newFar, float newFOV)
|
|
{
|
|
zNearP = newNear;
|
|
zFarP = newFar;
|
|
width = newWidth;
|
|
height = newHeight;
|
|
fov = newFOV;
|
|
Eye = XMFLOAT3(0, 0, 0);
|
|
At = XMFLOAT3(0, 0, 1);
|
|
Up = XMFLOAT3(0, 1, 0);
|
|
|
|
UpdateProjection();
|
|
UpdateCamera();
|
|
}
|
|
void CameraComponent::UpdateProjection()
|
|
{
|
|
XMStoreFloat4x4(&Projection, XMMatrixPerspectiveFovLH(fov, width / height, zFarP, zNearP)); // reverse zbuffer!
|
|
XMStoreFloat4x4(&realProjection, XMMatrixPerspectiveFovLH(fov, width / height, zNearP, zFarP)); // normal zbuffer!
|
|
}
|
|
void CameraComponent::UpdateCamera(const TransformComponent* transform)
|
|
{
|
|
XMVECTOR _Eye;
|
|
XMVECTOR _At;
|
|
XMVECTOR _Up;
|
|
|
|
if (transform != nullptr)
|
|
{
|
|
_Eye = XMLoadFloat3(&transform->translation);
|
|
_At = XMVectorSet(0, 0, 1, 0);
|
|
_Up = XMVectorSet(0, 1, 0, 0);
|
|
|
|
XMMATRIX _Rot = XMMatrixRotationQuaternion(XMLoadFloat4(&transform->rotation));
|
|
_At = XMVector3TransformNormal(_At, _Rot);
|
|
_Up = XMVector3TransformNormal(_Up, _Rot);
|
|
XMStoreFloat3x3(&rotationMatrix, _Rot);
|
|
}
|
|
else
|
|
{
|
|
_Eye = XMLoadFloat3(&Eye);
|
|
_At = XMLoadFloat3(&At);
|
|
_Up = XMLoadFloat3(&Up);
|
|
}
|
|
|
|
XMMATRIX _P = XMLoadFloat4x4(&Projection);
|
|
XMMATRIX _InvP = XMMatrixInverse(nullptr, _P);
|
|
XMStoreFloat4x4(&InvProjection, _InvP);
|
|
|
|
XMMATRIX _V = XMMatrixLookToLH(_Eye, _At, _Up);
|
|
XMMATRIX _VP = XMMatrixMultiply(_V, _P);
|
|
XMStoreFloat4x4(&View, _V);
|
|
XMStoreFloat4x4(&VP, _VP);
|
|
XMStoreFloat4x4(&InvView, XMMatrixInverse(nullptr, _V));
|
|
XMStoreFloat4x4(&InvVP, XMMatrixInverse(nullptr, _VP));
|
|
XMStoreFloat4x4(&Projection, _P);
|
|
XMStoreFloat4x4(&InvProjection, XMMatrixInverse(nullptr, _P));
|
|
|
|
XMStoreFloat3(&Eye, _Eye);
|
|
XMStoreFloat3(&At, _At);
|
|
XMStoreFloat3(&Up, _Up);
|
|
|
|
frustum.ConstructFrustum(zFarP, realProjection, View);
|
|
}
|
|
void CameraComponent::Reflect(const XMFLOAT4& plane)
|
|
{
|
|
XMVECTOR _Eye = XMLoadFloat3(&Eye);
|
|
XMVECTOR _At = XMLoadFloat3(&At);
|
|
XMVECTOR _Up = XMLoadFloat3(&Up);
|
|
XMMATRIX _Ref = XMMatrixReflect(XMLoadFloat4(&plane));
|
|
|
|
_Eye = XMVector3Transform(_Eye, _Ref);
|
|
_At = XMVector3TransformNormal(_At, _Ref);
|
|
_Up = XMVector3TransformNormal(_Up, _Ref);
|
|
|
|
XMStoreFloat3(&Eye, _Eye);
|
|
XMStoreFloat3(&At, _At);
|
|
XMStoreFloat3(&Up, _Up);
|
|
|
|
UpdateCamera();
|
|
}
|
|
|
|
|
|
void Scene::Update(float dt)
|
|
{
|
|
|
|
RunAnimationUpdateSystem(animations, transforms, dt);
|
|
|
|
RunTransformUpdateSystem(transforms);
|
|
|
|
RunHierarchyUpdateSystem(parents, transforms, layers);
|
|
|
|
RunArmatureUpdateSystem(transforms, bones, armatures);
|
|
|
|
RunPhysicsUpdateSystem(transforms, meshes, objects, physicscomponents);
|
|
|
|
RunMaterialUpdateSystem(materials, dt);
|
|
|
|
RunObjectUpdateSystem(transforms, meshes, materials, objects, cullables, bounds, waterPlane);
|
|
|
|
RunCameraUpdateSystem(transforms, cameras);
|
|
|
|
RunDecalUpdateSystem(transforms, cullables, decals);
|
|
|
|
RunProbeUpdateSystem(transforms, cullables, probes);
|
|
|
|
RunLightUpdateSystem(*cameras.GetComponent(wiRenderer::getCameraID()), transforms, cullables, lights, sunDirection, sunColor);
|
|
|
|
}
|
|
void Scene::Clear()
|
|
{
|
|
for (Entity entity : owned_entities)
|
|
{
|
|
names.Remove(entity);
|
|
layers.Remove(entity);
|
|
transforms.Remove(entity);
|
|
parents.Remove(entity);
|
|
materials.Remove(entity);
|
|
meshes.Remove(entity);
|
|
objects.Remove(entity);
|
|
physicscomponents.Remove(entity);
|
|
cullables.Remove(entity);
|
|
bones.Remove(entity);
|
|
armatures.Remove(entity);
|
|
lights.Remove(entity);
|
|
cameras.Remove(entity);
|
|
probes.Remove(entity);
|
|
forces.Remove(entity);
|
|
decals.Remove(entity);
|
|
animations.Remove(entity);
|
|
models.Remove(entity);
|
|
}
|
|
|
|
owned_entities.clear();
|
|
}
|
|
|
|
void Scene::Entity_Remove(Entity entity)
|
|
{
|
|
owned_entities.erase(entity);
|
|
|
|
names.Remove(entity);
|
|
layers.Remove(entity);
|
|
transforms.Remove(entity);
|
|
parents.Remove(entity);
|
|
materials.Remove(entity);
|
|
meshes.Remove(entity);
|
|
objects.Remove(entity);
|
|
physicscomponents.Remove(entity);
|
|
cullables.Remove(entity);
|
|
bones.Remove(entity);
|
|
armatures.Remove(entity);
|
|
lights.Remove(entity);
|
|
cameras.Remove(entity);
|
|
probes.Remove(entity);
|
|
forces.Remove(entity);
|
|
decals.Remove(entity);
|
|
animations.Remove(entity);
|
|
models.Remove(entity);
|
|
}
|
|
Entity Scene::Entity_FindByName(const std::string& name)
|
|
{
|
|
for (size_t i = 0; i < names.GetCount(); ++i)
|
|
{
|
|
if (strcmp(names[i].name, name.c_str()) == 0)
|
|
{
|
|
return names.GetEntity(i);
|
|
}
|
|
}
|
|
return INVALID_ENTITY;
|
|
}
|
|
Entity Scene::Entity_CreateModel(
|
|
const std::string& name
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity);
|
|
|
|
models.Create(entity);
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateMaterial(
|
|
const std::string& name
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
materials.Create(entity);
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateObject(
|
|
const std::string& name
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity);
|
|
|
|
cullables.Create(entity);
|
|
|
|
objects.Create(entity);
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateMesh(
|
|
const std::string& name
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
meshes.Create(entity);
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateLight(
|
|
const std::string& name,
|
|
const XMFLOAT3& position,
|
|
const XMFLOAT3& color,
|
|
float energy,
|
|
float range)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity).Translate(position);
|
|
|
|
cullables.Create(entity).aabb.createFromHalfWidth(position, XMFLOAT3(range, range, range));
|
|
|
|
LightComponent& light = lights.Create(entity);
|
|
light.energy = energy;
|
|
light.range = range;
|
|
light.fov = XM_PIDIV4;
|
|
light.color = color;
|
|
light.SetType(LightComponent::POINT);
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateForce(
|
|
const std::string& name,
|
|
const XMFLOAT3& position
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity).Translate(position);
|
|
|
|
ForceFieldComponent& force = forces.Create(entity);
|
|
force.gravity = 0;
|
|
force.range = 0;
|
|
force.type = ENTITY_TYPE_FORCEFIELD_POINT;
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateEnvironmentProbe(
|
|
const std::string& name,
|
|
const XMFLOAT3& position
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity).Translate(position);
|
|
|
|
cullables.Create(entity);
|
|
|
|
EnvironmentProbeComponent& probe = probes.Create(entity);
|
|
probe.isUpToDate = false;
|
|
probe.realTime = false;
|
|
probe.textureIndex = -1;
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateDecal(
|
|
const std::string& name,
|
|
const std::string& textureName,
|
|
const std::string& normalMapName
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity);
|
|
|
|
cullables.Create(entity);
|
|
|
|
DecalComponent& decal = decals.Create(entity);
|
|
decal.textureName = textureName;
|
|
decal.normalMapName = normalMapName;
|
|
|
|
if (!decal.textureName.empty())
|
|
{
|
|
decal.texture = (Texture2D*)wiResourceManager::GetGlobal()->add(decal.textureName);
|
|
}
|
|
if (!decal.normalMapName.empty())
|
|
{
|
|
decal.normal = (Texture2D*)wiResourceManager::GetGlobal()->add(decal.normalMapName);
|
|
}
|
|
|
|
return entity;
|
|
}
|
|
Entity Scene::Entity_CreateCamera(
|
|
const std::string& name,
|
|
float width, float height, float nearPlane, float farPlane, float fov
|
|
)
|
|
{
|
|
Entity entity = CreateEntity();
|
|
|
|
owned_entities.insert(entity);
|
|
|
|
names.Create(entity) = name;
|
|
|
|
layers.Create(entity);
|
|
|
|
transforms.Create(entity);
|
|
|
|
CameraComponent& camera = cameras.Create(entity);
|
|
camera.CreatePerspective(width, height, nearPlane, farPlane, fov);
|
|
|
|
return entity;
|
|
}
|
|
|
|
void Scene::Component_Attach(Entity entity, Entity parent)
|
|
{
|
|
assert(entity != parent);
|
|
|
|
ParentComponent* parentcomponent = parents.GetComponent(entity);
|
|
|
|
if (parentcomponent == nullptr)
|
|
{
|
|
parentcomponent = &parents.Create(entity);
|
|
}
|
|
|
|
TransformComponent* transform_parent = transforms.GetComponent(parent);
|
|
if (transform_parent != nullptr)
|
|
{
|
|
// Save the parent's inverse worldmatrix:
|
|
parentcomponent->parentID = parent;
|
|
XMStoreFloat4x4(&parentcomponent->world_parent_inverse_bind, XMMatrixInverse(nullptr, XMLoadFloat4x4(&transform_parent->world)));
|
|
}
|
|
|
|
TransformComponent* transform_child = transforms.GetComponent(entity);
|
|
if (transform_child != nullptr)
|
|
{
|
|
// Child updated immediately, to that it can be immediately attached to afterwards:
|
|
transform_child->UpdateParentedTransform(*transform_parent, parentcomponent->world_parent_inverse_bind);
|
|
}
|
|
|
|
LayerComponent* layer_child = layers.GetComponent(entity);
|
|
if (layer_child != nullptr)
|
|
{
|
|
// Save the initial layermask of the child so that it can be restored if detached:
|
|
parentcomponent->layerMask_bind = layer_child->GetLayerMask();
|
|
}
|
|
}
|
|
void Scene::Component_Detach(Entity entity)
|
|
{
|
|
const ParentComponent* parent = parents.GetComponent(entity);
|
|
|
|
if (parent != nullptr)
|
|
{
|
|
TransformComponent* transform = transforms.GetComponent(entity);
|
|
if (transform != nullptr)
|
|
{
|
|
transform->ApplyTransform();
|
|
}
|
|
|
|
LayerComponent* layer = layers.GetComponent(entity);
|
|
if (layer != nullptr)
|
|
{
|
|
layer->layerMask = parent->layerMask_bind;
|
|
}
|
|
|
|
parents.Remove(entity);
|
|
}
|
|
}
|
|
void Scene::Component_DetachChildren(Entity parent)
|
|
{
|
|
for (size_t i = 0; i < parents.GetCount(); )
|
|
{
|
|
if (parents[i].parentID == parent)
|
|
{
|
|
Entity entity = parents.GetEntity(i);
|
|
Component_Detach(entity);
|
|
}
|
|
else
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void RunAnimationUpdateSystem(
|
|
wiECS::ComponentManager<AnimationComponent>& animations,
|
|
wiECS::ComponentManager<TransformComponent>& transforms,
|
|
float dt
|
|
)
|
|
{
|
|
for (size_t i = 0; i < animations.GetCount(); ++i)
|
|
{
|
|
AnimationComponent& animation = animations[i];
|
|
|
|
for (auto& channel : animation.channels)
|
|
{
|
|
if (channel.keyframe_times.empty())
|
|
{
|
|
// No keyframes!
|
|
assert(0);
|
|
continue;
|
|
}
|
|
|
|
int keyLeft = 0;
|
|
int keyRight = 0;
|
|
|
|
if (channel.keyframe_times.back() < animation.timer)
|
|
{
|
|
// Rightmost keyframe is already outside animation, so just snap to last keyframe:
|
|
keyLeft = keyRight = (int)channel.keyframe_times.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
// Search for the right keyframe (greater/equal to anim time):
|
|
while (channel.keyframe_times[keyRight++] < animation.timer) {}
|
|
keyRight--;
|
|
|
|
// Left keyframe is just near right:
|
|
keyLeft = max(0, keyRight - 1);
|
|
}
|
|
|
|
float left = channel.keyframe_times[keyLeft];
|
|
|
|
TransformComponent& transform = *transforms.GetComponent(channel.target);
|
|
|
|
if (channel.mode == AnimationComponent::AnimationChannel::Mode::STEP || keyLeft == keyRight)
|
|
{
|
|
// Nearest neighbor method (snap to left):
|
|
switch (channel.type)
|
|
{
|
|
case AnimationComponent::AnimationChannel::Type::TRANSLATION:
|
|
{
|
|
transform.translation_local = ((const XMFLOAT3*)channel.keyframe_data.data())[keyLeft * 3];
|
|
}
|
|
break;
|
|
case AnimationComponent::AnimationChannel::Type::ROTATION:
|
|
{
|
|
transform.rotation_local = ((const XMFLOAT4*)channel.keyframe_data.data())[keyLeft * 4];
|
|
}
|
|
break;
|
|
case AnimationComponent::AnimationChannel::Type::SCALE:
|
|
{
|
|
transform.scale_local = ((const XMFLOAT3*)channel.keyframe_data.data())[keyLeft * 3];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Linear interpolation method:
|
|
float right = channel.keyframe_times[keyRight];
|
|
float t = (animation.timer - left) / (right - left);
|
|
|
|
switch (channel.type)
|
|
{
|
|
case AnimationComponent::AnimationChannel::Type::TRANSLATION:
|
|
{
|
|
const XMFLOAT3* data = (const XMFLOAT3*)channel.keyframe_data.data();
|
|
XMVECTOR vLeft = XMLoadFloat3(&data[keyLeft * 3]);
|
|
XMVECTOR vRight = XMLoadFloat3(&data[keyRight * 3]);
|
|
XMVECTOR vMiddle = XMVectorLerp(vLeft, vRight, t);
|
|
XMStoreFloat3(&transform.translation_local, vMiddle);
|
|
}
|
|
break;
|
|
case AnimationComponent::AnimationChannel::Type::ROTATION:
|
|
{
|
|
const XMFLOAT4* data = (const XMFLOAT4*)channel.keyframe_data.data();
|
|
XMVECTOR vLeft = XMLoadFloat4(&data[keyLeft * 4]);
|
|
XMVECTOR vRight = XMLoadFloat4(&data[keyRight * 4]);
|
|
XMVECTOR vMiddle = XMQuaternionSlerp(vLeft, vRight, t);
|
|
vMiddle = XMQuaternionNormalize(vMiddle);
|
|
XMStoreFloat4(&transform.rotation_local, vMiddle);
|
|
}
|
|
break;
|
|
case AnimationComponent::AnimationChannel::Type::SCALE:
|
|
{
|
|
const XMFLOAT3* data = (const XMFLOAT3*)channel.keyframe_data.data();
|
|
XMVECTOR vLeft = XMLoadFloat3(&data[keyLeft * 3]);
|
|
XMVECTOR vRight = XMLoadFloat3(&data[keyRight * 3]);
|
|
XMVECTOR vMiddle = XMVectorLerp(vLeft, vRight, t);
|
|
XMStoreFloat3(&transform.scale_local, vMiddle);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
transform.dirty = true;
|
|
|
|
}
|
|
|
|
if (animation.playing)
|
|
{
|
|
animation.timer += dt;
|
|
}
|
|
|
|
if (animation.looped && animation.timer > animation.length)
|
|
{
|
|
animation.timer = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
void RunTransformUpdateSystem(ComponentManager<TransformComponent>& transforms)
|
|
{
|
|
for (size_t i = 0; i < transforms.GetCount(); ++i)
|
|
{
|
|
TransformComponent& transform = transforms[i];
|
|
transform.UpdateTransform();
|
|
}
|
|
}
|
|
void RunHierarchyUpdateSystem(
|
|
const ComponentManager<ParentComponent>& parents,
|
|
ComponentManager<TransformComponent>& transforms,
|
|
ComponentManager<LayerComponent>& layers
|
|
)
|
|
{
|
|
for (size_t i = 0; i < parents.GetCount(); ++i)
|
|
{
|
|
const ParentComponent& parentcomponent = parents[i];
|
|
Entity entity = parents.GetEntity(i);
|
|
|
|
TransformComponent* transform_child = transforms.GetComponent(entity);
|
|
TransformComponent* transform_parent = transforms.GetComponent(parentcomponent.parentID);
|
|
if (transform_child != nullptr && transform_parent != nullptr)
|
|
{
|
|
transform_child->UpdateParentedTransform(*transform_parent, parentcomponent.world_parent_inverse_bind);
|
|
}
|
|
|
|
|
|
LayerComponent* layer_child = layers.GetComponent(entity);
|
|
LayerComponent* layer_parent = layers.GetComponent(parentcomponent.parentID);
|
|
if (layer_child != nullptr && layer_parent != nullptr)
|
|
{
|
|
layer_child->layerMask = parentcomponent.layerMask_bind & layer_parent->GetLayerMask();
|
|
}
|
|
|
|
}
|
|
}
|
|
void RunArmatureUpdateSystem(
|
|
const ComponentManager<TransformComponent>& transforms,
|
|
const ComponentManager<BoneComponent>& bones,
|
|
ComponentManager<ArmatureComponent>& armatures
|
|
)
|
|
{
|
|
for (size_t i = 0; i < armatures.GetCount(); ++i)
|
|
{
|
|
ArmatureComponent& armature = armatures[i];
|
|
Entity entity = armatures.GetEntity(i);
|
|
|
|
XMMATRIX R = XMLoadFloat4x4(&armature.skinningRemap);
|
|
|
|
if (armature.skinningMatrices.size() != armature.boneCollection.size())
|
|
{
|
|
armature.skinningMatrices.resize(armature.boneCollection.size());
|
|
}
|
|
|
|
int boneIndex = 0;
|
|
for (Entity boneEntity : armature.boneCollection)
|
|
{
|
|
const BoneComponent& bone = *bones.GetComponent(boneEntity);
|
|
const TransformComponent& bone_transform = *transforms.GetComponent(boneEntity);
|
|
|
|
XMMATRIX B = XMLoadFloat4x4(&bone.inverseBindPoseMatrix);
|
|
XMMATRIX W = XMLoadFloat4x4(&bone_transform.world);
|
|
XMMATRIX M = W * B * R;
|
|
|
|
XMStoreFloat4x4(&armature.skinningMatrices[boneIndex++], M);
|
|
}
|
|
|
|
}
|
|
}
|
|
void RunPhysicsUpdateSystem(
|
|
ComponentManager<TransformComponent>& transforms,
|
|
ComponentManager<MeshComponent>& meshes,
|
|
ComponentManager<ObjectComponent>& objects,
|
|
ComponentManager<PhysicsComponent>& physicscomponents
|
|
)
|
|
{
|
|
for (size_t i = 0; i < physicscomponents.GetCount(); ++i)
|
|
{
|
|
PhysicsComponent& physicscomponent = physicscomponents[i];
|
|
Entity entity = physicscomponents.GetEntity(i);
|
|
|
|
if (physicscomponent.softBody)
|
|
{
|
|
MeshComponent& mesh = *meshes.GetComponent(entity);
|
|
mesh.dynamicVB = true;
|
|
}
|
|
}
|
|
}
|
|
void RunMaterialUpdateSystem(ComponentManager<MaterialComponent>& materials, float dt)
|
|
{
|
|
for (size_t i = 0; i < materials.GetCount(); ++i)
|
|
{
|
|
MaterialComponent& material = materials[i];
|
|
|
|
material.texAnimSleep -= dt * material.texAnimFrameRate;
|
|
if (material.texAnimSleep <= 0)
|
|
{
|
|
material.texMulAdd.z = fmodf(material.texMulAdd.z + material.texAnimDirection.x, 1);
|
|
material.texMulAdd.w = fmodf(material.texMulAdd.w + material.texAnimDirection.y, 1);
|
|
material.texAnimSleep = 1.0f;
|
|
|
|
material.dirty = true; // will trigger constant buffer update!
|
|
}
|
|
|
|
material.engineStencilRef = STENCILREF_DEFAULT;
|
|
if (material.subsurfaceScattering > 0)
|
|
{
|
|
material.engineStencilRef = STENCILREF_SKIN;
|
|
}
|
|
|
|
}
|
|
}
|
|
void RunObjectUpdateSystem(
|
|
const ComponentManager<TransformComponent>& transforms,
|
|
const ComponentManager<MeshComponent>& meshes,
|
|
const ComponentManager<MaterialComponent>& materials,
|
|
ComponentManager<ObjectComponent>& objects,
|
|
ComponentManager<CullableComponent>& cullables,
|
|
AABB& sceneBounds,
|
|
XMFLOAT4& waterPlane
|
|
)
|
|
{
|
|
sceneBounds.create(XMFLOAT3(FLT_MAX, FLT_MAX, FLT_MAX), XMFLOAT3(-FLT_MAX, -FLT_MAX, -FLT_MAX));
|
|
|
|
for (size_t i = 0; i < objects.GetCount(); ++i)
|
|
{
|
|
ObjectComponent& object = objects[i];
|
|
Entity entity = objects.GetEntity(i);
|
|
CullableComponent& cullable = *cullables.GetComponent(entity);
|
|
|
|
cullable.aabb.create(XMFLOAT3(FLT_MAX, FLT_MAX, FLT_MAX), XMFLOAT3(-FLT_MAX, -FLT_MAX, -FLT_MAX));
|
|
object.rendertypeMask = 0;
|
|
object.dynamic = false;
|
|
object.cast_shadow = false;
|
|
|
|
if (object.meshID != INVALID_ENTITY)
|
|
{
|
|
const TransformComponent* transform = transforms.GetComponent(entity);
|
|
const MeshComponent* mesh = meshes.GetComponent(object.meshID);
|
|
|
|
if (mesh != nullptr && transform != nullptr)
|
|
{
|
|
cullable.aabb = mesh->aabb.get(transform->world);
|
|
sceneBounds = AABB::Merge(sceneBounds, cullable.aabb);
|
|
|
|
if (mesh->IsSkinned() || mesh->IsDynamicVB())
|
|
{
|
|
object.dynamic = true;
|
|
}
|
|
|
|
for (auto& subset : mesh->subsets)
|
|
{
|
|
const MaterialComponent* material = materials.GetComponent(subset.materialID);
|
|
|
|
if (material != nullptr)
|
|
{
|
|
if (material->IsTransparent())
|
|
{
|
|
object.rendertypeMask |= RENDERTYPE_TRANSPARENT;
|
|
}
|
|
else
|
|
{
|
|
object.rendertypeMask |= RENDERTYPE_OPAQUE;
|
|
}
|
|
|
|
if (material->IsWater())
|
|
{
|
|
object.rendertypeMask |= RENDERTYPE_TRANSPARENT | RENDERTYPE_WATER;
|
|
|
|
XMVECTOR _refPlane = XMPlaneFromPointNormal(XMLoadFloat3(&transform->translation), XMVectorSet(0, 1, 0, 0));
|
|
XMStoreFloat4(&waterPlane, _refPlane);
|
|
}
|
|
|
|
object.cast_shadow |= material->cast_shadow;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void RunCameraUpdateSystem(
|
|
const wiECS::ComponentManager<TransformComponent>& transforms,
|
|
wiECS::ComponentManager<CameraComponent>& cameras
|
|
)
|
|
{
|
|
for (size_t i = 0; i < cameras.GetCount(); ++i)
|
|
{
|
|
CameraComponent& camera = cameras[i];
|
|
Entity entity = cameras.GetEntity(i);
|
|
const TransformComponent* transform = transforms.GetComponent(entity);
|
|
camera.UpdateCamera(transform);
|
|
}
|
|
}
|
|
void RunDecalUpdateSystem(
|
|
const wiECS::ComponentManager<TransformComponent>& transforms,
|
|
wiECS::ComponentManager<CullableComponent>& cullables,
|
|
wiECS::ComponentManager<DecalComponent>& decals
|
|
)
|
|
{
|
|
for (size_t i = 0; i < decals.GetCount(); ++i)
|
|
{
|
|
DecalComponent& decal = decals[i];
|
|
Entity entity = decals.GetEntity(i);
|
|
const TransformComponent& transform = *transforms.GetComponent(entity);
|
|
decal.world = transform.world;
|
|
XMVECTOR front = XMVectorSet(0, 0, 1, 0);
|
|
front = XMVector3TransformNormal(front, XMLoadFloat4x4(&decal.world));
|
|
XMStoreFloat3(&decal.front, front);
|
|
|
|
CullableComponent& cullable = *cullables.GetComponent(entity);
|
|
cullable.aabb.createFromHalfWidth(XMFLOAT3(0, 0, 0), XMFLOAT3(1, 1, 1));
|
|
cullable.aabb = cullable.aabb.get(transform.world);
|
|
}
|
|
}
|
|
void RunProbeUpdateSystem(
|
|
const wiECS::ComponentManager<TransformComponent>& transforms,
|
|
wiECS::ComponentManager<CullableComponent>& cullables,
|
|
wiECS::ComponentManager<EnvironmentProbeComponent>& probes
|
|
)
|
|
{
|
|
for (size_t i = 0; i < probes.GetCount(); ++i)
|
|
{
|
|
EnvironmentProbeComponent& probe = probes[i];
|
|
Entity entity = probes.GetEntity(i);
|
|
const TransformComponent& transform = *transforms.GetComponent(entity);
|
|
|
|
probe.position = transform.translation;
|
|
|
|
CullableComponent& cullable = *cullables.GetComponent(entity);
|
|
cullable.aabb.createFromHalfWidth(XMFLOAT3(0, 0, 0), XMFLOAT3(1, 1, 1));
|
|
cullable.aabb = cullable.aabb.get(transform.world);
|
|
}
|
|
}
|
|
void RunLightUpdateSystem(
|
|
const CameraComponent& cascadeCamera,
|
|
const wiECS::ComponentManager<TransformComponent>& transforms,
|
|
wiECS::ComponentManager<CullableComponent>& cullables,
|
|
wiECS::ComponentManager<LightComponent>& lights,
|
|
XMFLOAT3& sunDirection, XMFLOAT3& sunColor
|
|
)
|
|
{
|
|
for (size_t i = 0; i < lights.GetCount(); ++i)
|
|
{
|
|
LightComponent& light = lights[i];
|
|
Entity entity = lights.GetEntity(i);
|
|
const TransformComponent& transform = *transforms.GetComponent(entity);
|
|
CullableComponent& cullable = *cullables.GetComponent(entity);
|
|
|
|
XMMATRIX world = XMLoadFloat4x4(&transform.world);
|
|
XMVECTOR translation = XMLoadFloat3(&transform.translation);
|
|
XMVECTOR rotation = XMLoadFloat4(&transform.rotation);
|
|
|
|
XMStoreFloat3(&light.direction, XMVector3TransformNormal(XMVectorSet(0, 1, 0, 1), world));
|
|
|
|
switch (light.type)
|
|
{
|
|
case LightComponent::DIRECTIONAL:
|
|
{
|
|
sunDirection = light.direction;
|
|
sunColor = light.color;
|
|
|
|
if (light.shadow)
|
|
{
|
|
XMFLOAT2 screen = XMFLOAT2((float)wiRenderer::GetInternalResolution().x, (float)wiRenderer::GetInternalResolution().y);
|
|
float nearPlane = cascadeCamera.zNearP;
|
|
float farPlane = cascadeCamera.zFarP;
|
|
XMMATRIX view = cascadeCamera.GetView();
|
|
XMMATRIX projection = cascadeCamera.GetRealProjection();
|
|
XMMATRIX world = XMMatrixIdentity();
|
|
|
|
// Set up three shadow cascades (far - mid - near):
|
|
const float referenceFrustumDepth = 800.0f; // this was the frustum depth used for reference
|
|
const float currentFrustumDepth = farPlane - nearPlane; // current frustum depth
|
|
const float lerp0 = referenceFrustumDepth / currentFrustumDepth * 0.5f; // third slice distance from cam (percentage)
|
|
const float lerp1 = referenceFrustumDepth / currentFrustumDepth * 0.12f; // second slice distance from cam (percentage)
|
|
const float lerp2 = referenceFrustumDepth / currentFrustumDepth * 0.016f; // first slice distance from cam (percentage)
|
|
|
|
// Create shadow cascades for main camera:
|
|
if (light.shadowCam_dirLight.empty())
|
|
{
|
|
light.shadowCam_dirLight.resize(3);
|
|
}
|
|
|
|
// Place the shadow cascades inside the viewport:
|
|
if (!light.shadowCam_dirLight.empty())
|
|
{
|
|
// frustum top left - near
|
|
XMVECTOR a0 = XMVector3Unproject(XMVectorSet(0, 0, 0, 1), 0, 0, screen.x, screen.y, 0.0f, 1.0f, projection, view, world);
|
|
// frustum top left - far
|
|
XMVECTOR a1 = XMVector3Unproject(XMVectorSet(0, 0, 1, 1), 0, 0, screen.x, screen.y, 0.0f, 1.0f, projection, view, world);
|
|
// frustum bottom right - near
|
|
XMVECTOR b0 = XMVector3Unproject(XMVectorSet(screen.x, screen.y, 0, 1), 0, 0, screen.x, screen.y, 0.0f, 1.0f, projection, view, world);
|
|
// frustum bottom right - far
|
|
XMVECTOR b1 = XMVector3Unproject(XMVectorSet(screen.x, screen.y, 1, 1), 0, 0, screen.x, screen.y, 0.0f, 1.0f, projection, view, world);
|
|
|
|
// calculate cascade projection sizes:
|
|
float size0 = XMVectorGetX(XMVector3Length(XMVectorSubtract(XMVectorLerp(b0, b1, lerp0), XMVectorLerp(a0, a1, lerp0))));
|
|
float size1 = XMVectorGetX(XMVector3Length(XMVectorSubtract(XMVectorLerp(b0, b1, lerp1), XMVectorLerp(a0, a1, lerp1))));
|
|
float size2 = XMVectorGetX(XMVector3Length(XMVectorSubtract(XMVectorLerp(b0, b1, lerp2), XMVectorLerp(a0, a1, lerp2))));
|
|
|
|
XMVECTOR rotDefault = XMQuaternionIdentity();
|
|
|
|
// create shadow cascade projections:
|
|
light.shadowCam_dirLight[0] = LightComponent::SHCAM(size0, rotDefault, -farPlane * 0.5f, farPlane * 0.5f);
|
|
light.shadowCam_dirLight[1] = LightComponent::SHCAM(size1, rotDefault, -farPlane * 0.5f, farPlane * 0.5f);
|
|
light.shadowCam_dirLight[2] = LightComponent::SHCAM(size2, rotDefault, -farPlane * 0.5f, farPlane * 0.5f);
|
|
|
|
// frustum center - near
|
|
XMVECTOR c = XMVector3Unproject(XMVectorSet(screen.x * 0.5f, screen.y * 0.5f, 0, 1), 0, 0, screen.x, screen.y, 0.0f, 1.0f, projection, view, world);
|
|
// frustum center - far
|
|
XMVECTOR d = XMVector3Unproject(XMVectorSet(screen.x * 0.5f, screen.y * 0.5f, 1, 1), 0, 0, screen.x, screen.y, 0.0f, 1.0f, projection, view, world);
|
|
|
|
// Avoid shadowmap texel swimming by aligning them to a discrete grid:
|
|
float f0 = light.shadowCam_dirLight[0].size / (float)wiRenderer::SHADOWRES_2D;
|
|
float f1 = light.shadowCam_dirLight[1].size / (float)wiRenderer::SHADOWRES_2D;
|
|
float f2 = light.shadowCam_dirLight[2].size / (float)wiRenderer::SHADOWRES_2D;
|
|
XMVECTOR e0 = XMVectorFloor(XMVectorLerp(c, d, lerp0) / f0) * f0;
|
|
XMVECTOR e1 = XMVectorFloor(XMVectorLerp(c, d, lerp1) / f1) * f1;
|
|
XMVECTOR e2 = XMVectorFloor(XMVectorLerp(c, d, lerp2) / f2) * f2;
|
|
|
|
XMMATRIX rrr = XMMatrixRotationQuaternion(XMLoadFloat4(&transform.rotation));
|
|
|
|
light.shadowCam_dirLight[0].Update(rrr, e0);
|
|
light.shadowCam_dirLight[1].Update(rrr, e1);
|
|
light.shadowCam_dirLight[2].Update(rrr, e2);
|
|
}
|
|
}
|
|
|
|
cullable.aabb.createFromHalfWidth(wiRenderer::getCamera()->Eye, XMFLOAT3(10000, 10000, 10000));
|
|
}
|
|
break;
|
|
case LightComponent::SPOT:
|
|
{
|
|
if (light.shadow)
|
|
{
|
|
if (light.shadowCam_spotLight.empty())
|
|
{
|
|
light.shadowCam_spotLight.push_back(LightComponent::SHCAM(XMFLOAT4(0, 0, 0, 1), 0.1f, 1000.0f, light.fov));
|
|
}
|
|
light.shadowCam_spotLight[0].Update(world);
|
|
light.shadowCam_spotLight[0].farplane = light.range;
|
|
light.shadowCam_spotLight[0].Create_Perspective(light.fov);
|
|
}
|
|
|
|
cullable.aabb.createFromHalfWidth(transform.translation, XMFLOAT3(light.range, light.range, light.range));
|
|
}
|
|
break;
|
|
case LightComponent::POINT:
|
|
case LightComponent::SPHERE:
|
|
case LightComponent::DISC:
|
|
case LightComponent::RECTANGLE:
|
|
case LightComponent::TUBE:
|
|
{
|
|
if (light.shadow)
|
|
{
|
|
if (light.shadowCam_pointLight.empty())
|
|
{
|
|
light.shadowCam_pointLight.push_back(LightComponent::SHCAM(XMFLOAT4(0.5f, -0.5f, -0.5f, -0.5f), 0.1f, 1000.0f, XM_PIDIV2)); //+x
|
|
light.shadowCam_pointLight.push_back(LightComponent::SHCAM(XMFLOAT4(0.5f, 0.5f, 0.5f, -0.5f), 0.1f, 1000.0f, XM_PIDIV2)); //-x
|
|
|
|
light.shadowCam_pointLight.push_back(LightComponent::SHCAM(XMFLOAT4(1, 0, 0, -0), 0.1f, 1000.0f, XM_PIDIV2)); //+y
|
|
light.shadowCam_pointLight.push_back(LightComponent::SHCAM(XMFLOAT4(0, 0, 0, -1), 0.1f, 1000.0f, XM_PIDIV2)); //-y
|
|
|
|
light.shadowCam_pointLight.push_back(LightComponent::SHCAM(XMFLOAT4(0.707f, 0, 0, -0.707f), 0.1f, 1000.0f, XM_PIDIV2)); //+z
|
|
light.shadowCam_pointLight.push_back(LightComponent::SHCAM(XMFLOAT4(0, 0.707f, 0.707f, 0), 0.1f, 1000.0f, XM_PIDIV2)); //-z
|
|
}
|
|
for (auto& x : light.shadowCam_pointLight) {
|
|
x.Update(translation);
|
|
x.farplane = max(1.0f, light.range);
|
|
x.Create_Perspective(XM_PIDIV2);
|
|
}
|
|
}
|
|
|
|
if (light.type == LightComponent::POINT)
|
|
{
|
|
cullable.aabb.createFromHalfWidth(transform.translation, XMFLOAT3(light.range, light.range, light.range));
|
|
}
|
|
else
|
|
{
|
|
// area lights have no bounds, just like directional lights (maybe todo)
|
|
cullable.aabb.createFromHalfWidth(wiRenderer::getCamera()->Eye, XMFLOAT3(10000, 10000, 10000));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|