finish BuildNormals

This commit is contained in:
yyc12345 2023-09-19 15:20:40 +08:00
parent be6cbc5692
commit 2d190ea30b
7 changed files with 225 additions and 15 deletions

View File

@ -35,6 +35,11 @@ def GetTmplOperOffset(sname: str, svars: tuple[str]) -> str:
\t\t\t{sp.join(map(lambda x: f'case {x}: return {svars[x]};', range(len(svars))))} \t\t\t{sp.join(map(lambda x: f'case {x}: return {svars[x]};', range(len(svars))))}
\t\t\tdefault: return {svars[0]}; \t\t\tdefault: return {svars[0]};
\t\t}} \t\t}}
\t}}\tconst CKFLOAT& operator[](size_t i) const {{
\t\tswitch (i) {{
\t\t\t{sp.join(map(lambda x: f'case {x}: return {svars[x]};', range(len(svars))))}
\t\t\tdefault: return {svars[0]};
\t\t}}
\t}}""" \t}}"""
def GetTmplOperAddMinus(sname: str, svars: tuple[str], oper: str) -> str: def GetTmplOperAddMinus(sname: str, svars: tuple[str], oper: str) -> str:
@ -58,6 +63,9 @@ def GetTmplOperMul(sname: str, svars: tuple[str]) -> str:
\t}} \t}}
\tfriend {sname} operator*(CKFLOAT lhs, const {sname}& rhs) {{ \tfriend {sname} operator*(CKFLOAT lhs, const {sname}& rhs) {{
\t\treturn {sname}({', '.join(map(lambda x: f'lhs * rhs.{x}', svars))}); \t\treturn {sname}({', '.join(map(lambda x: f'lhs * rhs.{x}', svars))});
\t}}
\tfriend CKFLOAT operator*(const {sname}& lhs, const {sname}& rhs) {{
\t\treturn ({' + '.join(map(lambda x: f'lhs.{x} * rhs.{x}', svars))});
\t}}""" \t}}"""
def GetTmplOperDiv(sname: str, svars: tuple[str]) -> str: def GetTmplOperDiv(sname: str, svars: tuple[str]) -> str:
@ -73,7 +81,6 @@ def GetTmplOperDiv(sname: str, svars: tuple[str]) -> str:
\t}}""" \t}}"""
def GetTmplOperEqual(sname: str, svars: tuple[str]) -> str: def GetTmplOperEqual(sname: str, svars: tuple[str]) -> str:
sp: str = '\n\t\t'
return f"""\tbool operator==(const {sname}& rhs) const {{ return f"""\tbool operator==(const {sname}& rhs) const {{
\t\treturn ({' && '.join(map(lambda x: f'{x} == rhs.{x}', svars))}); \t\treturn ({' && '.join(map(lambda x: f'{x} == rhs.{x}', svars))});
\t}} \t}}
@ -81,6 +88,27 @@ def GetTmplOperEqual(sname: str, svars: tuple[str]) -> str:
\t\treturn !(*this == rhs); \t\treturn !(*this == rhs);
\t}}""" \t}}"""
def GetTmplLength(sname: str, svars: tuple[str]) -> str:
return f"""\tCKFLOAT SquaredLength() const {{
\t\treturn ({' + '.join(map(lambda x: f'{x} * {x}', svars))});
\t}}
\tCKFLOAT Length() const {{
\t\treturn std::sqrt(SquaredLength());
\t}}"""
def GetTmplNormalize(sname: str, svars: tuple[str]) -> str:
sp: str = '\n\t\t'
return f"""\tvoid Normalized() {{
\t\tCKFLOAT len = Length();
\t\tif (len == 0.0f) return;
\t\t{sp.join(map(lambda x: f'{x} /= len;', svars))}
\t}}
\t{sname} Normalize() const {{
\t\tCKFLOAT len = Length();
\t\tif (len == 0.0f) return {sname}();
\t\treturn {sname}({', '.join(map(lambda x: f'{x} / len', svars))});
\t}}"""
def GetTmplVector(sname: str, svars: tuple[str]) -> str: def GetTmplVector(sname: str, svars: tuple[str]) -> str:
return f""" return f"""
struct {sname} {{ struct {sname} {{
@ -94,6 +122,8 @@ struct {sname} {{
{GetTmplOperMul(sname, svars)} {GetTmplOperMul(sname, svars)}
{GetTmplOperDiv(sname, svars)} {GetTmplOperDiv(sname, svars)}
{GetTmplOperEqual(sname, svars)} {GetTmplOperEqual(sname, svars)}
{GetTmplLength(sname, svars)}
{GetTmplNormalize(sname, svars)}
}}; }};
""" """

View File

@ -226,6 +226,8 @@ namespace LibCmo::CK2::ObjImpls {
return true; return true;
} }
#pragma region Misc Section
void CKMesh::CleanMesh() { void CKMesh::CleanMesh() {
// clear material channel first // clear material channel first
SetMtlChannelCount(0); SetMtlChannelCount(0);
@ -236,9 +238,46 @@ namespace LibCmo::CK2::ObjImpls {
SetLineCount(0); SetLineCount(0);
} }
void CKMesh::BuildNormals() {} void CKMesh::BuildNormals() {
if (m_FaceCount == 0 || m_VertexCount == 0) return;
void CKMesh::BuildFaceNormals() {} // build face normal first
BuildFaceNormals();
// iterate all face and add face normal to each point's normal
for (CKDWORD fid = 0; fid < m_FaceCount; ++fid) {
m_VertexNormal[m_FaceIndices[fid * 3]] += m_Faces[fid].m_Normal;
m_VertexNormal[m_FaceIndices[fid * 3 + 1]] += m_Faces[fid].m_Normal;
m_VertexNormal[m_FaceIndices[fid * 3 + 2]] += m_Faces[fid].m_Normal;
}
// then normalize all vertex normal
for (auto& nml : m_VertexNormal) {
nml.Normalized();
}
}
void CKMesh::BuildFaceNormals() {
if (m_FaceCount == 0 || m_VertexCount == 0) return;
// iertate all face to build face normal according to position data
for (CKDWORD fid = 0; fid < m_FaceCount; ++fid) {
VxMath::VxVector3 *p0 = &m_VertexPosition[m_FaceIndices[fid * 3]];
VxMath::VxVector3 p0_p1 = m_VertexPosition[m_FaceIndices[fid * 3 + 1]] - *p0,
p0_p2 = m_VertexPosition[m_FaceIndices[fid * 3 + 2]] - *p0;
// cross product to get normal
// and normalize it
VxMath::VxVector3 nml = VxMath::NSVxVector::CrossProduct(p0_p1, p0_p2);
nml.Normalized();
// assign it
m_Faces[fid].m_Normal = nml;
}
}
#pragma endregion
#pragma region Vertex Section #pragma region Vertex Section

View File

@ -124,6 +124,33 @@ namespace LibCmo::VxMath {
#pragma endregion #pragma endregion
#pragma region Patched
namespace NSVxVector {
float LibCmo::VxMath::NSVxVector::DotProduct(const VxVector2& lhs, const VxVector2& rhs) {
return lhs * rhs;
}
float LibCmo::VxMath::NSVxVector::DotProduct(const VxVector3& lhs, const VxVector3& rhs) {
return lhs * rhs;
}
float LibCmo::VxMath::NSVxVector::DotProduct(const VxVector4& lhs, const VxVector4& rhs) {
return lhs * rhs;
}
VxVector3 CrossProduct(const VxVector3& lhs, const VxVector3& rhs) {
return VxVector3(
lhs.y * rhs.z - lhs.z * rhs.y,
lhs.z * rhs.x - lhs.x * rhs.z,
lhs.x * rhs.y - lhs.y * rhs.x
);
}
}
#pragma endregion
} }

View File

@ -83,5 +83,18 @@ namespace LibCmo::VxMath {
*/ */
void VxDoAlphaBlit(VxImageDescEx* dst_desc, const CKBYTE* AlphaValues); void VxDoAlphaBlit(VxImageDescEx* dst_desc, const CKBYTE* AlphaValues);
// ========== Patch Section ==========
namespace NSVxVector {
CKFLOAT DotProduct(const VxVector2& lhs, const VxVector2& rhs);
CKFLOAT DotProduct(const VxVector3& lhs, const VxVector3& rhs);
CKFLOAT DotProduct(const VxVector4& lhs, const VxVector4& rhs);
VxVector3 CrossProduct(const VxVector3& lhs, const VxVector3& rhs);
}
} }

