1652 lines
54 KiB
C++
1652 lines
54 KiB
C++
#include "wiScene_Components.h"
|
|
#include "wiTextureHelper.h"
|
|
#include "wiResourceManager.h"
|
|
#include "wiPhysics.h"
|
|
#include "wiRenderer.h"
|
|
#include "wiJobSystem.h"
|
|
#include "wiSpinLock.h"
|
|
#include "wiHelper.h"
|
|
#include "wiRenderer.h"
|
|
#include "wiBacklog.h"
|
|
#include "wiTimer.h"
|
|
#include "wiUnorderedMap.h"
|
|
#include "wiLua.h"
|
|
|
|
using namespace wi::ecs;
|
|
using namespace wi::enums;
|
|
using namespace wi::graphics;
|
|
using namespace wi::primitive;
|
|
|
|
namespace wi::scene
|
|
{
|
|
|
|
|
|
XMFLOAT3 TransformComponent::GetPosition() const
|
|
{
|
|
return *((XMFLOAT3*)&world._41);
|
|
}
|
|
XMFLOAT4 TransformComponent::GetRotation() const
|
|
{
|
|
XMFLOAT4 rotation;
|
|
XMStoreFloat4(&rotation, GetRotationV());
|
|
return rotation;
|
|
}
|
|
XMFLOAT3 TransformComponent::GetScale() const
|
|
{
|
|
XMFLOAT3 scale;
|
|
XMStoreFloat3(&scale, GetScaleV());
|
|
return scale;
|
|
}
|
|
XMVECTOR TransformComponent::GetPositionV() const
|
|
{
|
|
return XMLoadFloat3((XMFLOAT3*)&world._41);
|
|
}
|
|
XMVECTOR TransformComponent::GetRotationV() const
|
|
{
|
|
XMVECTOR S, R, T;
|
|
XMMatrixDecompose(&S, &R, &T, XMLoadFloat4x4(&world));
|
|
return R;
|
|
}
|
|
XMVECTOR TransformComponent::GetScaleV() const
|
|
{
|
|
XMVECTOR S, R, T;
|
|
XMMatrixDecompose(&S, &R, &T, XMLoadFloat4x4(&world));
|
|
return S;
|
|
}
|
|
XMMATRIX TransformComponent::GetLocalMatrix() const
|
|
{
|
|
XMVECTOR S_local = XMLoadFloat3(&scale_local);
|
|
XMVECTOR R_local = XMLoadFloat4(&rotation_local);
|
|
XMVECTOR T_local = XMLoadFloat3(&translation_local);
|
|
return
|
|
XMMatrixScalingFromVector(S_local) *
|
|
XMMatrixRotationQuaternion(R_local) *
|
|
XMMatrixTranslationFromVector(T_local);
|
|
}
|
|
void TransformComponent::UpdateTransform()
|
|
{
|
|
if (IsDirty())
|
|
{
|
|
SetDirty(false);
|
|
|
|
XMStoreFloat4x4(&world, GetLocalMatrix());
|
|
}
|
|
}
|
|
void TransformComponent::UpdateTransform_Parented(const TransformComponent& parent)
|
|
{
|
|
XMMATRIX W = GetLocalMatrix();
|
|
XMMATRIX W_parent = XMLoadFloat4x4(&parent.world);
|
|
W = W * W_parent;
|
|
|
|
XMStoreFloat4x4(&world, W);
|
|
}
|
|
void TransformComponent::ApplyTransform()
|
|
{
|
|
SetDirty();
|
|
|
|
XMVECTOR S, R, T;
|
|
XMMatrixDecompose(&S, &R, &T, XMLoadFloat4x4(&world));
|
|
XMStoreFloat3(&scale_local, S);
|
|
XMStoreFloat4(&rotation_local, R);
|
|
XMStoreFloat3(&translation_local, T);
|
|
}
|
|
void TransformComponent::ClearTransform()
|
|
{
|
|
SetDirty();
|
|
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)
|
|
{
|
|
SetDirty();
|
|
translation_local.x += value.x;
|
|
translation_local.y += value.y;
|
|
translation_local.z += value.z;
|
|
}
|
|
void TransformComponent::Translate(const XMVECTOR& value)
|
|
{
|
|
XMFLOAT3 translation;
|
|
XMStoreFloat3(&translation, value);
|
|
Translate(translation);
|
|
}
|
|
void TransformComponent::RotateRollPitchYaw(const XMFLOAT3& value)
|
|
{
|
|
SetDirty();
|
|
|
|
// 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)
|
|
{
|
|
SetDirty();
|
|
|
|
XMVECTOR result = XMQuaternionMultiply(XMLoadFloat4(&rotation_local), XMLoadFloat4(&quaternion));
|
|
result = XMQuaternionNormalize(result);
|
|
XMStoreFloat4(&rotation_local, result);
|
|
}
|
|
void TransformComponent::Rotate(const XMVECTOR& quaternion)
|
|
{
|
|
XMFLOAT4 rotation;
|
|
XMStoreFloat4(&rotation, quaternion);
|
|
Rotate(rotation);
|
|
}
|
|
void TransformComponent::Scale(const XMFLOAT3& value)
|
|
{
|
|
SetDirty();
|
|
scale_local.x *= value.x;
|
|
scale_local.y *= value.y;
|
|
scale_local.z *= value.z;
|
|
}
|
|
void TransformComponent::Scale(const XMVECTOR& value)
|
|
{
|
|
XMFLOAT3 scale;
|
|
XMStoreFloat3(&scale, value);
|
|
Scale(scale);
|
|
}
|
|
void TransformComponent::MatrixTransform(const XMFLOAT4X4& matrix)
|
|
{
|
|
MatrixTransform(XMLoadFloat4x4(&matrix));
|
|
}
|
|
void TransformComponent::MatrixTransform(const XMMATRIX& matrix)
|
|
{
|
|
SetDirty();
|
|
|
|
XMVECTOR S;
|
|
XMVECTOR R;
|
|
XMVECTOR T;
|
|
XMMatrixDecompose(&S, &R, &T, GetLocalMatrix() * matrix);
|
|
|
|
XMStoreFloat3(&scale_local, S);
|
|
XMStoreFloat4(&rotation_local, R);
|
|
XMStoreFloat3(&translation_local, T);
|
|
}
|
|
void TransformComponent::Lerp(const TransformComponent& a, const TransformComponent& b, float t)
|
|
{
|
|
SetDirty();
|
|
|
|
XMVECTOR aS, aR, aT;
|
|
XMMatrixDecompose(&aS, &aR, &aT, XMLoadFloat4x4(&a.world));
|
|
|
|
XMVECTOR bS, bR, bT;
|
|
XMMatrixDecompose(&bS, &bR, &bT, XMLoadFloat4x4(&b.world));
|
|
|
|
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)
|
|
{
|
|
SetDirty();
|
|
|
|
XMVECTOR aS, aR, aT;
|
|
XMMatrixDecompose(&aS, &aR, &aT, XMLoadFloat4x4(&a.world));
|
|
|
|
XMVECTOR bS, bR, bT;
|
|
XMMatrixDecompose(&bS, &bR, &bT, XMLoadFloat4x4(&b.world));
|
|
|
|
XMVECTOR cS, cR, cT;
|
|
XMMatrixDecompose(&cS, &cR, &cT, XMLoadFloat4x4(&c.world));
|
|
|
|
XMVECTOR dS, dR, dT;
|
|
XMMatrixDecompose(&dS, &dR, &dT, XMLoadFloat4x4(&d.world));
|
|
|
|
XMVECTOR T = XMVectorCatmullRom(aT, bT, cT, dT, t);
|
|
|
|
XMVECTOR setupA;
|
|
XMVECTOR setupB;
|
|
XMVECTOR setupC;
|
|
|
|
aR = XMQuaternionNormalize(aR);
|
|
bR = XMQuaternionNormalize(bR);
|
|
cR = XMQuaternionNormalize(cR);
|
|
dR = XMQuaternionNormalize(dR);
|
|
|
|
XMQuaternionSquadSetup(&setupA, &setupB, &setupC, aR, bR, cR, dR);
|
|
XMVECTOR R = XMQuaternionSquad(bR, setupA, setupB, setupC, t);
|
|
|
|
XMVECTOR S = XMVectorCatmullRom(aS, bS, cS, dS, t);
|
|
|
|
XMStoreFloat3(&translation_local, T);
|
|
XMStoreFloat4(&rotation_local, R);
|
|
XMStoreFloat3(&scale_local, S);
|
|
}
|
|
|
|
void MaterialComponent::WriteShaderMaterial(ShaderMaterial* dest) const
|
|
{
|
|
ShaderMaterial material;
|
|
material.baseColor = baseColor;
|
|
material.emissive_r11g11b10 = wi::math::Pack_R11G11B10_FLOAT(XMFLOAT3(emissiveColor.x * emissiveColor.w, emissiveColor.y * emissiveColor.w, emissiveColor.z * emissiveColor.w));
|
|
material.specular_r11g11b10 = wi::math::Pack_R11G11B10_FLOAT(XMFLOAT3(specularColor.x * specularColor.w, specularColor.y * specularColor.w, specularColor.z * specularColor.w));
|
|
material.texMulAdd = texMulAdd;
|
|
material.roughness = roughness;
|
|
material.reflectance = reflectance;
|
|
material.metalness = metalness;
|
|
material.refraction = refraction;
|
|
material.normalMapStrength = (textures[NORMALMAP].resource.IsValid() ? normalMapStrength : 0);
|
|
material.parallaxOcclusionMapping = parallaxOcclusionMapping;
|
|
material.displacementMapping = displacementMapping;
|
|
XMFLOAT4 sss = subsurfaceScattering;
|
|
sss.x *= sss.w;
|
|
sss.y *= sss.w;
|
|
sss.z *= sss.w;
|
|
XMFLOAT4 sss_inv = XMFLOAT4(
|
|
sss_inv.x = 1.0f / ((1 + sss.x) * (1 + sss.x)),
|
|
sss_inv.y = 1.0f / ((1 + sss.y) * (1 + sss.y)),
|
|
sss_inv.z = 1.0f / ((1 + sss.z) * (1 + sss.z)),
|
|
sss_inv.w = 1.0f / ((1 + sss.w) * (1 + sss.w))
|
|
);
|
|
material.subsurfaceScattering = sss;
|
|
material.subsurfaceScattering_inv = sss_inv;
|
|
|
|
material.sheenColor_r11g11b10 = wi::math::Pack_R11G11B10_FLOAT(XMFLOAT3(sheenColor.x, sheenColor.y, sheenColor.z));
|
|
material.sheenRoughness = sheenRoughness;
|
|
material.clearcoat = clearcoat;
|
|
material.clearcoatRoughness = clearcoatRoughness;
|
|
material.alphaTest = 1 - alphaRef;
|
|
material.layerMask = layerMask;
|
|
material.transmission = transmission;
|
|
if (shaderType == SHADERTYPE_PBR_ANISOTROPIC)
|
|
{
|
|
material.anisotropy_strength = wi::math::Clamp(anisotropy_strength, 0, 0.99f);
|
|
material.anisotropy_rotation_sin = std::sin(anisotropy_rotation);
|
|
material.anisotropy_rotation_cos = std::cos(anisotropy_rotation);
|
|
}
|
|
else
|
|
{
|
|
material.anisotropy_strength = 0;
|
|
material.anisotropy_rotation_sin = 0;
|
|
material.anisotropy_rotation_cos = 0;
|
|
}
|
|
material.stencilRef = wi::renderer::CombineStencilrefs(engineStencilRef, userStencilRef);
|
|
material.shaderType = (uint)shaderType;
|
|
material.userdata = userdata;
|
|
|
|
material.options = 0;
|
|
if (IsUsingVertexColors())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_USE_VERTEXCOLORS;
|
|
}
|
|
if (IsUsingSpecularGlossinessWorkflow())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_SPECULARGLOSSINESS_WORKFLOW;
|
|
}
|
|
if (IsOcclusionEnabled_Primary())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_OCCLUSION_PRIMARY;
|
|
}
|
|
if (IsOcclusionEnabled_Secondary())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_OCCLUSION_SECONDARY;
|
|
}
|
|
if (IsUsingWind())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_USE_WIND;
|
|
}
|
|
if (IsReceiveShadow())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_RECEIVE_SHADOW;
|
|
}
|
|
if (IsCastingShadow())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_CAST_SHADOW;
|
|
}
|
|
if (IsDoubleSided())
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_DOUBLE_SIDED;
|
|
}
|
|
if (GetFilterMask() & FILTER_TRANSPARENT)
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_TRANSPARENT;
|
|
}
|
|
if (userBlendMode == BLENDMODE_ADDITIVE)
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_ADDITIVE;
|
|
}
|
|
if (shaderType == SHADERTYPE_UNLIT)
|
|
{
|
|
material.options |= SHADERMATERIAL_OPTION_BIT_UNLIT;
|
|
}
|
|
|
|
GraphicsDevice* device = wi::graphics::GetDevice();
|
|
for (int i = 0; i < TEXTURESLOT_COUNT; ++i)
|
|
{
|
|
material.textures[i].uvset_lodclamp = (textures[i].uvset & 1) | (XMConvertFloatToHalf(textures[i].lod_clamp) << 1u);
|
|
if (textures[i].resource.IsValid())
|
|
{
|
|
int subresource = -1;
|
|
switch (i)
|
|
{
|
|
case BASECOLORMAP:
|
|
case EMISSIVEMAP:
|
|
case SPECULARMAP:
|
|
case SHEENCOLORMAP:
|
|
subresource = textures[i].resource.GetTextureSRGBSubresource();
|
|
break;
|
|
case SURFACEMAP:
|
|
if (IsUsingSpecularGlossinessWorkflow())
|
|
{
|
|
subresource = textures[i].resource.GetTextureSRGBSubresource();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
material.textures[i].texture_descriptor = device->GetDescriptorIndex(textures[i].GetGPUResource(), SubresourceType::SRV, subresource);
|
|
}
|
|
else
|
|
{
|
|
material.textures[i].texture_descriptor = -1;
|
|
}
|
|
material.textures[i].sparse_residencymap_descriptor = textures[i].sparse_residencymap_descriptor;
|
|
material.textures[i].sparse_feedbackmap_descriptor = textures[i].sparse_feedbackmap_descriptor;
|
|
}
|
|
|
|
if (sampler_descriptor < 0)
|
|
{
|
|
material.sampler_descriptor = device->GetDescriptorIndex(wi::renderer::GetSampler(wi::enums::SAMPLER_OBJECTSHADER));
|
|
}
|
|
else
|
|
{
|
|
material.sampler_descriptor = sampler_descriptor;
|
|
}
|
|
|
|
std::memcpy(dest, &material, sizeof(ShaderMaterial)); // memcpy whole structure into mapped pointer to avoid read from uncached memory
|
|
}
|
|
void MaterialComponent::WriteShaderTextureSlot(ShaderMaterial* dest, int slot, int descriptor)
|
|
{
|
|
std::memcpy(&dest->textures[slot].texture_descriptor, &descriptor, sizeof(descriptor)); // memcpy into mapped pointer to avoid read from uncached memory
|
|
}
|
|
void MaterialComponent::WriteTextures(const wi::graphics::GPUResource** dest, int count) const
|
|
{
|
|
count = std::min(count, (int)TEXTURESLOT_COUNT);
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
dest[i] = textures[i].GetGPUResource();
|
|
}
|
|
}
|
|
uint32_t MaterialComponent::GetFilterMask() const
|
|
{
|
|
if (IsCustomShader() && customShaderID < (int)wi::renderer::GetCustomShaders().size())
|
|
{
|
|
auto& customShader = wi::renderer::GetCustomShaders()[customShaderID];
|
|
return customShader.filterMask;
|
|
}
|
|
if (shaderType == SHADERTYPE_WATER)
|
|
{
|
|
return FILTER_TRANSPARENT | FILTER_WATER;
|
|
}
|
|
if (transmission > 0)
|
|
{
|
|
return FILTER_TRANSPARENT;
|
|
}
|
|
if (userBlendMode == BLENDMODE_OPAQUE)
|
|
{
|
|
return FILTER_OPAQUE;
|
|
}
|
|
return FILTER_TRANSPARENT;
|
|
}
|
|
void MaterialComponent::CreateRenderData()
|
|
{
|
|
for (uint32_t slot = 0; slot < TEXTURESLOT_COUNT; ++slot)
|
|
{
|
|
auto& textureslot = textures[slot];
|
|
if (!textureslot.name.empty())
|
|
{
|
|
wi::resourcemanager::Flags flags = wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA;
|
|
switch (slot)
|
|
{
|
|
case NORMALMAP:
|
|
case CLEARCOATNORMALMAP:
|
|
flags |= wi::resourcemanager::Flags::IMPORT_NORMALMAP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
textureslot.resource = wi::resourcemanager::Load(textureslot.name, flags);
|
|
}
|
|
}
|
|
}
|
|
uint32_t MaterialComponent::GetStencilRef() const
|
|
{
|
|
return wi::renderer::CombineStencilrefs(engineStencilRef, userStencilRef);
|
|
}
|
|
|
|
void MeshComponent::DeleteRenderData()
|
|
{
|
|
generalBuffer = {};
|
|
streamoutBuffer = {};
|
|
ib = {};
|
|
vb_pos_nor_wind = {};
|
|
vb_tan = {};
|
|
vb_uvs = {};
|
|
vb_atl = {};
|
|
vb_col = {};
|
|
vb_bon = {};
|
|
so_pos_nor_wind = {};
|
|
so_tan = {};
|
|
so_pre = {};
|
|
BLASes.clear();
|
|
for (MorphTarget& morph : morph_targets)
|
|
{
|
|
morph.offset_pos = ~0ull;
|
|
morph.offset_nor = ~0ull;
|
|
}
|
|
}
|
|
void MeshComponent::CreateRenderData()
|
|
{
|
|
DeleteRenderData();
|
|
|
|
GraphicsDevice* device = wi::graphics::GetDevice();
|
|
|
|
if (vertex_tangents.empty() && !vertex_uvset_0.empty() && !vertex_normals.empty())
|
|
{
|
|
// Generate tangents if not found:
|
|
vertex_tangents.resize(vertex_positions.size());
|
|
|
|
uint32_t first_subset = 0;
|
|
uint32_t last_subset = 0;
|
|
GetLODSubsetRange(0, first_subset, last_subset);
|
|
for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
|
|
{
|
|
const MeshComponent::MeshSubset& subset = subsets[subsetIndex];
|
|
for (size_t i = 0; i < subset.indexCount; i += 3)
|
|
{
|
|
const uint32_t i0 = indices[subset.indexOffset + i + 0];
|
|
const uint32_t i1 = indices[subset.indexOffset + i + 1];
|
|
const uint32_t i2 = indices[subset.indexOffset + i + 2];
|
|
|
|
const XMFLOAT3 v0 = vertex_positions[i0];
|
|
const XMFLOAT3 v1 = vertex_positions[i1];
|
|
const XMFLOAT3 v2 = vertex_positions[i2];
|
|
|
|
const XMFLOAT2 u0 = vertex_uvset_0[i0];
|
|
const XMFLOAT2 u1 = vertex_uvset_0[i1];
|
|
const XMFLOAT2 u2 = vertex_uvset_0[i2];
|
|
|
|
const XMFLOAT3 n0 = vertex_normals[i0];
|
|
const XMFLOAT3 n1 = vertex_normals[i1];
|
|
const XMFLOAT3 n2 = vertex_normals[i2];
|
|
|
|
const XMVECTOR nor0 = XMLoadFloat3(&n0);
|
|
const XMVECTOR nor1 = XMLoadFloat3(&n1);
|
|
const XMVECTOR nor2 = XMLoadFloat3(&n2);
|
|
|
|
const XMVECTOR facenormal = XMVector3Normalize(nor0 + nor1 + nor2);
|
|
|
|
const float x1 = v1.x - v0.x;
|
|
const float x2 = v2.x - v0.x;
|
|
const float y1 = v1.y - v0.y;
|
|
const float y2 = v2.y - v0.y;
|
|
const float z1 = v1.z - v0.z;
|
|
const float z2 = v2.z - v0.z;
|
|
|
|
const float s1 = u1.x - u0.x;
|
|
const float s2 = u2.x - u0.x;
|
|
const float t1 = u1.y - u0.y;
|
|
const float t2 = u2.y - u0.y;
|
|
|
|
const float r = 1.0f / (s1 * t2 - s2 * t1);
|
|
const XMVECTOR sdir = XMVectorSet((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
|
|
(t2 * z1 - t1 * z2) * r, 0);
|
|
const XMVECTOR tdir = XMVectorSet((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
|
|
(s1 * z2 - s2 * z1) * r, 0);
|
|
|
|
XMVECTOR tangent;
|
|
tangent = XMVector3Normalize(sdir - facenormal * XMVector3Dot(facenormal, sdir));
|
|
float sign = XMVectorGetX(XMVector3Dot(XMVector3Cross(tangent, facenormal), tdir)) < 0.0f ? -1.0f : 1.0f;
|
|
|
|
XMFLOAT3 t;
|
|
XMStoreFloat3(&t, tangent);
|
|
|
|
vertex_tangents[i0].x += t.x;
|
|
vertex_tangents[i0].y += t.y;
|
|
vertex_tangents[i0].z += t.z;
|
|
vertex_tangents[i0].w = sign;
|
|
|
|
vertex_tangents[i1].x += t.x;
|
|
vertex_tangents[i1].y += t.y;
|
|
vertex_tangents[i1].z += t.z;
|
|
vertex_tangents[i1].w = sign;
|
|
|
|
vertex_tangents[i2].x += t.x;
|
|
vertex_tangents[i2].y += t.y;
|
|
vertex_tangents[i2].z += t.z;
|
|
vertex_tangents[i2].w = sign;
|
|
}
|
|
}
|
|
}
|
|
|
|
const size_t uv_count = std::max(vertex_uvset_0.size(), vertex_uvset_1.size());
|
|
|
|
GPUBufferDesc bd;
|
|
bd.usage = Usage::DEFAULT;
|
|
bd.bind_flags = BindFlag::VERTEX_BUFFER | BindFlag::INDEX_BUFFER | BindFlag::SHADER_RESOURCE;
|
|
bd.misc_flags = ResourceMiscFlag::BUFFER_RAW;
|
|
if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
|
|
{
|
|
bd.misc_flags |= ResourceMiscFlag::RAY_TRACING;
|
|
}
|
|
const uint64_t alignment = device->GetMinOffsetAlignment(&bd);
|
|
bd.size =
|
|
AlignTo(indices.size() * GetIndexStride(), alignment) +
|
|
AlignTo(vertex_positions.size() * sizeof(Vertex_POS), alignment) +
|
|
AlignTo(vertex_tangents.size() * sizeof(Vertex_TAN), alignment) +
|
|
AlignTo(uv_count * sizeof(Vertex_UVS), alignment) +
|
|
AlignTo(vertex_atlas.size() * sizeof(Vertex_TEX), alignment) +
|
|
AlignTo(vertex_colors.size() * sizeof(Vertex_COL), alignment) +
|
|
AlignTo(vertex_boneindices.size() * sizeof(Vertex_BON), alignment)
|
|
;
|
|
|
|
for (MorphTarget& morph : morph_targets)
|
|
{
|
|
if (!morph.vertex_positions.empty())
|
|
{
|
|
bd.size += AlignTo(vertex_positions.size() * sizeof(XMHALF4), alignment);
|
|
}
|
|
if (!morph.vertex_normals.empty())
|
|
{
|
|
bd.size += AlignTo(vertex_normals.size() * sizeof(XMHALF4), alignment);
|
|
}
|
|
}
|
|
|
|
auto init_callback = [&](void* dest) {
|
|
uint8_t* buffer_data = (uint8_t*)dest;
|
|
uint64_t buffer_offset = 0ull;
|
|
|
|
// Create index buffer GPU data:
|
|
if (GetIndexFormat() == IndexBufferFormat::UINT32)
|
|
{
|
|
ib.offset = buffer_offset;
|
|
ib.size = indices.size() * sizeof(uint32_t);
|
|
uint32_t* indexdata = (uint32_t*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(ib.size, alignment);
|
|
std::memcpy(indexdata, indices.data(), ib.size);
|
|
}
|
|
else
|
|
{
|
|
ib.offset = buffer_offset;
|
|
ib.size = indices.size() * sizeof(uint16_t);
|
|
uint16_t* indexdata = (uint16_t*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(ib.size, alignment);
|
|
for (size_t i = 0; i < indices.size(); ++i)
|
|
{
|
|
std::memcpy(indexdata + i, &indices[i], sizeof(uint16_t));
|
|
}
|
|
}
|
|
|
|
XMFLOAT3 _min = XMFLOAT3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
|
|
XMFLOAT3 _max = XMFLOAT3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
|
|
|
|
// vertexBuffer - POSITION + NORMAL + WIND:
|
|
{
|
|
vb_pos_nor_wind.offset = buffer_offset;
|
|
vb_pos_nor_wind.size = vertex_positions.size() * sizeof(Vertex_POS);
|
|
Vertex_POS* vertices = (Vertex_POS*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(vb_pos_nor_wind.size, alignment);
|
|
for (size_t i = 0; i < vertex_positions.size(); ++i)
|
|
{
|
|
const XMFLOAT3& pos = vertex_positions[i];
|
|
XMFLOAT3 nor = vertex_normals.empty() ? XMFLOAT3(1, 1, 1) : vertex_normals[i];
|
|
XMStoreFloat3(&nor, XMVector3Normalize(XMLoadFloat3(&nor)));
|
|
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
|
|
Vertex_POS vert;
|
|
vert.FromFULL(pos, nor, wind);
|
|
std::memcpy(vertices + i, &vert, sizeof(vert));
|
|
|
|
_min = wi::math::Min(_min, pos);
|
|
_max = wi::math::Max(_max, pos);
|
|
}
|
|
}
|
|
|
|
aabb = AABB(_min, _max);
|
|
|
|
// vertexBuffer - TANGENTS
|
|
if (!vertex_tangents.empty())
|
|
{
|
|
vb_tan.offset = buffer_offset;
|
|
vb_tan.size = vertex_tangents.size() * sizeof(Vertex_TAN);
|
|
Vertex_TAN* vertices = (Vertex_TAN*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(vb_tan.size, alignment);
|
|
for (size_t i = 0; i < vertex_tangents.size(); ++i)
|
|
{
|
|
Vertex_TAN vert;
|
|
vert.FromFULL(vertex_tangents[i]);
|
|
std::memcpy(vertices + i, &vert, sizeof(vert));
|
|
}
|
|
}
|
|
|
|
// vertexBuffer - UV SETS
|
|
if (!vertex_uvset_0.empty() || !vertex_uvset_1.empty())
|
|
{
|
|
const XMFLOAT2* uv0_stream = vertex_uvset_0.empty() ? vertex_uvset_1.data() : vertex_uvset_0.data();
|
|
const XMFLOAT2* uv1_stream = vertex_uvset_1.empty() ? vertex_uvset_0.data() : vertex_uvset_1.data();
|
|
|
|
vb_uvs.offset = buffer_offset;
|
|
vb_uvs.size = uv_count * sizeof(Vertex_UVS);
|
|
Vertex_UVS* vertices = (Vertex_UVS*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(vb_uvs.size, alignment);
|
|
for (size_t i = 0; i < uv_count; ++i)
|
|
{
|
|
Vertex_UVS vert;
|
|
vert.uv0.FromFULL(uv0_stream[i]);
|
|
vert.uv1.FromFULL(uv1_stream[i]);
|
|
std::memcpy(vertices + i, &vert, sizeof(vert));
|
|
}
|
|
}
|
|
|
|
// vertexBuffer - ATLAS
|
|
if (!vertex_atlas.empty())
|
|
{
|
|
vb_atl.offset = buffer_offset;
|
|
vb_atl.size = vertex_atlas.size() * sizeof(Vertex_TEX);
|
|
Vertex_TEX* vertices = (Vertex_TEX*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(vb_atl.size, alignment);
|
|
for (size_t i = 0; i < vertex_atlas.size(); ++i)
|
|
{
|
|
Vertex_TEX vert;
|
|
vert.FromFULL(vertex_atlas[i]);
|
|
std::memcpy(vertices + i, &vert, sizeof(vert));
|
|
}
|
|
}
|
|
|
|
// vertexBuffer - COLORS
|
|
if (!vertex_colors.empty())
|
|
{
|
|
vb_col.offset = buffer_offset;
|
|
vb_col.size = vertex_colors.size() * sizeof(Vertex_COL);
|
|
Vertex_COL* vertices = (Vertex_COL*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(vb_col.size, alignment);
|
|
for (size_t i = 0; i < vertex_colors.size(); ++i)
|
|
{
|
|
Vertex_COL vert;
|
|
vert.color = vertex_colors[i];
|
|
std::memcpy(vertices + i, &vert, sizeof(vert));
|
|
}
|
|
}
|
|
|
|
// skinning buffers:
|
|
if (!vertex_boneindices.empty())
|
|
{
|
|
vb_bon.offset = buffer_offset;
|
|
vb_bon.size = vertex_boneindices.size() * sizeof(Vertex_BON);
|
|
Vertex_BON* vertices = (Vertex_BON*)(buffer_data + buffer_offset);
|
|
buffer_offset += AlignTo(vb_bon.size, alignment);
|
|
assert(vertex_boneindices.size() == vertex_boneweights.size());
|
|
for (size_t i = 0; i < vertex_boneindices.size(); ++i)
|
|
{
|
|
XMFLOAT4& wei = vertex_boneweights[i];
|
|
// normalize bone weights
|
|
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;
|
|
}
|
|
Vertex_BON vert;
|
|
vert.FromFULL(vertex_boneindices[i], wei);
|
|
std::memcpy(vertices + i, &vert, sizeof(vert));
|
|
}
|
|
}
|
|
|
|
// morph buffers:
|
|
if (!morph_targets.empty())
|
|
{
|
|
for (MorphTarget& morph : morph_targets)
|
|
{
|
|
if (!morph.vertex_positions.empty())
|
|
{
|
|
morph.offset_pos = buffer_offset;
|
|
XMHALF4* vertices = (XMHALF4*)(buffer_data + buffer_offset);
|
|
std::fill(vertices, vertices + vertex_positions.size(), 0);
|
|
if (morph.sparse_indices_positions.empty())
|
|
{
|
|
// flat morphs:
|
|
for (size_t i = 0; i < morph.vertex_positions.size(); ++i)
|
|
{
|
|
XMStoreHalf4(vertices + i, XMLoadFloat3(&morph.vertex_positions[i]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// sparse morphs will be flattened for GPU because they will be evaluated in skinning for every vertex:
|
|
for (size_t i = 0; i < morph.sparse_indices_positions.size(); ++i)
|
|
{
|
|
const uint32_t ind = morph.sparse_indices_positions[i];
|
|
XMStoreHalf4(vertices + ind, XMLoadFloat3(&morph.vertex_positions[i]));
|
|
}
|
|
}
|
|
buffer_offset += AlignTo(morph.vertex_positions.size() * sizeof(XMHALF4), alignment);
|
|
}
|
|
if (!morph.vertex_normals.empty())
|
|
{
|
|
morph.offset_nor = buffer_offset;
|
|
XMHALF4* vertices = (XMHALF4*)(buffer_data + buffer_offset);
|
|
std::fill(vertices, vertices + vertex_normals.size(), 0);
|
|
if (morph.sparse_indices_normals.empty())
|
|
{
|
|
// flat morphs:
|
|
for (size_t i = 0; i < morph.vertex_normals.size(); ++i)
|
|
{
|
|
XMStoreHalf4(vertices + i, XMLoadFloat3(&morph.vertex_normals[i]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// sparse morphs will be flattened for GPU because they will be evaluated in skinning for every vertex:
|
|
for (size_t i = 0; i < morph.sparse_indices_normals.size(); ++i)
|
|
{
|
|
const uint32_t ind = morph.sparse_indices_normals[i];
|
|
XMStoreHalf4(vertices + ind, XMLoadFloat3(&morph.vertex_normals[i]));
|
|
}
|
|
}
|
|
buffer_offset += AlignTo(morph.vertex_normals.size() * sizeof(XMHALF4), alignment);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool success = device->CreateBuffer2(&bd, init_callback, &generalBuffer);
|
|
assert(success);
|
|
device->SetName(&generalBuffer, "MeshComponent::generalBuffer");
|
|
|
|
assert(ib.IsValid());
|
|
const Format ib_format = GetIndexFormat() == IndexBufferFormat::UINT32 ? Format::R32_UINT : Format::R16_UINT;
|
|
ib.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, ib.offset, ib.size, &ib_format);
|
|
ib.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, ib.subresource_srv);
|
|
|
|
assert(vb_pos_nor_wind.IsValid());
|
|
vb_pos_nor_wind.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_pos_nor_wind.offset, vb_pos_nor_wind.size);
|
|
vb_pos_nor_wind.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_pos_nor_wind.subresource_srv);
|
|
|
|
if (vb_tan.IsValid())
|
|
{
|
|
vb_tan.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_tan.offset, vb_tan.size);
|
|
vb_tan.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_tan.subresource_srv);
|
|
}
|
|
if (vb_uvs.IsValid())
|
|
{
|
|
vb_uvs.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_uvs.offset, vb_uvs.size);
|
|
vb_uvs.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_uvs.subresource_srv);
|
|
}
|
|
if (vb_atl.IsValid())
|
|
{
|
|
vb_atl.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_atl.offset, vb_atl.size);
|
|
vb_atl.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_atl.subresource_srv);
|
|
}
|
|
if (vb_col.IsValid())
|
|
{
|
|
vb_col.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_col.offset, vb_col.size);
|
|
vb_col.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_col.subresource_srv);
|
|
}
|
|
if (vb_bon.IsValid())
|
|
{
|
|
vb_bon.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_bon.offset, vb_bon.size);
|
|
vb_bon.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_bon.subresource_srv);
|
|
}
|
|
|
|
if (!vertex_boneindices.empty() || !morph_targets.empty())
|
|
{
|
|
CreateStreamoutRenderData();
|
|
}
|
|
}
|
|
void MeshComponent::CreateStreamoutRenderData()
|
|
{
|
|
GraphicsDevice* device = wi::graphics::GetDevice();
|
|
|
|
GPUBufferDesc desc;
|
|
desc.usage = Usage::DEFAULT;
|
|
desc.bind_flags = BindFlag::VERTEX_BUFFER | BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
|
|
desc.misc_flags = ResourceMiscFlag::BUFFER_RAW;
|
|
if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
|
|
{
|
|
desc.misc_flags |= ResourceMiscFlag::RAY_TRACING;
|
|
}
|
|
const uint64_t alignment = device->GetMinOffsetAlignment(&desc);
|
|
desc.size =
|
|
AlignTo(vertex_positions.size() * sizeof(Vertex_POS) * 2, alignment) + // *2 because prevpos also goes into this!
|
|
AlignTo(vertex_tangents.size() * sizeof(Vertex_TAN), alignment)
|
|
;
|
|
|
|
bool success = device->CreateBuffer(&desc, nullptr, &streamoutBuffer);
|
|
assert(success);
|
|
device->SetName(&streamoutBuffer, "MeshComponent::streamoutBuffer");
|
|
|
|
uint64_t buffer_offset = 0ull;
|
|
|
|
so_pos_nor_wind.offset = buffer_offset;
|
|
so_pos_nor_wind.size = vb_pos_nor_wind.size;
|
|
buffer_offset += AlignTo(so_pos_nor_wind.size, alignment);
|
|
so_pos_nor_wind.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_pos_nor_wind.offset, so_pos_nor_wind.size);
|
|
so_pos_nor_wind.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_pos_nor_wind.offset, so_pos_nor_wind.size);
|
|
so_pos_nor_wind.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_pos_nor_wind.subresource_srv);
|
|
so_pos_nor_wind.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_pos_nor_wind.subresource_uav);
|
|
|
|
if (vb_tan.IsValid())
|
|
{
|
|
so_tan.offset = buffer_offset;
|
|
so_tan.size = vb_tan.size;
|
|
buffer_offset += AlignTo(so_tan.size, alignment);
|
|
so_tan.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_tan.offset, so_tan.size);
|
|
so_tan.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_tan.offset, so_tan.size);
|
|
so_tan.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_tan.subresource_srv);
|
|
so_tan.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_tan.subresource_uav);
|
|
}
|
|
|
|
so_pre.offset = buffer_offset;
|
|
so_pre.size = vb_pos_nor_wind.size;
|
|
buffer_offset += AlignTo(so_pre.size, alignment);
|
|
so_pre.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_pre.offset, so_pre.size);
|
|
so_pre.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_pre.offset, so_pre.size);
|
|
so_pre.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_pre.subresource_srv);
|
|
so_pre.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_pre.subresource_uav);
|
|
}
|
|
void MeshComponent::CreateRaytracingRenderData()
|
|
{
|
|
GraphicsDevice* device = wi::graphics::GetDevice();
|
|
|
|
if (!device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
|
|
return;
|
|
|
|
BLAS_state = MeshComponent::BLAS_STATE_NEEDS_REBUILD;
|
|
|
|
const uint32_t lod_count = GetLODCount();
|
|
BLASes.resize(lod_count);
|
|
for (uint32_t lod = 0; lod < lod_count; ++lod)
|
|
{
|
|
RaytracingAccelerationStructureDesc desc;
|
|
desc.type = RaytracingAccelerationStructureDesc::Type::BOTTOMLEVEL;
|
|
|
|
if (streamoutBuffer.IsValid())
|
|
{
|
|
desc.flags |= RaytracingAccelerationStructureDesc::FLAG_ALLOW_UPDATE;
|
|
desc.flags |= RaytracingAccelerationStructureDesc::FLAG_PREFER_FAST_BUILD;
|
|
}
|
|
else
|
|
{
|
|
desc.flags |= RaytracingAccelerationStructureDesc::FLAG_PREFER_FAST_TRACE;
|
|
}
|
|
|
|
uint32_t first_subset = 0;
|
|
uint32_t last_subset = 0;
|
|
GetLODSubsetRange(lod, first_subset, last_subset);
|
|
for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
|
|
{
|
|
const MeshComponent::MeshSubset& subset = subsets[subsetIndex];
|
|
desc.bottom_level.geometries.emplace_back();
|
|
auto& geometry = desc.bottom_level.geometries.back();
|
|
geometry.type = RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::TRIANGLES;
|
|
geometry.triangles.vertex_buffer = generalBuffer;
|
|
geometry.triangles.vertex_byte_offset = vb_pos_nor_wind.offset;
|
|
geometry.triangles.index_buffer = generalBuffer;
|
|
geometry.triangles.index_format = GetIndexFormat();
|
|
geometry.triangles.index_count = subset.indexCount;
|
|
geometry.triangles.index_offset = ib.offset / GetIndexStride() + subset.indexOffset;
|
|
geometry.triangles.vertex_count = (uint32_t)vertex_positions.size();
|
|
geometry.triangles.vertex_format = Format::R32G32B32_FLOAT;
|
|
geometry.triangles.vertex_stride = sizeof(MeshComponent::Vertex_POS);
|
|
}
|
|
|
|
bool success = device->CreateRaytracingAccelerationStructure(&desc, &BLASes[lod]);
|
|
assert(success);
|
|
device->SetName(&BLASes[lod], std::string("MeshComponent::BLAS[LOD" + std::to_string(lod) + "]").c_str());
|
|
}
|
|
}
|
|
void MeshComponent::ComputeNormals(COMPUTE_NORMALS compute)
|
|
{
|
|
// Start recalculating normals:
|
|
|
|
if (compute != COMPUTE_NORMALS_SMOOTH_FAST)
|
|
{
|
|
// Compute hard surface normals:
|
|
|
|
// Right now they are always computed even before smooth setting
|
|
|
|
wi::vector<uint32_t> newIndexBuffer;
|
|
wi::vector<XMFLOAT3> newPositionsBuffer;
|
|
wi::vector<XMFLOAT3> newNormalsBuffer;
|
|
wi::vector<XMFLOAT2> newUV0Buffer;
|
|
wi::vector<XMFLOAT2> newUV1Buffer;
|
|
wi::vector<XMFLOAT2> newAtlasBuffer;
|
|
wi::vector<XMUINT4> newBoneIndicesBuffer;
|
|
wi::vector<XMFLOAT4> newBoneWeightsBuffer;
|
|
wi::vector<uint32_t> newColorsBuffer;
|
|
|
|
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];
|
|
|
|
XMFLOAT3& p0 = vertex_positions[i0];
|
|
XMFLOAT3& p1 = vertex_positions[i1];
|
|
XMFLOAT3& p2 = vertex_positions[i2];
|
|
|
|
XMVECTOR U = XMLoadFloat3(&p2) - XMLoadFloat3(&p0);
|
|
XMVECTOR V = XMLoadFloat3(&p1) - XMLoadFloat3(&p0);
|
|
|
|
XMVECTOR N = XMVector3Cross(U, V);
|
|
N = XMVector3Normalize(N);
|
|
|
|
XMFLOAT3 normal;
|
|
XMStoreFloat3(&normal, N);
|
|
|
|
newPositionsBuffer.push_back(p0);
|
|
newPositionsBuffer.push_back(p1);
|
|
newPositionsBuffer.push_back(p2);
|
|
|
|
newNormalsBuffer.push_back(normal);
|
|
newNormalsBuffer.push_back(normal);
|
|
newNormalsBuffer.push_back(normal);
|
|
|
|
if (!vertex_uvset_0.empty())
|
|
{
|
|
newUV0Buffer.push_back(vertex_uvset_0[i0]);
|
|
newUV0Buffer.push_back(vertex_uvset_0[i1]);
|
|
newUV0Buffer.push_back(vertex_uvset_0[i2]);
|
|
}
|
|
|
|
if (!vertex_uvset_1.empty())
|
|
{
|
|
newUV1Buffer.push_back(vertex_uvset_1[i0]);
|
|
newUV1Buffer.push_back(vertex_uvset_1[i1]);
|
|
newUV1Buffer.push_back(vertex_uvset_1[i2]);
|
|
}
|
|
|
|
if (!vertex_atlas.empty())
|
|
{
|
|
newAtlasBuffer.push_back(vertex_atlas[i0]);
|
|
newAtlasBuffer.push_back(vertex_atlas[i1]);
|
|
newAtlasBuffer.push_back(vertex_atlas[i2]);
|
|
}
|
|
|
|
if (!vertex_boneindices.empty())
|
|
{
|
|
newBoneIndicesBuffer.push_back(vertex_boneindices[i0]);
|
|
newBoneIndicesBuffer.push_back(vertex_boneindices[i1]);
|
|
newBoneIndicesBuffer.push_back(vertex_boneindices[i2]);
|
|
}
|
|
|
|
if (!vertex_boneweights.empty())
|
|
{
|
|
newBoneWeightsBuffer.push_back(vertex_boneweights[i0]);
|
|
newBoneWeightsBuffer.push_back(vertex_boneweights[i1]);
|
|
newBoneWeightsBuffer.push_back(vertex_boneweights[i2]);
|
|
}
|
|
|
|
if (!vertex_colors.empty())
|
|
{
|
|
newColorsBuffer.push_back(vertex_colors[i0]);
|
|
newColorsBuffer.push_back(vertex_colors[i1]);
|
|
newColorsBuffer.push_back(vertex_colors[i2]);
|
|
}
|
|
|
|
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:
|
|
vertex_positions = newPositionsBuffer;
|
|
vertex_normals = newNormalsBuffer;
|
|
vertex_uvset_0 = newUV0Buffer;
|
|
vertex_uvset_1 = newUV1Buffer;
|
|
vertex_atlas = newAtlasBuffer;
|
|
vertex_colors = newColorsBuffer;
|
|
if (!vertex_boneindices.empty())
|
|
{
|
|
vertex_boneindices = newBoneIndicesBuffer;
|
|
}
|
|
if (!vertex_boneweights.empty())
|
|
{
|
|
vertex_boneweights = newBoneWeightsBuffer;
|
|
}
|
|
indices = newIndexBuffer;
|
|
}
|
|
|
|
switch (compute)
|
|
{
|
|
case MeshComponent::COMPUTE_NORMALS_HARD:
|
|
break;
|
|
|
|
case MeshComponent::COMPUTE_NORMALS_SMOOTH:
|
|
{
|
|
// Compute smooth surface normals:
|
|
|
|
// 1.) Zero normals, they will be averaged later
|
|
for (size_t i = 0; i < vertex_normals.size(); i++)
|
|
{
|
|
vertex_normals[i] = XMFLOAT3(0, 0, 0);
|
|
}
|
|
|
|
// 2.) Find identical vertices by POSITION, accumulate face normals
|
|
for (size_t i = 0; i < vertex_positions.size(); i++)
|
|
{
|
|
XMFLOAT3& v_search_pos = vertex_positions[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];
|
|
|
|
XMFLOAT3& v0 = vertex_positions[i0];
|
|
XMFLOAT3& v1 = vertex_positions[i1];
|
|
XMFLOAT3& v2 = vertex_positions[i2];
|
|
|
|
bool match_pos0 =
|
|
fabs(v_search_pos.x - v0.x) < FLT_EPSILON &&
|
|
fabs(v_search_pos.y - v0.y) < FLT_EPSILON &&
|
|
fabs(v_search_pos.z - v0.z) < FLT_EPSILON;
|
|
|
|
bool match_pos1 =
|
|
fabs(v_search_pos.x - v1.x) < FLT_EPSILON &&
|
|
fabs(v_search_pos.y - v1.y) < FLT_EPSILON &&
|
|
fabs(v_search_pos.z - v1.z) < FLT_EPSILON;
|
|
|
|
bool match_pos2 =
|
|
fabs(v_search_pos.x - v2.x) < FLT_EPSILON &&
|
|
fabs(v_search_pos.y - v2.y) < FLT_EPSILON &&
|
|
fabs(v_search_pos.z - v2.z) < FLT_EPSILON;
|
|
|
|
if (match_pos0 || match_pos1 || match_pos2)
|
|
{
|
|
XMVECTOR U = XMLoadFloat3(&v2) - XMLoadFloat3(&v0);
|
|
XMVECTOR V = XMLoadFloat3(&v1) - XMLoadFloat3(&v0);
|
|
|
|
XMVECTOR N = XMVector3Cross(U, V);
|
|
N = XMVector3Normalize(N);
|
|
|
|
XMFLOAT3 normal;
|
|
XMStoreFloat3(&normal, N);
|
|
|
|
vertex_normals[i].x += normal.x;
|
|
vertex_normals[i].y += normal.y;
|
|
vertex_normals[i].z += normal.z;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// 3.) Find duplicated vertices by POSITION and UV0 and UV1 and ATLAS and SUBSET and remove them:
|
|
for (auto& subset : subsets)
|
|
{
|
|
for (uint32_t i = 0; i < subset.indexCount - 1; i++)
|
|
{
|
|
uint32_t ind0 = indices[subset.indexOffset + (uint32_t)i];
|
|
const XMFLOAT3& p0 = vertex_positions[ind0];
|
|
const XMFLOAT2& u00 = vertex_uvset_0.empty() ? XMFLOAT2(0, 0) : vertex_uvset_0[ind0];
|
|
const XMFLOAT2& u10 = vertex_uvset_1.empty() ? XMFLOAT2(0, 0) : vertex_uvset_1[ind0];
|
|
const XMFLOAT2& at0 = vertex_atlas.empty() ? XMFLOAT2(0, 0) : vertex_atlas[ind0];
|
|
|
|
for (uint32_t j = i + 1; j < subset.indexCount; j++)
|
|
{
|
|
uint32_t ind1 = indices[subset.indexOffset + (uint32_t)j];
|
|
|
|
if (ind1 == ind0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const XMFLOAT3& p1 = vertex_positions[ind1];
|
|
const XMFLOAT2& u01 = vertex_uvset_0.empty() ? XMFLOAT2(0, 0) : vertex_uvset_0[ind1];
|
|
const XMFLOAT2& u11 = vertex_uvset_1.empty() ? XMFLOAT2(0, 0) : vertex_uvset_1[ind1];
|
|
const XMFLOAT2& at1 = vertex_atlas.empty() ? XMFLOAT2(0, 0) : vertex_atlas[ind1];
|
|
|
|
const bool duplicated_pos =
|
|
fabs(p0.x - p1.x) < FLT_EPSILON &&
|
|
fabs(p0.y - p1.y) < FLT_EPSILON &&
|
|
fabs(p0.z - p1.z) < FLT_EPSILON;
|
|
|
|
const bool duplicated_uv0 =
|
|
fabs(u00.x - u01.x) < FLT_EPSILON &&
|
|
fabs(u00.y - u01.y) < FLT_EPSILON;
|
|
|
|
const bool duplicated_uv1 =
|
|
fabs(u10.x - u11.x) < FLT_EPSILON &&
|
|
fabs(u10.y - u11.y) < FLT_EPSILON;
|
|
|
|
const bool duplicated_atl =
|
|
fabs(at0.x - at1.x) < FLT_EPSILON &&
|
|
fabs(at0.y - at1.y) < FLT_EPSILON;
|
|
|
|
if (duplicated_pos && duplicated_uv0 && duplicated_uv1 && duplicated_atl)
|
|
{
|
|
// Erase vertices[ind1] because it is a duplicate:
|
|
if (ind1 < vertex_positions.size())
|
|
{
|
|
vertex_positions.erase(vertex_positions.begin() + ind1);
|
|
}
|
|
if (ind1 < vertex_normals.size())
|
|
{
|
|
vertex_normals.erase(vertex_normals.begin() + ind1);
|
|
}
|
|
if (ind1 < vertex_uvset_0.size())
|
|
{
|
|
vertex_uvset_0.erase(vertex_uvset_0.begin() + ind1);
|
|
}
|
|
if (ind1 < vertex_uvset_1.size())
|
|
{
|
|
vertex_uvset_1.erase(vertex_uvset_1.begin() + ind1);
|
|
}
|
|
if (ind1 < vertex_atlas.size())
|
|
{
|
|
vertex_atlas.erase(vertex_atlas.begin() + ind1);
|
|
}
|
|
if (ind1 < vertex_boneindices.size())
|
|
{
|
|
vertex_boneindices.erase(vertex_boneindices.begin() + ind1);
|
|
}
|
|
if (ind1 < vertex_boneweights.size())
|
|
{
|
|
vertex_boneweights.erase(vertex_boneweights.begin() + ind1);
|
|
}
|
|
|
|
// The vertices[ind1] was removed, so each index after that needs to be updated:
|
|
for (auto& index : indices)
|
|
{
|
|
if (index > ind1 && index > 0)
|
|
{
|
|
index--;
|
|
}
|
|
else if (index == ind1)
|
|
{
|
|
index = ind0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case MeshComponent::COMPUTE_NORMALS_SMOOTH_FAST:
|
|
{
|
|
for (size_t i = 0; i < vertex_normals.size(); i++)
|
|
{
|
|
vertex_normals[i] = XMFLOAT3(0, 0, 0);
|
|
}
|
|
for (size_t i = 0; i < indices.size() / 3; ++i)
|
|
{
|
|
uint32_t index1 = indices[i * 3];
|
|
uint32_t index2 = indices[i * 3 + 1];
|
|
uint32_t index3 = indices[i * 3 + 2];
|
|
|
|
XMVECTOR side1 = XMLoadFloat3(&vertex_positions[index1]) - XMLoadFloat3(&vertex_positions[index3]);
|
|
XMVECTOR side2 = XMLoadFloat3(&vertex_positions[index1]) - XMLoadFloat3(&vertex_positions[index2]);
|
|
XMVECTOR N = XMVector3Normalize(XMVector3Cross(side1, side2));
|
|
XMFLOAT3 normal;
|
|
XMStoreFloat3(&normal, N);
|
|
|
|
vertex_normals[index1].x += normal.x;
|
|
vertex_normals[index1].y += normal.y;
|
|
vertex_normals[index1].z += normal.z;
|
|
|
|
vertex_normals[index2].x += normal.x;
|
|
vertex_normals[index2].y += normal.y;
|
|
vertex_normals[index2].z += normal.z;
|
|
|
|
vertex_normals[index3].x += normal.x;
|
|
vertex_normals[index3].y += normal.y;
|
|
vertex_normals[index3].z += normal.z;
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
vertex_tangents.clear(); // <- will be recomputed
|
|
|
|
CreateRenderData(); // <- normals will be normalized here!
|
|
}
|
|
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 (auto& normal : vertex_normals)
|
|
{
|
|
normal.x *= -1;
|
|
normal.y *= -1;
|
|
normal.z *= -1;
|
|
}
|
|
|
|
CreateRenderData();
|
|
}
|
|
void MeshComponent::Recenter()
|
|
{
|
|
XMFLOAT3 center = aabb.getCenter();
|
|
|
|
for (auto& pos : vertex_positions)
|
|
{
|
|
pos.x -= center.x;
|
|
pos.y -= center.y;
|
|
pos.z -= center.z;
|
|
}
|
|
|
|
CreateRenderData();
|
|
}
|
|
void MeshComponent::RecenterToBottom()
|
|
{
|
|
XMFLOAT3 center = aabb.getCenter();
|
|
center.y -= aabb.getHalfWidth().y;
|
|
|
|
for (auto& pos : vertex_positions)
|
|
{
|
|
pos.x -= center.x;
|
|
pos.y -= center.y;
|
|
pos.z -= center.z;
|
|
}
|
|
|
|
CreateRenderData();
|
|
}
|
|
Sphere MeshComponent::GetBoundingSphere() const
|
|
{
|
|
Sphere sphere;
|
|
sphere.center = aabb.getCenter();
|
|
sphere.radius = aabb.getRadius();
|
|
return sphere;
|
|
}
|
|
|
|
void ObjectComponent::ClearLightmap()
|
|
{
|
|
lightmap = Texture();
|
|
lightmapWidth = 0;
|
|
lightmapHeight = 0;
|
|
lightmapIterationCount = 0;
|
|
lightmapTextureData.clear();
|
|
SetLightmapRenderRequest(false);
|
|
}
|
|
|
|
#if __has_include("OpenImageDenoise/oidn.hpp")
|
|
#define OPEN_IMAGE_DENOISE
|
|
#include "OpenImageDenoise/oidn.hpp"
|
|
#pragma comment(lib,"OpenImageDenoise.lib")
|
|
#pragma comment(lib,"tbb.lib")
|
|
// Also provide OpenImageDenoise.dll and tbb.dll near the exe!
|
|
#endif
|
|
void ObjectComponent::SaveLightmap()
|
|
{
|
|
if (lightmap.IsValid() && has_flag(lightmap.desc.bind_flags, BindFlag::RENDER_TARGET))
|
|
{
|
|
SetLightmapRenderRequest(false);
|
|
|
|
bool success = wi::helper::saveTextureToMemory(lightmap, lightmapTextureData);
|
|
assert(success);
|
|
|
|
#ifdef OPEN_IMAGE_DENOISE
|
|
if (success)
|
|
{
|
|
wi::vector<uint8_t> texturedata_dst(lightmapTextureData.size());
|
|
|
|
size_t width = (size_t)lightmapWidth;
|
|
size_t height = (size_t)lightmapHeight;
|
|
{
|
|
// https://github.com/OpenImageDenoise/oidn#c11-api-example
|
|
|
|
// Create an Intel Open Image Denoise device
|
|
static oidn::DeviceRef device = oidn::newDevice();
|
|
static bool init = false;
|
|
if (!init)
|
|
{
|
|
device.commit();
|
|
init = true;
|
|
}
|
|
|
|
// Create a denoising filter
|
|
oidn::FilterRef filter = device.newFilter("RTLightmap");
|
|
filter.setImage("color", lightmapTextureData.data(), oidn::Format::Float3, width, height, 0, sizeof(XMFLOAT4));
|
|
filter.setImage("output", texturedata_dst.data(), oidn::Format::Float3, width, height, 0, sizeof(XMFLOAT4));
|
|
filter.commit();
|
|
|
|
// Filter the image
|
|
filter.execute();
|
|
|
|
// Check for errors
|
|
const char* errorMessage;
|
|
auto error = device.getError(errorMessage);
|
|
if (error != oidn::Error::None && error != oidn::Error::Cancelled)
|
|
{
|
|
wi::backlog::post(std::string("[OpenImageDenoise error] ") + errorMessage);
|
|
}
|
|
}
|
|
|
|
lightmapTextureData = std::move(texturedata_dst); // replace old (raw) data with denoised data
|
|
}
|
|
#endif // OPEN_IMAGE_DENOISE
|
|
|
|
CompressLightmap();
|
|
|
|
wi::texturehelper::CreateTexture(lightmap, lightmapTextureData.data(), lightmapWidth, lightmapHeight, lightmap.desc.format);
|
|
wi::graphics::GetDevice()->SetName(&lightmap, "lightmap");
|
|
}
|
|
}
|
|
void ObjectComponent::CompressLightmap()
|
|
{
|
|
|
|
// BC6 Block compression code that uses DirectXTex library, but it's not cross platform, so disabled:
|
|
#if 0
|
|
wi::Timer timer;
|
|
wi::backlog::post("compressing lightmap...");
|
|
|
|
lightmap.desc.Format = lightmap_block_format;
|
|
lightmap.desc.BindFlags = BindFlag::SHADER_RESOURCE;
|
|
|
|
static constexpr wi::graphics::FORMAT lightmap_block_format = wi::graphics::FORMAT_BC6H_UF16;
|
|
static constexpr uint32_t lightmap_blocksize = wi::graphics::GetFormatBlockSize(lightmap_block_format);
|
|
static_assert(lightmap_blocksize == 4u);
|
|
const uint32_t bc6_width = lightmapWidth / lightmap_blocksize;
|
|
const uint32_t bc6_height = lightmapHeight / lightmap_blocksize;
|
|
wi::vector<uint8_t> bc6_data;
|
|
bc6_data.resize(sizeof(XMFLOAT4) * bc6_width * bc6_height);
|
|
const XMFLOAT4* raw_data = (const XMFLOAT4*)lightmapTextureData.data();
|
|
|
|
for (uint32_t x = 0; x < bc6_width; ++x)
|
|
{
|
|
for (uint32_t y = 0; y < bc6_height; ++y)
|
|
{
|
|
uint32_t bc6_idx = x + y * bc6_width;
|
|
uint8_t* ptr = (uint8_t*)((XMFLOAT4*)bc6_data.data() + bc6_idx);
|
|
|
|
XMVECTOR raw_vec[lightmap_blocksize * lightmap_blocksize];
|
|
for (uint32_t i = 0; i < lightmap_blocksize; ++i)
|
|
{
|
|
for (uint32_t j = 0; j < lightmap_blocksize; ++j)
|
|
{
|
|
uint32_t raw_idx = (x * lightmap_blocksize + i) + (y * lightmap_blocksize + j) * lightmapWidth;
|
|
uint32_t block_idx = i + j * lightmap_blocksize;
|
|
raw_vec[block_idx] = XMLoadFloat4(raw_data + raw_idx);
|
|
}
|
|
}
|
|
static_assert(arraysize(raw_vec) == 16); // it will work only for a certain block size!
|
|
D3DXEncodeBC6HU(ptr, raw_vec, 0);
|
|
}
|
|
}
|
|
|
|
lightmapTextureData = std::move(bc6_data); // replace old (raw) data with compressed data
|
|
|
|
wi::backlog::post(
|
|
"compressing lightmap [" +
|
|
std::to_string(lightmapWidth) +
|
|
"x" +
|
|
std::to_string(lightmapHeight) +
|
|
"] finished in " +
|
|
std::to_string(timer.elapsed_seconds()) +
|
|
" seconds"
|
|
);
|
|
#else
|
|
|
|
// Simple compression to R11G11B10_FLOAT format:
|
|
using namespace PackedVector;
|
|
wi::vector<uint8_t> packed_data;
|
|
packed_data.resize(sizeof(XMFLOAT3PK) * lightmapWidth * lightmapHeight);
|
|
XMFLOAT3PK* packed_ptr = (XMFLOAT3PK*)packed_data.data();
|
|
XMFLOAT4* raw_ptr = (XMFLOAT4*)lightmapTextureData.data();
|
|
|
|
uint32_t texelcount = lightmapWidth * lightmapHeight;
|
|
for (uint32_t i = 0; i < texelcount; ++i)
|
|
{
|
|
XMStoreFloat3PK(packed_ptr + i, XMLoadFloat4(raw_ptr + i));
|
|
}
|
|
|
|
lightmapTextureData = std::move(packed_data);
|
|
lightmap.desc.format = Format::R11G11B10_FLOAT;
|
|
lightmap.desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
|
|
#endif
|
|
}
|
|
|
|
AnimationComponent::AnimationChannel::PathDataType AnimationComponent::AnimationChannel::GetPathDataType() const
|
|
{
|
|
switch (path)
|
|
{
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::TRANSLATION:
|
|
return PathDataType::Float3;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::ROTATION:
|
|
return PathDataType::Float4;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::SCALE:
|
|
return PathDataType::Float3;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::WEIGHTS:
|
|
return PathDataType::Weights;
|
|
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::LIGHT_COLOR:
|
|
return PathDataType::Float3;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::LIGHT_RANGE:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE:
|
|
return PathDataType::Float;
|
|
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::SOUND_PLAY:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::SOUND_STOP:
|
|
return PathDataType::Event;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::SOUND_VOLUME:
|
|
return PathDataType::Float;
|
|
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::EMITTER_EMITCOUNT:
|
|
return PathDataType::Float;
|
|
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::CAMERA_FOV:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::CAMERA_FOCAL_LENGTH:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::CAMERA_APERTURE_SIZE:
|
|
return PathDataType::Float;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::CAMERA_APERTURE_SHAPE:
|
|
return PathDataType::Float2;
|
|
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::SCRIPT_PLAY:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::SCRIPT_STOP:
|
|
return PathDataType::Event;
|
|
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::MATERIAL_COLOR:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::MATERIAL_EMISSIVE:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::MATERIAL_TEXMULADD:
|
|
return PathDataType::Float4;
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::MATERIAL_ROUGHNESS:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::MATERIAL_REFLECTANCE:
|
|
case wi::scene::AnimationComponent::AnimationChannel::Path::MATERIAL_METALNESS:
|
|
return PathDataType::Float;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
return PathDataType::Event;
|
|
}
|
|
|
|
void SoftBodyPhysicsComponent::CreateFromMesh(const MeshComponent& mesh)
|
|
{
|
|
vertex_positions_simulation.resize(mesh.vertex_positions.size());
|
|
vertex_tangents_tmp.resize(mesh.vertex_tangents.size());
|
|
vertex_tangents_simulation.resize(mesh.vertex_tangents.size());
|
|
|
|
XMMATRIX W = XMLoadFloat4x4(&worldMatrix);
|
|
XMFLOAT3 _min = XMFLOAT3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
|
|
XMFLOAT3 _max = XMFLOAT3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
|
|
for (size_t i = 0; i < vertex_positions_simulation.size(); ++i)
|
|
{
|
|
XMFLOAT3 pos = mesh.vertex_positions[i];
|
|
XMStoreFloat3(&pos, XMVector3Transform(XMLoadFloat3(&pos), W));
|
|
XMFLOAT3 nor = mesh.vertex_normals.empty() ? XMFLOAT3(1, 1, 1) : mesh.vertex_normals[i];
|
|
XMStoreFloat3(&nor, XMVector3Normalize(XMVector3TransformNormal(XMLoadFloat3(&nor), W)));
|
|
const uint8_t wind = mesh.vertex_windweights.empty() ? 0xFF : mesh.vertex_windweights[i];
|
|
vertex_positions_simulation[i].FromFULL(pos, nor, wind);
|
|
_min = wi::math::Min(_min, pos);
|
|
_max = wi::math::Max(_max, pos);
|
|
}
|
|
aabb = AABB(_min, _max);
|
|
|
|
if (physicsToGraphicsVertexMapping.empty())
|
|
{
|
|
// Create a mapping that maps unique vertex positions to all vertex indices that share that. Unique vertex positions will make up the physics mesh:
|
|
wi::unordered_map<size_t, uint32_t> uniquePositions;
|
|
graphicsToPhysicsVertexMapping.resize(mesh.vertex_positions.size());
|
|
physicsToGraphicsVertexMapping.clear();
|
|
weights.clear();
|
|
|
|
for (size_t i = 0; i < mesh.vertex_positions.size(); ++i)
|
|
{
|
|
const XMFLOAT3& position = mesh.vertex_positions[i];
|
|
|
|
size_t hashes[] = {
|
|
std::hash<float>{}(position.x),
|
|
std::hash<float>{}(position.y),
|
|
std::hash<float>{}(position.z),
|
|
};
|
|
size_t vertexHash = (((hashes[0] ^ (hashes[1] << 1) >> 1) ^ (hashes[2] << 1)) >> 1);
|
|
|
|
if (uniquePositions.count(vertexHash) == 0)
|
|
{
|
|
uniquePositions[vertexHash] = (uint32_t)physicsToGraphicsVertexMapping.size();
|
|
physicsToGraphicsVertexMapping.push_back((uint32_t)i);
|
|
}
|
|
graphicsToPhysicsVertexMapping[i] = uniquePositions[vertexHash];
|
|
}
|
|
|
|
weights.resize(physicsToGraphicsVertexMapping.size());
|
|
std::fill(weights.begin(), weights.end(), 1.0f);
|
|
}
|
|
}
|
|
|
|
void CameraComponent::CreatePerspective(float newWidth, float newHeight, float newNear, float newFar, float newFOV)
|
|
{
|
|
zNearP = newNear;
|
|
zFarP = newFar;
|
|
width = newWidth;
|
|
height = newHeight;
|
|
fov = newFOV;
|
|
|
|
SetCustomProjectionEnabled(false);
|
|
|
|
UpdateCamera();
|
|
}
|
|
void CameraComponent::UpdateCamera()
|
|
{
|
|
if (!IsCustomProjectionEnabled())
|
|
{
|
|
XMStoreFloat4x4(&Projection, XMMatrixPerspectiveFovLH(fov, width / height, zFarP, zNearP)); // reverse zbuffer!
|
|
Projection.m[2][0] = jitter.x;
|
|
Projection.m[2][1] = jitter.y;
|
|
}
|
|
|
|
XMVECTOR _Eye = XMLoadFloat3(&Eye);
|
|
XMVECTOR _At = XMLoadFloat3(&At);
|
|
XMVECTOR _Up = XMLoadFloat3(&Up);
|
|
|
|
XMMATRIX _V = XMMatrixLookToLH(_Eye, _At, _Up);
|
|
XMStoreFloat4x4(&View, _V);
|
|
|
|
XMMATRIX _P = XMLoadFloat4x4(&Projection);
|
|
XMMATRIX _InvP = XMMatrixInverse(nullptr, _P);
|
|
XMStoreFloat4x4(&InvProjection, _InvP);
|
|
|
|
XMMATRIX _VP = XMMatrixMultiply(_V, _P);
|
|
XMStoreFloat4x4(&View, _V);
|
|
XMStoreFloat4x4(&VP, _VP);
|
|
XMMATRIX _InvV = XMMatrixInverse(nullptr, _V);
|
|
XMStoreFloat4x4(&InvView, _InvV);
|
|
XMStoreFloat3x3(&rotationMatrix, _InvV);
|
|
XMStoreFloat4x4(&InvVP, XMMatrixInverse(nullptr, _VP));
|
|
XMStoreFloat4x4(&Projection, _P);
|
|
XMStoreFloat4x4(&InvProjection, XMMatrixInverse(nullptr, _P));
|
|
|
|
frustum.Create(_VP);
|
|
}
|
|
void CameraComponent::TransformCamera(const XMMATRIX& W)
|
|
{
|
|
XMVECTOR _Eye = XMVector3Transform(XMVectorSet(0, 0, 0, 1), W);
|
|
XMVECTOR _At = XMVector3Normalize(XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), W));
|
|
XMVECTOR _Up = XMVector3Normalize(XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), W));
|
|
|
|
XMMATRIX _V = XMMatrixLookToLH(_Eye, _At, _Up);
|
|
XMStoreFloat4x4(&View, _V);
|
|
|
|
XMStoreFloat3x3(&rotationMatrix, XMMatrixInverse(nullptr, _V));
|
|
|
|
XMStoreFloat3(&Eye, _Eye);
|
|
XMStoreFloat3(&At, _At);
|
|
XMStoreFloat3(&Up, _Up);
|
|
}
|
|
void CameraComponent::Reflect(const XMFLOAT4& plane)
|
|
{
|
|
XMVECTOR _Eye = XMLoadFloat3(&Eye);
|
|
XMVECTOR _At = XMLoadFloat3(&At);
|
|
XMVECTOR _Up = XMLoadFloat3(&Up);
|
|
XMMATRIX _Ref = XMMatrixReflect(XMLoadFloat4(&plane));
|
|
|
|
// reverse clipping if behind clip plane ("if underwater")
|
|
clipPlane = plane;
|
|
float d = XMVectorGetX(XMPlaneDotCoord(XMLoadFloat4(&clipPlane), _Eye));
|
|
if (d < 0)
|
|
{
|
|
clipPlane.x *= -1;
|
|
clipPlane.y *= -1;
|
|
clipPlane.z *= -1;
|
|
clipPlane.w *= -1;
|
|
}
|
|
|
|
_Eye = XMVector3Transform(_Eye, _Ref);
|
|
_At = XMVector3TransformNormal(_At, _Ref);
|
|
_Up = XMVector3TransformNormal(_Up, _Ref);
|
|
|
|
XMStoreFloat3(&Eye, _Eye);
|
|
XMStoreFloat3(&At, _At);
|
|
XMStoreFloat3(&Up, _Up);
|
|
|
|
UpdateCamera();
|
|
}
|
|
void CameraComponent::Lerp(const CameraComponent& a, const CameraComponent& b, float t)
|
|
{
|
|
SetDirty();
|
|
|
|
width = wi::math::Lerp(a.width, b.width, t);
|
|
height = wi::math::Lerp(a.height, b.height, t);
|
|
zNearP = wi::math::Lerp(a.zNearP, b.zNearP, t);
|
|
zFarP = wi::math::Lerp(a.zFarP, b.zFarP, t);
|
|
fov = wi::math::Lerp(a.fov, b.fov, t);
|
|
focal_length = wi::math::Lerp(a.focal_length, b.focal_length, t);
|
|
aperture_size = wi::math::Lerp(a.aperture_size, b.aperture_size, t);
|
|
aperture_shape = wi::math::Lerp(a.aperture_shape, b.aperture_shape, t);
|
|
}
|
|
|
|
void ScriptComponent::CreateFromFile(const std::string& filename)
|
|
{
|
|
this->filename = filename;
|
|
resource = wi::resourcemanager::Load(filename, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA);
|
|
script.clear(); // will be created on first Update()
|
|
}
|
|
|
|
}
|