verus/Verus/src/Scene/BaseMesh.cpp

702 lines
16 KiB
C++

#include "verus.h"
using namespace verus;
using namespace verus::Scene;
BaseMesh::BaseMesh()
{
VERUS_ZERO_MEM(_posDeq);
VERUS_ZERO_MEM(_tc0Deq);
VERUS_ZERO_MEM(_tc1Deq);
VERUS_CT_ASSERT(16 == sizeof(VertexInputBinding0));
VERUS_CT_ASSERT(16 == sizeof(VertexInputBinding1));
VERUS_CT_ASSERT(16 == sizeof(VertexInputBinding2));
VERUS_CT_ASSERT(8 == sizeof(VertexInputBinding3));
}
BaseMesh::~BaseMesh()
{
Done();
}
void BaseMesh::Init(CSZ url)
{
VERUS_INIT();
_url = url;
IO::Async::I().Load(url, this);
}
void BaseMesh::Done()
{
IO::Async::Cancel(this);
DoneShape();
VERUS_DONE(BaseMesh);
}
void BaseMesh::Async_Run(CSZ url, RcBlob blob)
{
if (_url == url)
{
Load(blob);
LoadPrimaryBones();
LoadRig();
LoadWarp();
return;
}
}
void BaseMesh::Load(RcBlob blob)
{
IO::StreamPtr sp(blob);
UINT32 magic = 0;
sp >> magic;
if (magic != ' D3X')
{
if (magic == 'D3X<')
{
return LoadX3D3(blob);
}
else
{
throw VERUS_RECOVERABLE << "Load(), invalid magic number";
}
}
VERUS_LOG_WARN("Load(), Old X3D version");
}
void BaseMesh::LoadX3D3(RcBlob blob)
{
IO::StreamPtr sp(blob);
bool hasNormals = false;
bool hasTBN = false;
UINT32 temp;
UINT32 blockType = 0;
INT64 blockSize = 0;
char buffer[IO::Stream::s_bufferSize] = {};
sp.Read(buffer, 5);
sp >> temp;
sp >> temp;
sp.ReadString(buffer);
VERUS_RT_ASSERT(!strcmp(buffer, "3.0"));
while (!sp.IsEnd())
{
sp >> temp;
sp >> blockType;
sp >> blockSize;
switch (blockType)
{
case '>XI<':
{
sp.ReadString(buffer);
_faceCount = atoi(buffer);
_indexCount = _faceCount * 3;
VERUS_RT_ASSERT(_vIndices.empty());
_vIndices.resize(_indexCount);
UINT16* pIB = _vIndices.data();
VERUS_FOR(i, _faceCount)
sp.Read(&pIB[i * 3], 6);
}
break;
case '>XV<':
{
sp.ReadString(buffer);
_vertCount = atoi(buffer);
#ifdef VERUS_RELEASE_DEBUG
Vector<UINT64> vPolyCheck;
vPolyCheck.reserve(_faceCount);
VERUS_FOR(i, _faceCount)
{
UINT16 indices[3];
memcpy(indices, &_vIndices[i * 3], sizeof(indices));
std::sort(indices, indices + 3);
const UINT64 tag = UINT64(indices[0]) | UINT64(indices[1]) << 16 | UINT64(indices[2]) << 32;
vPolyCheck.push_back(tag);
}
std::sort(vPolyCheck.begin(), vPolyCheck.end());
if (vPolyCheck.end() != std::adjacent_find(vPolyCheck.begin(), vPolyCheck.end()))
VERUS_LOG_WARN("Duplicate triangle in " << _url);
Vector<UINT16> vSorted;
vSorted.assign(_vIndices.begin(), _vIndices.end());
std::sort(vSorted.begin(), vSorted.end());
bool isolatedVertex = false;
if (vSorted.end() != std::adjacent_find(vSorted.begin(), vSorted.end(), [](const UINT16& a, const UINT16& b) {return b - a > 1; }))
isolatedVertex = true;
if (isolatedVertex)
VERUS_LOG_WARN("Isolated vertex in " << _url);
if (_vertCount <= vSorted.back())
VERUS_LOG_WARN("Index out of bounds in " << _url);
#endif
sp.Read(&_posDeq[0], 12);
sp.Read(&_posDeq[3], 12);
VERUS_RT_ASSERT(_vBinding0.empty());
_vBinding0.resize(_vertCount);
VertexInputBinding0* pVB = _vBinding0.data();
short mn = 0, mx = 0;
VERUS_FOR(i, _vertCount)
{
sp.Read(pVB[i]._pos, 6);
#ifdef VERUS_RELEASE_DEBUG
const short* pMin = std::min_element(pVB[i]._pos, pVB[i]._pos + 3);
const short* pMax = std::max_element(pVB[i]._pos, pVB[i]._pos + 3);
mn = Math::Min(mn, *pMin);
mx = Math::Max(mx, *pMax);
#endif
}
#ifdef VERUS_RELEASE_DEBUG
VERUS_RT_ASSERT(mn < -32700);
VERUS_RT_ASSERT(mx > +32700);
#endif
}
break;
case '>LN<':
{
hasNormals = true;
VertexInputBinding0* pVB = _vBinding0.data();
VERUS_FOR(i, _vertCount)
{
sp.Read(pVB[i]._nrm, 3);
#ifdef VERUS_RELEASE_DEBUG
const int len =
pVB[i]._nrm[0] * pVB[i]._nrm[0] +
pVB[i]._nrm[1] * pVB[i]._nrm[1] +
pVB[i]._nrm[2] * pVB[i]._nrm[2];
VERUS_RT_ASSERT(len > 125 * 125);
#endif
Convert::ToDeviceNormal(pVB[i]._nrm, pVB[i]._nrm);
}
}
break;
case '>ST<':
{
hasTBN = true;
VERUS_RT_ASSERT(_vBinding2.empty());
_vBinding2.resize(_vertCount);
VertexInputBinding2* pVB = _vBinding2.data();
char temp[3];
VERUS_FOR(i, _vertCount)
{
sp.Read(temp, 3);
Convert::Sint8ToSint16(temp, pVB[i]._tan, 3); // Single byte is not supported in OpenGL.
sp.Read(temp, 3);
Convert::Sint8ToSint16(temp, pVB[i]._bin, 3);
#ifdef VERUS_RELEASE_DEBUG
const int lenT =
pVB[i]._tan[0] * pVB[i]._tan[0] +
pVB[i]._tan[1] * pVB[i]._tan[1] +
pVB[i]._tan[2] * pVB[i]._tan[2];
VERUS_RT_ASSERT(lenT > 32100 * 32100);
const int lenB =
pVB[i]._bin[0] * pVB[i]._bin[0] +
pVB[i]._bin[1] * pVB[i]._bin[1] +
pVB[i]._bin[2] * pVB[i]._bin[2];
VERUS_RT_ASSERT(lenB > 32100 * 32100);
#endif
}
}
break;
case '>0T<':
{
sp.Read(&_tc0Deq[0], 8);
sp.Read(&_tc0Deq[2], 8);
VertexInputBinding0* pVB = _vBinding0.data();
short mn = 0, mx = 0;
VERUS_FOR(i, _vertCount)
{
sp.Read(pVB[i]._tc0, 4);
#ifdef VERUS_RELEASE_DEBUG
const short* pMin = std::min_element(pVB[i]._tc0, pVB[i]._tc0 + 2);
const short* pMax = std::max_element(pVB[i]._tc0, pVB[i]._tc0 + 2);
mn = Math::Min(mn, *pMin);
mx = Math::Max(mx, *pMax);
#endif
}
#ifdef VERUS_RELEASE_DEBUG
VERUS_RT_ASSERT(mn < -32700);
VERUS_RT_ASSERT(mx > +32700);
#endif
}
break;
case '>1T<':
{
sp.Read(&_tc1Deq[0], 8);
sp.Read(&_tc1Deq[2], 8);
VERUS_RT_ASSERT(_vBinding3.empty());
_vBinding3.resize(_vertCount);
VertexInputBinding3* pVB = _vBinding3.data();
VERUS_FOR(i, _vertCount)
sp.Read(&pVB[i]._tc1, 4);
}
break;
case '>HB<':
{
BYTE boneCount = 0;
sp >> boneCount;
VERUS_RT_ASSERT(boneCount <= VERUS_MAX_BONES);
_boneCount = boneCount;
_skeleton.Init();
VERUS_FOR(i, _boneCount)
{
Anim::Skeleton::Bone bone;
sp.ReadString(buffer); bone._name = buffer;
sp.ReadString(buffer); bone._parentName = buffer;
Transform3 matOffset;
sp.Read(&matOffset, 64);
bone._matToBoneSpace = matOffset;
bone._matFromBoneSpace = VMath::orthoInverse(matOffset);
_skeleton.InsertBone(bone);
}
}
break;
case '>SS<':
{
VERUS_RT_ASSERT(_vBinding1.empty());
_vBinding1.resize(_vertCount);
VertexInputBinding1* pVB = _vBinding1.data();
bool abnormal = false;
VERUS_FOR(i, _vertCount)
{
BYTE data[8];
sp.Read(data, 8);
VERUS_FOR(j, 4)
{
pVB[i]._bw[j] = data[j];
pVB[i]._bi[j] = data[j + 4];
}
#ifdef VERUS_RELEASE_DEBUG
const int sum =
pVB[i]._bw[0] +
pVB[i]._bw[1] +
pVB[i]._bw[2] +
pVB[i]._bw[3];
if (sum != 255)
abnormal = true;
#endif
}
if (abnormal)
VERUS_LOG_WARN(_C("WARNING: Abnormal skin weights in " + _url));
}
break;
case '>RS<':
{
VertexInputBinding0* pVB = _vBinding0.data();
BYTE index = 0;
VERUS_FOR(i, _vertCount)
{
sp >> index;
pVB[i]._pos[3] = index;
}
_rigidSkeleton = true;
}
break;
default:
sp.Advance(blockSize);
}
}
if (_loadOnly)
return;
if (!hasTBN)
{
_vBinding2.resize(_vertCount);
ComputeTangentSpace();
}
if (_initShape)
InitShape(Transform3::identity());
CreateDeviceBuffers();
}
void BaseMesh::LoadPrimaryBones()
{
#if 0
if (!m_skeleton.IsInitialized())
return;
String url(_url);
Str::ReplaceExtension(url, ".primary");
Vector<BYTE> vData;
IO::FileSystem::I().LoadResourceFromCache(_C(url), vData, false);
if (vData.size() <= 1)
return;
Vector<String> vPrimaryBones;
Str::ReadLines(reinterpret_cast<CSZ>(vData.data()), vPrimaryBones);
if (!vPrimaryBones.empty())
{
_skeleton.AdjustPrimaryBones(vPrimaryBones);
VERUS_FOREACH(Vector<VertexInputBinding1>, _vBinding1, it)
{
VERUS_FOR(i, 4)
it->bi[i] = _skeleton.RemapBoneIndex(it->bi[i]);
}
UpdateVertexBuffer(_vBinding1.data(), 1);
}
#endif
}
void BaseMesh::LoadRig()
{
if (!_skeleton.IsInitialized())
return;
String url(_url);
Str::ReplaceExtension(url, ".rig");
Vector<BYTE> vData;
IO::FileSystem::I().LoadResourceFromCache(_C(url), vData, false);
if (vData.size() <= 1)
return;
_skeleton.LoadRigInfoFromPtr(reinterpret_cast<SZ>(vData.data()));
}
void BaseMesh::LoadWarp()
{
if (_warpUrl.empty())
return;
Vector<BYTE> vData;
IO::FileSystem::I().LoadResourceFromCache(_C(_warpUrl), vData, false);
if (vData.size() > 1)
{
VERUS_RT_ASSERT(!(_vBinding0.empty() || _vBinding2.empty()));
VERUS_RT_ASSERT(vData.size() - 1 == _vertCount * 6);
CSZ ps = reinterpret_cast<CSZ>(vData.data());
VERUS_FOR(i, _vertCount)
{
int r, g, b;
sscanf(ps + i * 6, "%d %d %d", &r, &g, &b);
_vBinding0[i]._pos[3] = r * SHRT_MAX / 9;
_vBinding2[i]._tan[3] = g * SHRT_MAX / 9;
_vBinding2[i]._bin[3] = b * SHRT_MAX / 9;
}
UpdateVertexBuffer(_vBinding0.data(), 0);
UpdateVertexBuffer(_vBinding2.data(), 2);
}
vData.clear();
String url(_warpUrl);
Str::ReplaceExtension(url, ".xwa");
IO::FileSystem::I().LoadResourceFromCache(_C(url), vData, false);
if (vData.size() > 1)
{
_warp.Init();
_warp.LoadFromPtr(reinterpret_cast<SZ>(vData.data()));
}
}
void BaseMesh::ComputeDeq(glm::vec3& scale, glm::vec3& bias, const glm::vec3& extents, const glm::vec3& minPos)
{
scale = extents * (1.f / SHRT_MAX);
bias = minPos + extents;
}
void BaseMesh::QuantizeV(glm::vec3& v, const glm::vec3& extents, const glm::vec3& minPos)
{
v = ((v - minPos - extents) / extents) * float(SHRT_MAX);
v.x = (v.x >= 0) ? v.x + 0.5f : v.x - 0.5f;
v.y = (v.y >= 0) ? v.y + 0.5f : v.y - 0.5f;
v.z = (v.z >= 0) ? v.z + 0.5f : v.z - 0.5f;
v = glm::clamp(v, glm::vec3(-SHRT_MAX), glm::vec3(SHRT_MAX));
}
void BaseMesh::DequantizeUsingDeq2D(const short* in, const float* deq, glm::vec2& out)
{
out.x = in[0] * deq[0] + deq[2];
out.y = in[1] * deq[1] + deq[3];
}
void BaseMesh::DequantizeUsingDeq2D(const short* in, const float* deq, RPoint3 out)
{
out = Point3(
in[0] * deq[0] + deq[2],
in[1] * deq[1] + deq[3],
0);
}
void BaseMesh::DequantizeUsingDeq3D(const short* in, const float* deq, glm::vec3& out)
{
out.x = in[0] * deq[0] + deq[3];
out.y = in[1] * deq[1] + deq[4];
out.z = in[2] * deq[2] + deq[5];
}
void BaseMesh::DequantizeUsingDeq3D(const short* in, const float* deq, RPoint3 out)
{
out = Point3(
in[0] * deq[0] + deq[3],
in[1] * deq[1] + deq[4],
in[2] * deq[2] + deq[5]);
}
void BaseMesh::DequantizeNrm(const char* in, glm::vec3& out)
{
out.x = Convert::Sint8ToSnorm(in[0]);
out.y = Convert::Sint8ToSnorm(in[1]);
out.z = Convert::Sint8ToSnorm(in[2]);
}
void BaseMesh::DequantizeNrm(const char* in, RVector3 out)
{
out = Vector3(
Convert::Sint8ToSnorm(in[0]),
Convert::Sint8ToSnorm(in[1]),
Convert::Sint8ToSnorm(in[2]));
}
void BaseMesh::DequantizeTanBin(const short* in, RVector3 out)
{
out = Vector3(
Convert::Sint16ToSnorm(in[0]),
Convert::Sint16ToSnorm(in[1]),
Convert::Sint16ToSnorm(in[2]));
}
void BaseMesh::ComputeTangentSpace()
{
Vector<glm::vec3> vV, vN, vTan, vBin;
Vector<glm::vec2> vTex;
vV.resize(_vertCount);
vTex.resize(_vertCount);
VERUS_FOR(i, _vertCount)
{
DequantizeUsingDeq3D(_vBinding0[i]._pos, _posDeq, vV[i]);
DequantizeUsingDeq2D(_vBinding0[i]._tc0, _tc0Deq, vTex[i]);
}
Math::NormalComputer::ComputeNormals(_vIndices, vV, vN, 1);
VERUS_FOR(i, _vertCount)
Convert::SnormToSint8(&vN[i].x, _vBinding0[i]._nrm, 3);
if (!_vBinding2.empty())
{
Math::NormalComputer::ComputeTangentSpace(_vIndices, vV, vN, vTex, vTan, vBin);
VERUS_FOR(i, _vertCount)
{
Convert::SnormToSint16(&vTan[i].x, _vBinding2[i]._tan, 3);
Convert::SnormToSint16(&vBin[i].x, _vBinding2[i]._bin, 3);
}
}
}
btBvhTriangleMeshShape* BaseMesh::InitShape(RcTransform3 tr, CSZ url)
{
if (!_vertCount)
return nullptr;
DoneShape();
String finalUrl = url ? url : _url;
String cacheFilename;
if (!finalUrl.empty())
{
String filename = Str::ToPakFriendlyUrl(_C(finalUrl));
if (url)
filename += ".INST";
filename = "[Models]:PhyCache/" + filename + ".bullet";
cacheFilename = IO::FileSystem::ConvertRelativePathToAbsolute(filename, true);
if (IO::FileSystem::FileExist(_C(filename)))
{
Vector<BYTE> vMesh;
IO::FileSystem::LoadResource(_C(filename), vMesh);
btBulletWorldImporter bwi(0);
if (!vMesh.empty() && bwi.loadFileFromMemory(reinterpret_cast<char*>(vMesh.data()), Utils::Cast32(vMesh.size())))
{
const int count = bwi.getNumCollisionShapes();
if (count)
_pShape = static_cast<btBvhTriangleMeshShape*>(bwi.getCollisionShapeByIndex(0));
}
return _pShape;
}
}
btTriangleMesh* pTriangleMesh = new btTriangleMesh(_vIndices.empty());
pTriangleMesh->preallocateVertices(_vertCount);
pTriangleMesh->preallocateIndices(_indexCount);
VERUS_FOR(i, _vertCount)
{
Point3 pos;
DequantizeUsingDeq3D(_vBinding0[i]._pos, _posDeq, pos);
pos = tr * pos;
pTriangleMesh->findOrAddVertex(pos.Bullet(), false);
}
VERUS_FOR(i, _faceCount)
{
const int i0 = _vIndices.empty() ? _vIndices32[i * 3 + 0] : _vIndices[i * 3 + 0];
const int i1 = _vIndices.empty() ? _vIndices32[i * 3 + 1] : _vIndices[i * 3 + 1];
const int i2 = _vIndices.empty() ? _vIndices32[i * 3 + 2] : _vIndices[i * 3 + 2];
pTriangleMesh->addTriangleIndices(i0, i1, i2);
}
_pShape = new btBvhTriangleMeshShape(pTriangleMesh, true);
if (!cacheFilename.empty())
{
btDefaultSerializer s;
s.startSerialization();
_pShape->serializeSingleShape(&s);
s.finishSerialization();
IO::File file;
if (file.Open(_C(cacheFilename), "wb"))
file.Write(s.getBufferPointer(), s.getCurrentBufferSize());
}
return _pShape;
}
void BaseMesh::DoneShape()
{
if (_pShape)
{
delete _pShape->getMeshInterface();
delete _pShape;
_pShape = nullptr;
}
}
void BaseMesh::GetBounds(RPoint3 mn, RPoint3 mx) const
{
const short smin[] = { -SHRT_MAX, -SHRT_MAX, -SHRT_MAX };
const short smax[] = { +SHRT_MAX, +SHRT_MAX, +SHRT_MAX };
DequantizeUsingDeq3D(smin, _posDeq, mn);
DequantizeUsingDeq3D(smax, _posDeq, mx);
}
Math::Bounds BaseMesh::GetBounds() const
{
Point3 mn, mx;
GetBounds(mn, mx);
return Math::Bounds(mn, mx);
}
String BaseMesh::ToXmlString() const
{
Point3 mn, mx;
GetBounds(mn, mx);
StringStream ss;
ss << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << VERUS_CRNL;
ss << "<x3d>" << VERUS_CRNL;
ss << "<version v=\"1.1\" />" << VERUS_CRNL;
ss << "<aabb min=\"" << mn.ToString() << "\" max=\"" << mx.ToString() << "\" />" << VERUS_CRNL;
// IB:
ss << "<i>";
if (!_vIndices.empty())
{
VERUS_FOR(i, _indexCount)
{
if (i)
ss << ' ';
ss << _vIndices[i];
}
}
else
{
VERUS_FOR(i, _indexCount)
{
if (i)
ss << ' ';
ss << _vIndices32[i];
}
}
ss << "</i>" << VERUS_CRNL;
// Position:
ss << "<p>";
VERUS_FOR(i, _vertCount)
{
Point3 pos;
DequantizeUsingDeq3D(_vBinding0[i]._pos, _posDeq, pos);
if (i)
ss << ' ';
ss << pos.ToString();
}
ss << "</p>" << VERUS_CRNL;
// Normal:
ss << "<n>";
VERUS_FOR(i, _vertCount)
{
Vector3 norm;
DequantizeNrm(_vBinding0[i]._nrm, norm);
if (i)
ss << ' ';
ss << norm.ToString();
}
ss << "</n>" << VERUS_CRNL;
// TC 0:
ss << "<tc0>";
VERUS_FOR(i, _vertCount)
{
Point3 tc;
DequantizeUsingDeq2D(_vBinding0[i]._tc0, _tc0Deq, tc);
if (i)
ss << ' ';
ss << tc.ToString2();
}
ss << "</tc0>" << VERUS_CRNL;
// TC 1:
if (false)
{
ss << "<tc1>";
VERUS_FOR(i, _vertCount)
{
Point3 tc;
DequantizeUsingDeq2D(_vBinding3[i]._tc1, _tc1Deq, tc);
if (i)
ss << ' ';
ss << tc.ToString2();
}
ss << "</tc1>" << VERUS_CRNL;
}
ss << "</x3d>";
return ss.str();
}
String BaseMesh::ToObjString() const
{
StringStream ss;
VERUS_FOR(i, _vertCount)
{
Point3 pos;
DequantizeUsingDeq3D(_vBinding0[i]._pos, _posDeq, pos);
ss << "v " << Point3(VMath::scale(pos, 100)).ToString() << VERUS_CRNL;
}
ss << VERUS_CRNL;
if (!_vIndices.empty())
{
VERUS_FOR(i, _faceCount)
{
ss << "f ";
ss << _vIndices[i * 3 + 0] + 1 << ' ';
ss << _vIndices[i * 3 + 1] + 1 << ' ';
ss << _vIndices[i * 3 + 2] + 1 << VERUS_CRNL;
}
}
else
{
VERUS_FOR(i, _faceCount)
{
ss << "f ";
ss << _vIndices32[i * 3 + 0] + 1 << ' ';
ss << _vIndices32[i * 3 + 1] + 1 << ' ';
ss << _vIndices32[i * 3 + 2] + 1 << VERUS_CRNL;
}
}
return ss.str();
}