View File

@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include <cstring> #include <cstring>
#include <cinttypes> #include <cinttypes>
#include <cmath>
/** /**
* @brief The VxMath part of LibCmo. * @brief The VxMath part of LibCmo.
@ -35,6 +36,12 @@ namespace LibCmo::VxMath {
case 1: return y; case 1: return y;
default: return x; default: return x;
} }
} const CKFLOAT& operator[](size_t i) const {
switch (i) {
case 0: return x;
case 1: return y;
default: return x;
}
} }
VxVector2& operator+=(const VxVector2& rhs) { VxVector2& operator+=(const VxVector2& rhs) {
x += rhs.x; x += rhs.x;
@ -63,6 +70,9 @@ namespace LibCmo::VxMath {
friend VxVector2 operator*(CKFLOAT lhs, const VxVector2& rhs) { friend VxVector2 operator*(CKFLOAT lhs, const VxVector2& rhs) {
return VxVector2(lhs * rhs.x, lhs * rhs.y); return VxVector2(lhs * rhs.x, lhs * rhs.y);
} }
friend CKFLOAT operator*(const VxVector2& lhs, const VxVector2& rhs) {
return (lhs.x * rhs.x + lhs.y * rhs.y);
}
VxVector2& operator/=(CKFLOAT rhs) { VxVector2& operator/=(CKFLOAT rhs) {
if (rhs == 0.0f) return *this; if (rhs == 0.0f) return *this;
x /= rhs; x /= rhs;
@ -79,6 +89,23 @@ namespace LibCmo::VxMath {
bool operator!=(const VxVector2& rhs) const { bool operator!=(const VxVector2& rhs) const {
return !(*this == rhs); return !(*this == rhs);
} }
CKFLOAT SquaredLength() const {
return (x * x + y * y);
}
CKFLOAT Length() const {
return std::sqrt(SquaredLength());
}
void Normalized() {
CKFLOAT len = Length();
if (len == 0.0f) return;
x /= len;
y /= len;
}
VxVector2 Normalize() const {
CKFLOAT len = Length();
if (len == 0.0f) return VxVector2();
return VxVector2(x / len, y / len);
}
}; };
struct VxVector3 { struct VxVector3 {
@ -93,6 +120,13 @@ namespace LibCmo::VxMath {
case 2: return z; case 2: return z;
default: return x; default: return x;
} }
} const CKFLOAT& operator[](size_t i) const {
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
default: return x;
}
} }
VxVector3& operator+=(const VxVector3& rhs) { VxVector3& operator+=(const VxVector3& rhs) {
x += rhs.x; x += rhs.x;
@ -124,6 +158,9 @@ namespace LibCmo::VxMath {
friend VxVector3 operator*(CKFLOAT lhs, const VxVector3& rhs) { friend VxVector3 operator*(CKFLOAT lhs, const VxVector3& rhs) {
return VxVector3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); return VxVector3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
} }
friend CKFLOAT operator*(const VxVector3& lhs, const VxVector3& rhs) {
return (lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z);
}
VxVector3& operator/=(CKFLOAT rhs) { VxVector3& operator/=(CKFLOAT rhs) {
if (rhs == 0.0f) return *this; if (rhs == 0.0f) return *this;
x /= rhs; x /= rhs;
@ -141,6 +178,24 @@ namespace LibCmo::VxMath {
bool operator!=(const VxVector3& rhs) const { bool operator!=(const VxVector3& rhs) const {
return !(*this == rhs); return !(*this == rhs);
} }
CKFLOAT SquaredLength() const {
return (x * x + y * y + z * z);
}
CKFLOAT Length() const {
return std::sqrt(SquaredLength());
}
void Normalized() {
CKFLOAT len = Length();
if (len == 0.0f) return;
x /= len;
y /= len;
z /= len;
}
VxVector3 Normalize() const {
CKFLOAT len = Length();
if (len == 0.0f) return VxVector3();
return VxVector3(x / len, y / len, z / len);
}
}; };
struct VxVector4 { struct VxVector4 {
@ -156,6 +211,14 @@ namespace LibCmo::VxMath {
case 3: return w; case 3: return w;
default: return x; default: return x;
} }
} const CKFLOAT& operator[](size_t i) const {
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
case 3: return w;
default: return x;
}
} }
VxVector4& operator+=(const VxVector4& rhs) { VxVector4& operator+=(const VxVector4& rhs) {
x += rhs.x; x += rhs.x;
@ -190,6 +253,9 @@ namespace LibCmo::VxMath {
friend VxVector4 operator*(CKFLOAT lhs, const VxVector4& rhs) { friend VxVector4 operator*(CKFLOAT lhs, const VxVector4& rhs) {
return VxVector4(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z, lhs * rhs.w); return VxVector4(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z, lhs * rhs.w);
} }
friend CKFLOAT operator*(const VxVector4& lhs, const VxVector4& rhs) {
return (lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w);
}
VxVector4& operator/=(CKFLOAT rhs) { VxVector4& operator/=(CKFLOAT rhs) {
if (rhs == 0.0f) return *this; if (rhs == 0.0f) return *this;
x /= rhs; x /= rhs;
@ -208,6 +274,25 @@ namespace LibCmo::VxMath {
bool operator!=(const VxVector4& rhs) const { bool operator!=(const VxVector4& rhs) const {
return !(*this == rhs); return !(*this == rhs);
} }
CKFLOAT SquaredLength() const {
return (x * x + y * y + z * z + w * w);
}
CKFLOAT Length() const {
return std::sqrt(SquaredLength());
}
void Normalized() {
CKFLOAT len = Length();
if (len == 0.0f) return;
x /= len;
y /= len;
z /= len;
w /= len;
}
VxVector4 Normalize() const {
CKFLOAT len = Length();
if (len == 0.0f) return VxVector4();
return VxVector4(x / len, y / len, z / len, w / len);
}
}; };
struct VxQuaternion { struct VxQuaternion {
@ -309,8 +394,7 @@ namespace LibCmo::VxMath {
public: public:
VxStridedData(_Ty ptr, CKDWORD stride) : VxStridedData(_Ty ptr, CKDWORD stride) :
m_Ptr(reinterpret_cast<CKBYTE*>(m_Ptr)), m_Ptr(reinterpret_cast<CKBYTE*>(m_Ptr)),
m_Stride(stride) m_Stride(stride) {}
{}
~VxStridedData() {} ~VxStridedData() {}
_Ty operator[](size_t idx) { _Ty operator[](size_t idx) {

View File

@ -1,3 +1,20 @@
# Tools # Tools
The developer need to know the loaded data whether is correct when testing LibCmo. So we create this folder and you can use Unvirt and the tools located in this folder to test the correction of loaded data.
Unvirt can show the data of each CKObject, such as Texture, Mesh and etc. For example, Unvirt can provide vertex's position, normal, UV, even the face's indices data for CKMesh. You can use tools to broswer memory to get them, but you couldn't evaluate them how they shape a mesh. This is the reason why this folder existed and in this README I will tell you how to debug the loaded data.
I suggest you to use HxD to broswer memory, but if you have other softwares, use it freely.
## CKTexture
* Install [PixelViewer](https://github.com/carina-studio/PixelViewer) first.
* Change profile to `BGRA_8888` (actually is little-endian RGBA8888, but I think the developer of PixelViewer get confused).
* The image resolution can be gotten from Uvirt. Set it in PixelViewer.
* The image address also can be gotten from Unvirt. Save the memory image data to local file and open it by PixelViewer.
## CKMesh
* Have a executable Python.
* Save VertexPosition, VertexNormal, VertexUV, FaceIndices data into file according to the given memory address by Unvirt.
* Call `MeshConv.py`, set the argument properly, then you will get a converted Wavefront OBJ file.

View File

@ -123,32 +123,32 @@ namespace Unvirt::StructFormatter {
fputs("VertexPositions: ", stdout); fputs("VertexPositions: ", stdout);
PrintPointer(obj->GetVertexPositions()); PrintPointer(obj->GetVertexPositions());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector3));
fputs("VertexNormals: ", stdout); fputs("VertexNormals: ", stdout);
PrintPointer(obj->GetVertexNormals()); PrintPointer(obj->GetVertexNormals());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector3));
fputs("VertexUVs: ", stdout); fputs("VertexUVs: ", stdout);
PrintPointer(obj->GetVertexUVs()); PrintPointer(obj->GetVertexUVs());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector2));
fputs("VertexColors: ", stdout); fputs("VertexColors: ", stdout);
PrintPointer(obj->GetVertexColors()); PrintPointer(obj->GetVertexColors());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKDWORD));
fputs("VertexSpecularColors: ", stdout); fputs("VertexSpecularColors: ", stdout);
PrintPointer(obj->GetVertexSpecularColors()); PrintPointer(obj->GetVertexSpecularColors());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKDWORD));
fputs("VertexWeights: ", stdout); fputs("VertexWeights: ", stdout);
PrintPointer(obj->GetVertexWeights()); PrintPointer(obj->GetVertexWeights());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKFLOAT));
fputs("Face:\n", stdout); fputs("Face:\n", stdout);
fprintf(stdout, "Face Count: %" PRIuCKDWORD "\n", obj->GetFaceCount()); fprintf(stdout, "Face Count: %" PRIuCKDWORD "\n", obj->GetFaceCount());
fputs("FaceIndices: ", stdout); fputs("FaceIndices: ", stdout);
PrintPointer(obj->GetFaceIndices()); PrintPointer(obj->GetFaceIndices());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetFaceCount() * 3 * CKSizeof(LibCmo::CKWORD));
fputs("FaceMaterialSlotIndexs: ", stdout); fputs("FaceMaterialSlotIndexs: ", stdout);
PrintPointer(obj->GetFaceMaterialSlotIndexs()); PrintPointer(obj->GetFaceMaterialSlotIndexs());
fputc('\n', stdout); fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetFaceCount() * CKSizeof(LibCmo::CKWORD));
} }