2023-09-15 13:21:49 +08:00
#include "CKMesh.hpp"
#include "../CKStateChunk.hpp"
#include "../CKContext.hpp"
#include "CKMaterial.hpp"
namespace LibCmo :: CK2 :: ObjImpls {
CKMesh :: CKMesh ( CKContext * ctx , CK_ID ckid , CKSTRING name ) :
2023-09-20 14:42:44 +08:00
CKBeObject ( ctx , ckid , name ),
// init vertex
m_VertexCount ( 0 ),
m_VertexPosition (), m_VertexNormal (), m_VertexUV (),
m_VertexColor (), m_VertexSpecularColor (),
// init mtl slots
2023-10-08 20:56:29 +08:00
m_MaterialSlotCount ( 0 ),
2023-09-20 14:42:44 +08:00
m_MaterialSlot (),
// init face data
m_FaceCount ( 0 ),
2023-10-01 23:48:55 +08:00
m_FaceIndices (), m_FaceMtlIndex (), m_FaceOthers (),
2023-09-20 14:42:44 +08:00
// init line
m_LineCount ( 0 ),
m_LineIndices (),
// init flags
2024-11-03 19:05:27 +08:00
m_Flags ( YYCC :: EnumHelper :: Merge (
2023-12-01 23:31:09 +08:00
VxMath :: VXMESH_FLAGS :: VXMESH_VISIBLE ,
VxMath :: VXMESH_FLAGS :: VXMESH_RENDERCHANNELS
2024-11-03 19:05:27 +08:00
)) {
2023-09-20 14:42:44 +08:00
// set visible in default
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Add ( m_ObjectFlags , CK_OBJECT_FLAGS :: CK_OBJECT_VISIBLE );
2023-09-20 14:42:44 +08:00
}
2023-09-15 13:21:49 +08:00
CKMesh ::~ CKMesh () {}
2023-09-20 15:25:43 +08:00
void CKMesh :: CheckPreDeletion () {
CKBeObject :: CheckPreDeletion ();
// check material slots
for ( auto & slot : m_MaterialSlot ) {
if ( slot != nullptr && slot -> IsToBeDeleted ()) {
slot = nullptr ;
}
}
}
2023-09-15 13:21:49 +08:00
bool CKMesh :: Save ( CKStateChunk * chunk , CKFileVisitor * file , CKDWORD flags ) {
bool suc = CKBeObject :: Save ( chunk , file , flags );
if ( ! suc ) return false ;
2023-10-01 23:48:55 +08:00
// write mesh flags
{
chunk -> WriteIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHFLAGS );
chunk -> WriteStruct ( m_Flags );
}
// write material slots
2023-12-01 23:31:09 +08:00
// MARK: due to virtools shit implement, we must make sure there is at least one material channel existed.
// so if the material slot is empty, we write a mullptr slot for it.
{
2023-10-01 23:48:55 +08:00
chunk -> WriteIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHMATERIALS );
2023-12-01 23:31:09 +08:00
if ( GetMaterialSlotCount () != 0 ) {
// write real slots
chunk -> WriteStruct ( GetMaterialSlotCount ());
for ( auto & mtlSlot : m_MaterialSlot ) {
// write object id
chunk -> WriteObjectPointer ( mtlSlot );
// MARK: write a zero? idk what the fuck it is.
chunk -> WriteStruct ( static_cast < CKDWORD > ( 0 ));
}
} else {
// write fake one like real one
chunk -> WriteStruct ( static_cast < CKDWORD > ( 1 ));
// write id and blank
chunk -> WriteObjectPointer ( nullptr );
2023-10-01 23:48:55 +08:00
chunk -> WriteStruct ( static_cast < CKDWORD > ( 0 ));
}
}
// write face data
if ( GetFaceCount () != 0 ) {
CKDWORD faceCount = GetFaceCount ();
chunk -> WriteIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHFACES );
chunk -> WriteStruct ( faceCount );
// write compressed data, see Read for more info about this struct
// lock buffer first
auto buf = chunk -> LockWriteBufferWrapper ( faceCount * CKSizeof ( CKDWORD ) * 2 );
CKWORD * rawbuf = static_cast < CKWORD *> ( buf . get ());
// copy indice
VxMath :: VxCopyStructure (
faceCount ,
rawbuf ,
2 * CKSizeof ( CKDWORD ),
3 * CKSizeof ( CKWORD ),
m_FaceIndices . data (),
3 * CKSizeof ( CKWORD )
);
// copy mtl index
VxMath :: VxCopyStructure (
faceCount ,
rawbuf + 3 ,
2 * CKSizeof ( CKDWORD ),
CKSizeof ( CKWORD ),
m_FaceMtlIndex . data (),
CKSizeof ( CKWORD )
);
// free buf
buf . reset ();
}
// write line data
if ( GetLineCount () != 0 ) {
CKDWORD lineCount = GetLineCount ();
chunk -> WriteIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHLINES );
chunk -> WriteStruct ( lineCount );
chunk -> WriteBuffer ( m_LineIndices . data (), CKSizeof ( CKWORD ) * 2 * lineCount );
}
// write vertex data
if ( GetVertexCount () != 0 ) {
CKDWORD vtxCount = GetVertexCount ();
chunk -> WriteIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHVERTICES );
chunk -> WriteStruct ( vtxCount );
// construct vertex save flags
// and save it
VertexSaveFlags saveflags = GenerateSaveFlags ();
chunk -> WriteStruct ( saveflags );
// reserve enough space for full data written, but we can specify the real consumed size later
// we also need calc the consumed size when writing file
auto buf = chunk -> LockWriteBufferWrapper ((
CKSizeof ( VxMath :: VxVector3 ) + // vertex position
CKSizeof ( CKDWORD ) + CKSizeof ( CKDWORD ) + // color and specular color
CKSizeof ( VxMath :: VxVector3 ) + // vertex normal
CKSizeof ( VxMath :: VxVector2 ) // vertex uv
) * vtxCount ); // mul vertex count
CKBYTE * rawbuf = static_cast < CKBYTE *> ( buf . get ());
// reserve length data
2023-10-02 09:16:45 +08:00
CKDWORD * reservedBufDwordSize = reinterpret_cast < CKDWORD *> ( rawbuf );
2023-10-01 23:48:55 +08:00
rawbuf += CKSizeof ( CKDWORD );
// write vertex position
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: NoPos )) {
2023-10-01 23:48:55 +08:00
CKDWORD consumed = CKSizeof ( VxMath :: VxVector3 ) * vtxCount ;
std :: memcpy ( rawbuf , m_VertexPosition . data (), consumed );
rawbuf += consumed ;
}
// write color and specular color
{
CKDWORD consumed = 0 ;
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: SingleColor )) {
2023-10-01 23:48:55 +08:00
consumed = CKSizeof ( CKDWORD ) * vtxCount ;
} else {
consumed = CKSizeof ( CKDWORD );
}
std :: memcpy ( rawbuf , m_VertexColor . data (), consumed );
rawbuf += consumed ;
}
{
CKDWORD consumed = 0 ;
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: SingleSpecularColor )) {
2023-10-01 23:48:55 +08:00
consumed = CKSizeof ( CKDWORD ) * vtxCount ;
} else {
consumed = CKSizeof ( CKDWORD );
}
std :: memcpy ( rawbuf , m_VertexSpecularColor . data (), consumed );
rawbuf += consumed ;
}
// write normal
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: NoNormal )) {
2023-10-01 23:48:55 +08:00
CKDWORD consumed = CKSizeof ( VxMath :: VxVector3 ) * vtxCount ;
std :: memcpy ( rawbuf , m_VertexNormal . data (), consumed );
rawbuf += consumed ;
}
// write uv
{
CKDWORD consumed = 0 ;
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: SingleUV )) {
2023-10-01 23:48:55 +08:00
consumed = CKSizeof ( VxMath :: VxVector2 ) * vtxCount ;
} else {
consumed = CKSizeof ( VxMath :: VxVector2 );
}
std :: memcpy ( rawbuf , m_VertexUV . data (), consumed );
rawbuf += consumed ;
}
// calc real consumed size
2023-10-29 12:36:03 +08:00
CKDWORD realConsumedSize = static_cast < CKDWORD > ( rawbuf - static_cast < CKBYTE *> ( buf . get ()));
2023-10-01 23:48:55 +08:00
// assign to reserved length field
// length also include length indicator it self
2023-10-02 09:16:45 +08:00
* reservedBufDwordSize = realConsumedSize / CKSizeof ( CKDWORD );
2023-10-01 23:48:55 +08:00
// notify buffer real consumed size
buf . get_deleter (). SetConsumedSize ( realConsumedSize );
// free buffer
buf . reset ();
}
2023-10-02 11:32:18 +08:00
chunk -> SetClassId ( CK_CLASSID :: CKCID_MESH );
2023-09-15 13:21:49 +08:00
return true ;
}
bool CKMesh :: Load ( CKStateChunk * chunk , CKFileVisitor * file ) {
bool suc = CKBeObject :: Load ( chunk , file );
if ( ! suc ) return false ;
2023-09-15 17:03:36 +08:00
// clear all data
CleanMesh ();
2023-09-15 16:15:07 +08:00
2023-09-15 17:03:36 +08:00
// check data version.
// MARK: too low data is not supported.
// because my work are not related to them
if ( chunk -> GetDataVersion () < CK_STATECHUNK_DATAVERSION :: CHUNK_MESHCHANGE_VERSION ) {
return false ;
}
// read flag
if ( chunk -> SeekIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHFLAGS )) {
2023-09-20 13:13:08 +08:00
chunk -> ReadStruct ( m_Flags );
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Mask ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_ALLFLAGS );
2023-09-15 17:03:36 +08:00
// I don't know why, just interpter the IDA code.
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( m_Flags ,
2023-09-15 17:03:36 +08:00
VxMath :: VXMESH_FLAGS :: VXMESH_BOUNDINGUPTODATE ,
VxMath :: VXMESH_FLAGS :: VXMESH_OPTIMIZED
2024-11-03 19:05:27 +08:00
);
2023-09-15 17:03:36 +08:00
}
// read material slots
if ( chunk -> SeekIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHMATERIALS )) {
// get and set material count
CKDWORD mtlCount ;
chunk -> ReadStruct ( mtlCount );
SetMaterialSlotCount ( mtlCount );
// read slot
CKDWORD ph ;
2023-09-20 14:42:44 +08:00
CKObject * objptr = nullptr ;
2023-09-15 17:03:36 +08:00
for ( auto & mtlSlot : m_MaterialSlot ) {
// read id
2023-09-20 14:42:44 +08:00
chunk -> ReadObjectPointer ( objptr );
2023-09-15 17:03:36 +08:00
// and read a place holder idk what the fuck it is.
chunk -> ReadStruct ( ph );
2023-09-20 14:42:44 +08:00
// try to assign
2023-09-15 17:03:36 +08:00
if ( objptr != nullptr && objptr -> GetClassID () == CK_CLASSID :: CKCID_MATERIAL ) {
mtlSlot = static_cast < CKMaterial *> ( objptr );
} else {
mtlSlot = nullptr ;
}
}
}
// read vertex data
2023-09-18 21:06:34 +08:00
VertexSaveFlags saveflags = VertexSaveFlags :: None ;
2023-09-15 17:03:36 +08:00
if ( chunk -> SeekIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHVERTICES )) {
// read and set vertex count
CKDWORD vertexCount ;
chunk -> ReadStruct ( vertexCount );
2023-09-18 23:11:33 +08:00
SetVertexCount ( vertexCount );
2023-09-15 17:03:36 +08:00
if ( vertexCount != 0 ) {
2023-09-18 21:06:34 +08:00
// read save flags
2023-09-15 17:03:36 +08:00
chunk -> ReadStruct ( saveflags );
2023-09-18 23:11:33 +08:00
// read size in dword (including it self)
2023-09-18 21:06:34 +08:00
CKDWORD sizeInDword ;
chunk -> ReadStruct ( sizeInDword );
2023-09-18 23:11:33 +08:00
-- sizeInDword ; // remove self.
2023-09-15 17:03:36 +08:00
2023-09-18 21:06:34 +08:00
// lock read buffer
auto buf = chunk -> LockReadBufferWrapper ( sizeInDword * CKSizeof ( CKDWORD ));
const CKBYTE * rawbuf = static_cast < const CKBYTE *> ( buf . get ());
2023-09-15 17:03:36 +08:00
2023-09-18 21:06:34 +08:00
// copy position if it have
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: NoPos )) {
2023-09-18 21:06:34 +08:00
CKDWORD consumed = CKSizeof ( VxMath :: VxVector3 ) * vertexCount ;
std :: memcpy ( m_VertexPosition . data (), rawbuf , consumed );
rawbuf += consumed ;
}
2023-09-15 17:03:36 +08:00
2023-09-18 21:06:34 +08:00
// copy color or apply single color
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: SingleColor )) {
2023-09-18 21:06:34 +08:00
CKDWORD consumed = CKSizeof ( CKDWORD ) * vertexCount ;
std :: memcpy ( m_VertexColor . data (), rawbuf , consumed );
rawbuf += consumed ;
} else {
VxMath :: VxCopyStructure (
vertexCount ,
m_VertexColor . data (),
CKSizeof ( CKDWORD ),
CKSizeof ( CKDWORD ),
rawbuf ,
0 // InStride = 0 to make sure copy this single value to every elements.
);
rawbuf += CKSizeof ( CKDWORD );
}
// copy specular color or apply a single color
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: SingleSpecularColor )) {
2023-09-18 21:06:34 +08:00
CKDWORD consumed = CKSizeof ( CKDWORD ) * vertexCount ;
std :: memcpy ( m_VertexSpecularColor . data (), rawbuf , consumed );
rawbuf += consumed ;
} else {
VxMath :: VxCopyStructure (
vertexCount ,
m_VertexSpecularColor . data (),
CKSizeof ( CKDWORD ),
CKSizeof ( CKDWORD ),
rawbuf ,
0 // InStride = 0 to make sure copy this single value to every elements.
);
rawbuf += CKSizeof ( CKDWORD );
}
// copy normals if it has
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: NoNormal )) {
2023-09-18 21:06:34 +08:00
CKDWORD consumed = CKSizeof ( VxMath :: VxVector3 ) * vertexCount ;
std :: memcpy ( m_VertexNormal . data (), rawbuf , consumed );
rawbuf += consumed ;
}
// copy uv or apply single uv
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: SingleUV )) {
2023-09-18 21:06:34 +08:00
CKDWORD consumed = CKSizeof ( VxMath :: VxVector2 ) * vertexCount ;
std :: memcpy ( m_VertexUV . data (), rawbuf , consumed );
rawbuf += consumed ;
} else {
VxMath :: VxCopyStructure (
vertexCount ,
m_VertexUV . data (),
CKSizeof ( VxMath :: VxVector2 ),
CKSizeof ( VxMath :: VxVector2 ),
rawbuf ,
0 // InStride = 0 to make sure copy this single value to every elements.
);
rawbuf += CKSizeof ( VxMath :: VxVector2 );
}
// free buf
buf . reset ();
2023-09-15 17:03:36 +08:00
}
}
2023-09-15 16:15:07 +08:00
2023-09-18 21:06:34 +08:00
// read face data
if ( chunk -> SeekIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHFACES )) {
// read face count and set
CKDWORD faceCount ;
chunk -> ReadStruct ( faceCount );
SetFaceCount ( faceCount );
// lock buffer
auto buf = chunk -> LockReadBufferWrapper ( faceCount * CKSizeof ( CKDWORD ) * 2 );
const CKWORD * rawbuf = static_cast < const CKWORD *> ( buf . get ());
// each face use 2 CKDWORD to describe
// first CKDWORD describe first 2 face vertex indices
// HIGH >>> 0xFFFF(indice 1) 0xFFFF(indice 0) <<< LOW
// second CKDWORD describe the third indices and used material slot index
// HIGH >>> 0xFFFF(mtl slot index) 0xFFFF(indice 2) <<< LOW
// due to little endian, the data listed before are placed in memory like this:
// (indice 0) (indice 1) (indice 2) (mtl idx)
// copy indice
VxMath :: VxCopyStructure (
faceCount ,
m_FaceIndices . data (),
3 * CKSizeof ( CKWORD ),
3 * CKSizeof ( CKWORD ),
rawbuf ,
2 * CKSizeof ( CKDWORD )
);
// copy mtl index
VxMath :: VxCopyStructure (
faceCount ,
m_FaceMtlIndex . data (),
CKSizeof ( CKWORD ),
CKSizeof ( CKWORD ),
rawbuf + 3 ,
2 * CKSizeof ( CKDWORD )
);
// free buf
buf . reset ();
}
// read line data
if ( chunk -> SeekIdentifier ( CK_STATESAVEFLAGS_MESH :: CK_STATESAVE_MESHLINES )) {
// read and set line count;
CKDWORD lineCount ;
chunk -> ReadStruct ( lineCount );
SetLineCount ( lineCount );
2023-10-01 23:48:55 +08:00
chunk -> ReadAndFillBuffer ( m_LineIndices . data ());
2023-09-18 23:11:33 +08:00
}
// build normals
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( saveflags , VertexSaveFlags :: NoNormal )) {
2023-09-18 23:11:33 +08:00
BuildNormals ();
} else {
BuildFaceNormals ();
2023-09-18 21:06:34 +08:00
}
2023-10-01 23:48:55 +08:00
// MARK: material channels, vertex weight, face mask added originally
// but removed at Oct 1st, 2023 because I will not use them and I couldn't test them.
2023-09-20 14:42:44 +08:00
// MARK: progressive mesh data is dropper.
2023-09-15 13:21:49 +08:00
return true ;
}
2023-09-20 13:13:08 +08:00
void CKMesh :: Show ( CK_OBJECT_SHOWOPTION show ) {
CKObject :: Show ( show );
if ( show == CK_OBJECT_SHOWOPTION :: CKSHOW ) {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Add ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_VISIBLE );
2023-09-20 13:13:08 +08:00
} else {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_VISIBLE );
2023-09-20 13:13:08 +08:00
}
}
2023-09-19 15:20:40 +08:00
#pragma region Misc Section
2023-09-15 16:15:07 +08:00
void CKMesh :: CleanMesh () {
SetVertexCount ( 0 );
SetMaterialSlotCount ( 0 );
SetFaceCount ( 0 );
SetLineCount ( 0 );
}
2023-09-22 16:40:10 +08:00
VxMath :: VXMESH_FLAGS CKMesh :: GetMeshFlags () const {
return m_Flags ;
}
2023-11-15 21:47:58 +08:00
void CKMesh :: SetMeshFlags ( VxMath :: VXMESH_FLAGS flags ) {
// set value
m_Flags = flags ;
// sync visibility to CKObject layer.
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_VISIBLE )) {
YYCC :: EnumHelper :: Add ( m_ObjectFlags , CK_OBJECT_FLAGS :: CK_OBJECT_VISIBLE );
2023-11-15 21:47:58 +08:00
} else {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( m_ObjectFlags , CK_OBJECT_FLAGS :: CK_OBJECT_VISIBLE );
2023-11-15 21:47:58 +08:00
}
}
VxMath :: VXMESH_LITMODE CKMesh :: GetLitMode () const {
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_PRELITMODE )) {
2023-11-15 21:47:58 +08:00
return VxMath :: VXMESH_LITMODE :: VX_PRELITMESH ;
} else {
return VxMath :: VXMESH_LITMODE :: VX_LITMESH ;
}
}
void CKMesh :: SetLitMode ( VxMath :: VXMESH_LITMODE mode ) {
switch ( mode ) {
case VxMath :: VXMESH_LITMODE :: VX_PRELITMESH :
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Add ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_PRELITMODE );
2023-11-15 21:47:58 +08:00
break ;
case VxMath :: VXMESH_LITMODE :: VX_LITMESH :
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_PRELITMODE );
2023-11-15 21:47:58 +08:00
break ;
}
}
VxMath :: VXTEXTURE_WRAPMODE CKMesh :: GetWrapMode () const {
VxMath :: VXTEXTURE_WRAPMODE ret = VxMath :: VXTEXTURE_WRAPMODE :: VXTEXTUREWRAP_NONE ;
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_WRAPU )) {
YYCC :: EnumHelper :: Add ( ret , VxMath :: VXTEXTURE_WRAPMODE :: VXTEXTUREWRAP_U );
2023-11-15 21:47:58 +08:00
}
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_WRAPV )) {
YYCC :: EnumHelper :: Add ( ret , VxMath :: VXTEXTURE_WRAPMODE :: VXTEXTUREWRAP_V );
2023-11-15 21:47:58 +08:00
}
return ret ;
}
void CKMesh :: SetWrapMode ( VxMath :: VXTEXTURE_WRAPMODE mode ) {
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( mode , VxMath :: VXTEXTURE_WRAPMODE :: VXTEXTUREWRAP_U )) {
YYCC :: EnumHelper :: Add ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_WRAPU );
2023-11-15 21:47:58 +08:00
} else {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_WRAPU );
2023-11-15 21:47:58 +08:00
}
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( mode , VxMath :: VXTEXTURE_WRAPMODE :: VXTEXTUREWRAP_V )) {
YYCC :: EnumHelper :: Add ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_WRAPV );
2023-11-15 21:47:58 +08:00
} else {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_WRAPV );
2023-11-15 21:47:58 +08:00
}
}
2023-10-01 23:48:55 +08:00
CKMesh :: VertexSaveFlags CKMesh :: GenerateSaveFlags () {
// set to initial status
2024-11-03 19:05:27 +08:00
VertexSaveFlags saveflags = YYCC :: EnumHelper :: Merge (
2023-10-01 23:48:55 +08:00
VertexSaveFlags :: SingleColor ,
VertexSaveFlags :: SingleSpecularColor ,
VertexSaveFlags :: NoNormal ,
VertexSaveFlags :: SingleUV
2024-11-03 19:05:27 +08:00
);
2023-10-01 23:48:55 +08:00
// check no pos
// if position is generated, skip saving position
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_PROCEDURALPOS )) {
YYCC :: EnumHelper :: Add ( saveflags , VertexSaveFlags :: NoPos );
2023-10-01 23:48:55 +08:00
}
// check uv
// if uv is not generated and all uv are not the same value, remove single uv
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_PROCEDURALUV )) {
2023-10-01 23:48:55 +08:00
for ( const auto & uv : m_VertexUV ) {
if ( uv != m_VertexUV . front ()) {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( saveflags , VertexSaveFlags :: SingleUV );
2023-10-01 23:48:55 +08:00
break ;
}
}
}
// check color and specular color
// if all color are not the same value, remove single color
for ( const auto & col : m_VertexColor ) {
if ( col != m_VertexColor . front ()) {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( saveflags , VertexSaveFlags :: SingleColor );
2023-10-01 23:48:55 +08:00
break ;
}
}
for ( const auto & col : m_VertexSpecularColor ) {
if ( col != m_VertexSpecularColor . front ()) {
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( saveflags , VertexSaveFlags :: SingleSpecularColor );
2023-10-01 23:48:55 +08:00
break ;
}
}
// if normal not changed, and position is not generated, we should consider whether we need save normal (step into if)
2024-11-03 19:05:27 +08:00
if ( ! YYCC :: EnumHelper :: Has ( m_Flags , VxMath :: VXMESH_FLAGS :: VXMESH_NORMAL_CHANGED , VxMath :: VXMESH_FLAGS :: VXMESH_PROCEDURALPOS )) {
2023-10-01 23:48:55 +08:00
// MARK: we should build face normal first
// then we build vertex normal like BuildNormals.
// then, we compare the difference between the generated normals and user specified normals, by simply using operator- (userNml - generatedNml) and abs the result.
// then we accumulate these difference, by simply adding them together.
// then we div the accumulation by the count of vertex, we got a normalized accumulated difference.
// we compare its length with 0.001. if is length is lower than 0.001, it prove that the difference is enough small and we can skip normal save.
// othersize we should save normal one by one.
BuildFaceNormals ();
// init generated nml list first
XContainer :: XArray < VxMath :: VxVector3 > generated ( m_VertexCount , VxMath :: VxVector3 ());
// and accumulated for each normal
for ( CKDWORD fid = 0 ; fid < m_FaceCount ; ++ fid ) {
generated [ m_FaceIndices [ fid * 3 ]] += m_FaceOthers [ fid ]. m_Normal ;
generated [ m_FaceIndices [ fid * 3 + 1 ]] += m_FaceOthers [ fid ]. m_Normal ;
generated [ m_FaceIndices [ fid * 3 + 2 ]] += m_FaceOthers [ fid ]. m_Normal ;
}
// init accumulated difference vector first
VxMath :: VxVector3 accnml ;
// accumulate difference
for ( CKDWORD vid = 0 ; vid < m_VertexCount ; ++ vid ) {
// normalize generated normal first
generated [ vid ]. Normalized ();
// get diff by distance
VxMath :: VxVector3 diff = m_VertexNormal [ vid ] - generated [ vid ];
// abs the diff and add into accumulated diff
VxMath :: NSVxVector :: Abs ( diff );
accnml += diff ;
}
// div by vertex count and compare its length
accnml /= static_cast < CKFLOAT > ( m_VertexCount );
if ( accnml . Length () > 0.001f ) {
// too large difference, we need save normal
2024-11-03 19:05:27 +08:00
YYCC :: EnumHelper :: Remove ( saveflags , VertexSaveFlags :: NoNormal );
2023-10-01 23:48:55 +08:00
}
}
return saveflags ;
}
2023-09-19 15:20:40 +08:00
void CKMesh :: BuildNormals () {
if ( m_FaceCount == 0 || m_VertexCount == 0 ) return ;
2023-09-18 23:11:33 +08:00
2023-09-19 15:20:40 +08:00
// 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 ) {
2023-10-01 23:48:55 +08:00
m_VertexNormal [ m_FaceIndices [ fid * 3 ]] += m_FaceOthers [ fid ]. m_Normal ;
m_VertexNormal [ m_FaceIndices [ fid * 3 + 1 ]] += m_FaceOthers [ fid ]. m_Normal ;
m_VertexNormal [ m_FaceIndices [ fid * 3 + 2 ]] += m_FaceOthers [ fid ]. m_Normal ;
2023-09-19 15:20:40 +08:00
}
// 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
2023-10-01 23:48:55 +08:00
m_FaceOthers [ fid ]. m_Normal = nml ;
2023-09-19 15:20:40 +08:00
}
}
#pragma endregion
2023-09-18 23:11:33 +08:00
2023-09-15 16:15:07 +08:00
#pragma region Vertex Section
2023-09-22 14:48:45 +08:00
CKDWORD CKMesh :: GetVertexCount () const {
2023-09-15 16:15:07 +08:00
return m_VertexCount ;
}
void CKMesh :: SetVertexCount ( CKDWORD count ) {
m_VertexCount = count ;
m_VertexPosition . resize ( count );
m_VertexNormal . resize ( count );
m_VertexUV . resize ( count );
m_VertexColor . resize ( count , 0xFFFFFFFF );
m_VertexSpecularColor . resize ( count , 0x00000000 );
}
VxMath :: VxVector3 * CKMesh :: GetVertexPositions () {
2023-10-08 20:56:29 +08:00
if ( m_VertexCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_VertexPosition . data ();
}
VxMath :: VxVector3 * CKMesh :: GetVertexNormals () {
2023-10-08 20:56:29 +08:00
if ( m_VertexCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_VertexNormal . data ();
}
VxMath :: VxVector2 * CKMesh :: GetVertexUVs () {
2023-10-08 20:56:29 +08:00
if ( m_VertexCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_VertexUV . data ();
}
CKDWORD * CKMesh :: GetVertexColors () {
2023-10-08 20:56:29 +08:00
if ( m_VertexCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_VertexColor . data ();
}
CKDWORD * CKMesh :: GetVertexSpecularColors () {
2023-10-08 20:56:29 +08:00
if ( m_VertexCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_VertexSpecularColor . data ();
}
#pragma endregion
#pragma region Material Slot Section
2023-09-22 14:48:45 +08:00
CKDWORD CKMesh :: GetMaterialSlotCount () const {
2023-10-08 20:56:29 +08:00
return m_MaterialSlotCount ;
2023-09-15 16:15:07 +08:00
}
void CKMesh :: SetMaterialSlotCount ( CKDWORD count ) {
2023-10-08 20:56:29 +08:00
m_MaterialSlotCount = count ;
2023-09-15 16:15:07 +08:00
m_MaterialSlot . resize ( count , nullptr );
}
2023-09-22 14:48:45 +08:00
CKMaterial ** CKMesh :: GetMaterialSlots () {
2023-10-08 20:56:29 +08:00
if ( m_MaterialSlotCount == 0 ) return nullptr ;
2023-09-22 14:48:45 +08:00
return m_MaterialSlot . data ();
2023-09-15 16:15:07 +08:00
}
#pragma endregion
#pragma region Face Section
2023-09-22 14:48:45 +08:00
CKDWORD CKMesh :: GetFaceCount () const {
2023-09-15 16:15:07 +08:00
return m_FaceCount ;
}
void CKMesh :: SetFaceCount ( CKDWORD count ) {
m_FaceCount = count ;
m_FaceIndices . resize ( count * 3 , 0 );
m_FaceMtlIndex . resize ( count , 0 );
2023-10-01 23:48:55 +08:00
m_FaceOthers . resize ( count );
2023-09-15 16:15:07 +08:00
}
CKWORD * CKMesh :: GetFaceIndices () {
2023-10-08 20:56:29 +08:00
if ( m_FaceCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_FaceIndices . data ();
}
CKWORD * CKMesh :: GetFaceMaterialSlotIndexs () {
2023-10-08 20:56:29 +08:00
if ( m_FaceCount == 0 ) return nullptr ;
return m_FaceMtlIndex . data ();
2023-09-15 16:15:07 +08:00
}
VxMath :: VxVector3 * CKMesh :: GetFaceNormals ( CKDWORD & stride ) {
stride = CKSizeof ( FaceData_t );
2023-10-08 20:56:29 +08:00
if ( m_FaceCount == 0 ) return nullptr ;
2023-10-01 23:48:55 +08:00
return & m_FaceOthers . data () -> m_Normal ;
2023-09-15 16:15:07 +08:00
}
#pragma endregion
#pragma region Line Section
2023-09-22 14:48:45 +08:00
CKDWORD CKMesh :: GetLineCount () const {
2023-09-15 16:15:07 +08:00
return m_LineCount ;
}
void CKMesh :: SetLineCount ( CKDWORD count ) {
m_LineCount = count ;
m_LineIndices . resize ( count * 2 , 0 );
}
CKWORD * CKMesh :: GetLineIndices () {
2023-10-08 20:56:29 +08:00
if ( m_LineCount == 0 ) return nullptr ;
2023-09-15 16:15:07 +08:00
return m_LineIndices . data ();
}
#pragma endregion
2023-09-15 13:21:49 +08:00
}