This commit is contained in:
romkazvo
2023-08-07 19:29:24 +08:00
commit 34d6c5d489
4832 changed files with 1389451 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
class CAutoFile
{
FILE* m_f;
public:
CAutoFile (const char* szName, const char* szMode)
{
m_f = fopen (szName, szMode);
}
~CAutoFile ()
{
if (m_f)
fclose (m_f);
}
long GetSize()
{
if (fseek (m_f, 0, SEEK_END))
return -1;
long nSize = ftell (m_f);
if (fseek(m_f, 0, SEEK_SET))
return -1;
return nSize;
}
bool Read (void* pData, unsigned nSize)
{
return (1 == fread (pData, nSize, 1, m_f));
}
bool isEof()
{
return feof(m_f)?true:false;
}
operator FILE*() {return m_f;}
};

View File

@@ -0,0 +1,200 @@
#include "stdafx.h"
#ifdef _CRY_ANIMATION_BASE_HEADER_
#include "CVars.h"
#endif
#include "BoneLightBindInfo.h"
// initializes this from the structures found in the cgf file
void CBoneLightBindInfo::load (const SBoneLightBind& bind, const LIGHT_CHUNK_DESC& light, const char* szLightName, float fScale)
{
// set up the flags that will be set to the DLight
m_nDLightFlags = 0;
// the light source must have a name with substrings _ls and/or _hs
// _ls means a light source, _hs means a heat source
// if none is present, then the heat source flag is automatically set up
if (strstr(szLightName,"_ls"))
m_nDLightFlags |= DLF_LIGHTSOURCE;
if (strstr(szLightName, "_hs"))
m_nDLightFlags |= DLF_HEATSOURCE;
if (!(m_nDLightFlags & (DLF_HEATSOURCE|DLF_LIGHTSOURCE)))
m_nDLightFlags |= DLF_HEATSOURCE;
if (strstr(szLightName, "local"))
m_nDLightFlags |= DLF_LOCAL;
if (light.szLightImage[0])
m_nDLightFlags |= DLF_PROJECT;
else
m_nDLightFlags |= DLF_POINT;
m_nBone = bind.nBoneId;
m_vPos = bind.vLightOffset;
m_qRot = exp( quaternionf(0,bind.vRotLightOrientation) );
constructMatLight();
m_nType = light.type;
m_rgbColor.r = light.color.r / 255.0f;
m_rgbColor.g = light.color.g / 255.0f;
m_rgbColor.b = light.color.b / 255.0f;
m_rgbColor.a = 1.0f;
m_fIntensity = light.intens;
m_fHotSpotSize = light.hotsize;
m_fFalloffSize = light.fallsize;
m_fNearAttenStart = light.nearAttenStart;
m_fNearAttenEnd = light.nearAttenEnd;
m_fFarAttenStart = light.attenStart;
m_fFarAttenEnd = light.attenEnd;
m_vDirection = light.vDirection;
m_strLightImage = light.szLightImage;
m_bOn = light.on;
m_bUseNearAtten = light.useNearAtten;
m_bUseFarAtten = light.useAtten;
m_bShadow = light.shadow;
if (!m_bUseFarAtten)
{
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogWarning ("\003no far attenuation in the heat source (bone) light, using default");
#endif
m_fFarAttenEnd = 40;
}
scale (fScale);
}
void CBoneLightBindInfo::scale (float fScale)
{
m_vPos *= fScale;
m_matLight.SetTranslationOLD (m_matLight.GetTranslationOLD()*fScale);
m_fNearAttenStart *= fScale;
m_fNearAttenEnd *= fScale;
m_fFarAttenStart *= fScale;
m_fFarAttenEnd *= fScale;
if (!m_bUseFarAtten)
m_fFarAttenEnd *= fScale;
}
void CBoneLightBindInfo::constructMatLight()
{
m_matLight=Matrix44(m_qRot);
m_matLight.SetTranslationOLD(m_vPos);
}
//////////////////////////////////////////////////////////////////////////
// initializes the given DLight structure out of the given bone instance
// this is one-time call that is only required after construction of the DLight
// to initialize its constant parameters
void CBoneLightBindInfo::initDLight (CDLight& rDLight)
{
rDLight.m_Flags &= ~(DLF_LIGHTSOURCE|DLF_HEATSOURCE|DLF_PROJECT|DLF_POINT);
rDLight.m_Flags |= m_nDLightFlags;
rDLight.m_Flags |= DLF_LOCAL;
rDLight.m_fDirectFactor = 0;
rDLight.m_Color = m_rgbColor;
rDLight.m_fRadius = m_fFarAttenEnd;
}
//////////////////////////////////////////////////////////////////////////
// per-frame update of the DLight structure. Updates the light position and radius
// PARAMETERS:
// matParentBone - the partent coordinate frame
// fRadiusFactor - the multiplier for the original radius of the light
// rDLight - the light to update
void CBoneLightBindInfo::updateDLight (const Matrix44& matParentBone, float fRadiusMultiplier, CDLight& DLight)
{
DLight.m_Origin = matParentBone.TransformPointOLD(m_vPos);
DLight.m_fRadius = m_fFarAttenEnd * fRadiusMultiplier;
}
// returns true if this light source is local (affects only the character)
bool CBoneLightBindInfo::isLocal()const
{
return (m_nDLightFlags & DLF_LOCAL) != 0;
}
bool CBoneLightBindInfo::isHeatSource()const
{
return (m_nDLightFlags & DLF_HEATSOURCE) != 0;
}
bool CBoneLightBindInfo::isLightSource()const
{
return (m_nDLightFlags & DLF_LIGHTSOURCE) != 0;
}
// returns the priority to sort
int CBoneLightBindInfo::getPriority()const
{
int nPriority = isLocal()?0:1;
nPriority <<= 1;
nPriority |= isHeatSource()?0:1;
nPriority <<= 1;
return nPriority;
}
//////////////////////////////////////////////////////////////////////////
// Serialization to/from memory buffer.
// if the buffer is NULL, and bSave is true, returns the required buffer size
// otherwise, tries to save/load from the buffer, returns the number of bytes written/read
// or 0 if the buffer is too small or some error occured
unsigned CBoneLightBindInfo::Serialize (bool bSave, void* pBuffer, unsigned nSize)
{
const int nVersion = 1;
typedef CBoneLightBindDesc Desc;
if (bSave)
{
// calculate the required size
unsigned nRequiredSize = unsigned(sizeof(nVersion) + sizeof(Desc) + m_strLightImage.length()+1);
if (!pBuffer)
return nRequiredSize;
if (nSize < nRequiredSize)
return 0;
*(int*)pBuffer = 1;
pBuffer = (int*)pBuffer+1;
*((Desc*)pBuffer) = *(Desc*)this;
pBuffer = ((Desc*)pBuffer)+1;
memcpy (pBuffer, m_strLightImage.c_str(), m_strLightImage.length()+1);
return nRequiredSize;
}
else
{
if (nSize < sizeof(nVersion) +sizeof(Desc))
return 0;
const char* pBufEnd = (const char*)pBuffer + nSize;
int nRealVersion = *(int*)pBuffer;
if (nRealVersion != nVersion)
return 0;
void* pRawData = (int*)pBuffer + 1;
*(Desc*)this = *(const Desc*)pRawData;
pRawData = ((Desc*)pRawData)+1;
const char* pName = (const char*)pRawData;
m_strLightImage = "";
for(;*pName && pName < pBufEnd;++pName)
m_strLightImage += *pName;
constructMatLight();
return (unsigned)(pName+1 - (const char*)pBuffer);
}
}

View File

@@ -0,0 +1,89 @@
#ifndef _BONELIGHTBIND_HDR_
#define _BONELIGHTBIND_HDR_
// the storable in file structure for permanent data in CBoneLightBindInfo
struct CBoneLightBindDesc
{
unsigned m_nDLightFlags; // additional flags that DLight receives (maybe light or heatsource)
unsigned m_nBone; // the bone to which the binding is made
Vec3 m_vPos; // position of the light in the bone CS
CryQuat m_qRot; // orientation of the light within the bone CS
LightTypes m_nType; // one of the LightTypes values
CFColor m_rgbColor; // RGB color
float m_fIntensity; // multiplier value
float m_fHotSpotSize; // for spot and direct lights hotspot value
float m_fFalloffSize; // for spot and direct lights falloff value
float m_fNearAttenStart; // near attenuation ranges
float m_fNearAttenEnd;
float m_fFarAttenStart; // far attenuation ranges
float m_fFarAttenEnd;
Vec3 m_vDirection; //spot light direction
// for better alignment, these flags are at the end of the structure
bool m_bOn; // light is on
bool m_bUseNearAtten; // near atten. on
bool m_bUseFarAtten; // far atteniation
bool m_bShadow; // shadow is on
};
//////////////////////////////////////////////////////////////////////////
// the structure describing binding of a light to a bone.
// refers to the bone by the id, and contains the whole info about the ligtht
class CBoneLightBindInfo: public CBoneLightBindDesc
{
public:
CBoneLightBindInfo()
{m_nBone = 0;}
// initializes this from the structures found in the cgf file
void load (const SBoneLightBind&, const LIGHT_CHUNK_DESC&, const char* szLightName, float fScale);
void scale (float fScale);
// returns the index of the bone to which the light source is bound
unsigned getBone() const {return m_nBone;}
// initializes the given DLight structure out of the given bone instance
// this is one-time call that is only required after construction of the DLight to initialize its constant parameters
void initDLight (CDLight& DLight);
// per-frame update of the DLight structure. Updates the light position and radius
void updateDLight (const Matrix44& matParentBone, float fRadiusMultiplier, CDLight& DLight);
// returns true if this light source is local (affects only the character)
bool isLocal()const;
bool isHeatSource()const;
bool isLightSource()const;
// returns the priority to sort
int getPriority()const;
bool operator < (const CBoneLightBindInfo& rRight)const
{
return getPriority() < rRight.getPriority();
}
// Serialization to/from memory buffer.
// if the buffer is NULL, and bSave is true, returns the required buffer size
// otherwise, tries to save/load from the buffer, returns the number of bytes written/read
// or 0 if the buffer is too small or some error occured
unsigned Serialize (bool bSave, void* pBuffer, unsigned nSize);
const char* getLightImageCStr()const {return m_strLightImage.c_str();}
protected:
string m_strLightImage; //spot light texture
// the following members are not serialized:
Matrix44 m_matLight; // transform matrix of the light in the bone CS
void constructMatLight();
};
#endif

View File

@@ -0,0 +1,9 @@
//////////////////////////////////////////////////////////////////////////
// Declaration of convertor for CAL files that will compile them into easy-to-parse
// and compact format which will contain the necessary information about the animation files.
#ifndef _CAL_CONVERTOR_HDR_
#define _CAL_CONVERTOR_HDR_
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: cgfconvertor.h
// Version: v1.00
// Created: 5/11/2002 by Timur.
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef __cgfconvertor_h__
#define __cgfconvertor_h__
#pragma once
#include "IRCLog.h"
#include "IConvertor.h"
#include "CryCompiledFile.h"
#include "CryChunkedFile.h"
#include "RenderMeshBuilder.h"
struct ConvertContext;
/** Convertor implementation for CGF files.
*/
class CGFConvertor : public IConvertor
{
public:
// this class will be thrown from the internal method during conversion
// indicating some error that should abort conversion
class Error
{
public:
Error (int nCode);
Error (const char* szFormat, ...);
const char* c_str()const {return m_strReason.c_str();}
protected:
string m_strReason;
};
CGFConvertor ();
~CGFConvertor();
//! Release memory of interface.
void Release() { delete this; };
//! Process file.
virtual bool Process( ConvertContext &cc );
bool ProcessStatic( ConvertContext &cc );
//! Return name of output file that will be produced from this file.
// @param sourceFileName File name plus extension of source file, must not contain path.
virtual bool GetOutputFile( ConvertContext &cc );
//! Return platforms supported by this convertor.
virtual int GetNumPlatforms() const;
//! Get supported platform.
//! @param index Index of platform must be in range 0 < index < GetNumPlatforms().
virtual Platform GetPlatform( int index ) const;
//! Get number of supported extensions.
virtual int GetNumExt() const { return 1+m_pStatCGFCompiler->GetNumExt(); };
//! Get supported extension.
//! @param index Index of extension must be in range 0 < index < GetNumExt().
virtual const char* GetExt( int index ) const { return index?"cgf":m_pStatCGFCompiler->GetExt(index-1); };
// this should retrieve the timestamp of the convertor executable:
// when it was created by the linker, normally. This date/time is used to
// compare with the compiled file date/time and even if the compiled file
// is not older than the source file, it will be recompiled if it's older than the
// convertor
virtual DWORD GetTimestamp() const ;
protected:
// internally used stuff
// writes the data directly into the file
void writeRaw (const void* pData, unsigned nSize);
template <class T>
void write (const T& rData)
{
writeRaw (&rData, sizeof(T));
}
template <class T>
void write (const T* pData, size_t numElements)
{
writeRaw (pData, numElements * sizeof(T));
}
template <class T>
void writeArray (const std::vector<T>& arrData)
{
if (!arrData.empty())
write (&arrData[0], arrData.size());
}
// writes the 0-terminated string, returns the number of bytes (including the terminating 0) written
size_t writeString (const char* szString)
{
size_t n = strlen(szString)+1;
writeRaw (szString, n);
return n;
}
// writes the 0-terminated string, returns the number of bytes (including the terminating 0) written
size_t writeString (const string& strString)
{
size_t n = strString.length() + 1;
writeRaw (strString.c_str(), n);
return n;
}
//void LogV (const char* szFormat, va_list args);
//void Log (const char* szFormat, ...);
void WriteGeometryInfo (unsigned nLOD);
void WriteHeader();
void WriteBoneInfo();
void WriteMaterials( const char *inszSrcFileName );
void writeIntFaces(unsigned nLOD);
void WriteVertices (CryChunkedFile* pSource);
void WriteNormals (CryChunkedFile* pSource);
void writeShadowConnectivity (unsigned nLOD);
void writeVertexSkin (unsigned nLOD);
void writeNormalSkin (unsigned nLOD);
void writeTangSkin (unsigned nLOD);
void validateTangents (const TangData* pTangents, unsigned numTangents);
void WriteBoneGeometry (unsigned nLOD);
void writeBGBone(unsigned nLOD, unsigned nBone, CryChunkedFile::MeshDesc* pMesh);
void WriteMorphTargetSet ();
void writeMorphTargets (unsigned nLOD);
void WriteLights();
bool isAnimatedFastCheck( const char *filename );
string GetCalFilePath ();
string GetSourceDir();
// loads the given animation info into the given structure
// returns false upon failure
bool LoadAnimInfo (const string& strFilePath, CCFAnimInfo &animInfo);
protected:
// loads the m_arrLODs
bool LoadLODs();
IConvertor* getStatCGFCompiler();
// builds the m_arrRenderMeshes array with the CRenderMeshBuilder
// structures containing all the additional info required for leaf buffer creation
void BuildRenderMeshes();
// Updates physics if needed (if there is LOD1, which contains physical data for LOD1 physics
void UpdateDeadBodyPhysics();
// remaps, if necessary, the bones from the LOD source to the Master source
// and changes the links in the LOD source so that the boneid's there point to the indices (not IDs!)
// of the bones in the Master Source, not LOD Source
// This assumes that the bone information from the LOD source won't be used at all
// throws an error if there's some unrecognized bone in the LOD source
void RemapBones (unsigned nLOD);
// remaps the bone indices, throws error if some indices cannot be remapped for some reason
// (e.g. mapping contains -1, i.e. no mapping)
void RemapBoneIndices (CryChunkedFile* pLOD, const std::vector<int>& arrMap);
// releases all the resources
void clear();
// !!!OBSOLETE!!!
// calculates the number of materials used by the previous LODs (< than given)
// this is to offset the mtl numbers of the nLOD to use the range of materials
// belonging to that LOD
// !!!OBSOLETE!!!
unsigned getLODMtlOffset (unsigned nLOD);
// Reads the CAL file or the list of caf files in the same directory with the corresponding name,
// and writes the animation list chunk with the animation names, file paths and compiled directives
void WriteAnimListWithCAL (FILE* fCal);
void WriteAnimListNoCAL ();
void WriteAnimInfo (CCFFileWriter &rSubChunks, const char* szAnimName, const string& strFilePath, unsigned nFlag);
void WriteSceneProps();
void LogWarning (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
m_pContext->pLog->LogV (IMiniLog::eWarning, szFormat, args);
va_end(args);
}
void LogError (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
m_pContext->pLog->LogV (IMiniLog::eError, szFormat, args);
va_end(args);
}
void Log (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
m_pContext->pLog->LogV (IMiniLog::eMessage, szFormat, args);
va_end(args);
}
protected:
ConvertContext* m_pContext;
// the source files: one file for each LOD. At least LOD 0 Must be present for conversion.
CryChunkedFile_AutoArray m_arrLODs;
// the mapping from bone indices in Master (LOD0) to bone indices in LOD>0
typedef std::vector<int> BoneMap;
std::vector<BoneMap> m_arrLODBoneMaps;
typedef std::map<CString,int> PhysMatIDMap;
PhysMatIDMap m_physMtlToFaceIdMap; // Map for every LOD (except first).
// the render meshes for each LOD
std::vector<CRenderMeshBuilder> m_arrRenderMeshes;
CCFFileWriter m_Writer;
// the target file
FILE* m_fTarget;
IConvertor* m_pStatCGFCompiler;
};
#endif // __cgfconvertor_h__

View File

@@ -0,0 +1,542 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// History:
// Created by Sergiy Migdalskiy
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "StringUtils.h"
#include "CgfUtils.h"
using namespace CryStringUtils;
//////////////////////////////////////////////////////////////////////////
// given the chunk, parses it and returns the array of pointers to the strings with the bone names
// in the supplied array. Returns true when successful
// PARAMETERS:
// chunkHeader - the header of the chunk in the chunk table
// pChunk - the raw chunk data in the file
// nChunkSize - the size of the raw data piece in the file
// arrNames - the array that will be resized according to the number of entries, and each element of this array
// will be the pointer to the corresponding 0-terminated name string.
bool LoadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize, std::vector<const char*>& arrNames)
{
switch (chunkHeader.ChunkVersion)
{
case BONENAMELIST_CHUNK_DESC_0744::VERSION:
{
// the old chunk version - fixed-size name list
const BONENAMELIST_CHUNK_DESC_0744* pNameChunk = (const BONENAMELIST_CHUNK_DESC_0744*)(pChunk);
int nGeomBones = pNameChunk->nEntities;
// just fill in the pointers to the fixed-size string buffers
const NAME_ENTITY* pGeomBoneNameTable = (const NAME_ENTITY*)(pNameChunk+1);
if(nGeomBones < 0 || nGeomBones > 0x800)
return false;
arrNames.resize(nGeomBones);
for (int i = 0; i < nGeomBones; ++i)
arrNames[i] = pGeomBoneNameTable[i].name;
}
break;
case BONENAMELIST_CHUNK_DESC_0745::VERSION:
{
// the new memory-economizing chunk with variable-length packed strings following tightly each other
const BONENAMELIST_CHUNK_DESC_0745* pNameChunk = (const BONENAMELIST_CHUNK_DESC_0745*)(pChunk);
int nGeomBones = pNameChunk->numEntities;
// we know how many strings there are there; the whole bunch of strings may
// be followed by pad zeros, to enable alignment
arrNames.resize(nGeomBones, "");
// scan through all the strings, each string following immediately the 0 terminator of the previous one
const char* pNameListEnd = ((const char*)pNameChunk) + nChunkSize;
const char* pName = (const char*)(pNameChunk+1);
int nName = 0;
while (*pName && pName < pNameListEnd && nName < nGeomBones)
{
arrNames[nName] = pName;
pName += strnlen(pName, pNameListEnd) + 1;
++nName;
}
if (nName < nGeomBones)
{
// the chunk is truncated
#ifdef _CRY_ANIMATION_BASE_HEADER_
// if this is in the engine, log the error
g_GetLog()->LogWarning ("\003inconsistent bone name list chunk: only %d out of %d bone names have been read.", nName, nGeomBones);
#endif
return false;
}
}
break;
}
return true;
}
// Attempts to load the material from the given material chunk to the "me" structure
MatChunkLoadErrorEnum LoadMatEntity (const CHUNK_HEADER& chunkHeader, const void* pChunkData, unsigned nChunkSize, MAT_ENTITY& me)
{
memset(&me, 0, sizeof(MAT_ENTITY));
switch (chunkHeader.ChunkVersion)
{
case MTL_CHUNK_DESC_0746::VERSION:
{
const MTL_CHUNK_DESC_0746* pMatChunk = (const MTL_CHUNK_DESC_0746*)pChunkData;
me.m_New = 2;
strcpy(me.name, pMatChunk->name);
switch (pMatChunk->MtlType)
{
case MTL_STANDARD:
{
me.IsStdMat = true;
me.col_d = pMatChunk->col_d;
me.col_a = pMatChunk->col_a;
//me.col_a.g=0;
//me.col_a.b=0;
me.col_s = pMatChunk->col_s;
me.specLevel = pMatChunk->specLevel;
me.specShininess = pMatChunk->specShininess*100;
me.opacity = pMatChunk->opacity;
me.selfIllum = pMatChunk->selfIllum;
me.flags = pMatChunk->flags;
me.Dyn_Bounce = pMatChunk->Dyn_Bounce;
me.Dyn_StaticFriction = pMatChunk->Dyn_StaticFriction;
me.Dyn_SlidingFriction = pMatChunk->Dyn_SlidingFriction;
/* //Timur[10/24/2001]
strcpy(me.map_a, pMatChunk->tex_a.name);
strcpy(me.map_d, pMatChunk->tex_d.name);
strcpy(me.map_o, pMatChunk->tex_o.name);
strcpy(me.map_b, pMatChunk->tex_b.name);
strcpy(me.map_s, pMatChunk->tex_s.name);
strcpy(me.map_g, pMatChunk->tex_g.name);
strcpy(me.map_c, pMatChunk->tex_c.name);
strcpy(me.map_e, pMatChunk->tex_rl.name);
strcpy(me.map_rr, pMatChunk->tex_rr.name);
strcpy(me.map_det, pMatChunk->tex_det.name);
*/
me.map_a = pMatChunk->tex_a;
me.map_d = pMatChunk->tex_d;
me.map_o = pMatChunk->tex_o;
me.map_b = pMatChunk->tex_b;
me.map_s = pMatChunk->tex_s;
me.map_g = pMatChunk->tex_g;
me.map_detail = pMatChunk->tex_fl;
me.map_e = pMatChunk->tex_rl;
me.map_subsurf = pMatChunk->tex_subsurf;
me.map_displ = pMatChunk->tex_det;
me.nChildren = pMatChunk->nChildren;
me.alpharef = pMatChunk->alphaTest;
}
return MCLE_Success;
default:
return MCLE_IgnoredType;
}
}
break;
case MTL_CHUNK_DESC_0745::VERSION:
{
const MTL_CHUNK_DESC_0745* pMatChunk = (const MTL_CHUNK_DESC_0745*)pChunkData;
me.m_New = 1;
strcpy(me.name, pMatChunk->name);
switch (pMatChunk->MtlType)
{
case MTL_STANDARD:
me.IsStdMat = true;
me.col_d = pMatChunk->col_d;
me.col_a = pMatChunk->col_a;
//me.col_a.g=0;
//me.col_a.b=0;
me.col_s = pMatChunk->col_s;
me.specLevel = pMatChunk->specLevel;
me.specShininess = pMatChunk->specShininess*100;
me.opacity = pMatChunk->opacity;
me.selfIllum = pMatChunk->selfIllum;
me.flags = pMatChunk->flags;
me.Dyn_Bounce = pMatChunk->Dyn_Bounce;
me.Dyn_StaticFriction = pMatChunk->Dyn_StaticFriction;
me.Dyn_SlidingFriction = pMatChunk->Dyn_SlidingFriction;
me.map_a = pMatChunk->tex_a;
me.map_d = pMatChunk->tex_d;
me.map_o = pMatChunk->tex_o;
me.map_b = pMatChunk->tex_b;
me.map_s = pMatChunk->tex_s;
me.map_g = pMatChunk->tex_g;
me.map_detail = pMatChunk->tex_c;
me.map_e = pMatChunk->tex_rl;
me.map_subsurf = pMatChunk->tex_subsurf;
me.map_displ = pMatChunk->tex_det;
me.nChildren = pMatChunk->nChildren;
return MCLE_Success;
default:
return MCLE_IgnoredType;
}
}
break;
default:
return MCLE_UnknownVersion;
}
}
// if the given string is null, assigns the "" to the pointer
void chkNullString (const char*&pszString)
{
if (!pszString)
pszString = "";
}
static void rtrim (char* szString)
{
for (char* p = szString + strlen (szString) - 1; p >= szString && isspace(*p); --p)
*p = '\0';
}
static void ltrim (const char*& szString)
{
while (*szString && isspace(*szString))
++szString;
}
CMatEntityNameTokenizer::CMatEntityNameTokenizer ():
m_szMtlName (NULL),
szName (""),
szTemplate (""),
szPhysMtl (""),
nSortValue (0),
bInvert (false)
{
}
void CMatEntityNameTokenizer::tokenize (const char* szMtlFullName)
{
if (m_szMtlName)
{
free (m_szMtlName);
m_szMtlName = NULL;
}
if (!szMtlFullName)
return;
int nLen = (int)strlen(szMtlFullName);
m_szMtlName = (char*)malloc (nLen+1);
memcpy (m_szMtlName, szMtlFullName, nLen + 1);
szName = NULL;
szTemplate = NULL;
szPhysMtl = NULL;
nSortValue = 0;
bInvert = false;
// the state machine will parse the whole string
enum StateEnum
{
kUnknown,
kName,
kTemplate,
kPhysMtl,
kIndex
};
StateEnum nState = kName; // by default, the string begins with name
this->szName = m_szMtlName;
for (char* p = m_szMtlName; *p; ++p)
{
switch (*p)
{
case '(': // template name begins
this->szTemplate = p+1;
nState = kTemplate;
*p = '\0';
break;
case '[': // render priority number begins
*p = '\0';
nState = kIndex;
break;
case '/':
this->szPhysMtl = p+1;
*p = '\0';
nState = kPhysMtl;
break;
default:
switch (nState)
{
case kName:
switch (*p)
{
/*case ' ': // there are no spaces in the name
*p = '\0';
break;*/
case ')':
case ']':
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogError ("Invalid material name (unexpected closing bracket) \"%s\"", szMtlFullName);
#endif
break;
};
break;
case kTemplate:
switch (*p)
{
case ')':
nState = kUnknown;
*p = '\0';
break;
}
break;
case kIndex:
{
switch (*p)
{
case ']':
nState = kUnknown;
break;
case ' ':
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
this->nSortValue *= 10;
this->nSortValue += *p - '0';
break;
default:
nState = kUnknown;
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogError ("Invalid material name (unexpected symbol in index field) \"%s\"", szMtlFullName);
#endif
break;
}
}
break;
};
break;
}
}
// take into account hte old form $s_... of setting the template name
// if there was no () template, then use this one (after _)
if ((!this->szTemplate || !this->szTemplate[0]) &&
this->szName[0] == '$' && tolower(this->szName[1]) == 's' && this->szName[2] == '_')
{
this->szTemplate = this->szName + 3;
}
// make sure all the strings get their pointers - if there's no name, then it will be an empty name
chkNullString(this->szName);
chkNullString(this->szTemplate);
chkNullString(this->szPhysMtl);
// a special case (one more) - template name preceded by # means (or meant) the inverted template
if (this->szTemplate[0] == '#')
{
this->szTemplate++;
this->bInvert = true;
}
// trim unneeded left and right leading spaces
rtrim ((char*)this->szName);
rtrim ((char*)this->szTemplate);
rtrim ((char*)this->szPhysMtl);
ltrim (this->szName);
ltrim (this->szTemplate);
ltrim (this->szPhysMtl);
}
CMatEntityNameTokenizer::~CMatEntityNameTokenizer ()
{
if (m_szMtlName)
free (m_szMtlName);
}
// operator that sorts the materials for rendering
bool CMatEntityNameTokenizer::operator < (const CMatEntityNameTokenizer& right)const
{
if (this->nSortValue < right.nSortValue)
return true;
if (this->nSortValue > right.nSortValue)
return false;
int nComp = stricmp (this->szTemplate, right.szTemplate);
if (nComp < 0)
return true;
if (nComp > 0)
return false;
nComp = stricmp (this->szName, right.szName);
if (nComp < 0)
return true;
if (nComp > 0)
return false;
return false; // they're equal
}
// given the in-permutation, constructs the inverse out-permutation, so that:
// pOut[pIn[i]] == i
// pIn[pOut[i]] == i
void ConstructReversePermutation (const unsigned* pIn, unsigned* pOut, unsigned num)
{
unsigned i;
#ifdef _DEBUG
// we'll check the correctness of permutation by checking if there are duplicate entries in pIn
// if there are no duplicate entries, according to Dirichle principle, there are no missed entries in pOut
for (i = 0; i < num; ++i)
pOut[i] = (unsigned)-1;
#endif
for (i = 0; i < num; ++i)
{
assert (pIn[i] < num);
assert ((int)pOut[pIn[i]] ==(unsigned)-1);
pOut[pIn[i]] = i;
}
}
// Remaps the materials according to the given permutation
// the permutation is perm[new index] == old index
void RemapMatEntities (MAT_ENTITY* pMtls, unsigned numMtls, unsigned* pPerm)
{
MAT_ENTITY* pOldMtls = new MAT_ENTITY[numMtls];
memcpy (pOldMtls, pMtls, sizeof(MAT_ENTITY)*numMtls);
for (unsigned nNewMtl = 0; nNewMtl < numMtls; ++nNewMtl)
memcpy (pMtls + nNewMtl, pOldMtls + pPerm[nNewMtl], sizeof(MAT_ENTITY));
delete[]pOldMtls;
}
// copies the matrix from SBoneInitPosMatrix to Matrix
void copyMatrix (Matrix44& matDst, const SBoneInitPosMatrix& matSrc)
{
for (unsigned i = 0; i < 4; ++i)
{
matDst(i,0) = matSrc[i][0];
matDst(i,1) = matSrc[i][1];
matDst(i,2) = matSrc[i][2];
}
matDst(0,3) = 0;
matDst(1,3) = 0;
matDst(2,3) = 0;
matDst(3,3) = 1;
}
const char* getMtlType (unsigned nMtlType)
{
switch (nMtlType)
{
case MTL_UNKNOWN:
return "UNKNOWN";
case MTL_STANDARD:
return "STANDARD";
case MTL_MULTI:
return "MULTI";
case MTL_2SIDED:
return "2SIDED";
default:
return "#Unknown#";
}
}
const char* getTexType (unsigned char nTexType)
{
switch (nTexType)
{
case TEXMAP_AUTOCUBIC:
return "TEXMAP_AUTOCUBIC";
case TEXMAP_CUBIC:
return "TEXMAP_CUBIC";
case TEXMAP_ENVIRONMENT:
return "TEXMAP_ENVIRONMENT";
case TEXMAP_SCREENENVIRONMENT:
return "!TEXMAP_SCREENENVIRONMENT(unsupported)!";
default:
return "#Unknown#";
}
}
string getLightType (LightTypes nType)
{
switch (nType)
{
case LT_OMNI:
return "Omni";
case LT_SPOT:
return "Spot";
case LT_DIRECT:
return "Direct";
case LT_AMBIENT:
return "Ambient";
default:
{
char szBuffer[32];
printf (szBuffer, "Unknown(%d)", nType);
return szBuffer;
}
}
}
string getMtlFlags (int nFlags)
{
string strResult;
if (nFlags & MTLFLAG_WIRE)
strResult += "MTLFLAG_WIRE|";
if (nFlags & MTLFLAG_2SIDED)
strResult += "MTLFLAG_2SIDED|";
if (nFlags & MTLFLAG_FACEMAP)
strResult += "MTLFLAG_FACEMAP|";
if (nFlags & MTLFLAG_FACETED)
strResult += "MTLFLAG_FACETED|";
if (nFlags & MTLFLAG_ADDITIVE)
strResult += "MTLFLAG_ADDITIVE|";
if (nFlags & MTLFLAG_SUBTRACTIVE)
strResult += "MTLFLAG_SUBTRACTIVE|";
if (!strResult.empty ())
strResult.resize (strResult.length ()-1);
return strResult;
}

View File

@@ -0,0 +1,205 @@
#include "StdAfx.h"
#include "FileMapping.h"
#include "chunkfilereader.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const char* CChunkFileReader::gLastError = "";
CChunkFileReader::CChunkFileReader():
m_pChunks(NULL)
//,m_arrChunkSize ("CChunkFileReader.ChunkSize")
{
}
CChunkFileReader::~CChunkFileReader()
{
close();
}
bool CChunkFileReader::open (const string& strFileName, unsigned nFlags)
{
return open (strFileName.c_str(), nFlags);
}
//////////////////////////////////////////////////////////////////////////
// attaches the file mapping object to this file reader and checks
// whether the file is a valid chunked file
bool CChunkFileReader::open(CFileMapping* pFile)
{
close();
m_pFile = pFile;
bool bSuccess = false;
if ( (m_pFile != (CFileMapping*)NULL) && (m_pFile->getData() != NULL) )
{
if (m_pFile->getSize() >= sizeof(FileHeader))
{// the file must contain the header
const FileHeader& fileHeader = getFileHeader();
if (m_pFile->getSize() >= fileHeader.ChunkTableOffset + sizeof(int)
&& (int)fileHeader.ChunkTableOffset > (int)sizeof(fileHeader)
)
{// there must be room for the chunk table header
unsigned numChunks = *static_cast<const unsigned*>(m_pFile->getData(fileHeader.ChunkTableOffset));
unsigned nChunk;
if (m_pFile->getSize() >= fileHeader.ChunkTableOffset + sizeof(int) + numChunks*sizeof(ChunkHeader)
&& numChunks <= (pFile->getSize () - fileHeader.ChunkTableOffset - sizeof(int)) / sizeof(ChunkHeader))
{
// the file must contain the full chunk table
m_pChunks = (const ChunkHeader*)m_pFile->getData(fileHeader.ChunkTableOffset + sizeof(int));
bool bInvalidChunkOffsetsFound = false; // sets to true if there are chunks pointing outside the raw data section of the file
// step through all the chunks to fetch file offsets
std::set<int> setOffsets;
for (nChunk = 0; nChunk < numChunks; ++nChunk)
{
int nOffset = m_pChunks[nChunk].FileOffset;
if (nOffset < sizeof(FileHeader) || nOffset >= fileHeader.ChunkTableOffset)
{
gLastError = "CryFile is corrupted: chunk table is corrupted (invalid chunk offsets found)";
bInvalidChunkOffsetsFound = true;
}
setOffsets.insert(nOffset);
}
m_arrChunkSize.clear();
m_arrChunkSize.resize (numChunks);
for (nChunk = 0; nChunk < numChunks; ++nChunk)
{
int nOffset = m_pChunks[nChunk].FileOffset;
int nSize = 0; // the size for invalid chunks (with invalid offsets)
if (nOffset >= sizeof(FileHeader) && nOffset < fileHeader.ChunkTableOffset)
{
// find the next offset
std::set<int>::const_iterator it = setOffsets.find (nOffset);
assert (it != setOffsets.end());
assert (*it == nOffset);
++it;
nSize = (it==setOffsets.end()?fileHeader.ChunkTableOffset:*it) - nOffset;
}
assert (nSize >= 0);
m_arrChunkSize[nChunk] = nSize;
}
bSuccess = true;
// don't let the files with invalid chunks..
//bSuccess = !bInvalidChunkOffsetsFound;
}
else
gLastError = "CryFile is corrupted: chunk table is corrupted or truncated (file too small)";
}
else
gLastError = "CryFile is corrupted: chunk table is trucated (file header is probably corrupted)";
}
else
gLastError = "CryFile is corrupted: header is truncated (file too small)";
}
else
gLastError = "Invalid file mapping passed to CChunkFileReader::open";
if (!bSuccess)
close();
return bSuccess;
}
//////////////////////////////////////////////////////////////////////////
// attaches a new file mapping object to this file reader and checks
// whether the file is a valid chunked file
bool CChunkFileReader::open(const char* szFileName, unsigned nFlags)
{
CFileMapping_AutoPtr pFileMapping = new CFileMapping(szFileName, nFlags);
if (!pFileMapping->getData())
{
gLastError = "Cannot open file";
return false;
}
return open (pFileMapping);
}
void CChunkFileReader::close()
{
m_arrChunkSize.clear();
m_pFile = NULL;
m_pChunks = NULL;
}
// returns the raw data of the file from the given offset
const void* CChunkFileReader::getRawData(unsigned nOffset)const
{
if ((m_pFile != (CFileMapping*)NULL) && m_pFile->getData())
return ((char*)m_pFile->getData())+nOffset;
else
return NULL;
}
// retrieves the raw chunk header, as it appears in the file
const CChunkFileReader::ChunkHeader& CChunkFileReader::getChunkHeader(int nChunkIdx)const
{
return m_pChunks[nChunkIdx];
}
// returns the raw data of the i-th chunk
const void* CChunkFileReader::getChunkData(int nChunkIdx)const
{
if (nChunkIdx>= 0 && nChunkIdx < numChunks())
{
int nOffset = m_pChunks[nChunkIdx].FileOffset;
if (nOffset < sizeof(FileHeader) || nOffset >= getFileHeader().ChunkTableOffset)
return 0;
else
return m_pFile->getData(nOffset);
}
else
return 0;
}
// number of chunks
int CChunkFileReader::numChunks()const
{
return (int)m_arrChunkSize.size();
}
// number of chunks of the specified type
int CChunkFileReader::numChunksOfType (ChunkTypes nChunkType)const
{
int nResult = 0;
for (int i = 0; i < numChunks(); ++i)
{
if (m_pChunks[i].ChunkType == nChunkType)
++nResult;
}
return nResult;
}
// returns the file headers
const CChunkFileReader::FileHeader& CChunkFileReader::getFileHeader() const
{
return m_pFile?*((const FileHeader*)(m_pFile->getData())):*(const FileHeader*)NULL;
}
bool CChunkFileReader::isValid () const
{
return m_pFile != (CFileMapping*)NULL;
}
//////////////////////////////////////////////////////////////////////////
// calculates the chunk size, based on the very next chunk with greater offset
// or the end of the raw data portion of the file
int CChunkFileReader::getChunkSize(int nChunkIdx) const
{
assert (nChunkIdx >= 0 && nChunkIdx < numChunks());
return m_arrChunkSize[nChunkIdx];
}

View File

@@ -0,0 +1,186 @@
//////////////////////////////////////////////////////////////////////
//
// CryEngine Source code
//
// File:ChunkFileReader.h
// Declaration of class CChunkFileReader
//
// History:
// 06/26/2002 :Created by Sergiy Migdalskiy <sergiy@crytek.de>
//
//////////////////////////////////////////////////////////////////////
#ifndef _CHUNK_FILE_READER_HDR_
#define _CHUNK_FILE_READER_HDR_
#include "CryHeaders.h"
#include "smartptr.h"
class CFileMapping;
TYPEDEF_AUTOPTR(CFileMapping);
////////////////////////////////////////////////////////////////////////
// Chunk file reader.
// Accesses a chunked file structure through file mapping object.
// Opens a chunk file and checks for its validity.
// If it's invalid, closes it as if there was no open operation.
// Error handling is performed through the return value of open: it must
// be true for successfully open files
////////////////////////////////////////////////////////////////////////
class CChunkFileReader:
public _reference_target_t
{
public:
typedef FILE_HEADER FileHeader;
typedef CHUNK_HEADER ChunkHeader;
CChunkFileReader();
~CChunkFileReader();
// attaches the file mapping object to this file reader and checks
// whether the file is a valid chunked file
bool open(CFileMapping* pFile);
// attaches a new file mapping object to this file reader and checks
// whether the file is a valid chunked file
bool open(const char* szFileName, unsigned nFlags = 0);
bool open (const string& strFileName, unsigned nFlags = 0);
// closes the file mapping object and thus detaches the file from this reader
void close();
// returns the raw data of the file from the given offset
const void* getRawData(unsigned nOffset)const;
// returns the raw data of the i-th chunk
const void* getChunkData(int nChunkIdx)const;
// retrieves the raw chunk header, as it appears in the file
const ChunkHeader& getChunkHeader(int nChunkIdx)const;
// calculates the chunk size, based on the very next chunk with greater offset
// or the end of the raw data portion of the file
int getChunkSize(int nChunkIdx) const;
// number of chunks
int numChunks()const;
// number of chunks of the specified type
int numChunksOfType (ChunkTypes nChunkType) const;
// returns the file headers
const FileHeader& getFileHeader() const;
bool isValid () const;
const char* getLastError()const {return gLastError;}
protected:
// this variable contains the last error occured in this class
static const char* gLastError;
CFileMapping_AutoPtr m_pFile;
// array of offsets used by the chunks
typedef std::vector<int> ChunkSizeArray;
ChunkSizeArray m_arrChunkSize;
// pointer to the array of chunks in the m_pFile
const ChunkHeader* m_pChunks;
};
TYPEDEF_AUTOPTR(CChunkFileReader);
// this function eats the given number of elements from the raw data pointer pRawData
// and increments the pRawData to point to the end of data just eaten
template <typename T>
void EatRawData (T*pArray, unsigned nSize, const void*&pRawData)
{
memcpy (pArray, pRawData, sizeof(T)*nSize);
pRawData = static_cast<const T*>(pRawData) + nSize;
}
// this function eats the given number of elements from the raw data pointer pRawData
// and increments the pRawData to point to the end of data just eaten
// RETURNS:
// false if failed to read the data
template <typename T>
bool EatRawData (T*pArray, unsigned nSize, const void*&pRawData, unsigned& nDataSize)
{
if (sizeof(T)*nSize <= nDataSize)
{
memcpy (pArray, pRawData, sizeof(T)*nSize);
pRawData = static_cast <const T*> (pRawData) + nSize;
nDataSize -= sizeof(T)*nSize;
return true;
}
else
return false;
}
template <typename T>
bool EatRawData (T*pArray, unsigned nSize, const void*&pRawData, const void* pRawDataEnd)
{
if ((const char*)pRawData + sizeof(T)*nSize <= (const char*)pRawDataEnd)
{
memcpy (pArray, pRawData, sizeof(T)*nSize);
pRawData = static_cast <const T*> (pRawData) + nSize;
return true;
}
else
return false;
}
// this function puts the pointer to the data to the given pointer, and moves
// the raw data pointer further; if fails, nothing happens and false is returned
// PARAMETERS:
// pArray - reference to the (pointer) variable to which the pointer to the actual data will be stored
// nSize - number of elements in the array (depending on this, the raw data pointer will be moved)
// pRawData - reference to the actual raw data pointer, this will be incremented
// nDataSize - reference to the data size counter, this will be decremented upon success
// RETURNS:
// false if failed to read the data
template <typename T>
bool EatRawDataPtr(const T*&pArray, unsigned nSize, const void*&pRawData, unsigned& nDataSize)
{
if (sizeof(T)*nSize <= nDataSize)
{
pArray = (const T*)pRawData;
pRawData = static_cast <const T*> (pRawData) + nSize;
nDataSize -= sizeof(T)*nSize;
return true;
}
else
return false;
}
template <typename T>
bool EatRawDataPtr(const T*&pArray, unsigned nSize, const void*&pRawData, const void* pRawDataEnd)
{
if ((const char*)pRawData + sizeof(T)*nSize <= (const char*)pRawDataEnd)
{
pArray = (const T*)pRawData;
pRawData = static_cast <const T*> (pRawData) + nSize;
return true;
}
else
return false;
}
// ... non-const version, this will hardly be ever needed
/*
template <typename T>
bool EatRawDataPtr(T*&pArray, unsigned nSize, void*&pRawData, unsigned& nDataSize)
{
if (sizeof(T)*nSize <= nDataSize)
{
pArray = (const T*)pRawData;
pRawData = static_cast <const T*> (pRawData) + nSize;
nDataSize -= sizeof(T)*nSize;
return true;
}
else
return false;
}
*/
#endif

View File

@@ -0,0 +1,129 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// History:
// Created by Sergiy Migdalskiy
//
// Notes:
// IController interface declaration
// See the IController comment for more info
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef _CRYTEK_CONTROLLER_HEADER_
#define _CRYTEK_CONTROLLER_HEADER_
#include "StringUtils.h"
#include "CryHeaders.h"
//////////////////////////////////////////////////////////////////////////////////////////
// interface IController
// Describes the position and orientation of an object, changing in time.
// Responsible for loading itself from a binary file, calculations
//////////////////////////////////////////////////////////////////////////////////////////
class IController: public _reference_target_t
{
public:
// each controller has an ID, by which it is identifiable
virtual unsigned GetID () const = 0;
// returns the orientation of the controller at the given time
virtual CryQuat GetOrientation (float t) = 0;
// returns the orientation of the controller at the given time, in logarithmic space
virtual Vec3 GetOrientation2 (float t) = 0;
// returns position of the controller at the given time
virtual Vec3 GetPosition (float t) = 0;
// returns scale of the controller at the given time
virtual Vec3 GetScale (float t) = 0;
// retrieves the position and orientation within one call
// may be optimal in some applications
virtual void GetValue (float t, CryQuat& q, Vec3 &p) = 0;
// retrieves the position and orientation (in the logarithmic space, i.e. instead of quaternion, its logarithm is returned)
// may be optimal for motion interpolation
struct PQLog
{
Vec3 vPos;
Vec3 vRotLog; // logarithm of the rotation
string toString()const {return "{pos:" + CryStringUtils::toString(vPos)+", rot="+CryStringUtils::toString (vRotLog) + "}";}
// returns the rotation quaternion
CryQuat getOrientation()const;
// resets the state to zero
void reset ();
// blends the pqSource[0] and pqSource[1] with weights 1-fBlend and fBlend into pqResult
void blendPQ (const PQLog* pqSource, float fBlend);
// blends the pqFrom and pqTo with weights 1-fBlend and fBlend into pqResult
void blendPQ (const PQLog& pqFrom, const PQLog& pqTo, float fBlend);
// builds the matrix out of the position and orientation stored in this PQLog
void buildMatrix(Matrix44& mat)const;
// a special version of the buildMatrix that adds a rotation to the rotation of this PQLog
void buildMatrixPlusRot(Matrix44& mat, const CryQuat& qRotPlus) const;
// returns the equivalent rotation in logarithmic space (the quaternion of which is negative the original)
Vec3 getComplementaryRotLog() const;
// constructs the position/rotation from the given matrix
void assignFromMatrix (const Matrix44& mat);
PQLog operator * (float f)const
{
PQLog res;
res.vPos = this->vPos * f;
res.vRotLog = this->vRotLog * f;
return res;
}
const PQLog& operator += (const PQLog& pq)
{
this->vPos += pq.vPos;
this->vRotLog += pq.vRotLog;
return *this;
}
};
virtual void GetValue2 (float t, PQLog& pq) = 0;
// returns the start time
virtual float GetTimeStart () = 0;
// returns the end time
virtual float GetTimeEnd() = 0;
virtual size_t sizeofThis () const = 0;
virtual bool IsLooping() const { return false; };
};
TYPEDEF_AUTOPTR(IController);
//typedef IController*IController_AutoPtr;
// adjusts the rotation of these PQs: if necessary, flips them or one of them (effectively NOT changing the whole rotation,but
// changing the rotation angle to Pi-X and flipping the rotation axis simultaneously)
// this is needed for blending between animations represented by quaternions rotated by ~PI in quaternion space
// (and thus ~2*PI in real space)
extern void AdjustLogRotations (Vec3& vRotLog1, Vec3& vRotLog2);
extern void AdjustLogRotationTo (const Vec3& vRotLog1, Vec3& vRotLog2);
extern void AdjustLogRotation(Vec3& vRotLog);
//////////////////////////////////////////////////////////////////////////
// This class is a non-trivial predicate used for sorting an
// ARRAY OF smart POINTERS to IController's. That's why I need a separate
// predicate class for that. Also, to avoid multiplying predicate classes,
// there are a couple of functions that are also used to find a IController
// in a sorted by ID array of IController* pointers, passing only ID to the
// lower_bound function instead of creating and passing a dummy IController*
class AnimCtrlSortPred
{
public:
bool operator() (const IController_AutoPtr& a, const IController_AutoPtr& b) {assert (a!=(IController*)NULL && b != (IController*)NULL); return a->GetID() < b->GetID();}
bool operator() (const IController_AutoPtr& a, unsigned nID) {assert (a != (IController*)NULL);return a->GetID() < nID;}
};
#endif

View File

@@ -0,0 +1,207 @@
#ifndef _CRY_ANIMATION_CRY_ANIMATION_INFO_HDR_
#define _CRY_ANIMATION_CRY_ANIMATION_INFO_HDR_
struct AnimEvent { AnimSinkEventData UserData; float fTime; };
inline bool operator < (const AnimEvent& left, const AnimEvent& right)
{
return left.fTime < right.fTime;
}
// returns true if the given events are equal with the given time tolerance
inline bool isEqual (const AnimEvent& left, const AnimEvent& right, float fTolerance = 1e-3f)
{
return left.UserData == right.UserData
&& fabs(left.fTime - right.fTime) < fTolerance;
}
//! this structure contains info about loaded animations
struct AnimData
{
string strName; // the name of the animation (not the name of the file) - unique per-model
float fStart, fStop; // start and stop time, in seconds
int nGlobalAnimId;
bool bLoop;
AnimData():
fStart(0), fStop(0), bLoop(false),
nGlobalAnimId (-1)
{
}
bool isStatic () const {return fStart == fStop;}
float getLength() const {return fStop - fStart;}
size_t sizeofThis()const
{
return sizeof(*this) + strName.capacity();
}
};
struct MiniRangeEntity {
int start;
int end;
MiniRangeEntity()
{
start = end = 0;
}
void operator = (const RANGE_ENTITY& right)
{
this->start = right.start;
this->end = right.end;
}
};
//////////////////////////////////////////////////////////////////////////
// this is the animation information on the module level (not on the per-model level)
// it doesn't know the name of the animation (which is model-specific), but does know the file name
// Implements some services for bone binding and ref counting
struct GlobalAnimation
{
// Since we know the number of controllers per animation from the beginning and don't
// change it, we could use more econimical TFixedArray here instead of STL or other array.
typedef IController_AutoArray ControllerArray;
// the flags used in the nFlags member
enum
{
// if this is true, then the animation has valid info data (has been loaded at least once)
FLAGS_INFO_LOADED = 1,
// this doesn't allow the auto-unloading to happen
FLAGS_DISABLE_AUTO_UNLOAD = 1 << 1,
// this forces the animation to be loaded immediately
FLAGS_DISABLE_DELAY_LOAD = 1 << 2,
// this disables the error log in LoadAnimation() in case the animation wasn't found on disk
FLAGS_DISABLE_LOAD_ERROR_LOG = 1 << 3,
// if this flag is set, it means that the animation is being loaded right now via asynchronous operation
// and shouldn't be attempted to be played back or loaded
FLAGS_LOAD_PENDING = 1 << 4,
// this is the flag combination that should be applied only to default animations
FLAGS_DEFAULT_ANIMATION = FLAGS_DISABLE_DELAY_LOAD|FLAGS_DISABLE_LOAD_ERROR_LOG,
// combination of all possible flags
FLAGS_ALL_FLAGS = (1<<5) - 1,
// the flags by default
FLAGS_DEFAULT_FLAGS = 0
};
GlobalAnimation ()
{
// some standard values to fill in before the animation will be loaded
nRefCount = 0;
nFlags = FLAGS_DEFAULT_FLAGS;
fSecsPerTick = 0.000208f;
nLastAccessFrameId = g_nFrameID;
nTickCount = nStartCount = nApplyCount = 0;
nTicksPerFrame = 160;
// the defaults that are anyway immediately overridden
fScale = 0.01f;
rangeGlobal.end = 900; // this is in ticks, means 30 frames
}
IController*GetController(unsigned nControllerID)
{
ControllerArray::iterator it = std::lower_bound(arrCtrls.begin(), arrCtrls.end(), nControllerID, AnimCtrlSortPred());
if (it != arrCtrls.end() && (*it)->GetID() == nControllerID)
{
IController *c = *it;
#ifdef _DEBUG
// set this to true in the debugger to obtain the 0th frame p and q
bool bCheck = false;
if (bCheck)
{
CryQuat q; Vec3d p;
c->GetValue(0, q, p);
}
#endif
return c;
}
return NULL;
}
bool IsLoaded()const {return !arrCtrls.empty();}
bool IsInfoLoaded()const {return (nFlags&FLAGS_INFO_LOADED) != 0;}
void OnInfoLoaded() {nFlags |= FLAGS_INFO_LOADED;}
bool IsAutoUnload()const {return (nFlags&FLAGS_DISABLE_AUTO_UNLOAD)== 0;}
void OnTick ()
{
++nTickCount;
nLastAccessFrameId = g_nFrameID;
}
void OnStart()
{
++nStartCount;
nLastAccessFrameId = g_nFrameID;
}
void OnApply()
{
++nApplyCount;
nLastAccessFrameId = g_nFrameID;
}
void AddRef()
{
++nRefCount;
}
void Release()
{
if (!--nRefCount)
{
#ifdef _DEBUG
for (ControllerArray::iterator it = arrCtrls.begin(); it!= arrCtrls.end(); ++it)
assert ((*it)->NumRefs()==1); // only this object references the controllers now
#endif
arrCtrls.clear(); // nobody uses the controllers; clean them up. This makes the animation effectively unloaded
}
}
#ifdef _DEBUG
// returns the maximum reference counter from all controllers. 1 means that nobody but this animation
// structure refers to them
int MaxControllerRefCount()
{
if (arrCtrls.empty())
return 0;
int nMax = arrCtrls[0]->NumRefs();
for (ControllerArray::iterator it = arrCtrls.begin()+1; it!= arrCtrls.end(); ++it)
if((*it)->NumRefs() > nMax)
nMax = (*it)->NumRefs();
return nMax;
}
#endif
size_t sizeofThis ()const;
// controllers comprising the animation; within the animation, they're sorted by ids
ControllerArray arrCtrls;
// timing data, retrieved from the timing_chunk_desc
int nTicksPerFrame;
float fSecsPerTick;
float fScale; // the parameter from the initial load animation, used for reloadin
MiniRangeEntity rangeGlobal;
// the file name of the animation
string strFileName;
// the number of times this animation has been accessed
unsigned nTickCount;
// the number of times this animation has been started
unsigned nStartCount;
// the number of times this animation has been applied to bones
unsigned nApplyCount;
// the last time the animation has been accessed
int nLastAccessFrameId;
// the number of referrers to this global animation record (doesn't matter if the controllers are currently loaded)
int nRefCount;
// the flags (see the enum in the top of the declaration)
unsigned nFlags;
};
#endif

View File

@@ -0,0 +1,186 @@
#include "stdafx.h"
#include "MathUtils.h"
#include "CryBoneDesc.h"
#include "StringUtils.h"
using namespace CryStringUtils;
CryBoneDesc::CryBoneDesc()
{
m_PhysInfo[0].pPhysGeom = m_PhysInfo[1].pPhysGeom = 0;
m_matInvDefGlobal.SetIdentity();; // allows to get difference to def pose matrices
}
CryBoneDesc::~CryBoneDesc()
{
}
//////////////////////////////////////////////////////////////////////////
// loads the bone from a raw chunk data (no header)
// PARAMETERS:
// pEntity - the chunk data to load the bone info from
// RETURNS:
// false if the bone couldn't be loaded
bool CryBoneDesc::LoadRaw (const BONE_ENTITY* pEntity)
{
//read bone info
assert(pEntity->nChildren<200);
// update the lod 0 physics of this bone
UpdatePhysics (*pEntity, 0);
//get bone info
m_nControllerID = pEntity->ControllerID;
return true;
}
//////////////////////////////////////////////////////////////////////////
// updates this bone physics, from the given entity descriptor, and of the given lod
void CryBoneDesc::UpdatePhysics (const BONE_ENTITY& entity, int nLod)
{
assert (nLod >= 0 && nLod < SIZEOF_ARRAY(m_PhysInfo));
CopyPhysInfo(m_PhysInfo[nLod], entity.phys);
int nFlags = 0;
if (entity.prop[0])
{
nFlags = joint_no_gravity|joint_isolated_accelerations;
}
else
{
if (!strnstr(entity.prop,"gravity", sizeof(entity.prop)))
nFlags |= joint_no_gravity;
if (!strnstr(entity.prop,"physical",sizeof(entity.prop)))
nFlags |= joint_isolated_accelerations;
}
m_PhysInfo[nLod].flags |= nFlags;
}
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
//! RETURNS:
// true if the corresponding physical geometry object has been found
//! NOTE:
//! The entries of the map that were used are deleted
bool CryBoneDesc::PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel)
{
phys_geometry*& pPhysGeom = m_PhysInfo[nLodLevel].pPhysGeom;
ChunkIdToPhysGeomMap::iterator it = mapChunkIdToPhysGeom.find ((INT_PTR)pPhysGeom);
if (it != mapChunkIdToPhysGeom.end())
{
// remap the chunk id to the actual pointer to the geometry
pPhysGeom = it->second;
mapChunkIdToPhysGeom.erase (it);
return true;
}
else
{
pPhysGeom = NULL;
return false;
}
}
// sets the name of the bone out of the given buffer of the given max size
void CryBoneDesc::setName (const char* szName)
{
m_strName.assign (szName);
static const char *g_arrLimbNames[4] = { "L UpperArm","R UpperArm","L Thigh","R Thigh" };
m_nLimbId = -1;
for (int j=0; j < SIZEOF_ARRAY(g_arrLimbNames) ;j++)
if (strstr(szName,g_arrLimbNames[j]))
{
m_nLimbId = j;
break;
}
}
// returns the bone name, if available
const char* CryBoneDesc::getNameCStr()const
{
return m_strName.c_str();
}
const string& CryBoneDesc::getName()const
{
return m_strName;
}
// compares two bone descriptions and returns true if they're the same bone
// (the same name and the same position in the hierarchy)
bool CryBoneDesc::isEqual (const CryBoneDesc& desc)const
{
return m_strName == desc.m_strName
&& m_nControllerID == desc.m_nControllerID
&& m_nOffsetParent == desc.m_nOffsetParent
&& m_numChildren == desc.m_numChildren
&& m_nOffsetChildren == desc.m_nOffsetChildren;
}
inline size_t align4 (size_t x)
{
return (x + 3)&~3;
}
// Serializes the description:
// returns the number of required bytes for serialization, if the data pointer is NULL
// returns 0 (if the buffer size is not enough) or the number of bytes written, if the data pointer is given
unsigned CryBoneDesc::Serialize (bool bSave, void *pStream, unsigned nSize)
{
if (bSave)
{
unsigned nSizeRequired = (unsigned)(sizeof(CryBoneDescData_Comp) + align4 (m_strName.length()+1));
if (!pStream)
return nSizeRequired;
if (nSize < nSizeRequired)
return 0;
CopyCryBone(*(CryBoneDescData_Comp*)pStream, *this);
memcpy ((CryBoneDescData_Comp*)pStream+1,m_strName.c_str(),m_strName.length()+1);
return nSizeRequired;
}
else
{
if (!pStream)
return 0;
if (nSize < sizeof(CryBoneDescData_Comp)+1)
return 0;
if (nSize & 3)
return 0; // alignment error
CopyCryBone(*this, *(CryBoneDescData_Comp*)pStream);
// end of the stream
const char* pEnd = (const char*)pStream + nSize;
// the start byte of the bone name
const char* pName = (const char*)((CryBoneDescData_Comp*)pStream+1);
// scan until the end of the name (\0 char) or the stream (pEnd address)
const char *pNameEnd;
for (pNameEnd = pName; pNameEnd < pEnd && *pNameEnd; ++pNameEnd);
m_strName.assign (pName, pNameEnd);
// return aligned size of the chunk including the string 0 terminator, but
// no more than the declared size of the stream
return min(nSize, (unsigned)align4 (pNameEnd+1-(const char*)pStream));
}
}
void CryBoneDesc::setDefaultGlobal(const Matrix44& matDefault)
{
m_matInvDefGlobal=OrthoUniformGetInverted (matDefault);
}
// scales the bone with the given multiplier
void CryBoneDesc::scale (float fScale)
{
m_matInvDefGlobal.SetTranslationOLD(m_matInvDefGlobal.GetTranslationOLD()*fScale);
}

View File

@@ -0,0 +1,170 @@
//////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// File:CryBoneDesc.h
//
// History:
// Created by Sergiy Migdalskiy 01/14/2003
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef _CRY_BONE_DESC_HDR_
#define _CRY_BONE_DESC_HDR_
// this class contains the information that's common to all instances of bones
// in the given model: like bone name, and misc. shared properties
// We use a base structure to separate the bunch of parameters that get serialized
// together
#pragma pack(push,4)
template <class TBonePhysics>
struct TCryBoneDescData
{
unsigned int m_nControllerID; // unic id of bone (generated from bone name in the max)
// [Sergiy] physics info for different lods
// lod 0 is the physics of alive body, lod 1 is the physics of a dead body
TBonePhysics m_PhysInfo[2];
float m_fMass;
Matrix44 m_matInvDefGlobal; // allows to get difference to def pose matrices
int m_nLimbId; // set by model state class
// this bone parent is this[m_nOffsetParent], 0 if the bone is root. Normally this is <= 0
int m_nOffsetParent;
// The whole hierarchy of bones is kept in one big array that belongs to the ModelState
// Each bone that has children has its own range of bone objects in that array,
// and this points to the beginning of that range and defines the number of bones.
unsigned m_numChildren;
// the beginning of the subarray of children is at this[m_nOffsetChildren]
// this is 0 if there are no children
int m_nOffsetChildren;
};
typedef TCryBoneDescData<BONE_PHYSICS> CryBoneDescData;
// compatible structure
typedef TCryBoneDescData<BONE_PHYSICS_COMP> CryBoneDescData_Comp;
#define __copy(x) left.x = right.x
inline void CopyCryBone (CryBoneDescData_Comp& left, const CryBoneDescData& right)
{
__copy(m_nControllerID);
CopyPhysInfo (left.m_PhysInfo[0], right.m_PhysInfo[0]);
CopyPhysInfo (left.m_PhysInfo[1], right.m_PhysInfo[1]);
__copy(m_fMass);
__copy(m_matInvDefGlobal);
__copy(m_nLimbId);
__copy(m_nOffsetParent);
__copy(m_numChildren);
__copy(m_nOffsetChildren);
}
inline void CopyCryBone (CryBoneDescData& left, const CryBoneDescData_Comp& right)
{
__copy(m_nControllerID);
CopyPhysInfo (left.m_PhysInfo[0], right.m_PhysInfo[0]);
CopyPhysInfo (left.m_PhysInfo[1], right.m_PhysInfo[1]);
__copy(m_fMass);
__copy(m_matInvDefGlobal);
__copy(m_nLimbId);
__copy(m_nOffsetParent);
__copy(m_numChildren);
__copy(m_nOffsetChildren);
}
#undef __copy
#pragma pack(pop)
class CryBoneDesc: public CryBoneDescData
{
public:
CryBoneDesc ();
~CryBoneDesc ();
// returns the bone name, if available
const char* getNameCStr()const;
const string& getName()const;
unsigned getControllerId()const {return m_nControllerID;}
// sets the name of the bone out of the given buffer of the given max size
void setName (const char* szName);
unsigned numChildren ()const {return m_numChildren;}
bool hasParent() const {return m_nOffsetParent != 0;}
int getParentIndexOffset()const {return m_nOffsetParent;}
int getFirstChildIndexOffset() const {return m_nOffsetChildren;}
const Matrix44& getInvDefGlobal() const {return m_matInvDefGlobal;}
void setDefaultGlobal(const Matrix44& mxDefault);
int getLimbId () const {return m_nLimbId;}
BONE_PHYSICS& getPhysInfo (int nLod) {return m_PhysInfo[nLod];}
// updates this bone physics, from the given entity descriptor, and of the given lod
void UpdatePhysics (const BONE_ENTITY& entity, int nLod);
void setPhysics (int nLod, const BONE_PHYSICS& BonePhysics)
{
assert (nLod >= 0 && nLod < sizeof(m_PhysInfo)/sizeof(m_PhysInfo[0]));
m_PhysInfo[nLod] = BonePhysics;
}
// the physics for the given LOD is not available
void resetPhysics (int nLod)
{
assert (nLod >= 0 && nLod < sizeof(m_PhysInfo)/sizeof(m_PhysInfo[0]));
memset (&m_PhysInfo[nLod], 0, sizeof(m_PhysInfo[nLod]));
}
const BONE_PHYSICS& getPhysics (int nLod)const
{
assert (nLod >= 0 && nLod < sizeof(m_PhysInfo)/sizeof(m_PhysInfo[0]));
return m_PhysInfo[nLod];
}
// Returns the id of the bone mesh chunk from which the bone physical geometry
// should be taken. The caller should take this id, find the corresponding chunk in the CCG/CGF file
// and construct physical geometry using IGeomManager::CreateMesh.
// Then, register it with RegisterGeometry(). It will return phys_geometry* that the caller
// should put into the corresponding LOD m_PhysInfo.pPhysGeom
// CryBoneInfo::PostInitPhysGeom uses this same id to find the physical geometry in the map
INT_PTR getPhysGeomId (unsigned nLOD) {return (INT_PTR)m_PhysInfo[nLOD].pPhysGeom;}
// compares two bone descriptions and returns true if they're the same bone
// (the same name and the same position in the hierarchy)
bool isEqual(const CryBoneDesc& desc)const;
// Serializes the description:
// returns the number of required bytes for serialization, if the data pointer is NULL
// returns 0 (if the buffer size is not enough) or the number of bytes written, if the data pointer is given
unsigned Serialize (bool bSave, void *pStream, unsigned nSize);
// scales the bone with the given multiplier
void scale (float fScale);
protected:
// loads the bone from a raw chunk data (no header)
// returns false if the bone couldn't be loaded
bool LoadRaw (const BONE_ENTITY* pEntity);
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
//! NOTE:
//! The entries of the map that were used are deleted
typedef std::map<INT_PTR, struct phys_geometry*> ChunkIdToPhysGeomMap;
bool PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel);
friend class CryBoneHierarchyLoader;
protected:
string m_strName;
};
#endif

View File

@@ -0,0 +1,366 @@
;****************************************************************************
;*
;* This is the 64bit SSE-version of CrySkinFull.cpp.
;* Rewritten by Ivo Herzeg
;*
;* Entry parameters:
;*
;* rcx = pAux
;* rdx = pVertex
;* r8 = pDest
;* r9 = pBone
;* [rSP+60] = pvMin ; After the call but before the push rbp
;* [rSP+68] = pBoneEnd
;*
;****************************************************************************
CRY_SKIN_AUX_INT_SIZE equ 2 ; This must match the define in CrySkinTypes.h
pvMin = 060h ; After the push rbp
pBoneEnd = 068h ; "
X00 = 000h
X10 = 004h
X20 = 008h
Y01 = 010h
Y11 = 014h
Y21 = 018h
Z02 = 020h
Z12 = 024h
Z22 = 028h
TransX = 030h ; After the push rbp
TransY = 034h ; After the push rbp
TransZ = 038h ; After the push rbp
_data SEGMENT
align 16
var6 qword 0,0
var7 qword 0,0
var8 qword 0,0
var9 qword 0,0
var10 qword 0,0
var11 qword 0,0
var12 qword 0,0
var13 qword 0,0
var14 qword 0,0
var15 qword 0,0
_text SEGMENT
PUBLIC Amd64Skinner
Amd64Skinner PROC FRAME
push rBP
push rSI
push rDI
push rAX
push rBX
push rCX
push rDX
movdqa var6,xmm6
movdqa var7,xmm7
movdqa var8,xmm8
movdqa var9,xmm9
movdqa var10,xmm10
movdqa var11,xmm11
movdqa var12,xmm12
movdqa var13,xmm13
movdqa var14,xmm14
movdqa var15,xmm15
; For debug, I will copy the parameters into the same registers which Crytek used in the inline assembler.
mov rSI, rdx ;parameter 1
mov rDX, rcx ;parameter 2
mov rDI, r8 ;parameter 3
; mov qqqqq, r9 ;parameter 4 //pointer to the matrix stack
startLoop:
cmp r9, pBoneEnd[rSP]
jz endLoop
; load the current matrix; we don't need the move component
movss xmm9, [r9+X00]
movss xmm10, [r9+Y01]
movss xmm11, [r9+X10]
movss xmm12, [r9+Y11]
movss xmm13, [r9+X20]
movss xmm14, [r9+Y21]
; load the counter for the number of non-flipped tangets for this bone
xor rCX,rCX
mov CX, word ptr [rdx] ; Was mov CX, word ptr [EDX]
add rDX, 2 ; add EDX, 2
test eCX, eCX
jz endLoopRigid
startLoopRigid:
; calculate the destination pointer
mov rax, [rSI+0Ch]
and rax, 0FFFFFFh
add rax, rax
prefetch [rSI+140h]
movss xmm0, [rSI+00h] ;x
movss xmm3, xmm0
movss xmm1, [rSI+04h] ;y
movss xmm4, xmm1
movss xmm2, [rSI+08h] ;z
movss xmm5, xmm2
prefetchw [rDI+rax*8+40h]
movss xmm6, xmm0
movss xmm7, xmm1
movss xmm8, xmm2
mulss xmm0, xmm13 ;x*M20
mulss xmm1, xmm14 ;y*M21
mulss xmm2, [r9+Z22] ;z*M22
addss xmm0, [r9+TransZ]
addss xmm1, xmm2
addss xmm0, xmm1
mulss xmm3, xmm9 ;x*M00
mulss xmm4, xmm10 ;y*M01
mulss xmm5, [r9+Z02] ;z*M02
addss xmm3, [r9+TransX]
addss xmm4, xmm5
addss xmm3, xmm4
mulss xmm6, xmm11 ;x*M10
mulss xmm7, xmm12 ;y*M11
mulss xmm8, [r9+Z12] ;z*M12
addss xmm6, [r9+TransY]
addss xmm7, xmm8
addss xmm6, xmm7
movss [rDI+rAX*8+08h], xmm0
movss [rDI+rAX*8+00h], xmm3
movss [rDI+rAX*8+04h], xmm6
add rSI, 010h ; rdi+rax*8 (EDI+EAX*8) points to the destination vector now
dec eCX
jnz startLoopRigid
endLoopRigid:
;/////////////////////////////////////////////////////////
;// Smooth-1 loop
;/////////////////////////////////////////////////////////
movss xmm9, [r9+X00]
movss xmm10, [r9+Y01]
movss xmm11, [r9+X10]
movss xmm12, [r9+Y11]
movss xmm13, [r9+X20]
movss xmm14, [r9+Y21]
; load the counter for the number of smooth vertices met for the first time
xor ECX,ECX
mov CX, word ptr [rdx]
add rDX, 2
test ECX, ECX
jz endLoopSmooth1
startLoopSmooth1:
; calculate the destination pointer
xor EAX,EAX
mov AX, word ptr [rdx]
add rDX, 2
shl rAX,1
prefetch [rSI+140h]
movss xmm0, [rSI+00h] ;x
movss xmm3, xmm0
movss xmm1, [rSI+04h] ;y
movss xmm4, xmm1
movss xmm2, [rSI+08h] ;z
movss xmm5, xmm2
movss xmm15, [rSI+0ch] ;w
prefetchw [rDI+rax*8+40h]
movss xmm6, xmm0
movss xmm7, xmm1
movss xmm8, xmm2
mulss xmm0, xmm13 ;x*M20
mulss xmm1, xmm14 ;y*M21
mulss xmm2, [r9+Z22] ;z*M22
addss xmm0, [r9+TransZ]
addss xmm1, xmm2
addss xmm0, xmm1
mulss xmm3, xmm9 ;x*M00
mulss xmm4, xmm10 ;y*M01
mulss xmm5, [r9+Z02] ;z*M02
addss xmm3, [r9+TransX]
addss xmm4, xmm5
addss xmm3, xmm4
mulss xmm6, xmm11 ;x*M10
mulss xmm7, xmm12 ;y*M11
mulss xmm8, [r9+Z12] ;z*M12
addss xmm6, [r9+TransY]
addss xmm7, xmm8
addss xmm6, xmm7
mulss xmm0, xmm15 ;Z*weight
mulss xmm3, xmm15 ;X*weight
mulss xmm6, xmm15 ;Y*weight
add rSI, 010h
dec eCX
movss [rDI+rAX*8+08h], xmm0
movss [rDI+rAX*8+00h], xmm3
movss [rDI+rAX*8+04h], xmm6
jnz startLoopSmooth1
endLoopSmooth1:
;//////////////////////////////////////////////////////////////////
;// Smooth-2 loop
;//////////////////////////////////////////////////////////////////
movss xmm9, [r9+X00]
movss xmm10, [r9+Y01]
movss xmm11, [r9+X10]
movss xmm12, [r9+Y11]
movss xmm13, [r9+X20]
movss xmm14, [r9+Y21]
;// load the counter for the number of smooth vertices met for the second time
xor ECX,ECX
mov CX, word ptr [rdx]
add rdx, 2
test ECX, ECX
jz endLoopSmooth2
startLoopSmooth2:
; calculate the destination pointer
xor EAX,EAX
mov ax, word ptr [rdx]
add rdx, 2
shl rax, 4
prefetch [rSI+140h]
movss xmm0, [rSI+00h] ;x
movss xmm3, xmm0
movss xmm1, [rSI+04h] ;y
movss xmm4, xmm1
movss xmm2, [rSI+08h] ;z
movss xmm5, xmm2
movss xmm15, [rSI+0ch] ;w
prefetchw [rDI+rAX+40h]
movss xmm6, xmm0
movss xmm7, xmm1
movss xmm8, xmm2
mulss xmm0, xmm13 ;x*M20
mulss xmm1, xmm14 ;y*M21
mulss xmm2, [r9+Z22] ;z*M22
addss xmm0, [r9+TransZ]
addss xmm1, xmm2
addss xmm0, xmm1
mulss xmm3, xmm9 ;x*M00
mulss xmm4, xmm10 ;y*M01
mulss xmm5, [r9+Z02] ;z*M02
addss xmm3, [r9+TransX]
addss xmm4, xmm5
addss xmm3, xmm4
mulss xmm6, xmm11 ;x*M10
mulss xmm7, xmm12 ;y*M11
mulss xmm8, [r9+Z12] ;z*M12
addss xmm6, [r9+TransY]
addss xmm7, xmm8
addss xmm6, xmm7
mulss xmm0, xmm15 ;Z*weight
mulss xmm3, xmm15 ;X*weight
mulss xmm6, xmm15 ;Y*weight
add rSI, 010h
dec eCX
addss xmm0,[rDI+rAX+08h]
addss xmm3,[rDI+rAX+00h]
addss xmm6,[rDI+rAX+04h]
movss [rDI+rAX+08h], xmm0
movss [rDI+rAX+00h], xmm3
movss [rDI+rAX+04h], xmm6
jnz startLoopSmooth2
endLoopSmooth2:
add r9, 040h
jmp startLoop
endLoop:
movdqa xmm6, var6
movdqa xmm7, var7
movdqa xmm8, var8
movdqa xmm9, var9
movdqa xmm10, var10
movdqa xmm11, var11
movdqa xmm12, var12
movdqa xmm13, var13
movdqa xmm14, var14
movdqa xmm15, var15
pop rDX
pop rCX
pop rBX
pop rAX
pop rDI
pop rSI
pop rBP
ret
Amd64Skinner ENDP
_text ENDS
.endprolog
END

View File

@@ -0,0 +1,276 @@
#include "stdafx.h"
#include <StlUtils.h>
#include "CrySkinBase.h"
CrySkinBase::CrySkinBase():
m_numBones(0),
m_arrVertices ("CrySkin*.Vertices"),
m_arrAux ("CrySkin*.Aux"),
m_numSkipBones (0),
m_numDests(0)
{
}
CrySkinBase::~CrySkinBase ()
{
}
void CrySkinBase::clear()
{
m_numBones = 0;
m_numSkipBones = 0;
m_arrVertices.clear();
m_arrAux.clear();
}
bool CrySkinBase::empty()const
{
return m_numBones == 0 || m_arrVertices.empty();
}
void CrySkinBase::init (unsigned numVerts, unsigned numAux, unsigned numSkipBones, unsigned numBones)
{
m_numBones = numBones;
m_numSkipBones = numSkipBones;
// (re)allocate the memory
m_arrVertices.reinit (numVerts);
m_arrAux.reinit (numAux);
}
// transforms the given smooth point into the destination with the matrix
void CrySkinBase::transformWPoint (Vec3d& pDest, const Matrix44& matBone, const Vertex& rVtx)
{
//pDest = matBone.TransformPoint(rVtx) * rVtx.fWeight;
pDest.x = ((matBone[0][0] * rVtx.pt.x) + (matBone[1][0] * rVtx.pt.y) + (matBone[2][0] * rVtx.pt.z) + matBone[3][0]) * rVtx.fWeight;
pDest.y = ((matBone[0][1] * rVtx.pt.x) + (matBone[1][1] * rVtx.pt.y) + (matBone[2][1] * rVtx.pt.z) + matBone[3][1]) * rVtx.fWeight;
pDest.z = ((matBone[0][2] * rVtx.pt.x) + (matBone[1][2] * rVtx.pt.y) + (matBone[2][2] * rVtx.pt.z) + matBone[3][2]) * rVtx.fWeight;
}
// adds the given smooth point into the destination with the matrix
void CrySkinBase::addWPoint (Vec3d& pDest, const Matrix44& matBone, const Vertex& rVtx)
{
//pDest += matBone.TransformPoint(rVtx) * rVtx.fWeight;
pDest.x += ((matBone[0][0] * rVtx.pt.x) + (matBone[1][0] * rVtx.pt.y) + (matBone[2][0] * rVtx.pt.z) + matBone[3][0]) * rVtx.fWeight;
pDest.y += ((matBone[0][1] * rVtx.pt.x) + (matBone[1][1] * rVtx.pt.y) + (matBone[2][1] * rVtx.pt.z) + matBone[3][1]) * rVtx.fWeight;
pDest.z += ((matBone[0][2] * rVtx.pt.x) + (matBone[1][2] * rVtx.pt.y) + (matBone[2][2] * rVtx.pt.z) + matBone[3][2]) * rVtx.fWeight;
}
// transforms the given _vector_ without applying the transitional part
/*
void CrySkinBase::transformVectorNoTrans (Vec3d& pDest, const Vec3d& pSrc, const Matrix& matBone)
{
pDest.x = matBone[0][0] * pSrc.x + matBone[1][0] * pSrc.y + matBone[2][0] * pSrc.z;
pDest.y = matBone[0][1] * pSrc.x + matBone[1][1] * pSrc.y + matBone[2][1] * pSrc.z;
pDest.z = matBone[0][2] * pSrc.x + matBone[1][2] * pSrc.y + matBone[2][2] * pSrc.z;
}
*/
// transforms the given smooth point into the destination with the matrix
void CrySkinBase::transformWVector (Vec3d& pDest, const Matrix44& matBone, const Vertex& rVtx)
{
//pDest = matBone.TransformPoint(rVtx) * rVtx.fWeight;
pDest.x = ((matBone[0][0] * rVtx.pt.x) + (matBone[1][0] * rVtx.pt.y) + (matBone[2][0] * rVtx.pt.z)) * rVtx.fWeight;
pDest.y = ((matBone[0][1] * rVtx.pt.x) + (matBone[1][1] * rVtx.pt.y) + (matBone[2][1] * rVtx.pt.z)) * rVtx.fWeight;
pDest.z = ((matBone[0][2] * rVtx.pt.x) + (matBone[1][2] * rVtx.pt.y) + (matBone[2][2] * rVtx.pt.z)) * rVtx.fWeight;
}
// adds the given smooth point into the destination with the matrix
void CrySkinBase::addWVector (Vec3d& pDest, const Matrix44& matBone, const Vertex& rVtx)
{
//pDest += matBone.TransformPoint(rVtx) * rVtx.fWeight;
pDest.x += ((matBone[0][0] * rVtx.pt.x) + (matBone[1][0] * rVtx.pt.y) + (matBone[2][0] * rVtx.pt.z)) * rVtx.fWeight;
pDest.y += ((matBone[0][1] * rVtx.pt.x) + (matBone[1][1] * rVtx.pt.y) + (matBone[2][1] * rVtx.pt.z)) * rVtx.fWeight;
pDest.z += ((matBone[0][2] * rVtx.pt.x) + (matBone[1][2] * rVtx.pt.y) + (matBone[2][2] * rVtx.pt.z)) * rVtx.fWeight;
}
// returns the number of bytes occupied by this structure and all its contained objects
unsigned CrySkinBase::sizeofThis()const
{
return sizeof(*this) + sizeofArray (m_arrAux) + sizeofArray(m_arrVertices);
}
//------------------------------------------------------------------------------
//----- this is the serialize version for little-endian CPUs -----
//------------------------------------------------------------------------------
unsigned CrySkinBase::Serialize_PC (bool bSave, void* pStream, unsigned nBufSize)
{
SerialHeader Header;
if (bSave)
{
Header.initFromSkin(this);
}
else
{
if (!pStream)
return 0;
if (nBufSize < sizeof(Header))
return 0;
Header = *((SerialHeader*)pStream);
}
unsigned nSizeAuxInts = ((sizeof(CrySkinAuxInt)*Header.numAuxInts+3)&~3);
unsigned nSizeRequired =
// the m_numBones, m_numSkipBones, m_numDests
// m_arrAux.size(), m_arrVertices.size()
sizeof(SerialHeader) +
nSizeAuxInts +
sizeof(Vertex) * Header.numVertices;
if (!pStream)
return bSave?nSizeRequired:0;
if (nBufSize < nSizeRequired)
return 0;
CrySkinAuxInt* pAuxInts = (CrySkinAuxInt*)(((SerialHeader*)pStream) + 1);
Vertex* pVertices = (Vertex*)(((char*)pAuxInts) + nSizeAuxInts);
if (bSave)
{
//saving
*((SerialHeader*)pStream) = Header;
memcpy (pAuxInts, &m_arrAux[0], sizeof(CrySkinAuxInt)*m_arrAux.size());
memcpy (pVertices, &m_arrVertices[0], sizeof(Vertex)*m_arrVertices.size());
}
else
{
// loading
m_arrAux.clear();
m_arrAux.resize (Header.numAuxInts);
m_arrVertices.clear();
m_arrVertices.resize (Header.numVertices);
m_numBones = Header.numBones;
m_numSkipBones = Header.numSkipBones;
m_numDests = Header.numDests;
memcpy (&m_arrAux[0], pAuxInts, sizeof(CrySkinAuxInt)*m_arrAux.size());
memcpy (&m_arrVertices[0], pVertices, sizeof(Vertex)*m_arrVertices.size());
}
return nSizeRequired;
}
//------------------------------------------------------------------------------
//----- this is the serialize version for big-endian CPUs -----
//------------------------------------------------------------------------------
unsigned CrySkinBase::Serialize_GC (bool bSave, void* pStream, unsigned nBufSize)
{
SerialHeader Header;
if (bSave)
{
Header.initFromSkin(this);
}
else
{
if (!pStream)
return 0;
if (nBufSize < sizeof(Header))
return 0;
Header = *((SerialHeader*)pStream);
}
unsigned nSizeAuxInts = ((sizeof(CrySkinAuxInt)*Header.numAuxInts+3)&~3);
unsigned nSizeRequired =
// the m_numBones, m_numSkipBones, m_numDests
// m_arrAux.size(), m_arrVertices.size()
sizeof(SerialHeader) +
nSizeAuxInts +
sizeof(Vertex) * Header.numVertices;
if (!pStream)
return bSave?nSizeRequired:0;
if (nBufSize < nSizeRequired)
return 0;
CrySkinAuxInt* pAuxInts = (CrySkinAuxInt*)(((SerialHeader*)pStream) + 1);
Vertex* pVertices = (Vertex*)(((char*)pAuxInts) + nSizeAuxInts);
if (bSave)
{
//saving
Header.numBones = SWAP32(Header.numBones);
Header.numSkipBones = SWAP32(Header.numSkipBones);
Header.numDests = SWAP32(Header.numDests);
Header.numAuxInts = SWAP32(Header.numAuxInts);
Header.numVertices = SWAP32(Header.numVertices);
*((SerialHeader*)pStream) = Header;
//swap data into big-endian-format
unsigned sa = sizeof(CrySkinAuxInt);
unsigned a = (unsigned)m_arrAux.size();
unsigned x;
for (x=0; x<a; x++) {
pAuxInts[x]=SWAP16(pAuxInts[x]);
}
unsigned va = sizeof(m_arrVertices.size());
unsigned v = (unsigned)m_arrVertices.size();
for (x=0; x<(v); x++) {
pVertices[x].nDest = SWAP32(pVertices[x].nDest);
pVertices[x].pt.x = FSWAP32(pVertices[x].pt.x);
pVertices[x].pt.y = FSWAP32(pVertices[x].pt.y);
pVertices[x].pt.z = FSWAP32(pVertices[x].pt.z);
}
//copy into stream
memcpy (pAuxInts, &m_arrAux[0], sizeof(CrySkinAuxInt)*m_arrAux.size());
memcpy (pVertices, &m_arrVertices[0], sizeof(Vertex)*m_arrVertices.size());
//..and new swap-back to little-endian,
//because we need the original format for futher calculation on PC
for (x=0; x<a; x++) {
pAuxInts[x]=SWAP16(pAuxInts[x]);
}
for (x=0; x<(v); x++) {
pVertices[x].nDest = SWAP32(pVertices[x].nDest);
pVertices[x].pt.x = FSWAP32(pVertices[x].pt.x);
pVertices[x].pt.y = FSWAP32(pVertices[x].pt.y);
pVertices[x].pt.z = FSWAP32(pVertices[x].pt.z);
}
}
else
{
// loading
m_arrAux.clear();
m_arrAux.resize (Header.numAuxInts);
m_arrVertices.clear();
m_arrVertices.resize (Header.numVertices);
m_numBones = Header.numBones;
m_numSkipBones = Header.numSkipBones;
m_numDests = Header.numDests;
memcpy (&m_arrAux[0], pAuxInts, sizeof(CrySkinAuxInt)*m_arrAux.size());
memcpy (&m_arrVertices[0], pVertices, sizeof(Vertex)*m_arrVertices.size());
}
return nSizeRequired;
}
CrySkinBase::CStatistics::CStatistics (const CrySkinBase*pSkin)
{
this->numBones = pSkin->m_numBones;
this->numSkipBones = pSkin->m_numSkipBones;
this->numAuxInts = (unsigned)pSkin->m_arrAux.size();
this->numVertices = (unsigned)pSkin->m_arrVertices.size();
// construct the set of destination vertices
for (unsigned nBone = numSkipBones; nBone < numBones; ++nBone)
{
}
}
void CrySkinBase::scaleVertices (float fScale)
{
// all we have to do is to scale all the offsets
for (VertexArray::iterator it = m_arrVertices.begin(), itEnd = m_arrVertices.end(); it!= itEnd; ++it)
it->pt *= fScale;
}

View File

@@ -0,0 +1,105 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_BASE_HDR_
#define _CRY_ANIMATION_CRY_SKIN_BASE_HDR_
#include "SSEUtils.h"
#include "CrySkinTypes.h"
// this is the base for the actual skins
// the actual skin does the validation and calculates the skin
class CrySkinBase
{
public:
void clear();
bool empty()const;
void scaleVertices (float fScale);
//version for little-endian
unsigned Serialize_PC (bool bSave, void* pBuffer, unsigned nBufSize);
//version for big-endian
unsigned Serialize_GC (bool bSave, void* pBuffer, unsigned nBufSize);
// returns the number of bytes occupied by this structure and all its contained objects
unsigned sizeofThis()const;
// this structure contains the statistical information about this skin; its calculation
// may take significant time and should not be used in game run time (only for debugging purposes
// and to output statistics in the tools)
class CStatistics
{
public:
CStatistics (const CrySkinBase*pSkin);
unsigned numBones;
unsigned numSkipBones;
unsigned numAuxInts;
unsigned numVertices;
};
friend class CStatistics;
unsigned numBones() const {return m_numBones;}
unsigned numSkipBones() const {return m_numSkipBones;}
protected:
CrySkinBase();
~CrySkinBase ();
void init (unsigned numVerts, unsigned numAux, unsigned numSkipBones, unsigned numBones);
typedef CrySkinVertexAligned Vertex;
// transforms the given _vector_ without applying the transitional part
//void transformVectorNoTrans (Vec3& pDest, const Vec3& pSrc, const Matrix& matBone);
// transforms the given smooth point into the destination with the matrix
static void transformWPoint (Vec3& pDest, const Matrix44& matBone, const Vertex& rVtx);
// adds the given smooth point into the destination with the matrix
static void addWPoint (Vec3& pDest, const Matrix44& matBone, const Vertex& rVtx);
// transforms the given smooth point into the destination with the matrix
static void transformWVector (Vec3& pDest, const Matrix44& matBone, const Vertex& rVtx);
// adds the given smooth point into the destination with the matrix
static void addWVector (Vec3& pDest, const Matrix44& matBone, const Vertex& rVtx);
//typedef TIncContAllocator<Vertex, &g_VectorAllocator> VertexAllocator;
//typedef TIncContAllocator<CrySkinAuxInt, &g_VectorAllocator> AuxIntAllocator;
typedef TAllocator16<Vertex> VertexAllocator;
// the vertex stream: contains the rigid and smooth vertex structures
typedef TFixedArray<Vertex, VertexAllocator> VertexArray;
VertexArray m_arrVertices;
// the auxiliary stream: contains the number of vertices in each group, and the group vertices' destination idices (for smooth vertices only)
TFixedArray<CrySkinAuxInt> m_arrAux;
// the total number of bones, NOT taking the m_numSkipBones into account
unsigned m_numBones;
// additional flexibility: skip m_numSkipBones bones before performing skinning;
// useful for optimizing the skin which has only one or a few bones' influences
unsigned m_numSkipBones;
// this is the header structure for serialization
struct SerialHeader
{
void initFromSkin(const CrySkinBase* pSkin)
{
numBones = pSkin->m_numBones;
numSkipBones = pSkin->m_numSkipBones;
numDests = pSkin->m_numDests;
numAuxInts = (unsigned)pSkin->m_arrAux.size();
numVertices = (unsigned)pSkin->m_arrVertices.size();
}
unsigned numBones;
unsigned numSkipBones;
unsigned numDests;
unsigned numAuxInts;
unsigned numVertices;
};
// the total number of destination vertices
unsigned m_numDests;
};
#endif

View File

@@ -0,0 +1,168 @@
#include "stdafx.h"
#include "CrySkinRigidBasis.h"
#include "CrySkinBasisBuilder.h"
CrySkinBasisBuilder::CrySkinBasisBuilder (const ICrySkinSource* pGeometry, const Matrix44* pMatInvDef, unsigned numBoneInfos):
CrySkinBuilderBase0 (pGeometry),
m_pMatInvDef(pMatInvDef),
m_numBoneInfos (numBoneInfos),
m_nDestIntervalBegin (0),
m_nDestIntervalEnd (0),
m_numBones (0),
m_nFirstBone (0)
{
}
//////////////////////////////////////////////////////////////////////////
// sets the destination vertex interval to operate on. Initially, this is infinity.
// The destination vertex interval is the interval within which the destination vertex
// index must lie in order to be skinned. The base of the interval is considered
// vertex 0 in the produced skinner
void CrySkinBasisBuilder::setDestinationInterval (unsigned nBegin, unsigned nEnd)
{
if (nEnd > m_pGeometry->numExtTangents())
nEnd = m_pGeometry->numExtTangents();
if (nBegin < nEnd)
{
m_nDestIntervalBegin = nBegin;
m_nDestIntervalEnd = nEnd;
preprocess();
makeBoneBases ();
}
else
{
m_nDestIntervalBegin = 0;
m_nDestIntervalEnd = 0;
m_numBones = 0;
m_nFirstBone = 0;
}
}
// initializes the given rigid basis builder
void CrySkinBasisBuilder::initRigidBasisSkin (CrySkinRigidBasis* pSkin)
{
assert (sizeof(SPipTangentsA16)==0x30);
unsigned numAuxInts = 2 * (m_numBones-m_nFirstBone);
unsigned numBases = m_nDestIntervalEnd-m_nDestIntervalBegin;
pSkin->init (2 * numBases, numAuxInts, m_nFirstBone, m_numBones);
CrySkinStreams stream, streamBegin, streamEnd;
streamBegin.pAux = pSkin->m_arrAux.begin();
streamBegin.pVert = pSkin->m_arrVertices.begin();
stream = streamBegin;
streamEnd.pAux = streamBegin.pAux + numAuxInts;
streamEnd.pVert = streamBegin.pVert + 2 * numBases;
for (unsigned nBone = m_nFirstBone; nBone < m_numBones; ++nBone)
{
// for each bone, fill the three groups
// we fill the left and right vertices
fillGroup (stream, m_arrBoneBases[nBone].arrRight);
assert (stream.pAux <= streamEnd.pAux);
assert (stream.pVert <= streamEnd.pVert);
fillGroup (stream, m_arrBoneBases[nBone].arrLeft);
// only when we processed the last bone, we should have the pAux pointing to the end
assert (stream.pAux <= streamEnd.pAux);
assert (stream.pVert <= streamEnd.pVert);
}
}
void clipDenormal (float& x)
{
if (x > -1e-4 && x < 1e-4)
x = 0;
}
void clipDenormals(Vec3d& v)
{
clipDenormal(v.x);
clipDenormal(v.y);
clipDenormal(v.z);
}
// makes up the basis array
void CrySkinBasisBuilder::makeBoneBases ()
{
const unsigned* pExtToInt = m_pGeometry->getExtToIntMapEntries();
m_arrBoneBases.clear();
m_arrBoneBases.resize (m_numBones);
unsigned i;
// preallocate for push_back
for (i = 0; i < m_numBones; ++i)
m_arrBoneBases[i].reserve (m_nDestIntervalEnd / m_numBones);
for (i = m_nDestIntervalBegin; i < m_nDestIntervalEnd; ++i)
{
unsigned nGeomVert = pExtToInt[i];
TangData rBasis = m_pGeometry->getExtTangent (i);
clipDenormals(rBasis.binormal);
clipDenormals(rBasis.tangent);
const CryVertexBinding& rLink = m_pGeometry->getLink(nGeomVert);
unsigned nBone = rLink[0].BoneID;
assert (nBone < m_numBoneInfos);
CrySkinRigidBasisArray* pTargetArray;
if ((rBasis.tangent ^ rBasis.binormal)*rBasis.tnormal > 0)
pTargetArray = &m_arrBoneBases[nBone].arrRight;
else
pTargetArray = &m_arrBoneBases[nBone].arrLeft;
const Matrix44& matInvDef = m_pMatInvDef[nBone];
pTargetArray->push_back(CrySkinRigidBaseInfo (matInvDef,rBasis,i-m_nDestIntervalBegin));
}
}
//////////////////////////////////////////////////////////////////////////
// calculate the number of used bones and the skip-bone
void CrySkinBasisBuilder::preprocess()
{
unsigned numTangents = m_pGeometry->numExtTangents();
const unsigned* pExtToInt = m_pGeometry->getExtToIntMapEntries();
m_numBones = 0;
m_nFirstBone = 0;
bool bNotInited = true;
for (unsigned i = m_nDestIntervalBegin; i < min(m_nDestIntervalEnd,numTangents); ++i)
{
unsigned nGeomVert = pExtToInt[i];
const CryVertexBinding& rLink = m_pGeometry->getLink(nGeomVert);
unsigned nBone = rLink[0].BoneID;
if (bNotInited)
{
m_nFirstBone = nBone;
m_numBones = nBone+1;
bNotInited = false;
}
else
{
m_nFirstBone = min (m_nFirstBone, nBone);
m_numBones = max (nBone+1, m_numBones);
}
}
}
// fills the given bases to the simple stream
typedef std::vector< CrySkinRigidBaseInfo > CrySkinRigidBasisArray;
void CrySkinBasisBuilder::fillGroup (CrySkinStreams& streams, const CrySkinRigidBasisArray& arrBases)
{
// the group header is the number of vertices in the group
*streams.pAux++ = (CrySkinAuxInt)arrBases.size();
CrySkinRigidBasisArray::const_iterator it = arrBases.begin(), itEnd = it + arrBases.size();
for (; it != itEnd; ++it)
{
it->build(streams.pVert);
streams.pVert += 2;
}
}

View File

@@ -0,0 +1,67 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_BASIS_BUILDER_HDR_
#define _CRY_ANIMATION_CRY_SKIN_BASIS_BUILDER_HDR_
#include "CrySkinBuilderBase.h"
class CrySkinBasisBuilder: public CrySkinBuilderBase0
{
public:
CrySkinBasisBuilder (const ICrySkinSource* pGeometry, const Matrix44* pMatInvDef, unsigned numBones);
// sets the destination vertex interval to operate on. Initially, this is infinity.
// The destination vertex interval is the interval within which the destination vertex
// index must lie in order to be skinned. The base of the interval is considered
// vertex 0 in the produced skinner
void setDestinationInterval (unsigned nBegin, unsigned nEnd);
// initializes the given rigid basis builder
void initRigidBasisSkin (class CrySkinRigidBasis* pSkin);
protected:
// calculate the number of used bones and the skip-bone
void preprocess();
// makes up the basis array
void makeBoneBases ();
// fills the given bases to the simple stream
typedef std::vector< CrySkinRigidBaseInfo > CrySkinRigidBasisArray;
void fillGroup (CrySkinStreams& stream, const CrySkinRigidBasisArray& arrBases);
protected:
// the bone info, from which we'll extract the default position
const Matrix44* m_pMatInvDef;
unsigned m_numBoneInfos;
// this structure describes the flipped and unflipped bases belonging to the bone
struct BoneBasisGroup
{
CrySkinRigidBasisArray arrRight;// unflipped, Normal = Tang ^ Binorm
CrySkinRigidBasisArray arrLeft; // flipped, Normal = Binorm ^ Tang
void reserve (unsigned numReserve)
{
arrRight.reserve (numReserve/2);
arrLeft.reserve (numReserve/2);
}
};
typedef std::vector<BoneBasisGroup> BoneBasisGroupArray;
BoneBasisGroupArray m_arrBoneBases;
// total number of bones used (max bone + 1)
unsigned m_numBones;
unsigned m_nFirstBone;
// the destination interval: if the destination vertex is within this interval,
// it is put into the skinner; the actual destination vertex index is dubtracted the interval begin
unsigned m_nDestIntervalBegin, m_nDestIntervalEnd;
/*
unsigned m_nFlags;
enum
{
flagForSSE = 1// if this is set, then the skin is constructed for SSE usage
}
*/
};
#endif

View File

@@ -0,0 +1,139 @@
#include "stdafx.h"
#include "CrySkinFull.h"
#include "CrySkinBuilder.h"
// initializes the builder for usage
CrySkinBuilder::CrySkinBuilder (const ICrySkinSource*pGeometry):
m_arrSmoothVertHitCount ("CrySkinBuilder.arrSmoothVertHitCount"),
CrySkinBuilderBase (pGeometry)
{
makeFullBoneVertexArrays();
// skip the non-infuencing bones
for (m_numSkipBones = 0; m_numSkipBones < m_numBones && m_arrBoneVerts[m_numSkipBones].empty(); ++m_numSkipBones)
continue;
// we need the aux ints for:
// 3 number per each bone to keep the number of groups
// 0 numbers for the rigid vertices
// 1 number for each smooth vertex
// we don't count the bones that are skipped
m_numAuxInts = 3 * (m_numBones-m_numSkipBones) + m_numSmoothLinks;
}
// initializes a new skin
void CrySkinBuilder::initSkinFull(CrySkinFull* pSkin)
{
pSkin->init(m_numLinks, m_numAuxInts, m_numSkipBones, m_numBones);
pSkin->m_numDests = m_pGeometry->numVertices();
// this will hold the number of times a smooth vertex is hit
m_arrSmoothVertHitCount.reinit (m_pGeometry->numVertices(), 0);
CrySkinStreams stream, streamBegin, streamEnd;
streamBegin.pAux = pSkin->m_arrAux.begin();
streamBegin.pVert = pSkin->m_arrVertices.begin();
stream = streamBegin;
streamEnd.pAux = streamBegin.pAux + m_numAuxInts;
streamEnd.pVert = streamBegin.pVert + m_numLinks;
for (unsigned nBone = m_numSkipBones; nBone < m_numBones; ++nBone)
{
// for each bone, fill the three groups
// we start from the rigid vertices
fillRigidGroup (stream, nBone);
assert (stream.pAux <= streamEnd.pAux);
assert (stream.pVert <= streamEnd.pVert);
fillSmoothGroups (stream, nBone);
// only when we processed the last bone, we should have the pAux pointing to the end
assert (stream.pAux <= streamEnd.pAux);
assert (stream.pVert <= streamEnd.pVert);
}
m_arrSmoothVertHitCount.clear();
assert (stream.pAux == streamEnd.pAux);
assert (stream.pVert == streamEnd.pVert);
#ifdef _DEBUG
validate (pSkin);
pSkin->validate (m_pGeometry);
#endif
}
//////////////////////////////////////////////////////////////////////////
// fills in 2 groups of aux ints for the given bone (the smooth vertex groups)
// returns the pointer to the next available auxint after the groups
// IMPLEMENTATION NOTE:
// there are TWO groups filled in here
void CrySkinBuilder::fillSmoothGroups (CrySkinStreams& streams, unsigned nBone)
{
// the group starts with the number of smooth vertices met the first time, belonging to this bone
CrySkinAuxInt& numSmooth1Verts = *streams.pAux++;
numSmooth1Verts = 0;
// the list of smooth vertices belonging to the bone
CrySkinSmoothVertexArray& arrSmooth = m_arrBoneVerts[nBone].arrSmooth;
CrySkinSmoothVertexArray::const_iterator itBegin = arrSmooth.begin(), itEnd = itBegin + arrSmooth.size(), it = itBegin;
// this will be the array of vertices met not for the first time -
// we'll fill it in during processing of the Smooth1 group
std::vector<CrySkinSmoothVertexArray::const_iterator> arrSmooth2Verts;
arrSmooth2Verts.reserve (arrSmooth.size());
// iterate through the smooth vertices, picking up Smooth1 vertices and memorizing Smooth2 vertices
// hit each vertex, either smooth1 or smooth2
for (; it != itEnd; ++it)
{
unsigned& nVertHitCount = m_arrSmoothVertHitCount[it->nDest];
#ifdef _DEBUG
const CryVertexBinding& rLink = m_pGeometry->getLink(it->nDest);
float fLegacyWeight = rLink.getBoneWeight(nBone);
assert (rLink.hasBoneWeight(nBone, it->fWeight));
#endif
// check which time the vertex is met
if (nVertHitCount == 0)
{
// this vertex is met for the first time
*streams.pAux++ = it->nDest;
it->build (*streams.pVert++);
++numSmooth1Verts;
}
else
// this vertex is met not for the first time - to be gone to the group Smooth2
arrSmooth2Verts.push_back(it);
// hit the vertex anyway
++nVertHitCount;
}
CrySkinAuxInt& numSmooth2Verts = *streams.pAux++;
// we already know the number of smooth2 group vertices
numSmooth2Verts = (CrySkinAuxInt)arrSmooth2Verts.size();
// now iterate through the smooth2 vertices
for (unsigned i = 0; i < arrSmooth2Verts.size(); ++i)
{
arrSmooth2Verts[i]->build (*streams.pVert++);
*streams.pAux++ = arrSmooth2Verts[i]->nDest;
}
}
// validates the created skin
void CrySkinBuilder::validate (CrySkinFull *pSkin)
{
CrySkinStreams stream;
stream.pAux = &pSkin->m_arrAux[0];
stream.pVert = &pSkin->m_arrVertices[0];
for (unsigned nBone = m_numSkipBones; nBone < pSkin->m_numBones; ++nBone)
{
/*
BoneVertexGroup& rGroup = m_arrBoneVerts[nBone];
assert (*stream.pAux++ == rGroup.arrRigid.size());
stream.pVert += rGroup.arrRigid.size();
*/
}
}

View File

@@ -0,0 +1,35 @@
#ifndef _CRY_SKIN_BUIDLER_HDR_
#define _CRY_SKIN_BUIDLER_HDR_
class CrySkinFull;
#include "CrySkinBuilderBase.h"
//////////////////////////////////////////////////////////////////////////
// Capable of building an optimized CrySkin class out of geometry info
class CrySkinBuilder: public CrySkinBuilderBase
{
public:
// initializes the builder for usage
CrySkinBuilder (const ICrySkinSource* pGeometry);
// creates a new skin, capable of skinning into a uninited mem, to be deleted by Release
void initSkinFull(CrySkinFull*);
protected:
// fills in 2 groups of aux ints for the given bone (the smooth vertex groups)
// returns the pointer to the next available auxint after the groups
void fillSmoothGroups (CrySkinStreams& streams, unsigned nBone);
// validates the created skin
void validate (CrySkinFull *pSkin);
protected:
unsigned m_numAuxInts; // number of aux ints required for the skin
// the number of times a vertex has been already met in the auxiliary stream
TElementaryArray<unsigned> m_arrSmoothVertHitCount;
// number of bones to skip when skinning
unsigned m_numSkipBones;
};
#endif

View File

@@ -0,0 +1,91 @@
#include "stdafx.h"
#include "CrySkinBuilderBase.h"
CrySkinBuilderBase::CrySkinBuilderBase(const ICrySkinSource* pGeometry):
CrySkinBuilderBase0 (pGeometry)
#ifdef DEBUG_STD_CONTAINERS
,m_arrBoneVerts ("CrySkinBuilder.arrBoneVerts")
#endif
{
preprocess();
}
//////////////////////////////////////////////////////////////////////////
// fills in the group of aux ints for the given bone (the rigid vertex group)
// returns the pointer to the next available auxint after the group
// IMPLEMENTATION NOTE:
// actually, the rigid group is empty in the auxiliary stream, so we just set the number of vertices
void CrySkinBuilderBase::fillRigidGroup (CrySkinStreams& streams, unsigned nBone)
{
CrySkinRigidVertexArray& arrRigid = m_arrBoneVerts[nBone].arrRigid;
// the group starts with the number of rigid vertices belonging to this bone
*streams.pAux++ = (CrySkinAuxInt)arrRigid.size();
CrySkinRigidVertexArray::const_iterator it = arrRigid.begin(), itEnd = it + arrRigid.size();
for (; it != itEnd; ++it)
it->build (*streams.pVert++);
}
//////////////////////////////////////////////////////////////////////////
// computes the max number of bone affecting a vertex + 1
// and the total number of links to smooth vertices - the sum of numbers of links of all smooth vertices
// the total number of links per all vertices. If the whole object is rigid,
// this is the same as the number of vertices
void CrySkinBuilderBase::preprocess()
{
// we need for each bone:
// Vert:
// RigidVertex for each rigid vertex, and SmoothVertex for each smooth vertex
// Aux:
// 3 counts of vertices in each group
// index of target for each smooth vertex
m_numBones = 0;
m_numSmoothLinks = 0;
m_numLinks = 0;
unsigned numVertices = m_pGeometry->numVertices();
for (unsigned nVert = 0; nVert < numVertices; ++nVert)
{
// the number of links for this vertex
unsigned numVertexLinks = (unsigned)m_pGeometry->getLink(nVert).size();
if (numVertexLinks > 1)
// if the vertex is smooth, we'll need to keep the weights and destination indices
m_numSmoothLinks += numVertexLinks;
m_numBones = max(m_pGeometry->getLink(nVert).maxBoneID(), m_numBones);
m_numLinks += numVertexLinks;
}
++m_numBones;
}
//////////////////////////////////////////////////////////////////////////
// calculates the vertex list of each bone
void CrySkinBuilderBase::makeFullBoneVertexArrays()
{
m_arrBoneVerts.clear();
m_arrBoneVerts.resize (m_numBones);
unsigned numVertices = m_pGeometry->numVertices();
// preallocate memory for the vertices in bones
for (unsigned i = 0; i < m_numBones; ++i)
// assume approximately uniform distribution
m_arrBoneVerts[i].reserve (numVertices / m_numBones);
for (unsigned nVert = 0; nVert < numVertices; ++nVert)
{
// the number of links for this vertex
const CryVertexBinding& rLinks = m_pGeometry->getLink (nVert);
if (rLinks.size() == 1)
m_arrBoneVerts[rLinks[0].BoneID].arrRigid.push_back(CrySkinRigidVertex (rLinks[0].offset, nVert));
else
for (unsigned i = 0; i < rLinks.size(); ++i)
{
m_arrBoneVerts[rLinks[i].BoneID].arrSmooth.push_back(CrySkinSmoothVertex (rLinks[i], nVert));
}
}
}

View File

@@ -0,0 +1,111 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_BUILDER_BASE_HDR_
#define _CRY_ANIMATION_CRY_SKIN_BUILDER_BASE_HDR_
#include "CrySkinTypes.h"
#include "CryVertexBinding.h"
// This interface presents all the necessary information to the skin builders
// the actual skinned info can actually be vertices or normals - doesn't really matter
class ICrySkinSource
{
public:
// this structure is initialized through the constructor
ICrySkinSource (
const CryVertexBinding* pLinks,
unsigned numLinks,
const Vec3* pVertices,
unsigned numVertices,
const TangData* pExtTangents,
unsigned numExtTangents,
const unsigned* pExtToIntMapping
):
m_pLinks (pLinks),
m_numLinks (numLinks),
m_pVertices (pVertices),
m_numVertices (numVertices),
m_pExtTangents (pExtTangents),
m_numExtTangents (numExtTangents),
m_pExtToIntMapping (pExtToIntMapping)
{}
unsigned numVertices()const {return m_numVertices;}
const Vec3& getVertex (unsigned i)const {return m_pVertices[i];}
unsigned numLinks() const {return m_numLinks;}
const CryVertexBinding& getLink (unsigned i) const {return m_pLinks[i];}
unsigned numExtTangents() const {return m_numExtTangents;}
const unsigned *getExtToIntMapEntries()const {return m_pExtToIntMapping;}
const TangData& getExtTangent(unsigned i)const {return m_pExtTangents[i];}
protected:
// this needs to be filled in by the derived class
unsigned m_numLinks;
const CryVertexBinding* m_pLinks;
unsigned m_numVertices;
const Vec3* m_pVertices;
const TangData* m_pExtTangents;
unsigned m_numExtTangents;
const unsigned* m_pExtToIntMapping;
};
class CrySkinBuilderBase0
{
public:
CrySkinBuilderBase0(const class ICrySkinSource* pGeometry):
m_pGeometry (pGeometry)
{}
typedef CrySkinVertexAligned Vertex;
struct CrySkinStreams
{
CrySkinAuxInt* pAux;
Vertex* pVert;
};
protected:
const ICrySkinSource* m_pGeometry;
};
class CrySkinBuilderBase: public CrySkinBuilderBase0
{
public:
protected:
CrySkinBuilderBase(const class ICrySkinSource* pGeometry);
// Calculates the total number of links per all vertices. If the whole object is rigid,
// the number of links is the same as the number of vertices.
// Also calculates the sets of vertices belonging to each bone
// calculates the max number of bone affecting a vertex + 1
// and the total number of links to smooth vertices - the sum of numbers of links of all smooth vertices
void preprocess();
// calculates the vertex list of each bone, for all vertices present in the geometry
void makeFullBoneVertexArrays();
// fills in the group of aux ints for the given bone (the rigid vertex group)
// returns the pointer to the next available auxint after the group
void fillRigidGroup (CrySkinStreams& streams, unsigned nBone);
protected:
// precalculated numbers
unsigned m_numLinks; // total number of links (number of aligned vertex structures)
unsigned m_numBones; // number of bones affecting the skin, in the original array (max bone id + 1)
unsigned m_numSmoothLinks; // number of smooth links
// the sets of vertices belonging to each bone
typedef std::vector< CrySkinRigidVertex > CrySkinRigidVertexArray;
typedef std::vector< CrySkinSmoothVertex > CrySkinSmoothVertexArray;
struct BoneVertexGroup
{
CrySkinRigidVertexArray arrRigid;
CrySkinSmoothVertexArray arrSmooth;
bool empty() const{return arrRigid.empty() && arrSmooth.empty();}
void reserve (unsigned numReserve)
{
arrRigid.reserve (numReserve/2);
arrSmooth.reserve (numReserve/2);
}
};
typedef std::vector< BoneVertexGroup > BoneVertexArray;
BoneVertexArray m_arrBoneVerts;
};
#endif

View File

@@ -0,0 +1,515 @@
#include "stdafx.h"
#include "MathUtils.h"
#include "CrySkinBuilderBase.h"
#include "CrySkinFull.h"
#include "platform.h"
#define FOR_TEST 0
// takes each offset and includes it into the bbox of corresponding bone
/*void CrySkinFull::computeBoneBBoxes(CryBBoxA16* pBBoxes)
{
CrySkinAuxInt* pAux = &m_arrAux[0];
Vertex* pVertex = &m_arrVertices[0];
CryBBoxA16* pBBox = pBBoxes + m_numSkipBones, *pBBoxEnd = pBBoxes + m_numBones;
for (; pBBox!= pBBoxEnd; ++pBBox)
{
// each bone has a group of vertices
// first process the rigid vertices
Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
pBBox->include(pVertex->pt);
// process the smooth1 vertices that were the first time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
pBBox->include(pVertex->pt);
// process the smooth vertices that were the second time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
pBBox->include(pVertex->pt);
}
}*/
//////////////////////////////////////////////////////////////////////////
// does the skinning out of the given array of global matrices
void CrySkinFull::skin (const Matrix44* pBones, Vec3d* pDest)
{
#ifdef DEFINE_PROFILER_FUNCTION
DEFINE_PROFILER_FUNCTION();
#endif
//PROFILE_FRAME_SELF(PureSkin);
#if FOR_TEST
for (int i = 0; i < g_GetCVars()->ca_TestSkinningRepeats(); ++i)
#endif
{
const Matrix44* pBone = pBones + m_numSkipBones;
const Matrix44* pBonesEnd = pBones + m_numBones;
u32 s = 0;
u32 t = 0;
#ifdef _DEBUG
TFixedArray<float> arrW;
arrW.reinit(m_numDests, 0);
#endif
for (; pBone!= pBonesEnd; ++pBone)
{
Matrix34 m34 = Matrix34( GetTransposed44(*pBone) );
// first process the rigid vertices
u32 a0=m_arrAux[t];
for (u32 i=0; i<a0; i++ )
{
//_mm_prefetch( (char*)&m_arrVertices[s+20].pt, _MM_HINT_T0 );
pDest[m_arrVertices[s].nDest] = m34 * m_arrVertices[s].pt;
#ifdef _DEBUG
assert (arrW[m_arrVertices[s].nDest] == 0);
arrW[m_arrVertices[s].nDest] = 1;
#endif
s++;
}
t++;
// process the smooth1 vertices that were the first time met
u32 a1=m_arrAux[t]; t++;
for (u32 i=0; i<a1; i++ )
{
//_mm_prefetch( (char*)&m_arrVertices[s+20].pt, _MM_HINT_T0 );
pDest[m_arrAux[t]]= (m34*m_arrVertices[s].pt) * m_arrVertices[s].fWeight;
#ifdef _DEBUG
assert (arrW[m_arrAux[t]] == 0);
arrW[m_arrAux[t]] = m_arrVertices[s].fWeight;
#endif
s++;
t++;
}
// process the smooth vertices that were the first time met
u32 a2=m_arrAux[t]; t++;
for (u32 i=0; i<a2; i++)
{
//_mm_prefetch( (char*)&m_arrVertices[s+20].pt, _MM_HINT_T0 );
pDest[m_arrAux[t]] += (m34*m_arrVertices[s].pt) * m_arrVertices[s].fWeight;
#ifdef _DEBUG
assert (arrW[m_arrAux[t]] > 0 && arrW[m_arrAux[t]] < 1.005f);
arrW[m_arrAux[t]] += m_arrVertices[s].fWeight;
assert (arrW[m_arrAux[t]] > 0 && arrW[m_arrAux[t]] < 1.005f);
#endif
s++;
t++;
}
}
/*#ifdef _DEBUG
for (unsigned i = 0; i < m_numDests; ++i)
assert (arrW[i] > 0.995f && arrW[i] < 1.005f);
#endif
*/
}
}
//////////////////////////////////////////////////////////////////////////
// does the skinning out of the given array of global matrices
void CrySkinFull::skinAsVec3d16 (const Matrix44* pBones, Vec3dA16* pDest)
{
//PROFILE_FRAME_SELF(PureSkin);
#if FOR_TEST
for (int i = 0; i < g_GetCVars()->ca_TestSkinningRepeats(); ++i)
#endif
{
const Matrix44* pBone = pBones + m_numSkipBones, *pBonesEnd = pBones + m_numBones;
CrySkinAuxInt* pAux = &m_arrAux[0];
Vertex* pVertex = &m_arrVertices[0];
#ifdef _DEBUG
TFixedArray<float> arrW;
arrW.reinit(m_numDests, 0);
#endif
for (; pBone!= pBonesEnd; ++pBone)
{
// each bone has a group of vertices
// first process the rigid vertices
Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
{
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
pDest[pVertex->nDest].v = pBone->TransformVectorOLD(pVertex->pt);
// Temporary fixed by Sergiy. A new operation in the Matrix must be made
//pDest[pVertex->nDest].v = GetTransposed44(*pBone) * (pVertex->pt);
//transformVectorNoTrans (pDest[pVertex->nDest].v, pVertex->pt, *pBone);
#ifdef _DEBUG
assert (arrW[pVertex->nDest] == 0);
arrW[pVertex->nDest] = 1;
#endif
}
// process the smooth1 vertices that were the first time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
transformWVector (pDest[*pAux].v, *pBone, *pVertex);
#ifdef _DEBUG
assert (arrW[*pAux] == 0);
arrW[*pAux] = pVertex->fWeight;
#endif
}
// process the smooth vertices that were the first time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
addWVector (pDest[*pAux].v, *pBone, *pVertex);
#ifdef _DEBUG
assert (arrW[*pAux] > 0 && arrW[*pAux] < 1.005f);
arrW[*pAux] += pVertex->fWeight;
assert (arrW[*pAux] > 0 && arrW[*pAux] < 1.005f);
#endif
}
}
#ifdef _DEBUG
for (unsigned i = 0; i < m_numDests; ++i)
assert (arrW[i] > 0.995f && arrW[i] < 1.005f);
#endif
}
}
void CrySkinFull::CStatistics::addDest(unsigned nDest)
{
if (arrNumLinks.size() < nDest+1)
arrNumLinks.resize (nDest+1,0);
++arrNumLinks[nDest];
setDests.insert (nDest);
}
void CrySkinFull::CStatistics::initSetDests (const CrySkinFull* pSkin)
{
const CrySkinAuxInt* pAux = &pSkin->m_arrAux[0];
const Vertex* pVertex = &pSkin->m_arrVertices[0];
arrNumLinks.clear();
for (unsigned nBone = pSkin->m_numSkipBones; nBone < pSkin->m_numBones; ++nBone)
{
// each bone has a group of vertices
// first process the rigid vertices
const Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
{
unsigned nDest = pVertex->nDest;
addDest (nDest);
assert (arrNumLinks[nDest] == 1);
}
// process the smooth1 vertices that were the first time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
unsigned nDest = *pAux;
addDest (nDest);
assert (arrNumLinks[nDest] == 1);
//pVertex->fWeight is the weight of the vertex
}
// process the smooth vertices that were the second/etc time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
unsigned nDest = *pAux;
addDest (nDest);
assert (arrNumLinks[nDest] > 1);
// pVertex->fWeight contains the weight of the vertex
}
}
}
//////////////////////////////////////////////////////////////////////////
// validates the skin against the given geom info
#if defined (_DEBUG)
void CrySkinFull::validate (const ICrySkinSource* pGeometry)
{
TElementaryArray<unsigned> arrNumLinks ("CrySkinFull::validate.arrNumLinks");
arrNumLinks.reinit (pGeometry->numVertices(), 0);
CrySkinAuxInt* pAux = &m_arrAux[0];
Vertex* pVertex = &m_arrVertices[0];
for (unsigned nBone = m_numSkipBones; nBone < m_numBones; ++nBone)
{
// each bone has a group of vertices
// first process the rigid vertices
Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
{
unsigned nDest = pVertex->nDest;
const CryVertexBinding& rLink = pGeometry->getLink(nDest);
assert (arrNumLinks[nDest] == 0);
arrNumLinks[nDest] = 1;
assert (rLink.size()==1);
assert (rLink[0].Blending == 1);
assert (rLink[0].BoneID == nBone);
}
// process the smooth1 vertices that were the first time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
unsigned nDest = *pAux;
const CryVertexBinding& rLink = pGeometry->getLink(nDest);
assert (arrNumLinks[nDest]++ == 0);
assert (rLink.size()>1);
float fLegacyWeight = rLink.getBoneWeight(nBone);
assert (pVertex->fWeight == fLegacyWeight);
}
// process the smooth vertices that were the first time met
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
unsigned nDest = *pAux;
const CryVertexBinding& rLink = pGeometry->getLink(nDest);
assert (arrNumLinks[nDest]++ > 0);
assert (arrNumLinks[nDest] <= rLink.size());
assert (rLink.size()>1);
float fLegacyWeight = rLink.getBoneWeight(nBone);
assert (rLink.hasBoneWeight(nBone,pVertex->fWeight));
}
}
for (unsigned nVert = 0; nVert < pGeometry->numVertices(); ++nVert)
assert (arrNumLinks[nVert] == pGeometry->getLink(nVert).size());
}
#endif
#if ( defined (_CPU_X86) || defined (_CPU_AMD64) ) & !defined(LINUX)
DEFINE_ALIGNED_DATA( CryBBoxA16, CrySkinFull::g_BBox, 32 ); // align by cache line boundaries
#if defined (_CPU_AMD64)
extern "C" void Amd64Skinner(CrySkinAuxInt* pAux, CrySkinVertexAligned* pVertex, Vec3dA16* pDest, const Matrix44* pBone, Vec3dA16* pvMin,const Matrix44* pBoneEnd);
#endif
void CrySkinFull::skinSSE (const Matrix44* pBones, Vec3dA16* pDest)
{
#ifdef DEFINE_PROFILER_FUNCTION
DEFINE_PROFILER_FUNCTION();
#endif
//PROFILE_FRAME_SELF(PureSkin);
const Matrix44* pBone = pBones + m_numSkipBones, *pBoneEnd = pBones + m_numBones;
CrySkinAuxInt* pAux = &m_arrAux[0];
Vertex* pVertex = &m_arrVertices[0];
// set the bbox to the negative volume to make sure the bbox will calculate starting from the first vertex
g_BBox.vMin.v = Vec3d(1e6,1e6,1e6);// = pBone->GetTranslation();
g_BBox.vMax.v = Vec3d(-1e6,-1e6,-1e6);// = pBone->GetTranslation();
#if FOR_TEST
for (int i = 0; i < g_GetCVars()->ca_TestSkinningRepeats(); ++i)
#endif
#if defined(_CPU_AMD64)
Amd64Skinner(pAux, pVertex, pDest, pBone, &g_BBox.vMin, pBoneEnd);
#else
_asm
{
mov EDX, pAux
mov EBX, pVertex
mov EDI, pDest
mov ESI, pBone
// load the current matrix; we don't need the move component
startLoop:
cmp ESI, pBoneEnd
jz endLoop
movaps xmm0, [ESI]
movaps xmm1, [ESI+0x10]
movaps xmm2, [ESI+0x20]
movaps xmm3, [ESI+0x30]
add ESI, 0x40
// load the counter for the number of non-flipped tangets for this bone
#if CRY_SKIN_AUX_INT_SIZE==2
xor ECX,ECX
mov CX, word ptr [EDX]
add EDX, 2
#else
mov ECX, dword ptr [EDX]
add EDX, 4
#endif
test ECX, ECX
jz endLoopRigid
startLoopRigid:
// load the offset
movaps xmm7, [EBX]
// calculate the destination pointer
mov EAX, [EBX+0xC]
and EAX, 0xFFFFFF
add EAX, EAX
// EDI+EAX*8 points to the destination vector now
add EBX, 0x10
// transform the vertex
movss xmm6, xmm7
shufps xmm6, xmm6, 0 // xmm6 = 4 copies of offset.x
mulps xmm6, xmm0
movaps xmm5, xmm7
shufps xmm5, xmm5, 0x55 // xmm5 = 4 copies of offset.y
mulps xmm5, xmm1
shufps xmm7, xmm7, 0xAA // xmm7 = 4 copies of offset.z
mulps xmm7, xmm2
addps xmm7, xmm5
addps xmm7, xmm6
addps xmm7, xmm3 // xmm7 = fully transformed vertex, store it
// xmm7 = transformed vertex
movaps [EDI+EAX*8], xmm7
//----------------------
// Calculation of BBox
// xmm5 will be the min, xmm6 will be the max of bbox
movaps xmm5, xmm7
movaps xmm6, xmm7
minps xmm5, g_BBox.vMin
maxps xmm6, g_BBox.vMax
movaps g_BBox.vMin, xmm5
movaps g_BBox.vMax, xmm6
loop startLoopRigid
endLoopRigid:
//////////////////////////////////////////////////////////
// Smooth-1 loop
// load the counter for the number of smooth vertices met for the first time
//////////////////////////////////////////////////////////
#if CRY_SKIN_AUX_INT_SIZE==2
xor ECX,ECX
mov CX, word ptr [EDX]
add EDX, 2
#else
mov ECX, dword ptr [EDX]
add EDX, 4
#endif
test ECX, ECX
jz endLoopSmooth1
startLoopSmooth1:
// load the offset & blending
movaps xmm7, [EBX]
// calculate the destination pointer
#if CRY_SKIN_AUX_INT_SIZE==2
xor EAX,EAX
mov AX, word ptr [EDX]
add EDX, 2
#else
mov EAX, dword ptr [EDX]
add EDX, 4
#endif
add EAX, EAX
// EDI+EAX*8 points to the destination vector now
add EBX, 0x10
// transform the vertex
movss xmm6, xmm7
shufps xmm6, xmm6, 0 // xmm6 = 4 copies of offset.x
mulps xmm6, xmm0
movaps xmm5, xmm7
shufps xmm5, xmm5, 0x55 // xmm5 = 4 copies of offset.y
mulps xmm5, xmm1
movaps xmm4, xmm7
shufps xmm4, xmm4, 0xAA // xmm4 = 4 copies of offset.z
mulps xmm4, xmm2
addps xmm4, xmm5
addps xmm4, xmm6
addps xmm4, xmm3 // xmm4 = fully transformed vertex, blend it
shufps xmm7, xmm7, 0xFF // xmm7 = 4 copies of blending
mulps xmm7, xmm4
// xmm7 = transformed and blended vertex
movaps [EDI+EAX*8], xmm7
loop startLoopSmooth1
//loop startLoopNonflipped
endLoopSmooth1:
//////////////////////////////////////////////////////////
// Smooth-2 loop
// load the counter for the number of smooth vertices met for the second time
//////////////////////////////////////////////////////////
#if CRY_SKIN_AUX_INT_SIZE==2
xor ECX,ECX
mov CX, word ptr [EDX]
add EDX, 2
#else
mov ECX, dword ptr [EDX]
add EDX, 4
#endif
test ECX, ECX
jz endLoopSmooth2
startLoopSmooth2:
// load the offset & blending
movaps xmm7, [EBX]
// calculate the destination pointer
#if CRY_SKIN_AUX_INT_SIZE==2
xor EAX,EAX
mov AX, word ptr [EDX]
add EDX, 2
#else
mov EAX, dword ptr [EDX]
add EDX, 4
#endif
shl EAX, 4
add EAX, EDI
// EAX points to the destination vector now
add EBX, 0x10
// transform the vertex
movss xmm6, xmm7
shufps xmm6, xmm6, 0 // xmm6 = 4 copies of offset.x
mulps xmm6, xmm0
movaps xmm5, xmm7
shufps xmm5, xmm5, 0x55 // xmm5 = 4 copies of offset.y
mulps xmm5, xmm1
movaps xmm4, xmm7
shufps xmm4, xmm4, 0xAA // xmm4 = 4 copies of offset.z
mulps xmm4, xmm2
addps xmm4, xmm5
addps xmm4, xmm6
addps xmm4, xmm3 // xmm4 = fully transformed vertex, blend it
shufps xmm7, xmm7, 0xFF // xmm7 = 4 copies of blending
mulps xmm7, xmm4
// xmm7 = transformed and blended vertex
addps xmm7, [EAX]
movaps [EAX], xmm7
loop startLoopSmooth2
//loop startLoopNonflipped
endLoopSmooth2:
jmp startLoop
endLoop:
}
#endif // _CPU_AMD64
}
#endif

View File

@@ -0,0 +1,63 @@
#ifndef _CRY_SKIN_HDR_
#define _CRY_SKIN_HDR_
#include "CrySkinTypes.h"
#include "CrySkinBase.h"
#include "platform.h"
//////////////////////////////////////////////////////////////////////////
// the optimized skinner; built with the CrySkinBuilder class instance,
// destroyed with the Release()
// This is the full skinner: it skins into a memory with garbage in it
class CrySkinFull: public CrySkinBase
{
public:
friend class CrySkinBuilder;
// does the skinning out of the given array of global matrices
void skin (const Matrix44* pBones, Vec3* pDest);
// Skins skipping the translation components of bone matrices
void skinAsVec3d16 (const Matrix44* pBones, Vec3dA16* pDest);
#if ( defined (_CPU_X86) || defined (_CPU_AMD64) ) & !defined(LINUX)
// skins using the given bone matrices, into the given destination array,
// SIDE EFFECT: calculates the bounding box into the g_BBox
void skinSSE (const Matrix44* pBones, Vec3dA16* pDest);
DEFINE_ALIGNED_DATA_STATIC( CryBBoxA16, g_BBox, 32 ); // align by cache line boundaries
#endif
// takes each offset and includes it into the bbox of corresponding bone
void computeBoneBBoxes(CryBBoxA16* pBBox);
void scale (float fScale)
{
scaleVertices(fScale);
}
// validates the skin against the given geom info
void validate (const class ICrySkinSource* pGeometry);
// this structure contains the statistical information about this skin; its calculation
// may take significant time and should not be used in game run time (only for debugging purposes
// and to output statistics in the tools)
class CStatistics: public CrySkinBase::CStatistics
{
public:
CStatistics (const CrySkinFull* pSkin):
CrySkinBase::CStatistics(pSkin)
{
initSetDests (pSkin);
}
void initSetDests (const CrySkinFull* pSkin);
void addDest(unsigned nDest);
// destination vertex set
std::set<unsigned> setDests;
// the number of links per each vertex
std::vector<unsigned> arrNumLinks;
};
friend class CStatistics;
};
#endif

View File

@@ -0,0 +1,154 @@
#include "stdafx.h"
#include "CrySkinMorph.h"
//////////////////////////////////////////////////////////////////////////
// does the skinning out of the given array of global matrices
void CrySkinMorph::skin (const Matrix44* pBones, float fWeight, Vec3d* pDest)const
{
#ifdef PROFILE_FRAME_SELF
//PROFILE_FRAME_SELF(PureSkin);
#endif
const Matrix44* pBone = pBones + m_numSkipBones, *pBonesEnd = pBones + m_numBones;
const CrySkinAuxInt* pAux = &m_arrAux[0];
const Vertex* pVertex = &m_arrVertices[0];
#ifdef _DEBUG
TFixedArray<float> arrW;
arrW.reinit(m_numDests, 0);
#endif
for (; pBone!= pBonesEnd; ++pBone)
{
// each bone has a group of vertices
// first process the rigid vertices
const Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
{
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
pDest[pVertex->nDest] += pBone->TransformVectorOLD(pVertex->pt) * fWeight;
//pDest[pVertex->nDest] += (GetTransposed44(*pBone)*(pVertex->pt)) * fWeight;
#ifdef _DEBUG
assert (arrW[pVertex->nDest] == 0);
arrW[pVertex->nDest] = 1;
#endif
}
// then process the smooth vertices
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
pDest[*pAux] += pBone->TransformVectorOLD(pVertex->pt) * (fWeight * pVertex->fWeight);
// pDest[*pAux] += (GetTransposed44(*pBone)*pVertex->pt) * fWeight * pVertex->fWeight;
#ifdef _DEBUG
assert (arrW[*pAux] >= 0 && arrW[*pAux] < 1.005f);
arrW[*pAux] += pVertex->fWeight;
assert (arrW[*pAux] >= 0 && arrW[*pAux] < 1.005f);
#endif
}
}
#ifdef _DEBUG
for (unsigned i = 0; i < m_numDests; ++i)
assert (arrW[i] == 0 || (arrW[i] > 0.995f && arrW[i] < 1.005f));
#endif
}
//////////////////////////////////////////////////////////////////////////
// does the skinning out of the given array of global matrices,
// tries to estimate the changes in normals
void CrySkinMorph::skin (const Matrix44* pBones, float fWeight, Vec3d* pDest, Vec3dA16* pDestNormalsA16, float fAmplify)const
{
//PROFILE_FRAME_SELF(PureSkin);
const Matrix44* pBone = pBones + m_numSkipBones, *pBonesEnd = pBones + m_numBones;
const CrySkinAuxInt* pAux = &m_arrAux[0];
const Vertex* pVertex = &m_arrVertices[0];
#ifdef _DEBUG
TFixedArray<float> arrW;
arrW.reinit(m_numDests, 0);
#endif
for (; pBone!= pBonesEnd; ++pBone)
{
// each bone has a group of vertices
// first process the rigid vertices
const Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
{
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
Vec3d vOffset = pBone->TransformVectorOLD(pVertex->pt) * fWeight;
pDest[pVertex->nDest] += vOffset;
pDestNormalsA16[pVertex->nDest].v += vOffset * fAmplify;
//pDest[pVertex->nDest] += (GetTransposed44(*pBone)*(pVertex->pt)) * fWeight;
#ifdef _DEBUG
assert (arrW[pVertex->nDest] == 0);
arrW[pVertex->nDest] = 1;
#endif
}
// then process the smooth vertices
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
Vec3d vOffset = pBone->TransformVectorOLD(pVertex->pt) * (fWeight * pVertex->fWeight);
pDest[*pAux] += vOffset;
pDestNormalsA16[*pAux].v += vOffset * fAmplify;
// pDest[*pAux] += (GetTransposed44(*pBone)*pVertex->pt) * fWeight * pVertex->fWeight;
#ifdef _DEBUG
assert (arrW[*pAux] >= 0 && arrW[*pAux] < 1.005f);
arrW[*pAux] += pVertex->fWeight;
assert (arrW[*pAux] >= 0 && arrW[*pAux] < 1.005f);
#endif
}
}
#ifdef _DEBUG
for (unsigned i = 0; i < m_numDests; ++i)
assert (arrW[i] == 0 || (arrW[i] > 0.995f && arrW[i] < 1.005f));
#endif
}
void CrySkinMorph::CStatistics::init (const CrySkinMorph* pSkin)
{
const CrySkinAuxInt* pAux = &pSkin->m_arrAux[0];
const Vertex* pVertex = &pSkin->m_arrVertices[0];
numRigid = numSmooth = 0;
fMinOffset = fMaxOffset = -1;
for (unsigned nBone = pSkin->m_numSkipBones; nBone < pSkin->m_numBones; ++nBone)
{
// each bone has a group of vertices
// first process the rigid vertices
const Vertex* pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex)
{
//pDest[pVertex->nDest] += pBone->TransformVector(pVertex->pt) * fWeight;
setDests.insert (pVertex->nDest);
addOffset (pVertex->pt);
++numRigid;
}
// then process the smooth vertices
pGroupEnd = pVertex + *pAux++;
for (;pVertex < pGroupEnd; ++pVertex, ++pAux)
{
//pDest[*pAux] += pBone->TransformVector(pVertex->pt) * (fWeight * pVertex->fWeight);
setDests.insert (*pAux);
addOffset (pVertex->pt);
++numSmooth;
}
}
}
void CrySkinMorph::CStatistics::addOffset (const Vec3d& v)
{
float d = v.Length();
if (fMaxOffset < 0 || d > fMaxOffset)
fMaxOffset = d;
if (fMinOffset < 0 || d < fMinOffset)
fMinOffset = d;
}

View File

@@ -0,0 +1,49 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_MORPH_HDR_
#define _CRY_ANIMATION_CRY_SKIN_MORPH_HDR_
#include "CrySkinBase.h"
//////////////////////////////////////////////////////////////////////////
// This skinner is capable only of morphing a few vertices and adding their
// displacements to the destination vertex array
class CrySkinMorph: public CrySkinBase
{
public:
// does the skinning out of the given array of global matrices:
// adds the corresponding displacements with the given weight
void skin (const Matrix44* pBones, float fWeight, Vec3d* pDest)const;
// does the skinning out of the given array of global matrices:
// adds the corresponding displacements with the given weight
// also tries to estimate the changes in normals
void skin (const Matrix44* pBones, float fWeight, Vec3d* pDest, Vec3dA16* pDestNormalsA16, float fAmplify = 1) const;
void scale (float fScale)
{
// to scale it, we just need to proportionally scale each vertex x,y,z
scaleVertices(fScale);
}
friend class CrySkinMorphBuilder;
class CStatistics: public CrySkinBase::CStatistics
{
public:
CStatistics (const CrySkinMorph* pSkin):
CrySkinBase::CStatistics(pSkin)
{
init(pSkin);
}
void init(const CrySkinMorph* pSkin);
void addOffset (const Vec3d& v);
// destination vertex set
std::set<unsigned> setDests;
// number of rigid and smooth vertices
unsigned numRigid, numSmooth;
// minimum and maximum offset length
float fMinOffset, fMaxOffset;
};
};
#endif

View File

@@ -0,0 +1,185 @@
#include "stdafx.h"
#include "CrySkinMorphBuilder.h"
#include "CrySkinMorph.h"
CrySkinMorphBuilder::CrySkinMorphBuilder(const ICrySkinSource* pGeometry, const Matrix44* pMatInvDef, unsigned numBones):
CrySkinBuilderBase (pGeometry),
m_pMatInvDef(pMatInvDef),
m_numBones(numBones)
{
}
//////////////////////////////////////////////////////////////////////////
// initializes the given skin out of the given morph target
void CrySkinMorphBuilder::initSkinMorph (const SMeshMorphTargetVertex* pMorphVerts, unsigned numMorphVerts, class CrySkinMorph* pSkin)
{
m_pMorphVerts = pMorphVerts;
m_numMorphVerts = numMorphVerts;
m_pSkinTarget = pSkin;
pSkin->m_numDests = m_pGeometry->numVertices();
if (!numMorphVerts)
{
pSkin->clear();
return;
}
findAffectingBoneRange();
makeMorphBoneVertexArray();
calculateNumMorphLinks();
validate();
// for aux ints, we need, for each bone:
// 2 ints for group headers
// for each smooth link,
// 1 int for index of the target vertex
unsigned numAuxInts = 2 * (m_numAffectingBones - m_nFirstAffectingBone) + m_numMorphSmoothLinks;
pSkin->init (m_numMorphRigidLinks + m_numMorphSmoothLinks,
numAuxInts,
m_nFirstAffectingBone,
m_numAffectingBones);
CrySkinStreams stream, streamBegin, streamEnd;
streamBegin.pAux = pSkin->m_arrAux.begin();
streamBegin.pVert = pSkin->m_arrVertices.begin();
stream = streamBegin;
streamEnd.pAux = streamBegin.pAux + numAuxInts;
streamEnd.pVert = streamBegin.pVert + m_numMorphRigidLinks + m_numMorphSmoothLinks;
for (unsigned nBone = m_nFirstAffectingBone; nBone < m_numAffectingBones; ++nBone)
{
// for each bone, fill the three groups
// we start from the rigid vertices
fillRigidGroup (stream, nBone);
assert (stream.pAux <= streamEnd.pAux);
assert (stream.pVert <= streamEnd.pVert);
fillSmoothGroup (stream, nBone);
// only when we processed the last bone, we should have the pAux pointing to the end
assert (stream.pAux <= streamEnd.pAux);
assert (stream.pVert <= streamEnd.pVert);
}
}
//////////////////////////////////////////////////////////////////////////
// for the given morph target(source) finds and initializes the
// m_nFirstAffectingBone and m_numAffectingBones
void CrySkinMorphBuilder::findAffectingBoneRange()
{
// init - find the first vertex binding info
const CryVertexBinding* pLinks = &m_pGeometry->getLink(m_pMorphVerts[0].nVertexId);
m_nFirstAffectingBone = pLinks->minBoneID();
m_numAffectingBones = pLinks->maxBoneID()+1;
// find all the other vertex binding info and init the bone range
for (unsigned nMorphVert = 1; nMorphVert < m_numMorphVerts; ++nMorphVert)
{
unsigned nGeomVert = m_pMorphVerts[nMorphVert].nVertexId;
pLinks = &m_pGeometry->getLink(nGeomVert);
m_nFirstAffectingBone = min (m_nFirstAffectingBone, pLinks->minBoneID());
m_numAffectingBones = max(m_numAffectingBones, pLinks->maxBoneID()+1);
}
assert (m_nFirstAffectingBone < m_numAffectingBones);
assert (m_numAffectingBones <= m_numBones);
}
//////////////////////////////////////////////////////////////////////////
// calculates the number of rigid and smooth links
// m_numMorphRigidLinks and m_numMorphSmoothLinks
void CrySkinMorphBuilder::calculateNumMorphLinks()
{
m_numMorphRigidLinks = 0;
m_numMorphSmoothLinks = 0;
for (unsigned nMorphVert = 0; nMorphVert < m_numMorphVerts; ++nMorphVert)
{
unsigned nGeomVert = m_pMorphVerts[nMorphVert].nVertexId;
const CryVertexBinding* pLinks = &m_pGeometry->getLink(nGeomVert);
if (pLinks->size() == 1)
++m_numMorphRigidLinks;
else
m_numMorphSmoothLinks += (unsigned)pLinks->size();
}
}
//////////////////////////////////////////////////////////////////////////
// calculates the vertex list of each bone, taking only those vertices
// present in the morph vertex array
void CrySkinMorphBuilder::makeMorphBoneVertexArray()
{
m_arrBoneVerts.clear();
m_arrBoneVerts.resize (m_numAffectingBones);
for (unsigned nMorphVert = 0; nMorphVert < m_numMorphVerts; ++nMorphVert)
{
unsigned nGeomVert = m_pMorphVerts[nMorphVert].nVertexId;
const CryVertexBinding* pLinks = &m_pGeometry->getLink(nGeomVert);
// the morph vertex target in the object coordinates
Vec3d ptMorphOffset = m_pMorphVerts[nMorphVert].ptVertex - m_pGeometry->getVertex(nGeomVert);
if (pLinks->size() == 1)
{
unsigned nBone = (*pLinks)[0].BoneID;
CrySkinRigidVertex v;
v.nDest = nGeomVert;
// transform the point into the bone coordinates
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
v.pt = m_pMatInvDef[nBone].TransformVectorOLD (ptMorphOffset);
//v.pt = GetTransposed44(m_pMatInvDef[nBone])*(ptMorphOffset);
m_arrBoneVerts[nBone].arrRigid.push_back(v);
}
else
{
for (unsigned nLink = 0; nLink < pLinks->size(); ++nLink)
{
unsigned nBone = (*pLinks)[nLink].BoneID;
CrySkinSmoothVertex v;
v.fWeight = (*pLinks)[nLink].Blending;
v.nDest = nGeomVert;
v.pt = m_pMatInvDef[nBone].TransformVectorOLD (ptMorphOffset);
m_arrBoneVerts[nBone].arrSmooth.push_back(v);
}
}
}
}
void CrySkinMorphBuilder::validate()
{
#ifdef _DEBUG
unsigned nBone;
for (nBone = 0; nBone < m_nFirstAffectingBone;++nBone)
assert (m_arrBoneVerts[nBone].empty());
assert (!m_arrBoneVerts[m_nFirstAffectingBone].empty());
#endif
}
// fills in the group of aux ints for the given bone (the smooth vertex group)
// returns the pointer to the next available auxint after the group
void CrySkinMorphBuilder::fillSmoothGroup (CrySkinStreams& streams, unsigned nBone)
{
CrySkinSmoothVertexArray& arrSmooth = m_arrBoneVerts[nBone].arrSmooth;
// the group starts with the number of rigid vertices belonging to this bone
*streams.pAux++ = (CrySkinAuxInt)arrSmooth.size();
CrySkinSmoothVertexArray::const_iterator it = arrSmooth.begin(), itEnd = it + arrSmooth.size();
for (; it != itEnd; ++it)
{
it->build (*streams.pVert++);
*streams.pAux++ = it->nDest;
}
}

View File

@@ -0,0 +1,57 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_MORPH_BUILDER_HDR_
#define _CRY_ANIMATION_CRY_SKIN_MORPH_BUILDER_HDR_
#include "CrySkinBuilderBase.h"
class CrySkinMorph;
// builds the skin morph
class CrySkinMorphBuilder: public CrySkinBuilderBase
{
public:
// Receives the geometry and the array of inverse-default-global matrices
// 1 matrix per 1 bone
CrySkinMorphBuilder(const ICrySkinSource* pGeometry, const Matrix44* pMatInvDef, unsigned numBones);
// initializes the given skin out of the given morph target
void initSkinMorph (const SMeshMorphTargetVertex* pMorphVerts, unsigned numMorphVerts, class CrySkinMorph* pSkin);
protected:
// for the given morph target(source) finds and initializes the m_nFirstAffectingBone and m_numAffectingBones
void findAffectingBoneRange();
// calculates the number of rigid and smooth links
void calculateNumMorphLinks();
// calculates the vertex list of each bone, taking only those vertices
// present in the morph vertex array
void makeMorphBoneVertexArray();
void validate();
// fills in the group of aux ints for the given bone (the smooth vertex group)
// returns the pointer to the next available auxint after the group
void fillSmoothGroup (CrySkinStreams& streams, unsigned nBone);
protected:
// the bone info that's used to translate the
// the matrices are from getInvDefGlobal() of each corresponding CryBoneInfo
const Matrix44* m_pMatInvDef;
unsigned m_numBones;
// this is set temporarily during initialization of the skin out of the morph target structure
const SMeshMorphTargetVertex* m_pMorphVerts;
unsigned m_numMorphVerts;
CrySkinMorph* m_pSkinTarget;
// the first bone affecting the morph target
unsigned m_nFirstAffectingBone;
// the number of bones that are to be taken into account, does NOT depend on m_numFirstAffectingBone
unsigned m_numAffectingBones;
// the total number of rigid links for the current morph target
unsigned m_numMorphRigidLinks;
// the total number of smooth links for the current morph target
unsigned m_numMorphSmoothLinks;
};
#endif

View File

@@ -0,0 +1,460 @@
#include "stdafx.h"
//#include "CryAnimationBase.h"
#include "CrySkinRigidBasis.h"
#define FOR_TEST 0
#if FOR_TEST
#include "CryAnimation.h"
#include "CVars.h"
#endif
// returns the size of the skin, the number of bases being calculated
// by this skin. The bases are calculated into a 0-base continuous array
// tangents may be divided into subskins, each having different number of bases
// to skin, based on the performance consideration (strip mining)
unsigned CrySkinRigidBasis::size()const
{
return m_numDestBases;
}
// does the same as the base class init() but also remembers the number of bases (numVerts/2)
// for future reference
void CrySkinRigidBasis::init (unsigned numVerts, unsigned numAux, unsigned numSkipBones, unsigned numBones)
{
m_numDestBases = numVerts >> 1;
CrySkinBase::init (numVerts, numAux, numSkipBones, numBones);
}
void CrySkinRigidBasis::CStatistics::initSetDests (const CrySkinRigidBasis* pSkin)
{
const CrySkinAuxInt* pAux = &pSkin->m_arrAux[0];
const Vertex* pVertex = &pSkin->m_arrVertices[0];
setDests.clear();
arrNumLinks.clear();
for (unsigned nBone = pSkin->m_numSkipBones; nBone < pSkin->m_numBones; ++nBone)
{
// each bone has a group of (always rigid) vertices
// this is to take into account two groups: non-flipped and flipped tangents
for (int t = 0; t < 2; ++t)
{
// for each actual basis, we have two vertices
const Vertex* pGroupEnd = pVertex + (*pAux++<<1);
for (;pVertex < pGroupEnd; pVertex+=2)
{
unsigned nDestOffset = pVertex[0].nDest & 0xFFFFFF;
assert (nDestOffset < pSkin->m_numDestBases*sizeof(SPipTangentsA) && nDestOffset % sizeof(SPipTangentsA) == 0);
unsigned nDest = nDestOffset / sizeof(SPipTangentsA);
addDest(nDest);
}
}
}
}
void CrySkinRigidBasis::CStatistics::addDest(unsigned nDest)
{
if (arrNumLinks.size() < nDest+1)
arrNumLinks.resize (nDest+1,0);
++arrNumLinks[nDest];
setDests.insert (nDest);
}
// does the skinning out of the given array of global matrices:
// calculates the bases and fills the PipVertices in
void CrySkinRigidBasis::skin (const Matrix44* pBones, SPipTangentsA* pDest)const
{
#ifdef DEFINE_PROFILER_FUNCTION
DEFINE_PROFILER_FUNCTION();
#endif
#if FOR_TEST
for (int i = 0; i < g_GetCVars()->ca_TestSkinningRepeats(); ++i)
#endif
{
const Matrix44* pBone = pBones + m_numSkipBones, *pBonesEnd = pBones + m_numBones;
const CrySkinAuxInt* pAux = &m_arrAux[0];
const Vertex* pVertex = &m_arrVertices[0];
for (; pBone!= pBonesEnd; ++pBone)
{
// each bone has a group of (always rigid) vertices
// for each actual basis, we have two vertices
const Vertex* pGroupEnd = pVertex + (*pAux++<<1);
for (;pVertex < pGroupEnd; pVertex+=2)
{
unsigned nDestOffset = pVertex[0].nDest & 0xFFFFFF;
assert (nDestOffset < m_numDestBases*sizeof(SPipTangentsA));
SPipTangentsA& rDest = *(SPipTangentsA*)(UINT_PTR(pDest) + nDestOffset);
Vec3d vTang = pBone->TransformVectorOLD(pVertex[0].pt);
Vec3d vBinorm = pBone->TransformVectorOLD(pVertex[1].pt);
rDest.m_Tangent = vTang;
rDest.m_Binormal = vBinorm;
rDest.m_TNormal = vTang^vBinorm;
}
// the flipped version
pGroupEnd = pVertex + (*pAux++<<1);
for (;pVertex < pGroupEnd; pVertex+=2)
{
unsigned nDestOffset = pVertex[0].nDest & 0xFFFFFF;
assert (nDestOffset < m_numDestBases*sizeof(SPipTangentsA));
SPipTangentsA& rDest = *(SPipTangentsA*)(UINT_PTR(pDest) + nDestOffset);
Vec3d vTang = pBone->TransformVectorOLD(pVertex[0].pt);
Vec3d vBinorm = pBone->TransformVectorOLD(pVertex[1].pt);
rDest.m_Tangent = vTang;
rDest.m_Binormal = vBinorm;
rDest.m_TNormal = vBinorm^vTang;
}
}
}
}
#if defined(_CPU_X86) && !defined(LINUX)
// uses SSE for skinning; NOTE: EVERYTHING must be 16-aligned:
// destination, bones, and the data in this object
void CrySkinRigidBasis::skinSSE (const Matrix44* pBones, SPipTangentsA* pDest)const
{
#ifdef DEFINE_PROFILER_FUNCTION
DEFINE_PROFILER_FUNCTION();
#endif
#if defined(_DEBUG) && FOR_TEST
TElementaryArray<SPipTangentsA> arrTest ("CrySkinRigidBasis::skinSSE");
arrTest.reinit(size());
skin (pBones, &arrTest[0]);
#endif
#if FOR_TEST
for (int i = 0; i < g_GetCVars()->ca_TestSkinningRepeats(); ++i)
#endif
{
const Matrix44* pBone = pBones + m_numSkipBones, *pBoneEnd = pBones + m_numBones;
const CrySkinAuxInt* pAux = &m_arrAux[0];
const Vertex* pVertex = &m_arrVertices[0];
_asm
{
mov EDX, pAux
mov EBX, pVertex
mov EDI, pDest
mov ESI, pBone
// load the current matrix; we don't need the move component
startLoop:
cmp ESI, pBoneEnd
jz endLoop
movaps xmm0, [ESI]
movaps xmm1, [ESI+0x10]
movaps xmm2, [ESI+0x20]
add ESI, 0x40
// load the counter for the number of non-flipped tangets for this bone
#if CRY_SKIN_AUX_INT_SIZE==2
xor ECX,ECX
mov CX, word ptr [EDX]
add EDX, 2
#else
mov ECX, dword ptr [EDX]
add EDX, 4
#endif
test ECX, ECX
jz endLoopNonflipped
startLoopNonflipped:
// load the tangent vector
movaps xmm7, [EBX]
// load the binormal
// calculate the destination pointer
mov EAX, [EBX+0xC]
and EAX, 0xFFFFFF
add EAX, EDI
// EAX points to the destination triplet of vectors now
movaps xmm6, [EBX+0x10]
add EBX, 0x20
//prefetchnta [EBX]
// calculate the transformed tangent and binormal
movss xmm5, xmm7
shufps xmm5, xmm5, 0 // xmm5 = 4 copies of tangent.x
mulps xmm5, xmm0
movaps xmm4, xmm7
shufps xmm4, xmm4, 0x55 // xmm4 = 4 copies of tangent.y
mulps xmm4, xmm1
shufps xmm7, xmm7, 0xAA // xmm7 = 4 copies of tangent.z
mulps xmm7, xmm2
addps xmm7, xmm4
addps xmm7, xmm5
// xmm7 = transformed tangent
#if sizeofSPipTangentsA == 0x30
movaps [EAX], xmm7
#else
//SSE_MOVSS(EAX,xmm7)
movss [EAX], xmm7
shufps xmm7,xmm7, 0x39 // roll right
movss [EAX+4], xmm7
shufps xmm7,xmm7, 0x39 // roll right
movss [EAX+8], xmm7
shufps xmm7, xmm7, 0x4E // roll left twice
#endif
// transform the binormal
movss xmm5, xmm6
shufps xmm5, xmm5, 0
mulps xmm5, xmm0
movaps xmm4, xmm6
shufps xmm4, xmm4, 0x55
mulps xmm4, xmm1
shufps xmm6, xmm6, 0xAA
mulps xmm6, xmm2
addps xmm6, xmm4
addps xmm6, xmm5
// xmm6 = transformed binormal
#if sizeofSPipTangentsA == 0x30
movaps [EAX+0x10], xmm6
#else
//SSE_MOVSS(EAX+0xC,xmm7)
movss [EAX+0xC], xmm6
shufps xmm6,xmm6, 0x39
movss [EAX+0xC+4], xmm6
shufps xmm6,xmm6, 0x39
movss [EAX+0xC+8], xmm6
shufps xmm6, xmm6, 0x4E // roll left twice
#endif
// calculate the cross product tangent (xmm7)^binormal (xmm6)
movaps xmm5, xmm7
shufps xmm5, xmm5, 0x09 // roll right 3-base
movaps xmm4, xmm6
shufps xmm4, xmm4, 0x12 // roll left 3-base
//shufps xmm4, xmm4, 0x09 // roll right 3-base
mulps xmm5, xmm4
shufps xmm7, xmm7, 0x12 // roll left 3-base
shufps xmm6, xmm6, 0x09 // roll right 3-base
//shufps xmm7, xmm7, 0x09 // roll right 3-base
mulps xmm7, xmm6
subps xmm5, xmm7
//shufps xmm5,xmm5, 0x09
#if sizeofSPipTangentsA == 0x30
movaps [EAX+0x20], xmm5
#else
//SSE_MOVSS(EAX+0x18,xmm5)
movss [EAX+0x18], xmm5
shufps xmm5,xmm5, 0x39
movss [EAX+0x18+4], xmm5
shufps xmm5,xmm5, 0x39
movss [EAX+0x18+8], xmm5
shufps xmm5, xmm5, 0x4E // roll left twice
#endif
dec ECX
jnz startLoopNonflipped
//loop startLoopNonflipped
endLoopNonflipped:
//////////////////////////////////////////////////////////
// Flipped loop
// load the counter for the number of flipped tangets for this bone
#if CRY_SKIN_AUX_INT_SIZE==2
xor ECX,ECX
mov CX, word ptr [EDX]
add EDX, 2
#else
mov ECX, dword ptr [EDX]
add EDX, 4
#endif
test ECX, ECX
jz endLoopFlipped
startLoopFlipped:
// load the tangent vector
movaps xmm7, [EBX]
// load the binormal
movaps xmm6, [EBX+0x10]
// calculate the destination pointer
mov EAX, [EBX+0xC]
and EAX, 0xFFFFFF
add EAX, EDI
// EAX points to the destination triplet of vectors now
add EBX, 0x20
//prefetchnta [EBX]
// calculate the transformed tangent and binormal
movss xmm5, xmm7
shufps xmm5, xmm5, 0 // xmm5 = 4 copies of tangent.x
mulps xmm5, xmm0
movaps xmm4, xmm7
shufps xmm4, xmm4, 0x55 // xmm4 = 4 copies of tangent.y
mulps xmm4, xmm1
shufps xmm7, xmm7, 0xAA // xmm7 = 4 copies of tangent.z
mulps xmm7, xmm2
addps xmm7, xmm4
addps xmm7, xmm5
// xmm7 = transformed tangent
#if sizeofSPipTangentsA == 0x30
movaps [EAX], xmm7
#else
//SSE_MOVSS(EAX,xmm7)
movss [EAX], xmm7
shufps xmm7,xmm7, 0x39
movss [EAX+4], xmm7
shufps xmm7,xmm7, 0x39
movss [EAX+8], xmm7
shufps xmm7, xmm7, 0x4E // roll left twice
#endif
// transform the binormal
movss xmm5, xmm6
shufps xmm5, xmm5, 0
mulps xmm5, xmm0
movaps xmm4, xmm6
shufps xmm4, xmm4, 0x55
mulps xmm4, xmm1
shufps xmm6, xmm6, 0xAA
mulps xmm6, xmm2
addps xmm6, xmm4
addps xmm6, xmm5
// xmm6 = transformed binormal
#if sizeofSPipTangentsA == 0x30
movaps [EAX+0x10], xmm6
#else
//SSE_MOVSS(EAX+0xC,xmm6)
movss [EAX+0xC], xmm6
shufps xmm6,xmm6, 0x39
movss [EAX+0xC+4], xmm6
shufps xmm6,xmm6, 0x39
movss [EAX+0xC+8], xmm6
shufps xmm6, xmm6, 0x4E // roll left twice
#endif
// calculate the cross product binormal (xmm6)^tangent (xmm7)
movaps xmm5, xmm7
shufps xmm5, xmm5, 0x09 // roll right 3-base
movaps xmm4, xmm6
shufps xmm4, xmm4, 0x12 // roll left 3-base
//shufps xmm4, xmm4, 0x09 // roll right 3-base
mulps xmm5, xmm4
shufps xmm7, xmm7, 0x12 // roll left 3-base
shufps xmm6, xmm6, 0x09 // roll right 3-base
//shufps xmm7, xmm7, 0x09 // roll right 3-base
mulps xmm7, xmm6
subps xmm7, xmm5
//shufps xmm7,xmm7, 9
#if sizeofSPipTangentsA == 0x30
movaps [EAX+0x20], xmm7
#else
//SSE_MOVSS(EAX+0x18,xmm7)
movss [EAX+0x18], xmm7
shufps xmm7,xmm7, 0x39
movss [EAX+0x18+4], xmm7
shufps xmm7,xmm7, 0x39
movss [EAX+0x18+8], xmm7
shufps xmm7, xmm7, 0x4E // roll left twice
#endif
dec ECX
jnz startLoopFlipped
//loop startLoopFlipped
endLoopFlipped:
jmp startLoop
endLoop:
}
}
#if defined(_DEBUG) && FOR_TEST
unsigned numBases = 0;
for (unsigned nBone = m_numSkipBones; nBone < m_numBones; ++nBone)
{
assert (numBases < size());
const CrySkinAuxInt* pAux = &m_arrAux[(nBone-m_numSkipBones)*2];
SPipTangentsA* pBTest;
SPipTangentsA* pBDest;
const Vertex*pVertex = &m_arrVertices[numBases*2];
// check the non-flipped bases
unsigned i, j;
float dT, dB, dN;
for (i = 0; i < pAux[0]; ++i, ++numBases, pVertex+=2)
{
pBTest = (SPipTangentsA*)(((unsigned)&arrTest[0])+(pVertex->nDest&0xFFFFFF));
pBDest = (SPipTangentsA*)(((unsigned)pDest)+(pVertex->nDest&0xFFFFFF));
dT = Distance2(pBTest->m_Tangent,pBDest->m_Tangent);
dB = Distance2(pBTest->m_Binormal,pBDest->m_Binormal);
dN = Distance2(pBTest->m_TNormal,pBDest->m_TNormal);
assert (dT < 1e-6 && dB < 1e-6 && dN < 1e-6);
}
for (j = 0; j < pAux[1]; ++j, ++numBases, pVertex+=2)
{
pBTest = (SPipTangentsA*)(((unsigned)&arrTest[0])+(pVertex->nDest&0xFFFFFF));
pBDest = (SPipTangentsA*)(((unsigned)pDest)+(pVertex->nDest&0xFFFFFF));
dT = Distance2(pBTest->m_Tangent,pBDest->m_Tangent);
dB = Distance2(pBTest->m_Binormal,pBDest->m_Binormal);
dN = Distance2(pBTest->m_TNormal,pBDest->m_TNormal);
assert (dT < 1e-6 && dB < 1e-6 && dN < 1e-6);
}
}
assert (numBases == size());
#endif
}
#endif
// returns the number of bytes occupied by this structure and all its contained objects
unsigned CrySkinRigidBasis::sizeofThis()const
{
return CrySkinBase::sizeofThis() + sizeof(CrySkinRigidBasis) - sizeof(CrySkinBase);
}
unsigned CrySkinRigidBasis::Serialize (bool bSave, void* pBuffer, unsigned nBufSize)
{
if (bSave)
{
unsigned nWrittenBytes = CrySkinBase::Serialize_PC(true, pBuffer, nBufSize);
if (nWrittenBytes)
{
if (pBuffer)
*(unsigned*)(((char*)pBuffer)+nWrittenBytes) = m_numDestBases;
return sizeof(unsigned) + nWrittenBytes;
}
else
{
// error
return 0;
}
}
else
{
unsigned nReadBytes = CrySkinBase::Serialize_PC(false, pBuffer, nBufSize);
if (nReadBytes)
{
if (nBufSize - nReadBytes >= sizeof(unsigned))
{
m_numDestBases = *(unsigned*)(((char*)pBuffer)+nReadBytes);
return nReadBytes + sizeof(unsigned);
}
else
{
//error - perhaps not the tang stream
m_numDestBases = 0;
clear();
return 0;
}
}
else
{
//error
return 0;
}
}
}

View File

@@ -0,0 +1,72 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_RIGID_BASIS_HDR_
#define _CRY_ANIMATION_CRY_SKIN_RIGID_BASIS_HDR_
#include "CrySkinBase.h"
//////////////////////////////////////////////////////////////////////////
// This is the skin that calculates the tangent bases
// the skin is assumed to be rigid.
// The basises can be flipped, but may not be non-normalized or non-orthogonal
//
// IMPLEMENTATION NOTES:
// each base is represented by the first 2 vectors in the vertex array.
// the 3rd one is the cross-product, optionally negated with the 0x80000000
// flag in the 2nd vertex's nDest field. The 1st vertex contains the destination
// vertex index itself
class CrySkinRigidBasis: public CrySkinBase
{
public:
// returns the size of the skin, the number of bases being calculated
// by this skin. The bases are calculated into a 0-base continuous array
// tangents may be divided into subskins, each having different number of bases
// to skin, based on the performance consideration (strip mining)
unsigned size()const;
// does the skinning out of the given array of global matrices:
// calculates the bases and fills the PipVertices in
void skin (const Matrix44* pBones, SPipTangentsA* pDest)const;
#if defined(_CPU_X86) && !defined(LINUX)
// uses SSE for skinning; NOTE: EVERYTHING must be 16-aligned:
// destination, bones, and the data in this object
void skinSSE (const Matrix44* pBones, SPipTangentsA* pDest)const;
#endif
friend class CrySkinBasisBuilder;
// does the same as the base class init() but also remembers the number of bases (numVerts/2)
// for future reference
void init (unsigned numVerts, unsigned numAux, unsigned numSkipBones, unsigned numBones);
// returns the number of bytes occupied by this structure and all its contained objects
unsigned sizeofThis()const;
friend class CStatistics;
// this structure contains the statistical information about this skin; its calculation
// may take significant time and should not be used in game run time (only for debugging purposes
// and to output statistics in the tools)
class CStatistics: public CrySkinBase::CStatistics
{
public:
CStatistics (const CrySkinRigidBasis* pSkin):
CrySkinBase::CStatistics(pSkin)
{
initSetDests (pSkin);
}
void initSetDests (const CrySkinRigidBasis* pSkin);
void addDest(unsigned nDest);
// destination vertex set
std::set<unsigned> setDests;
// the number of links per each vertex
std::vector<unsigned> arrNumLinks;
};
unsigned Serialize (bool bSave, void* pBuffer, unsigned nBufSize);
protected:
// The size of the skin, the number of bases being calculated
// by this skin. The bases are calculated into a 0-base continuous array
unsigned m_numDestBases;
};
#endif

View File

@@ -0,0 +1,181 @@
#ifndef _CRY_ANIMATION_CRY_SKIN_TYPES_HDR_
#define _CRY_ANIMATION_CRY_SKIN_TYPES_HDR_
#include "MathUtils.h"
//////////////////////////////////////////////////////////////////////////
// the packed skin vertex structure: rigid or smooth, 16 byte
// the point is the original point in the bone's CS
struct CrySkinVertexAligned
{
Vec3 pt;
union
{
unsigned nDest; // the destination vertex index to store the transformed point
float fWeight; // the weight of the point
};
};
// the way the packed indices/group headers are kept: short or int
#define CRY_SKIN_AUX_INT_SIZE 2
#if CRY_SKIN_AUX_INT_SIZE==2
typedef unsigned short CrySkinAuxInt;
#elif CRY_SKIN_AUX_INT_SIZE==4
typedef unsigned CrySkinAuxInt;
#if defined _CPU_AMD64
#error You will need to modify the file CrySkinAMD64.ASM to make this work!
#endif // _CPU_AMD64
#else
#error CRY_SKIN_AUX_INT_SIZE must be defined and be 2 or 4
#endif
//////////////////////////////////////////////////////////////////////////
// Unpacked rigid vertex structure
struct CrySkinRigidVertex
{
Vec3 pt;
unsigned nDest; // the destination vertex index
CrySkinRigidVertex(){}
// initializes the structure given the offset from the bone and the destination vertex index
CrySkinRigidVertex (const Vec3& _ptOffset, unsigned _nDest):
pt (_ptOffset),
nDest (_nDest)
{
}
// constructs the packed rigid vertex data from this structure
void build (CrySkinVertexAligned& vDst)const
{
vDst.pt = pt;
vDst.nDest = nDest;
}
};
// aligned by 16-byte boundary tangent vectors
// if you don't want the padding to be used, just define it as
// typedef SPipTangents SPipTangentsA16;
struct SPipTangentsA16
{
Vec3 m_Tangent;
unsigned int m_Pad0;
Vec3 m_Binormal;
unsigned int m_Pad1;
Vec3 m_TNormal;
unsigned int m_Pad2;
inline SPipTangentsA16& operator = (const SPipTangents& right)
{
m_Tangent = right.m_Tangent;
m_Binormal = right.m_Binormal;
m_TNormal = right.m_TNormal;
return *this;
}
inline void copyTo (SPipTangents* right)
{/*
#ifdef DO_ASM
_asm
{
mov EBX, this
mov EDX, right
mov EAX, [EBX]
mov [EDX], EAX
mov EAX, [EBX+4]
mov [EDX+4], EAX
mov EAX, [EBX+8]
mov [EDX+8], EAX
mov EAX, [EBX+0x10]
mov [EDX+0xC], EAX
mov EAX, [EBX+0x14]
mov [EDX+0x10], EAX
mov EAX, [EBX+0x18]
mov [EDX+0x14], EAX
mov EAX, [EBX+0x20]
mov [EDX+0x18], EAX
mov EAX, [EBX+0x24]
mov [EDX+0x1C], EAX
mov EAX, [EBX+0x28]
mov [EBX+0x20], EAX
}
#else
*/
right->m_Tangent = m_Tangent;
right->m_Binormal = m_Binormal;
right->m_TNormal = m_TNormal;
//#endif
}
};
// the size of the SPipTangentsA: can be either 0x30 for aligned structure or
// 0x24 for non-aligned
#define sizeofSPipTangentsA 0x24
#if sizeofSPipTangentsA == 0x24
// this is the actual structure that's to be used to store tangents
typedef SPipTangents SPipTangentsA;
#elif sizeofSPipTangentsA == 0x30
typedef SPipTangentsA16 SPipTangentsA;
#else
#error
#endif
//////////////////////////////////////////////////////////////////////////
// Unpacked rigid basis structure
struct CrySkinRigidBaseInfo
{
Vec3 ptTangent;
Vec3 ptBinormal;
unsigned nDest;
CrySkinRigidBaseInfo (){}
CrySkinRigidBaseInfo (const Matrix44& matInvDef, const TangData& rBasis, unsigned _nDest):
ptTangent (matInvDef.TransformVectorOLD(rBasis.tangent)),
ptBinormal(matInvDef.TransformVectorOLD(rBasis.binormal)),
nDest (_nDest)
{
}
// constructs the packed representation of the base: uses pDst[0] for tangent and destination
// vertex, and pDst[1] for binormal. Does not set the additional field of the pDst[1]
void build (CrySkinVertexAligned* pDst)const
{
pDst[0].pt = ptTangent;
// we multiply to get the actual offset (to avoid multiplication in assembly)
// and add the dummy bits that avoid denormalization assists in SSE
pDst[0].nDest = nDest * sizeof(SPipTangentsA) + 0x40000000;
pDst[1].pt = ptBinormal;
pDst[1].fWeight = 0;
}
};
//////////////////////////////////////////////////////////////////////////
// Unpacked smooth vertex structure
struct CrySkinSmoothVertex: public CrySkinRigidVertex
{
float fWeight;// the weight of the point
CrySkinSmoothVertex(){}
// initializes this smooth vertex structure
CrySkinSmoothVertex (const CryLink& rLink, unsigned _nDest):
CrySkinRigidVertex(rLink.offset, _nDest),
fWeight(rLink.Blending)
{
}
// constructs the packed rigid vertex data from this structure
void build (CrySkinVertexAligned& vDst)const
{
vDst.pt = pt;
vDst.fWeight = fWeight;
}
};
#endif

View File

@@ -0,0 +1,128 @@
#include "stdafx.h"
#include "CryVertexBinding.h"
CryVertexBinding::CryVertexBinding()
#ifdef DEBUG_STD_CONTAINERS
:std::vector<CryLink>("CryVertexBinding")
#endif
{
}
// normalizes the weights of links so that they sum up to 1
void CryVertexBinding::normalizeBlendWeights()
{
// renormalize blending
float fBlendSumm = 0;
unsigned j;
for (j = 0; j < size(); j++)
fBlendSumm += (*this)[j].Blending;
assert (fBlendSumm > 0.1f && fBlendSumm <=1.001f);
for (j=0; j<size(); j++)
(*this)[j].Blending /= fBlendSumm;
}
// prunes the weights that are less than the specified minimal blending factor
// ASSUMES: that the links are already sorted by the blending factors in descending order
void CryVertexBinding::pruneSmallWeights(float fMinBlending, unsigned numMinLinks)
{
// remove 0 blending links and merge the links to the same bones
unsigned j;
for (j = numMinLinks; j < size(); j++)
{
assert (j == 0 || (*this)[j].Blending <= (*this)[j-1].Blending);
if((*this)[j].Blending <= fMinBlending)
{
resize(j);
assert(j);
break;
}
}
/*
// the links to delete
std::set<unsigned> setToDel;
for (i = 0; i < size()-1; ++i)
for (j = i+1; j < size(); ++j)
if ((*this)[i].BoneID == (*this)[j].BoneID)
setToDel.insert (j);
// delete
for (std::set<unsigned>::reverse_iterator it = setToDel.rbegin(); it != setToDel.rend(); ++it)
this->erase (*it);
*/
}
// remaps the bone ids
void CryVertexBinding::remapBoneIds (const unsigned* arrBoneIdMap, unsigned numBoneIds)
{
for (iterator it = begin(); it != end(); ++it)
{
// if you get this assert, most probably there is dissynchronization between different LODs of the same model
// - all of them must be exported with exactly the same skeletons.
if(it->BoneID >= 0 && it->BoneID < (int)numBoneIds)
it->BoneID = arrBoneIdMap[it->BoneID];
else
{
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogError ("\001bone index is out of range");
#endif
it->BoneID = 0;
}
}
}
// scales all the link offsets multiplying the offset by the given scale
void CryVertexBinding::scaleOffsets(float fScale)
{
for (iterator itLink = begin(); itLink != end(); ++itLink)
itLink->offset *= fScale;
}
// sorts the links by the blending factor, descending order
void CryVertexBinding::sortByBlendingDescending()
{
// sort the links by blend factor to allow skip unimportant ones
std::sort (begin(), end(), CryLinkOrderByBlending());
}
// returns the maximum BoneID in the array of links
unsigned CryVertexBinding::maxBoneID ()const
{
unsigned nResult = 0;
for (unsigned i = 0; i < this->size(); ++i)
nResult = max((unsigned)(*this)[i].BoneID, nResult);
return nResult;
}
// returns the minimal BoneID in the array of links
unsigned CryVertexBinding::minBoneID () const
{
unsigned nResult = (unsigned)(*this)[0].BoneID;
for (unsigned i = 1; i < this->size(); ++i)
nResult = min((unsigned)(*this)[i].BoneID, nResult);
return nResult;
}
// returns the link weight to the given bone
float CryVertexBinding::getBoneWeight (int nBoneID)const
{
for (unsigned i = 0; i < this->size(); ++i)
if ((*this)[i].BoneID == nBoneID)
return (*this)[i].Blending;
return 0;
}
// returns true if there is such bone weight
bool CryVertexBinding::hasBoneWeight (int nBoneID, float fWeight) const
{
for (unsigned i = 0; i < this->size(); ++i)
if ((*this)[i].BoneID == nBoneID && (*this)[i].Blending == fWeight)
return true;
return false;
}

View File

@@ -0,0 +1,48 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// History:
// 11/05/2002 - Created by Sergiy Migdalskiy <sergiy@crytek.de>
//
// Contains:
// Declaration of CryVertexBinding, a class incapsulating the array of links of a vertex to bone.
// This class is only used during construction of the geometry, and shouldn't be used for
// calculating the actual skin in the run time.
/////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef _CRY_VERTEX_BINDING_HDR_
#define _CRY_VERTEX_BINDING_HDR_
// array of crylinks for one vertex
class CryVertexBinding: public std::vector<CryLink>
{
public:
CryVertexBinding ();
// scales all the link offsets multiplying the offset by the given scale
void scaleOffsets(float fScale);
// sorts the links by the blending factor, descending order
void sortByBlendingDescending();
// normalizes the weights of links so that they sum up to 1
void normalizeBlendWeights();
// prunes the weights that are less than the specified minimal blending factor.
// Leaves (unpruned) at least the first numMinLinks links
// ASSUMES:that the links are already sorted by the blending factors in descending order
void pruneSmallWeights(float fMinBlending, unsigned numMinLinks = 1);
// remaps the bone ids
void remapBoneIds (const unsigned* arrBoneIdMap, unsigned numBoneIds);
// returns the maximum BoneID in the array of links
unsigned maxBoneID ()const;
// returns the minimal BoneID in the array of links
unsigned minBoneID () const;
// returns the link weight to the given bone
float getBoneWeight (int nBoneID) const;
// returns true if there is such bone weight
bool hasBoneWeight (int nBoneID, float fWeight) const;
};
#endif

View File

@@ -0,0 +1,192 @@
#include "StdAfx.h"
#include "FileMapping.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////////////////////
// Initializes an empty file mapping object
CFileMapping::CFileMapping():
m_nSize(0)
,m_pData(0)
#ifdef USE_FILE_MAPPING
,m_hFile (INVALID_HANDLE_VALUE)
,m_hMapping (0)
#endif
{
}
//////////////////////////////////////////////////////////////////////////////
// initializes the object and tries to open the given file mapping
CFileMapping::CFileMapping (const char* szFileName, unsigned nFlags):
m_nSize(0)
,m_pData(0)
#ifdef USE_FILE_MAPPING
,m_hFile (INVALID_HANDLE_VALUE)
,m_hMapping (0)
#endif
{
open (szFileName, nFlags);
}
//////////////////////////////////////////////////////////////////////////////
// closes file mapping
CFileMapping::~CFileMapping()
{
close();
}
//////////////////////////////////////////////////////////////////////////////
// Retuns the size of the mapped file, or 0 if no file was mapped or the file is empty
unsigned CFileMapping::getSize()const
{
return m_nSize;
}
//////////////////////////////////////////////////////////////////////////////
// Returns the pointer to the mapped file start in memory, or NULL if the file
// wasn't mapped
CFileMapping::PData CFileMapping::getData() const
{
return m_pData;
}
//////////////////////////////////////////////////////////////////////////
// Returns the file data at the given offset
CFileMapping::PData CFileMapping::getData(unsigned nOffset) const
{
if (m_pData)
return ((char*)m_pData)+nOffset;
else
return NULL;
}
#ifndef USE_FILE_MAPPING
// sets the given (already allocated) buffer to this object
// the memory must be allocated with malloc()
void CFileMapping::attach (PData pData, unsigned nSize)
{
close();
m_pData = pData;
m_nSize = nSize;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// initializes the object, opening the given file
// if file open has failed, subsequent getData() and
// getSize() will return zeros
// Returns true if open was successful
bool CFileMapping::open (const char* szFileName, unsigned nFlags)
{
close();
#ifdef USE_FILE_MAPPING
m_hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
DWORD dwError = 0;
if (m_hFile != INVALID_HANDLE_VALUE)
{
m_nSize = GetFileSize(m_hFile, NULL);
m_hMapping = CreateFileMapping (m_hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (m_hMapping != NULL)
{
m_pData = MapViewOfFile (m_hMapping, FILE_MAP_READ, 0, 0, 0);
}
}
else
{
dwError = GetLastError();
}
#elif defined(_CRY_ANIMATION_BASE_HEADER_)
ICryPak* pPak = g_GetPak();
FILE* f = pPak->FOpen (szFileName, "rb", nFlags);
if (f != NULL)
{
if (0 == pPak->FSeek (f, 0, SEEK_END))
{
m_nSize = pPak->FTell (f);
if ((int)m_nSize >= 0)
{
if (0 == pPak->FSeek (f, 0, SEEK_SET))
{
void* pData = malloc (m_nSize);
if (pData != NULL && 1 != pPak->FRead (pData, m_nSize, 1, f))
free (pData);
else
m_pData = pData;
}
}
}
pPak->FClose (f);
}
#else
FILE* f = fxopen (szFileName, "rb");
if (f != NULL)
{
if (0 == fseek (f, 0, SEEK_END))
{
m_nSize = ftell (f);
if ((int)m_nSize >= 0)
{
if (0 == fseek (f, 0, SEEK_SET))
{
void* pData = malloc (m_nSize);
if (pData != NULL && 1 != fread (pData, m_nSize, 1, f))
free (pData);
else
m_pData = pData;
}
}
}
fclose (f);
}
#endif
if (!m_pData)
{
// we couldn't map the file
close();
return false;
}
else
return true;
}
//////////////////////////////////////////////////////////////////////////////
// closes file mapping
// NOTE:
// this function can also be used for rollback of unsuccessful file mapping open
// opration and thus must be able to close partially open file mapping object
void CFileMapping::close()
{
#ifdef USE_FILE_MAPPING
if (m_pData)
{
UnmapViewOfFile(m_pData);
m_pData = NULL;
}
if (m_hMapping != NULL)
{
CloseHandle (m_hMapping);
m_hMapping = NULL;
}
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle (m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
#else
if (m_pData)
{
if (m_pData)
free (m_pData);
m_pData = NULL;
}
#endif
m_nSize = 0;
}

View File

@@ -0,0 +1,86 @@
//////////////////////////////////////////////////////////////////////
//
// CryEngine Source code
//
// File:FileMapping
// Declaration of class CFileMapping
// USE_FILE_MAPPING must be defined in the project for this class to really
// use the file mapping. Otherwise it just emulates it
//
// History:
// 06/26/2002 :Created by Sergiy Migdalskiy <sergiy@crytek.de>
//
//////////////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////////
// class CFileMapping
// Generic file mapping object, is capable of mapping a file with
// a simple call of a method.
//
// NOTES:
//
// Read-only file mapping is supported only.
//
// Error handing is performed through examining the getData()
// address: NULL means that no file was open (no file or I/O error)
//
// No exceptions are thrown.
////////////////////////////////////////////////////////////////
class CFileMapping: public _reference_target_t
{
public:
// Initializes an empty file mapping object
CFileMapping();
// initializes the object and tries to open the given file mapping
CFileMapping (const char* szFileName, unsigned nFlags = 0);
// closes file mapping
~CFileMapping();
// Retuns the size of the mapped file, or 0 if no file was mapped or the file is empty
unsigned getSize()const;
typedef
#ifdef USE_FILE_MAPPING
const
#endif
void * PData;
// Returns the pointer to the mapped file start in memory, or NULL if the file
// wasn't mapped
PData getData() const;
// Returns the file data at the given offset
PData getData(unsigned nOffset) const;
#ifndef USE_FILE_MAPPING
// sets the given (already allocated) buffer to this object
// the memory must be allocated with malloc()
void attach (PData pData, unsigned nSize);
#endif
// initializes the object, opening the given file
// if file open has failed, subsequent getData() and
// getSize() will return zeros
// Returns true if open was successful
bool open (const char* szFileName, unsigned nFlags = 0);
// closes file mapping
void close();
protected:
// the data of the mapped file.
PData m_pData;
// the mapped file size
unsigned m_nSize;
#ifdef USE_FILE_MAPPING
// the mapped file handle
HANDLE m_hFile;
// the mapped file mapping handle
HANDLE m_hMapping;
#endif
};
TYPEDEF_AUTOPTR(CFileMapping)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: cgfconvertor.h
// Version: v1.00
// Created: 5/11/2002 by Timur.
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef __GC_cgfconvertor_h__
#define __GC_cgfconvertor_h__
#pragma once
#include "IRCLog.h"
#include "IConvertor.h"
#include "CryCompiledFile.h"
#include "CryChunkedFile.h"
#include "RenderMeshBuilder.h"
struct ConvertContext;
// This is utility that can be used to easily write CCF format files
// just open the file, create this object on stack and add the chunk with
// AddChunk(nType)
// Then write the chunk directly to the file, and then add new chunk,
// repeat the process..
// Destroy (or call CloseChunk()) this object before you close the file.
// To have nested chunks, create another instance of CCFWriter and use it for some time.
// When you destruct it, you can use the upper-level writer. And so on.
class GC_CCFFileWriter
{
public:
GC_CCFFileWriter (FILE* f):
m_pFile (f), m_nLastChunk (-1)
{
}
GC_CCFFileWriter ():
m_pFile(NULL), m_nLastChunk(-1)
{
}
~GC_CCFFileWriter()
{
CloseChunk();
}
// changes the file handle
void SetFile (FILE* f)
{
CloseChunk();
m_pFile = f;
}
// adds a new chunk of the specified type
// returns 1 if successfully wrote the chunk header or 0 if an error occurred
int AddChunk(CCFChunkTypeEnum nType)
{
// first, end the previous chunk
CloseChunk();
// remember the current position
m_nLastChunk = ftell (m_pFile);
// write the chunk header
CCFChunkHeader header;
header.nSize = 0x58585858;
header.nType = SWAP32(nType); //0x204f5649;
return fwrite (&header, sizeof(header), 1, m_pFile);
}
// Signals the end of the chunk that was added with AddChunk
// Automatically aligns the chunk data by 4-byte boundary, if needed
void CloseChunk()
{
if (m_nLastChunk < 0)
return; // no last chunk, or the last chunk was closed
long nNewChunkPos = ftell (m_pFile);
if (nNewChunkPos&3)
{
// align by 4-byte boundary
int nPad = 0;
fwrite (&nPad, 1, 4-(nNewChunkPos&3), m_pFile);
nNewChunkPos = ftell (m_pFile);
}
// write the size of the chunk to the chunk header
fseek (m_pFile, (int)(&((CCFChunkHeader*)m_nLastChunk)->nSize), SEEK_SET);
unsigned nSize = SWAP32(nNewChunkPos - m_nLastChunk);
fwrite (&nSize, sizeof(nSize), 1, m_pFile);
// set the file pointer back where it was
fseek (m_pFile, nNewChunkPos, SEEK_SET);
// forget about the last chunk
m_nLastChunk = -1;
}
protected:
// the file to which the chunks are written
FILE* m_pFile;
// the last chunk position within the file (used to update the chunk)
long m_nLastChunk;
};
/** Convertor implementation for CGF files.
*/
class GC_CGFConvertor : public IConvertor
{
public:
// this class will be thrown from the internal method during conversion
// indicating some error that should abort conversion
class Error
{
public:
Error (int nCode);
Error (const char* szFormat, ...);
const char* c_str()const {return m_strReason.c_str();}
protected:
string m_strReason;
};
GC_CGFConvertor ():
m_fTarget(NULL),
m_pStatCGFCompiler(NULL)
{
}
~GC_CGFConvertor()
{
clear();
if (m_pStatCGFCompiler)
m_pStatCGFCompiler->Release();
}
//! Release memory of interface.
void Release() { delete this; };
//! Process file.
virtual bool Process( ConvertContext &cc );
//! Return name of output file that will be produced from this file.
// @param sourceFileName File name plus extension of source file, must not contain path.
virtual bool GetOutputFile( ConvertContext &cc );
//! Return platforms supported by this convertor.
virtual int GetNumPlatforms() const;
//! Get supported platform.
//! @param index Index of platform must be in range 0 < index < GetNumPlatforms().
virtual Platform GetPlatform( int index ) const;
//! Get number of supported extensions.
virtual int GetNumExt() const { return 1; };
//! Get supported extension.
//! @param index Index of extension must be in range 0 < index < GetNumExt().
virtual const char* GetExt( int index ) const { return "cgf"; };
// this should retrieve the timestamp of the convertor executable:
// when it was created by the linker, normally. This date/time is used to
// compare with the compiled file date/time and even if the compiled file
// is not older than the source file, it will be recompiled if it's older than the
// convertor
virtual DWORD GetTimestamp() const ;
protected:
// internally used stuff
// writes the data directly into the file
void writeRaw (const void* pData, unsigned nSize);
template <class T>
void write (const T& rData)
{
writeRaw (&rData, sizeof(T));
}
template <class T>
void write (const T* pData, unsigned numElements)
{
writeRaw (pData, numElements * sizeof(T));
}
template <class T>
void writeArray (const std::vector<T>& arrData)
{
if (!arrData.empty()){
unsigned long a0=arrData.size();
write (&arrData[0], arrData.size());
}
}
//void LogV (const char* szFormat, va_list args);
//void Log (const char* szFormat, ...);
void WriteGeometryInfo (unsigned nLOD);
void WriteHeader();
void WriteBoneInfo();
void WriteMaterials();
void writeIntFaces(unsigned nLOD);
void WriteVertices (CryChunkedFile* pSource);
void WriteNormals (CryChunkedFile* pSource);
void writeShadowConnectivity (unsigned nLOD);
void writeVertexSkin (unsigned nLOD);
void writeNormalSkin (unsigned nLOD);
void writeTangSkin (unsigned nLOD);
void WriteBoneGeometry (unsigned nLOD);
void writeBGBone(unsigned nLOD, unsigned nBone, CryChunkedFile::MeshDesc* pMesh);
void WriteMorphTargetSet ();
void writeMorphTargets (unsigned nLOD);
void WriteLights();
bool isAnimationFastCheck();
protected:
// loads the m_arrLODs
bool LoadLODs();
IConvertor* getStatCGFCompiler();
// builds the m_arrRenderMeshes array with the CRenderMeshBuilder
// structures containing all the additional info required for leaf buffer creation
void BuildRenderMeshes();
// Updates physics if needed (if there is LOD1, which contains physical data for LOD1 physics
void UpdateDeadBodyPhysics();
// remaps, if necessary, the bones from the LOD source to the Master source
// and changes the links in the LOD source so that the boneid's there point to the indices (not IDs!)
// of the bones in the Master Source, not LOD Source
// This assumes that the bone information from the LOD source won't be used at all
// throws an error if there's some unrecognized bone in the LOD source
void RemapBones (unsigned nLOD);
// remaps the bone indices, throws error if some indices cannot be remapped for some reason
// (e.g. mapping contains -1, i.e. no mapping)
void RemapBoneIndices (CryChunkedFile* pLOD, const std::vector<int>& arrMap);
// releases all the resources
void clear();
// calculates the number of materials used by the previous LODs (< than given)
// this is to offset the mtl numbers of the nLOD to use the range of materials
// belonging to that LOD
unsigned getLODMtlOffset (unsigned nLOD);
void LogWarning (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
m_pContext->pLog->LogV (IMiniLog::eWarning, szFormat, args);
va_end(args);
}
void LogError (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
m_pContext->pLog->LogV (IMiniLog::eError, szFormat, args);
va_end(args);
}
void Log (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
m_pContext->pLog->LogV (IMiniLog::eMessage, szFormat, args);
va_end(args);
}
protected:
ConvertContext* m_pContext;
// the source files: one file for each LOD. At least LOD 0 Must be present for conversion.
CryChunkedFile_AutoArray m_arrLODs;
// the mapping from bone indices in Master (LOD0) to bone indices in LOD>0
typedef std::vector<int> BoneMap;
std::vector<BoneMap> m_arrLODBoneMaps;
// the render meshes for each LOD
std::vector<CRenderMeshBuilder> m_arrRenderMeshes;
GC_CCFFileWriter m_Writer;
// the target file
FILE* m_fTarget;
IConvertor* m_pStatCGFCompiler;
};
#endif // __cgfconvertor_h__

View File

@@ -0,0 +1,5 @@
SCC = This is a source code control file
[ResourceCompilerPC.vcproj]
SCC_Aux_Path = "P4SCC#perforce:1666##marcoc_code##PC018"
SCC_Project_Name = Perforce Project

View File

@@ -0,0 +1,284 @@
#ifndef _CRY_ANIMATION_MATH_UTILS_HDR_
#define _CRY_ANIMATION_MATH_UTILS_HDR_
#include "IBindable.h"
#include "Cry_Geo.h"
// aligned on 16-byte boundary vector
class Vec3dA16
{
public:
Vec3 v;
unsigned m_Pad;
Vec3dA16 () {}
Vec3dA16 (float x, float y, float z):
v(x,y,z)
{}
};
// for the given forward vector and position, builds up the right vector and up vector
// and builds the corresponding matrix
// Builds the right and up vector, given the forward vector, and initializes the matOut
void BuildMatrixFromFwd (const Vec3& ptNormal, const Vec3& ptPosition, Matrix44& matOut);
// for the given forward vector and position, builds up the right vector and up vector
// and builds the corresponding matrix
// Builds the right and up vector, given the forward vector, and initializes the matOut
void BuildMatrixFromFwdZRot (const Vec3& ptNormal, const Vec3& ptPosition, float fZRotate, Matrix44& matOut);
inline bool IsOrthoUniform (const Matrix44& b, float fTolerance = 0.01f)
{
for (int i = 0; i < 3; ++i)
{
if (fabs(b.GetRow(i).GetLengthSquared()-1) > fTolerance)
return false;
if (fabs(b.GetColumn(i).GetLengthSquared()-1) > fTolerance)
return false;
}
return true;
}
inline void OrthoNormalize(Matrix44& m)
{
m.NoScale();
}
// given the bone matrix, returns its inverted.
// the bone matrices are orthounitary
inline Matrix44 OrthoUniformGetInverted (const Matrix44& b)
{
#ifdef _DEBUG
for (int i = 0; i < 3; ++i)
{
assert (fabs(b.GetRow(i).len2()-1) < 1e-2);
assert (fabs(b.GetColumn(i).len2()-1) < 1e-2);
}
#endif
return GetInverted44(b);
Matrix44 m;
m(0,0) = b(0,0); m(0,1) = b(1,0); m(0,2)= b(2,0); m(0,3) = 0;
m(1,0) = b(0,1); m(1,1) = b(1,1); m(1,2)= b(2,1); m(1,3) = 0;
m(2,0) = b(0,2); m(2,1) = b(1,2); m(2,2)= b(2,2); m(2,3) = 0;
m.SetTranslationOLD(b.TransformVectorOLD(b.GetTranslationOLD())); m(3,3) = 1;
#ifdef _DEBUG
Matrix44 e = b * m;
assert (e.GetTranslationOLD().GetLengthSquared() < 0.01f);
#endif
return m;
}
// rotates the matrix by the "Angles" used by the FarCry game
void Rotate (Matrix44& matInOut, const Vec3& vAngles);
// rotates the matrix by the "Angles" used by the FarCry game,
// but in inverse order and in opposite direction (effectively constructing the inverse matrix)
void RotateInv (Matrix44& matInOut, const Vec3& vAngles);
// Smoothes linear blending into cubic (b-spline) with 0-derivatives
// near 0 and 1
extern float SmoothBlendValue (float fBlend);
template <typename T>
T min2 (T a, T b)
{
return a < b ? a : b;
}
template <typename T>
T max2 (T a, T b)
{
return a > b ? a : b;
}
template <typename T>
T max3(T val1, T val2, T val3)
{
return max2(max2(val1,val2),val3);
}
template <typename T>
T min3(T val1, T val2, T val3)
{
return min2(min2(val1,val2),val3);
}
template <typename T>
T min4 (T a, T b, T c, T d)
{
return min2(min2(a,b),min2(c,d));
}
#include "QuaternionExponentX87.h"
// checks whether the quaternions are almost equal
inline bool isEqual (const CryQuat& q, const CryQuat& p)
{
const float fEpsilon = 0.05f;
if (fabs(q.w-p.w) > fEpsilon)
return false;
if (fabs(q.v.x-p.v.x) > fEpsilon)
return false;
if (fabs(q.v.y-p.v.y) > fEpsilon)
return false;
if (fabs(q.v.z-p.v.z) > fEpsilon)
return false;
return true;
}
__inline void quaternionExponentOptimized(const Vec3& rSrcVector, CryQuat& rDstQuat)
{
#if DO_ASM && defined (_CPU_X86)
float fResultWXYZ[4];
quaternionExponent_x87 (&rSrcVector.x, fResultWXYZ);
rDstQuat.w = fResultWXYZ[0];
rDstQuat.v.x = fResultWXYZ[1];
rDstQuat.v.y = fResultWXYZ[2];
rDstQuat.v.z = fResultWXYZ[3];
assert (isEqual(rDstQuat,exp(quaternionf(0,rSrcVector))));
#else
CryQuat tmp;
tmp.v = rSrcVector;
tmp.w = 0;
rDstQuat = exp(tmp);
#endif
}
struct CryAABB
{
Vec3 vMin;
Vec3 vMax;
CryAABB() {}
/* CryAABB(struct IBindable* pObj) {
pObj->GetBBox(vMin, vMax);
}*/
CryAABB(const Vec3& a, const Vec3& b) {
vMin = a;
vMax = b;
}
CryAABB(const CryAABB& right) {
vMin = right.vMin;
vMax = right.vMax;
}
Vec3 getSize() const {return vMax-vMin;}
Vec3 getCenter() const {return (vMin+vMax)*0.5f;}
bool empty() const {return vMin.x >= vMax.x || vMin.y >= vMax.y || vMin.z >= vMax.z;}
void include(const Vec3& v) { AddToBounds(v, vMin, vMax);}
void include (const CryAABB& right) {
include (right.vMin);
include (right.vMax);
}
// static Vec3& toVec3d(Vec3&v) { return v; }
// static const Vec3& toVec3d(const Vec3&v) {return v;}
// static Vec3& toVec3d(Vec3dA16&v){return v.v;}
// static const Vec3& toVec3d(const Vec3dA16&v){return v.v;}
// CryAABB& operator = (const Vec3& v) {vMin=vMax = v; return *this;}
/*
CryAABB(const Vec3& a) {
toVec3d(vMin) = a;
toVec3d(vMax) = a;
}*/
};
template <typename TVec>
class CryBBox_tpl
{
public:
CryBBox_tpl(){}
CryBBox_tpl(const Vec3& a)
{
toVec3d(vMin) = a;
toVec3d(vMax) = a;
}
CryBBox_tpl(struct IBindable* pObj)
{
pObj->GetBBox(toVec3d(vMin), toVec3d(vMax));
}
CryBBox_tpl(const Vec3& a, const Vec3& b)
{
toVec3d(vMin) = a;
toVec3d(vMax) = b;
}
template <typename T2>
CryBBox_tpl(const CryBBox_tpl<T2>& right)
{
toVec3d(vMin) = toVec3d(right.vMin);
toVec3d(vMax) = toVec3d(right.vMax);
}
TVec vMin;
TVec vMax;
bool empty()const {return vMin.x >= vMax.x || vMin.y >= vMax.y || vMin.z >= vMax.z;}
static Vec3& toVec3d(Vec3&v){return v;}
static const Vec3& toVec3d(const Vec3&v){return v;}
static Vec3& toVec3d(Vec3dA16&v){return v.v;}
static const Vec3& toVec3d(const Vec3dA16&v){return v.v;}
Vec3 getSize() const {return toVec3d(vMax)-toVec3d(vMin);}
Vec3 getCenter() const {return (toVec3d(vMin)+toVec3d(vMax))*0.5f;}
CryBBox_tpl<TVec>& operator = (const Vec3& v) {toVec3d(vMin) = toVec3d(vMax) = v; return *this;}
void include(const Vec3& v) { AddToBounds(v, toVec3d(vMin), toVec3d(vMax));}
template <typename T3>
void include (const CryBBox_tpl<T3>&right)
{
include (toVec3d(right.vMin));
include (toVec3d(right.vMax));
}
};
//typedef CryBBox_tpl<Vec3> CryBBox;
typedef CryBBox_tpl<Vec3dA16> CryBBoxA16;
//extern Vec3 UntransformVector (const Matrix& m, const Vec3& v);
inline bool isUnit (const Vec3& v, float fTolerance)
{
float fLen = v.GetLengthSquared();
return fLen >= 1-fTolerance && fLen < 1+fTolerance;
}
inline bool isSane(float x)
{
return x > -1e9 && x < 1e9;
}
inline bool isSane (const Vec3& v)
{
return isSane(v.x) && isSane(v.y) && isSane(v.z);
}
inline bool isUnit (const TangData& td, float fTolerance)
{
return isUnit(td.binormal, fTolerance) && isUnit(td.tangent, fTolerance) && isUnit(td.tnormal, fTolerance);
}
#endif

View File

@@ -0,0 +1,311 @@
#ifdef WIN64
#include "WinDef.h"
#endif
#include "NvTriStripObjects.h"
#include "NvTriStrip.h"
#pragma warning(push)
#pragma warning(disable:4018) // signed/unsigned mismatch
#ifdef WIN64 //AMD Port
#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'xxx', possible loss of data
#endif
////////////////////////////////////////////////////////////////////////////////////////
//private data
static unsigned int cacheSize = CACHESIZE_GEFORCE3;// CACHESIZE_GEFORCE1_2;
static bool bStitchStrips = true;
static unsigned int minStripSize = 0;
static bool bListsOnly = false;
////////////////////////////////////////////////////////////////////////////////////////
// SetListsOnly()
//
// If set to true, will return an optimized list, with no strips at all.
//
// Default value: false
//
void SetListsOnly(const bool _bListsOnly)
{
bListsOnly = _bListsOnly;
}
////////////////////////////////////////////////////////////////////////////////////////
// SetCacheSize()
//
// Sets the cache size which the stripfier uses to optimize the data.
// Controls the length of the generated individual strips.
// This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2
// You may want to play around with this number to tweak performance.
//
// Default value: 16
//
void SetCacheSize(const unsigned int _cacheSize)
{
cacheSize = _cacheSize;
}
////////////////////////////////////////////////////////////////////////////////////////
// SetStitchStrips()
//
// bool to indicate whether to stitch together strips into one huge strip or not.
// If set to true, you'll get back one huge strip stitched together using degenerate
// triangles.
// If set to false, you'll get back a large number of separate strips.
//
// Default value: true
//
void SetStitchStrips(const bool _bStitchStrips)
{
bStitchStrips = _bStitchStrips;
}
////////////////////////////////////////////////////////////////////////////////////////
// SetMinStripSize()
//
// Sets the minimum acceptable size for a strip, in triangles.
// All strips generated which are shorter than this will be thrown into one big, separate list.
//
// Default value: 0
//
void SetMinStripSize(const unsigned int _minStripSize)
{
minStripSize = _minStripSize;
}
////////////////////////////////////////////////////////////////////////////////////////
// GenerateStrips()
//
// in_indices: input index list, the indices you would use to render
// in_numIndices: number of entries in in_indices
// primGroups: array of optimized/stripified PrimitiveGroups
// numGroups: number of groups returned
//
// Be sure to call delete[] on the returned primGroups to avoid leaking mem
//
void GenerateStrips(const unsigned short* in_indices, const unsigned int in_numIndices,
PrimitiveGroup** primGroups, unsigned short* numGroups)
{
//put data in format that the stripifier likes
int i;
WordVec tempIndices;
tempIndices.resize(in_numIndices);
unsigned short maxIndex = 0;
for(i = 0; i < (int)in_numIndices; i++)
{
tempIndices[i] = in_indices[i];
if(in_indices[i] > maxIndex)
maxIndex = in_indices[i];
}
NvStripInfoVec tempStrips;
NvFaceInfoVec tempFaces;
NvStripifier stripifier;
//do actual stripification
stripifier.Stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces);
//stitch strips together
IntVec stripIndices;
unsigned int numSeparateStrips = 0;
if(bListsOnly)
{
//if we're outputting only lists, we're done
*numGroups = 1;
(*primGroups) = new PrimitiveGroup[*numGroups];
PrimitiveGroup* primGroupArray = *primGroups;
//count the total number of indices
unsigned int numIndices = 0;
for(i = 0; i < tempStrips.size(); i++)
{
numIndices += tempStrips[i]->m_faces.size() * 3;
}
//add in the list
numIndices += tempFaces.size() * 3;
primGroupArray[0].type = PT_LIST;
primGroupArray[0].numIndices = numIndices;
primGroupArray[0].indices = new unsigned short[numIndices];
//do strips
unsigned int indexCtr = 0;
for(i = 0; i < tempStrips.size(); i++)
{
for(int j = 0; j < tempStrips[i]->m_faces.size(); j++)
{
//degenerates are of no use with lists
if(!NvStripifier::IsDegenerate(tempStrips[i]->m_faces[j]))
{
primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v0;
primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v1;
primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v2;
}
else
{
//we've removed a tri, reduce the number of indices
primGroupArray[0].numIndices -= 3;
}
}
}
//do lists
for(i = 0; i < tempFaces.size(); i++)
{
primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v0;
primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v1;
primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v2;
}
}
else
{
stripifier.CreateStrips(tempStrips, stripIndices, bStitchStrips, numSeparateStrips);
//if we're stitching strips together, we better get back only one strip from CreateStrips()
assert( (bStitchStrips && (numSeparateStrips == 1)) || !bStitchStrips);
//convert to output format
*numGroups = numSeparateStrips; //for the strips
if(tempFaces.size() != 0)
(*numGroups)++; //we've got a list as well, increment
(*primGroups) = new PrimitiveGroup[*numGroups];
PrimitiveGroup* primGroupArray = *primGroups;
//first, the strips
int startingLoc = 0;
for(int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++)
{
int stripLength = 0;
if(!bStitchStrips)
{
//if we've got multiple strips, we need to figure out the correct length
for(i = startingLoc; i < stripIndices.size(); i++)
{
if(stripIndices[i] == -1)
break;
}
stripLength = i - startingLoc;
}
else
stripLength = stripIndices.size();
primGroupArray[stripCtr].type = PT_STRIP;
primGroupArray[stripCtr].indices = new unsigned short[stripLength];
primGroupArray[stripCtr].numIndices = stripLength;
int indexCtr = 0;
for(int i = startingLoc; i < stripLength + startingLoc; i++)
primGroupArray[stripCtr].indices[indexCtr++] = stripIndices[i];
//we add 1 to account for the -1 separating strips
//this doesn't break the stitched case since we'll exit the loop
startingLoc += stripLength + 1;
}
//next, the list
if(tempFaces.size() != 0)
{
int faceGroupLoc = (*numGroups) - 1; //the face group is the last one
primGroupArray[faceGroupLoc].type = PT_LIST;
primGroupArray[faceGroupLoc].indices = new unsigned short[tempFaces.size() * 3];
primGroupArray[faceGroupLoc].numIndices = tempFaces.size() * 3;
int indexCtr = 0;
for(int i = 0; i < tempFaces.size(); i++)
{
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v0;
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v1;
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v2;
}
}
}
//clean up everything
//delete strips
for(i = 0; i < tempStrips.size(); i++)
{
for(int j = 0; j < tempStrips[i]->m_faces.size(); j++)
{
delete tempStrips[i]->m_faces[j];
tempStrips[i]->m_faces[j] = NULL;
}
tempStrips[i]->m_faces.resize(0);
delete tempStrips[i];
tempStrips[i] = NULL;
}
//delete faces
for(i = 0; i < tempFaces.size(); i++)
{
delete tempFaces[i];
tempFaces[i] = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////////////
// RemapIndices()
//
// Function to remap your indices to improve spatial locality in your vertex buffer.
//
// in_primGroups: array of PrimitiveGroups you want remapped
// numGroups: number of entries in in_primGroups
// numVerts: number of vertices in your vertex buffer, also can be thought of as the range
// of acceptable values for indices in your primitive groups.
// remappedGroups: array of remapped PrimitiveGroups
//
// Note that, according to the remapping handed back to you, you must reorder your
// vertex buffer.
//
void RemapIndices(const PrimitiveGroup* in_primGroups, const unsigned short numGroups,
const unsigned short numVerts, PrimitiveGroup** remappedGroups)
{
(*remappedGroups) = new PrimitiveGroup[numGroups];
//caches oldIndex --> newIndex conversion
int *indexCache;
indexCache = new int[numVerts];
memset(indexCache, -1, sizeof(int)*numVerts);
//loop over primitive groups
unsigned int indexCtr = 0;
for(int i = 0; i < numGroups; i++)
{
unsigned int numIndices = in_primGroups[i].numIndices;
//init remapped group
(*remappedGroups)[i].type = in_primGroups[i].type;
(*remappedGroups)[i].numIndices = numIndices;
(*remappedGroups)[i].indices = new unsigned short[numIndices];
for(int j = 0; j < numIndices; j++)
{
int cachedIndex = indexCache[in_primGroups[i].indices[j]];
if(cachedIndex == -1) //we haven't seen this index before
{
//point to "last" vertex in VB
(*remappedGroups)[i].indices[j] = indexCtr;
//add to index cache, increment
indexCache[in_primGroups[i].indices[j]] = indexCtr++;
}
else
{
//we've seen this index before
(*remappedGroups)[i].indices[j] = cachedIndex;
}
}
}
delete[] indexCache;
}
#pragma warning(pop)

View File

@@ -0,0 +1,131 @@
#ifndef NVTRISTRIP_H
#define NVTRISTRIP_H
#ifndef NULL
#define NULL 0
#endif
////////////////////////////////////////////////////////////////////////////////////////
// Public interface for stripifier
////////////////////////////////////////////////////////////////////////////////////////
//GeForce1 and 2 cache size
#define CACHESIZE_GEFORCE1_2 16
//GeForce3 cache size
#define CACHESIZE_GEFORCE3 24
enum PrimType
{
PT_LIST,
PT_STRIP,
PT_FAN
};
struct SPrimitiveGroup
{
PrimType type;
unsigned int numIndices;
unsigned int offsIndex;
unsigned int numTris;
unsigned int nFirstFace;
};
struct PrimitiveGroup
{
PrimType type;
unsigned int numIndices;
unsigned short* indices;
////////////////////////////////////////////////////////////////////////////////////////
PrimitiveGroup() : type(PT_STRIP), numIndices(0), indices(NULL) {}
~PrimitiveGroup()
{
if(indices)
delete[] indices;
indices = NULL;
}
};
////////////////////////////////////////////////////////////////////////////////////////
// SetCacheSize()
//
// Sets the cache size which the stripfier uses to optimize the data.
// Controls the length of the generated individual strips.
// This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2
// You may want to play around with this number to tweak performance.
//
// Default value: 16
//
void SetCacheSize(const unsigned int cacheSize);
////////////////////////////////////////////////////////////////////////////////////////
// SetStitchStrips()
//
// bool to indicate whether to stitch together strips into one huge strip or not.
// If set to true, you'll get back one huge strip stitched together using degenerate
// triangles.
// If set to false, you'll get back a large number of separate strips.
//
// Default value: true
//
void SetStitchStrips(const bool bStitchStrips);
////////////////////////////////////////////////////////////////////////////////////////
// SetMinStripSize()
//
// Sets the minimum acceptable size for a strip, in triangles.
// All strips generated which are shorter than this will be thrown into one big, separate list.
//
// Default value: 0
//
void SetMinStripSize(const unsigned int minSize);
////////////////////////////////////////////////////////////////////////////////////////
// SetListsOnly()
//
// If set to true, will return an optimized list, with no strips at all.
//
// Default value: false
//
void SetListsOnly(const bool bListsOnly);
////////////////////////////////////////////////////////////////////////////////////////
// GenerateStrips()
//
// in_indices: input index list, the indices you would use to render
// in_numIndices: number of entries in in_indices
// primGroups: array of optimized/stripified PrimitiveGroups
// numGroups: number of groups returned
//
// Be sure to call delete[] on the returned primGroups to avoid leaking mem
//
void GenerateStrips(const unsigned short* in_indices, const unsigned int in_numIndices,
PrimitiveGroup** primGroups, unsigned short* numGroups);
////////////////////////////////////////////////////////////////////////////////////////
// RemapIndices()
//
// Function to remap your indices to improve spatial locality in your vertex buffer.
//
// in_primGroups: array of PrimitiveGroups you want remapped
// numGroups: number of entries in in_primGroups
// numVerts: number of vertices in your vertex buffer, also can be thought of as the range
// of acceptable values for indices in your primitive groups.
// remappedGroups: array of remapped PrimitiveGroups
//
// Note that, according to the remapping handed back to you, you must reorder your
// vertex buffer.
//
// Credit goes to the MS Xbox crew for the idea for this interface.
//
void RemapIndices(const PrimitiveGroup* in_primGroups, const unsigned short numGroups,
const unsigned short numVerts, PrimitiveGroup** remappedGroups);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
#ifndef NV_TRISTRIP_OBJECTS_H
#define NV_TRISTRIP_OBJECTS_H
#include <assert.h>
#ifdef WIN32
#include <windows.h>
#endif
#include <vector>
#include <list>
#include "VertexCache.h"
/////////////////////////////////////////////////////////////////////////////////
//
// Types defined for stripification
//
/////////////////////////////////////////////////////////////////////////////////
struct MyVertex {
float x, y, z;
float nx, ny, nz;
};
typedef MyVertex MyVector;
struct MyFace {
int v1, v2, v3;
float nx, ny, nz;
};
class NvFaceInfo {
public:
// vertex indices
NvFaceInfo(int v0, int v1, int v2, bool bIsFake = false){
m_v0 = v0; m_v1 = v1; m_v2 = v2;
m_stripId = -1;
m_testStripId = -1;
m_experimentId = -1;
m_bIsFake = bIsFake;
}
// data members are left public
int m_v0, m_v1, m_v2;
int m_stripId; // real strip Id
int m_testStripId; // strip Id in an experiment
int m_experimentId; // in what experiment was it given an experiment Id?
bool m_bIsFake; //if true, will be deleted when the strip it's in is deleted
};
class NvEdgeInfo {
public:
// constructor puts 1 ref on us
NvEdgeInfo (int v0, int v1){
m_v0 = v0;
m_v1 = v1;
m_face0 = NULL;
m_face1 = NULL;
m_nextV0 = NULL;
m_nextV1 = NULL;
// we will appear in 2 lists. this is a good
// way to make sure we delete it the second time
// we hit it in the edge infos
m_refCount = 2;
}
// ref and unref
void Unref () { if (--m_refCount == 0) delete this; }
// data members are left public
unsigned int m_refCount;
NvFaceInfo *m_face0, *m_face1;
int m_v0, m_v1;
NvEdgeInfo *m_nextV0, *m_nextV1;
};
// This class is a quick summary of parameters used
// to begin a triangle strip. Some operations may
// want to create lists of such items, so they were
// pulled out into a class
class NvStripStartInfo {
public:
NvStripStartInfo(NvFaceInfo *startFace, NvEdgeInfo *startEdge, bool toV1){
m_startFace = startFace;
m_startEdge = startEdge;
m_toV1 = toV1;
}
NvFaceInfo *m_startFace;
NvEdgeInfo *m_startEdge;
bool m_toV1;
};
typedef std::vector<NvFaceInfo*> NvFaceInfoVec;
typedef std::list <NvFaceInfo*> NvFaceInfoList;
typedef std::list <NvFaceInfoVec*> NvStripList;
typedef std::vector<NvEdgeInfo*> NvEdgeInfoVec;
typedef std::vector<unsigned short> WordVec;
typedef std::vector<int> IntVec;
typedef std::vector<MyVertex> MyVertexVec;
typedef std::vector<MyFace> MyFaceVec;
template<class T>
inline void SWAP(T& first, T& second)
{
T temp = first;
first = second;
second = temp;
}
// This is a summary of a strip that has been built
class NvStripInfo {
public:
// A little information about the creation of the triangle strips
NvStripInfo(const NvStripStartInfo &startInfo, int stripId, int experimentId = -1) :
m_startInfo(startInfo)
{
m_stripId = stripId;
m_experimentId = experimentId;
visited = false;
m_numDegenerates = 0;
}
// This is an experiment if the experiment id is >= 0
inline bool IsExperiment () const { return m_experimentId >= 0; }
inline bool IsInStrip (const NvFaceInfo *faceInfo) const
{
if(faceInfo == NULL)
return false;
return (m_experimentId >= 0 ? faceInfo->m_testStripId == m_stripId : faceInfo->m_stripId == m_stripId);
}
bool SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos);
// take the given forward and backward strips and combine them together
void Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward);
//returns true if the face is "unique", i.e. has a vertex which doesn't exist in the faceVec
bool Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face);
// mark the triangle as taken by this strip
bool IsMarked (NvFaceInfo *faceInfo);
void MarkTriangle(NvFaceInfo *faceInfo);
// build the strip
void Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos);
// public data members
NvStripStartInfo m_startInfo;
NvFaceInfoVec m_faces;
int m_stripId;
int m_experimentId;
bool visited;
int m_numDegenerates;
};
typedef std::vector<NvStripInfo*> NvStripInfoVec;
//The actual stripifier
class NvStripifier {
public:
// Constructor
NvStripifier();
~NvStripifier();
//the target vertex cache size, the structure to place the strips in, and the input indices
void Stripify(const WordVec &in_indices, const int in_cacheSize, const int in_minStripLength,
const unsigned short maxIndex, NvStripInfoVec &allStrips, NvFaceInfoVec &allFaces);
void CreateStrips(const NvStripInfoVec& allStrips, IntVec& stripIndices, const bool bStitchStrips, unsigned int& numSeparateStrips);
static int GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB);
//static int GetSharedVertex(NvFaceInfo *faceA, NvFaceInfo *faceB);
static void GetSharedVertices(NvFaceInfo *faceA, NvFaceInfo *faceB, int* vertex0, int* vertex1);
static bool IsDegenerate(const NvFaceInfo* face);
protected:
WordVec indices;
int cacheSize;
int minStripLength;
float meshJump;
bool bFirstTimeResetPoint;
/////////////////////////////////////////////////////////////////////////////////
//
// Big mess of functions called during stripification
//
/////////////////////////////////////////////////////////////////////////////////
//********************
bool IsMoneyFace(const NvFaceInfo& face);
bool FaceContainsIndex(const NvFaceInfo& face, const unsigned int index);
bool IsCW(NvFaceInfo *faceInfo, int v0, int v1);
bool NextIsCW(const int numIndices);
bool IsDegenerate(const unsigned short v0, const unsigned short v1, const unsigned short v2);
static int GetNextIndex(const WordVec &indices, NvFaceInfo *face);
static NvEdgeInfo *FindEdgeInfo(NvEdgeInfoVec &edgeInfos, int v0, int v1);
static NvFaceInfo *FindOtherFace(NvEdgeInfoVec &edgeInfos, int v0, int v1, NvFaceInfo *faceInfo);
NvFaceInfo *FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
void FindAllStrips(NvStripInfoVec &allStrips, NvFaceInfoVec &allFaceInfos, NvEdgeInfoVec &allEdgeInfos, int numSamples);
void SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips, NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList);
void RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList);
bool FindTraversal(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, NvStripInfo *strip, NvStripStartInfo &startInfo);
int CountRemainingTris(std::list<NvStripInfo*>::iterator iter, std::list<NvStripInfo*>::iterator end);
void CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips);
float AvgStripSize(const NvStripInfoVec &strips);
int FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
void UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip);
void UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face);
float CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip);
int CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face);
int NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec);
void BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, const unsigned short maxIndex);
bool AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos);
// let our strip info classes and the other classes get
// to these protected stripificaton methods if they want
friend class NvStripInfo;
};
#endif

View File

@@ -0,0 +1,79 @@
#ifndef VERTEX_CACHE_H
#define VERTEX_CACHE_H
class VertexCache
{
public:
VertexCache(int size)
{
numEntries = size;
entries = new int[numEntries];
for(int i = 0; i < numEntries; i++)
entries[i] = -1;
}
VertexCache() { VertexCache(16); }
~VertexCache() { delete[] entries; entries = 0; }
bool InCache(int entry)
{
bool returnVal = false;
for(int i = 0; i < numEntries; i++)
{
if(entries[i] == entry)
{
returnVal = true;
break;
}
}
return returnVal;
}
int AddEntry(int entry)
{
int removed;
removed = entries[numEntries - 1];
//push everything right one
for(int i = numEntries - 2; i >= 0; i--)
{
entries[i + 1] = entries[i];
}
entries[0] = entry;
return removed;
}
void Clear()
{
memset(entries, -1, sizeof(int) * numEntries);
}
void Copy(VertexCache* inVcache)
{
for(int i = 0; i < numEntries; i++)
{
inVcache->Set(i, entries[i]);
}
}
int At(int index) { return entries[index]; }
void Set(int index, int value) { entries[index] = value; }
private:
int *entries;
int numEntries;
};
#endif

View File

@@ -0,0 +1,44 @@
#ifndef _CRY_ANIMATION_QUATERNION_EXPONENT_HDR_
#define _CRY_ANIMATION_QUATERNION_EXPONENT_HDR_
#include "platform.h"
#if defined(_CPU_X86) && !defined(LINUX)
#ifdef __cplusplus
extern "C" {
#endif
//////////////////////////////////////////////////////////////////////////
// x87 asm optimized quaternion exponent
// Estimated runtime in good conditions: 315 cycles on P4
// PARAMETERS:
// pSrcVector[IN] - the vector to calculate the exponent for
// pDstQuat [OUT]- the quaternion (exponent of the input)
// NOTE:
// The input vector mimics a quaternion with 0 real component (W)
// This version uses FSINCOS, which takes ~70% of execution time
//////////////////////////////////////////////////////////////////////////
void quaternionExponent_x87(const float* pSrc, float* pDst);
//////////////////////////////////////////////////////////////////////////
// x87 asm optimized quaternion exponent
// Estimated runtime: 110 cycles on P4
// PARAMETERS:
// pSrcVector[IN] - the vector to calculate the exponent for
// pDstQuat [OUT]- the quaternion (exponent of the input)
// WARNING:
// the source vector length should be no more than 3-4, otherwise the sin/cos
// approximations won't work
// NOTE:
// The input vector mimics a quaternion with 0 real component (W)
// This version uses approximation to FSINCOS (tailor series up to 9th magnitude)
//////////////////////////////////////////////////////////////////////////
void quaternionExponent_x87approx(const float* pSrc, float* pDst);
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@@ -0,0 +1,37 @@
========================================================================
DYNAMIC LINK LIBRARY : ResourceCompilerPC Project Overview
========================================================================
AppWizard has created this ResourceCompilerPC DLL for you.
This file contains a summary of what you will find in each of the files that
make up your ResourceCompilerPC application.
ResourceCompilerPC.vcproj
This is the main project file for VC++ projects generated using an Application Wizard.
It contains information about the version of Visual C++ that generated the file, and
information about the platforms, configurations, and project features selected with the
Application Wizard.
ResourceCompilerPC.cpp
This is the main DLL source file.
/////////////////////////////////////////////////////////////////////////////
Other standard files:
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file
named ResourceCompilerPC.pch and a precompiled types file named StdAfx.obj.
/////////////////////////////////////////////////////////////////////////////
Other notes:
This is a resource compiler DLL, which contains the Convertors for PC platform.
All the shared code (like CGF loading/processing, that is the same for all platforms)
must reside in the resource compiler exe. All platform-specific code must be here.
AppWizard uses "TODO:" comments to indicate parts of the source code you
should add to or customize.
/////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,459 @@
#include "stdafx.h"
#include "RenderMeshBuilder.h"
#include "StlUtils.h"
#include "NvTriStrip/NvTriStrip.h"
// constructs everything for the render mesh out of the given mesh
void CRenderMeshBuilder::build (const CryChunkedFile::MeshDesc* pMeshDesc)
{
clear();
m_pMeshDesc = pMeshDesc;
// build the tangent bases
MeshProxy Proxy;
Proxy.init(pMeshDesc);
m_TangBaseBuilder.CalculateTangentSpace (Proxy);
buildExtToIntMaps();
// create the m_arrMtlFaces: the faces are sorted and perhaps
// degraded faces are deleted
buildMtlFaces();
// create the indices and the array m_arrMaterials
buildIndexBuffer();
// optimize the final vertex buffer spacial locality
remapIndicesForVBCache();
//selfValidate();
}
// increases all indices of materials by the given offset
void CRenderMeshBuilder::addMaterialOffset (unsigned nOffset)
{
for (MaterialGroupArray::iterator it = m_arrPrimGroups.begin(); it != m_arrPrimGroups.end(); ++it)
it->nMaterial += nOffset;
}
// cleans up the object
void CRenderMeshBuilder::clear()
{
m_arrIndices.clear();
m_arrPrimGroups.clear();
m_arrExtTangMap.clear();
m_arrExtUVMap.clear();
m_arrExtTangents.clear();
m_arrMtlFaces.clear();
m_arrExtFaces.clear();
m_mapVUVP.clear();
m_arrExtToTBBMap.clear();
m_pMeshDesc = NULL;
}
// returns the number of vertices in the resulting vertex buffer
unsigned CRenderMeshBuilder::numVertices()const
{
// the number of external tangent bases determine this, because the
// tangent base calculation algorithm completely splits all the necessary
// vertices, so that the vertex buffer can be formed
return m_arrExtTangents.size();
}
// prepares the m_arrExtTangents, m_arrExtToTBBMap, m_arrExtTangMap and m_arrExtUVMap
void CRenderMeshBuilder::prepareExtToIntMapping ()
{
unsigned numTBBTangents = m_TangBaseBuilder.GetBaseCount();
unsigned numTBBTangents_Reserve = numTBBTangents*9/7;
// prepare ext->TBB and the table of actual tangents
m_arrExtToTBBMap.reserve (numTBBTangents_Reserve);
//m_arrExtToTBBMap.resize (numTBBTangents);
m_arrExtTangents.reserve (numTBBTangents_Reserve);
//m_arrExtTangents.resize (numTBBTangents);
//for (i = 0; i < numTBBTangents; ++i)
//{
// m_arrExtToTBBMap[i] = i;
// TangData& rBase = m_arrExtTangents[i];
// m_TangBaseBuilder.GetBase(i, &rBase.tangent.x, &rBase.binormal.x, &rBase.tnormal.x);
//}
// create the indexation map new->old
m_arrExtTangMap.reserve(numTBBTangents_Reserve);
//m_arrExtTangMap.resize (numTBBTangents,-1);
if (m_pMeshDesc->numTexFaces())
{
m_arrExtUVMap.reserve(numTBBTangents_Reserve);
//m_arrExtUVMap.resize (numTBBTangents, -1);
}
m_arrExtFaces.reserve (m_pMeshDesc->numFaces());
}
// adds an entry to all required maps - m_arrExtTangents, m_arrExtToTBBMap, m_arrExtTangMap and m_arrExtUVMap
bool CRenderMeshBuilder::addExtToIntMapEntry (DWORD FaceExt[3], const CryFace& FaceInt, const CryTexFace& TexFaceInt)
{
unsigned numTBBTangents = m_TangBaseBuilder.GetBaseCount();
assert (m_arrExtTangMap.size() == m_arrExtUVMap.size() || m_arrExtUVMap.empty());
assert (m_arrExtTangMap.size() == m_arrExtTangents.size());
CryFace NewExtFace = FaceInt;
if (FaceExt[0] == FaceExt[1] || FaceExt[1] == FaceExt[2] || FaceExt[2] == FaceExt[0])
{
return false;
}
for (unsigned i = 0; i < 3; ++i)
{
VertexUVPair VUVPair((ushort)FaceInt[i],(ushort)TexFaceInt[i], (ushort)FaceExt[i]);
VertexUVPairMap::iterator itVUVPair = m_mapVUVP.find (VUVPair);
unsigned nExtEntry;
if (itVUVPair == m_mapVUVP.end())
{
// no such pair, add a new one.
nExtEntry = m_arrExtTangents.size();
TangData rBase;
m_TangBaseBuilder.GetBase (FaceExt[i], &rBase.tangent.x, &rBase.binormal.x, &rBase.tnormal.x);
AdjustBase(rBase);
m_arrExtTangents.push_back (rBase);
m_arrExtTangMap.push_back(FaceInt[i]);
m_arrExtToTBBMap.push_back((ushort)FaceExt[i]);
if (m_pMeshDesc->numTexFaces())
m_arrExtUVMap.push_back(TexFaceInt[i]);
m_mapVUVP.insert (VertexUVPairMap::value_type(VUVPair, nExtEntry));
}
else
{
// there's already such a pair, use it
nExtEntry = itVUVPair->second;
}
NewExtFace[i] = nExtEntry;
}
assert(!NewExtFace.isDegenerate());
m_arrExtFaces.push_back(NewExtFace);
return true;
}
// creates the mapping from the external to internal indices
void CRenderMeshBuilder::buildExtToIntMaps()
{
unsigned i,numTBBTangents = m_TangBaseBuilder.GetBaseCount();
prepareExtToIntMapping();
unsigned numDegenerate = 0;
CryTexFace TexFaceInt (0,0,0);
DWORD FaceExt[3];
for (i = 0; i < m_pMeshDesc->numFaces(); ++i)
{
// internal indexation face
const CryFace& FaceInt = m_pMeshDesc->pFaces[i];
// external indexation face
m_TangBaseBuilder.GetTriangleBaseIndices(i, FaceExt);
if (m_pMeshDesc->numTexFaces())
TexFaceInt = m_pMeshDesc->pTexFaces[i];
if (!addExtToIntMapEntry (FaceExt, FaceInt, TexFaceInt))
{
++numDegenerate;
continue; // degenerate face
}
#ifdef _DEBUG
CryFace& NewExtFace = m_arrExtFaces.back();
for (int j = 0; j < 3; ++j)
{
assert (m_arrExtUVMap[NewExtFace[j]] == TexFaceInt[j]);
assert (m_arrExtTangMap[NewExtFace[j]] == FaceInt[j]);
}
#endif
}
if (numDegenerate)
LogWarning("%u degenerate faces (skipped)", numDegenerate);
}
// calculate the number of elements == nEl
// create the indices and the array m_arrMaterials
// Create the m_arrMtlFaces
// degraded faces are deleted
void CRenderMeshBuilder::buildMtlFaces()
{
unsigned nFace, numExtFaces = m_arrExtFaces.size();
// pass 1: calculate each material's number of faces, and the number of materials
// SKIPPED NOW
const unsigned nMaxMatID = 0x400;
// pass 2: create the face groups and reserve space for the faces
m_arrMtlFaces.reserve (nMaxMatID/4);
unsigned numSkippedFaces = 0;
// pass 3: create the faces in the face groups
for (nFace = 0; nFace < numExtFaces; ++nFace)
{
// external indexation face
const CryFace& rExtFace = m_arrExtFaces[nFace];
int nMatID = rExtFace.MatID;
// material id of the face to count
if (nMatID < 0 || nMatID > nMaxMatID)
{
++numSkippedFaces;
continue;
}
assert (!rExtFace.isDegenerate());
if (m_arrMtlFaces.size() <= (unsigned)nMatID)
m_arrMtlFaces.resize (nMatID+1);
m_arrMtlFaces[nMatID].push_back (Face(rExtFace));
}
if (numSkippedFaces)
LogWarning ("%d faces skipped: no material or material id is out of range", numSkippedFaces);
}
//////////////////////////////////////////////////////////////////////////
// create the indices and the array m_arrMaterials out of m_arrMtlFaces
void CRenderMeshBuilder::buildIndexBuffer()
{
m_arrIndices.reserve (m_pMeshDesc->numFaces()*3);
SetListsOnly (true);
for (unsigned nMaterial = 0; nMaterial < m_arrMtlFaces.size(); ++nMaterial)
{
const FaceArray& arrFaces = m_arrMtlFaces[nMaterial];
if (arrFaces.empty())
continue;
PrimitiveGroup* pGroup = NULL;
unsigned short numGroups = 0;
GenerateStrips((unsigned short*)&arrFaces[0], arrFaces.size() * 3, &pGroup, &numGroups);
for (unsigned nGroup = 0; nGroup < numGroups; ++nGroup)
appendNvidiaStrip (pGroup[nGroup], nMaterial);
delete[numGroups] pGroup;
}
}
//////////////////////////////////////////////////////////////////////////
// remaps (transposes, permutates) the indices to improve spatial locality of the vertex buffer
void CRenderMeshBuilder::remapIndicesForVBCache()
{
// this is the old->new indexation
std::vector<unsigned> arrVCache;
arrVCache.resize (m_arrExtTangents.size(), -1);
unsigned nNextVertex = 0;
for (unsigned i = 0; i < m_arrIndices.size(); ++i)
{
ushort nVertex = m_arrIndices[i];
if (arrVCache[nVertex] == -1)
// we've met this vertex for the first time
arrVCache[nVertex] = nNextVertex++;
}
remapExtIndices(&arrVCache[0], nNextVertex);
}
// permutate the contents of the array with a permutation old->new
template <class T>
void Permutate (std::vector<T>& arrOld, unsigned* pPermutation, unsigned newSize)
{
assert (newSize <= arrOld.size());
std::vector<T> arrNew;
arrNew.resize (newSize);
for (unsigned nEntry = 0; nEntry < arrOld.size(); ++nEntry)
{
if (pPermutation[nEntry] < arrNew.size())
{
arrNew[pPermutation[nEntry]] = arrOld[nEntry];
}
else
assert (pPermutation[nEntry] == -1);
}
arrOld.swap(arrNew);
}
// remaps external indices according to the given permutation old->new
void CRenderMeshBuilder::remapExtIndices (unsigned* pPermutation, unsigned numNewVertices)
{
unsigned numVertices = this->numVertices();
// remap the indices
for (unsigned nIndex = 0; nIndex < m_arrIndices.size(); ++nIndex)
{
assert (m_arrIndices[nIndex] < numVertices);
m_arrIndices[nIndex] = pPermutation[m_arrIndices[nIndex]];
assert (m_arrIndices[nIndex] < numNewVertices);
}
for (unsigned nFace = 0; nFace < m_arrExtFaces.size(); ++nFace)
{
m_arrExtFaces[nFace].v0 = pPermutation[m_arrExtFaces[nFace].v0];
m_arrExtFaces[nFace].v1 = pPermutation[m_arrExtFaces[nFace].v1];
m_arrExtFaces[nFace].v2 = pPermutation[m_arrExtFaces[nFace].v2];
}
// remap the ExtToInt mappings
assert (m_arrExtTangMap.size() == numVertices);
Permutate(m_arrExtTangMap, pPermutation, numNewVertices);
if (m_pMeshDesc->numTexFaces())
{
assert (m_arrExtUVMap.size() == numVertices);
Permutate(m_arrExtUVMap, pPermutation, numNewVertices);
}
assert (m_arrExtTangents.size() == numVertices);
// remap the tangent bases
Permutate(m_arrExtTangents, pPermutation, numNewVertices);
assert (m_arrExtTangents.size() == numNewVertices);
}
//////////////////////////////////////////////////////////////////////////
// add the primitive group(s) and indices (m_arrPrimGroups and m_arrIndices)
// from the given primitives generated by Nvidia Stripifier
void CRenderMeshBuilder::appendNvidiaStrip (const struct PrimitiveGroup& rGroup, unsigned nMaterial)
{
int j;
// in case we'll add this material group, collect info in it
MaterialGroup MatGroup;
MatGroup.nMaterial = nMaterial;
MatGroup.nIndexBase = m_arrIndices.size();
MatGroup.numIndices = 0;
for (int nIndex = 0; nIndex < (int)rGroup.numIndices - 2; )
{
int v[3];
unsigned short* src = rGroup.indices + nIndex;
switch (rGroup.type)
{
case PT_LIST:
v[0] = src[0];
v[1] = src[1];
v[2] = src[2];
nIndex += 3;
break;
case PT_STRIP:
if (nIndex&1)
{
v[0] = src[1];
v[1] = src[0];
v[2] = src[2];
}
else
{
v[0] = src[0];
v[1] = src[1];
v[2] = src[2];
}
nIndex += 1;
break;
case PT_FAN:
v[0] = rGroup.indices[0];
v[1] = src[1];
v[2] = src[2];
break;
}
if (v[0] == v[1] || v[1] == v[2] || v[2] == v[0])
continue;
MatGroup.numIndices += 3;
for (j = 0; j < 3; ++j)
m_arrIndices.push_back(v[j]);
}
if (MatGroup.numIndices)
{
// there were some triangles - add a new group or append those triangles to the previous group
if (!m_arrPrimGroups.empty() && m_arrPrimGroups.back().nMaterial == nMaterial)
m_arrPrimGroups.back().numIndices += MatGroup.numIndices;
else
m_arrPrimGroups.push_back(MatGroup);
}
}
void CRenderMeshBuilder::selfValidate()
{
assert (m_arrExtFaces.size() <= m_pMeshDesc->numFaces());
unsigned numFaces = m_arrExtFaces.size();
for (unsigned nFace = 0; nFace < numFaces; ++nFace)
{
CryFace ExtFace = m_arrExtFaces[nFace];
CryFace IntFace = m_pMeshDesc->pFaces[nFace];
CryTexFace TexFace = m_pMeshDesc->pTexFaces[nFace];
for (int i = 0; i < 3; ++i)
{
// this is only applicable to a normal manifold mesh
assert (m_arrExtUVMap[ExtFace[i]] == TexFace[i]);
assert (m_arrExtTangMap[ExtFace[i]] == IntFace[i]);
}
}
}
// adjusts the base - converts from Martin's algorithm's requirements to the engine requirements
void CRenderMeshBuilder::AdjustBase(TangData& rBase)
{
/*
float fBinormal = rBase.binormal * rBase.tnormal;
float fTangent = rBase.tangent * rBase.tnormal;
assert (fabs(fBinormal) < 1e-2 && fabs(fTangent) < 1e-2);
/* // normalize the normal
float fEpsilon = 0.0005f;
float fSqrt1_2 = 0.70710678118654752440084436210485f; // square root of 1/2
float fNormalLen = rBase.tnormal.Length();
if (fNormalLen < fEpsilon)
rBase.tnormal /= fNormalLen;
rBase.tnormal /= fNormalLen;
// make the bisect that
Vec3d vBisect = rBase.binormal+rBase.tangent;
vBisect -= (vBisect * rBase.tnormal) * rBase.tnormal; // make it orthogonal to the normal
float fBisectLen = vBisect.Length();
if (fBisectLen < fEpsilon)
return;
vBisect /= fBisectLen;
Vec3d vBase = vBisect ^ rBase.tnormal;
if (rBase.binormal * vBase > rBase.tangent * vBase)
{
assert (rBase.binormal * vBase > 0 && rBase.tangent * vBase < 0);
rBase.binormal = (vBisect + vBase) * fSqrt1_2;
rBase.tangent = (vBisect - vBase) * fSqrt1_2;
}
else
{
assert (rBase.binormal * vBase < 0 && rBase.tangent * vBase > 0);
rBase.binormal = (vBisect - vBase) * fSqrt1_2;
rBase.tangent = (vBisect + vBase) * fSqrt1_2;
}
*/
//std::swap(rBase.binormal, rBase.tangent);
rBase.binormal = -rBase.binormal;
}
void CRenderMeshBuilder::MeshProxy::GetPos( const DWORD indwPos, float outfPos[3] ) const
{
const Vec3d ptPos = m_pMeshDesc->pVertices[indwPos].p;
// unrotate the object
for (int i = 0; i < 3; ++i)
outfPos[i] = m_tm(0,i)*ptPos.x + m_tm(1,i)*ptPos.y + m_tm(2,i)*ptPos.z;
}

View File

@@ -0,0 +1,225 @@
// This is the class that makes a renderable (stripified) vertex/index buffers
// and tangent bases out of a CGF mesh
#ifndef _RC_RENDER_MESH_BUILDER_HDR_
#define _RC_RENDER_MESH_BUILDER_HDR_
#include "CryChunkedFile.h"
#include "TangentSpaceCalculation.h"
#include "CryCompiledFile.h"
// Calculates tangent spaces
// Builds index buffer (stripifies), material group array, ext-to-int map.
class CRenderMeshBuilder
{
public:
// constructs everything for the render mesh out of the given mesh
void build (const CryChunkedFile::MeshDesc* pMeshDesc);
// increases all indices of materials by the given offset
void addMaterialOffset (unsigned nOffset);
// cleans up the object
void clear();
// this error class is thrown from the constructor when the object can't be constructed
class Error
{
public:
Error(const char* szDesc):m_szDesc(szDesc){}
const char* c_str() const{return m_szDesc;}
protected:
const char* m_szDesc;
};
// returns the number of vertices in the resulting vertex buffer
unsigned numVertices()const;
// face/vertex index type
typedef unsigned short ushort;
// this is the index buffer (external indexation)
std::vector<ushort> m_arrIndices;
// this array represents the groups of indices in the index buffer:
// each group has its own material id and number of elements (indices, i.e. number of faces * 3 in case of strip stripification)
typedef CCFMaterialGroup MaterialGroup;
typedef std::vector<MaterialGroup> MaterialGroupArray;
MaterialGroupArray m_arrPrimGroups;
// this is the mapping from new indices to original
std::vector<ushort> m_arrExtTangMap;
// this is the mapping from new indices to original UV indices
// in the original CGF, texture and geometric mesh (faces/tex faces) are
// different (tex face indices are not necessarily the same as face indices),
// this is why the ExtToInt map doesn't coincide with this ExtUVMap
std::vector<ushort> m_arrExtUVMap;
// these are the tangent bases (external indexation)
std::vector<TangData> m_arrExtTangents;
#pragma pack(push,2)
// this is a group of faces, as they will be inside a group (material)
struct Face
{
ushort v[3];
Face(){}
Face(ushort v0, ushort v1, ushort v2)
{
v[0] = v0; v[1] = v1; v[2] = v2;
}
Face (const CryFace& rFace)
{
v[0] = rFace.v0; v[1] = rFace.v1; v[2] = rFace.v2;
}
Face (DWORD src[3])
{
v[0] = (ushort)src[0]; v[1] = (ushort)src[1]; v[2] = (ushort)src[2];
}
bool isDegenerate() const
{
return v[0] == v[1] || v[1] == v[2] || v[2] == v[0];
}
};
#pragma pack(pop)
// WARNING: this array must be binary-compatible with the array of unsigned shorts
// passed to Nvidia Stripifier. external indexation
typedef std::vector<Face> FaceArray;
// this is the array of faces for each material
std::vector<FaceArray> m_arrMtlFaces;
// this is the actual array of faces, but in the final external indexation
std::vector<CryFace> m_arrExtFaces;
protected:
// adjusts the base - converts from Martin's algorithm's requirements to the engine requirements
static void AdjustBase(TangData& rBase);
// creates the mapping from the external to internal indices
void buildExtToIntMaps();
// create m_arrMtlFaces; the degenerated faces are not included
void buildMtlFaces();
// create the indices and the array m_arrMaterials out of m_arrMtlFaces
void buildIndexBuffer();
// remaps (transposes, permutates) the indices to improve spatial locality of the vertex buffer
void remapIndicesForVBCache();
// remaps external indices according to the given permutation old->new
void remapExtIndices (unsigned* pPermutation, unsigned numNewTargets);
// prepares the m_arrExtTangents, m_arrExtToTBBMap, m_arrExtTangMap and m_arrExtUVMap
void prepareExtToIntMapping();
// adds an entry to all required maps - m_arrExtTangents, m_arrExtToTBBMap, m_arrExtTangMap and m_arrExtUVMap
bool addExtToIntMapEntry (DWORD FaceExt[3], const CryFace& FaceInt, const CryTexFace &TexFaceInt);
protected:
// add the primitive group(s) and indices (m_arrPrimGroups and m_arrIndices)
// from the given primitives generated by Nvidia Stripifier
void appendNvidiaStrip (const struct PrimitiveGroup& Group, unsigned nMaterial);
void selfValidate();
protected:
const CryChunkedFile::MeshDesc* m_pMeshDesc;
struct VertexUVPair
{
VertexUVPair(){}
VertexUVPair(ushort _nVertex, ushort _nTexVertex, ushort _nExtTangent):
nVertex(_nVertex), nTexVertex (_nTexVertex), nExtTangent(_nExtTangent){}
bool operator < (const VertexUVPair& right)const
{
return
nVertex < right.nVertex ? true:
nVertex > right.nVertex? false:
nTexVertex < right.nTexVertex? true:
nTexVertex > right.nTexVertex? false:
nExtTangent < right.nExtTangent;
}
ushort nVertex; // vertex in internal indexation
ushort nTexVertex; // texture vertex (UV) in internal indexation
ushort nExtTangent; // vertex in TBB indexation, or tangent in the array of tangents generated by TBB
};
// this is the map from the vertex-uv pair to the index of the
// temporary vertex mapping in this->arrVertMap;
typedef std::map <VertexUVPair,ushort> VertexUVPairMap;
//////////////////////////////////////////////////////////////////////////
// a proxy structure that gets passed to the tangent space calculation algorithm
struct MeshProxy
{
public:
MeshProxy ()
{
m_pMeshDesc = NULL;
}
// creates temporary mapping for splitting the vertices
// with different UVs
void init (const CryChunkedFile::MeshDesc* pMeshDesc)
{
m_pMeshDesc = pMeshDesc;
m_tm = m_pMeshDesc->pNode->pDesc->tm;
}
DWORD GetTriangleCount( void ) const
{
return m_pMeshDesc->numFaces();
}
void GetTriangleIndices( const DWORD indwTriNo, DWORD outdwPos[3], DWORD outdwNorm[3], DWORD outdwUV[3] ) const
{
const CryFace& rFace = m_pMeshDesc->pFaces[indwTriNo];
outdwNorm[0] = outdwPos[0] = rFace.v0;
outdwNorm[1] = outdwPos[1] = rFace.v1;
outdwNorm[2] = outdwPos[2] = rFace.v2;
if (m_pMeshDesc->numTexFaces())
{
const CryTexFace& rTexFace = m_pMeshDesc->pTexFaces[indwTriNo];
outdwUV[0] = rTexFace.t0;
outdwUV[1] = rTexFace.t1;
outdwUV[2] = rTexFace.t2;
}
else
{
outdwUV[0] = outdwUV[1] = outdwUV[2] = 0;
}
}
void GetPos( const DWORD indwPos, float outfPos[3] ) const;
void GetUV ( const DWORD indwPos, float outfUV[2] ) const
{
const CryUV& uv = m_pMeshDesc->pUVs[indwPos];
outfUV[0] = uv.u;
outfUV[1] = uv.v;
}
std::vector<VertexUVPair> arrVertMap;
protected:
const CryChunkedFile::MeshDesc* m_pMeshDesc;
Matrix44 m_tm;
};
CTangentSpaceCalculation<MeshProxy> m_TangBaseBuilder;
// this mapping gives the ext->tang ext mapping, i.e.
// maps from the final external indexation (that's found in
// m_arrExtTangMap, m_arrExtUVMap, m_arrExtTangents)
// to the tangent base indexation in m_TangBaseBuilder
// (GetBaseCount, GetTriangleBaseIndices, GetBase)
std::vector<ushort> m_arrExtToTBBMap;
// this is used during construction of the external maps to quickly find
// corresponding vertex-uv pairs and avoid collisions
// the vertex-uv indices are in internal indexations of vertices and UVs
VertexUVPairMap m_mapVUVP;
};
#endif

View File

@@ -0,0 +1,59 @@
// ResourceCompilerPC.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "CgfConvertor.h"
#include "GC_CgfConverter.h"
#include "StatCGFCompiler\StatCGFCompiler.h"
#include "ResourceCompilerPC.h"
IRCLog* g_pLog = NULL;
void LogWarning (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
if (g_pLog)
g_pLog->LogV (IMiniLog::eWarning, szFormat, args);
else
vprintf (szFormat, args);
va_end(args);
}
void Log (const char* szFormat, ...)
{
va_list args;
va_start (args, szFormat);
if (g_pLog)
g_pLog->LogV (IMiniLog::eMessage, szFormat, args);
else
vprintf (szFormat, args);
va_end(args);
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = (HMODULE)hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// This is an example of an exported function.
void __stdcall RegisterConvertors(IResourceCompiler*pRC)
{
pRC->RegisterConvertor(new CGFConvertor());
//pRC->RegisterConvertor(new GC_CGFConvertor());
//pRC->RegisterConvertor(new CALConvertor());
}
HMODULE g_hInst;

View File

@@ -0,0 +1,8 @@
#include "IRCLog.h"
#include "IResCompiler.h"
extern HMODULE g_hInst;
extern IRCLog* g_pLog;
extern void LogWarning (const char* szFormat, ...);
extern void Log (const char* szFormat, ...);

View File

@@ -0,0 +1,649 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="ResourceCompilerPC"
ProjectGUID="{43B9F5EF-C4FC-44FF-BEB0-70EFE792B6C0}"
SccProjectName="SAK"
SccAuxPath="SAK"
SccLocalPath="SAK"
SccProvider="SAK"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="D:\Games\FC\Bin32"
IntermediateDirectory="Debug"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
OptimizeForProcessor="0"
AdditionalIncludeDirectories="..\ResourceCompiler,..\CryCommon"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;RESOURCECOMPILERPC_EXPORTS"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="FALSE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="dbghelp.lib"
OutputFile="$(OutDir)/ResourceCompilerPC.dll"
LinkIncremental="2"
ModuleDefinitionFile="ResourceCompilerPlugin.def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/ResourceCompilerPC.pdb"
SubSystem="2"
ImportLibrary="$(OutDir)/ResourceCompilerPC.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="D:\Games\FC\Bin32"
IntermediateDirectory="Release"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="3"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="TRUE"
FavorSizeOrSpeed="2"
OmitFramePointers="TRUE"
EnableFiberSafeOptimizations="FALSE"
OptimizeForProcessor="2"
AdditionalIncludeDirectories="..\ResourceCompiler,..\CryCommon"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;RESOURCECOMPILERPC_EXPORTS;_RELEASE"
StringPooling="TRUE"
RuntimeLibrary="2"
BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="FALSE"
EnableEnhancedInstructionSet="0"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="FALSE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="dbghelp.lib"
OutputFile="$(OutDir)/ResourceCompilerPC.dll"
LinkIncremental="1"
ModuleDefinitionFile="ResourceCompilerPlugin.def"
GenerateDebugInformation="TRUE"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
ImportLibrary="$(OutDir)/ResourceCompilerPC.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Debug64|Win32"
OutputDirectory="D:\Games\FC\Bin32"
IntermediateDirectory="Debug64"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\ResourceCompiler,..\CryCommon"
PreprocessorDefinitions="_AMD64_;WIN64;WIN32;_DEBUG;_WINDOWS;_USRDLL;RESOURCECOMPILERPC_EXPORTS"
BasicRuntimeChecks="0"
SmallerTypeCheck="FALSE"
RuntimeLibrary="3"
BufferSecurityCheck="FALSE"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:AMD64"
AdditionalDependencies="dbghelp.lib"
OutputFile="$(OutDir)/$(ProjectName)64.dll"
LinkIncremental="2"
IgnoreDefaultLibraryNames=""
ModuleDefinitionFile="ResourceCompilerPlugin.def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
SubSystem="2"
LargeAddressAware="2"
ImportLibrary="$(OutDir)/$(ProjectName)64.lib"
TargetMachine="0"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release64|Win32"
OutputDirectory="D:\Games\FC\Bin32"
IntermediateDirectory="Release64"
ConfigurationType="2"
CharacterSet="2"
WholeProgramOptimization="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
GlobalOptimizations="FALSE"
InlineFunctionExpansion="0"
EnableIntrinsicFunctions="FALSE"
AdditionalIncludeDirectories="..\ResourceCompiler,..\CryCommon"
PreprocessorDefinitions="_AMD64_;WIN64;WIN32;NDEBUG;_WINDOWS;_USRDLL;RESOURCECOMPILERPC_EXPORTS"
StringPooling="TRUE"
BasicRuntimeChecks="0"
SmallerTypeCheck="FALSE"
RuntimeLibrary="2"
BufferSecurityCheck="FALSE"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:AMD64"
AdditionalDependencies="dbghelp.lib"
OutputFile="$(OutDir)/$(ProjectName)64.dll"
LinkIncremental="1"
ModuleDefinitionFile="ResourceCompilerPlugin.def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
SubSystem="2"
LargeAddressAware="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
ImportLibrary="$(OutDir)/$(ProjectName)64.lib"
TargetMachine="0"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Skin"
Filter="">
<File
RelativePath="CrySkinAMD64.asm">
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="TRUE">
<Tool
Name="VCCustomBuildTool"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="TRUE">
<Tool
Name="VCCustomBuildTool"/>
</FileConfiguration>
<FileConfiguration
Name="Debug64|Win32">
<Tool
Name="VCCustomBuildTool"
Description="Compiling $(InputName)"
CommandLine="ml64 /c /Fl&quot;$(OutDir)\$(InputName).cod&quot; /Fo&quot;$(OutDir)\$(InputName).obj&quot; $(InputPath)
"
Outputs="$(OutDir)\$(InputName).obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release64|Win32">
<Tool
Name="VCCustomBuildTool"
Description="Compiling $(InputName)"
CommandLine="ml64 /c /Fl&quot;$(OutDir)\$(InputName).cod&quot; /Fo&quot;$(OutDir)\$(InputName).obj&quot; $(InputPath)
"
Outputs="$(OutDir)\$(InputName).obj"/>
</FileConfiguration>
</File>
<File
RelativePath="CrySkinBase.cpp">
</File>
<File
RelativePath="CrySkinBase.h">
</File>
<File
RelativePath="CrySkinBasisBuilder.cpp">
</File>
<File
RelativePath="CrySkinBasisBuilder.h">
</File>
<File
RelativePath="CrySkinBuilder.cpp">
</File>
<File
RelativePath="CrySkinBuilder.h">
</File>
<File
RelativePath="CrySkinBuilderBase.cpp">
</File>
<File
RelativePath="CrySkinBuilderBase.h">
</File>
<File
RelativePath="CrySkinFull.cpp">
</File>
<File
RelativePath="CrySkinFull.h">
</File>
<File
RelativePath="CrySkinMorph.cpp">
</File>
<File
RelativePath="CrySkinMorph.h">
</File>
<File
RelativePath="CrySkinMorphBuilder.cpp">
</File>
<File
RelativePath="CrySkinMorphBuilder.h">
</File>
<File
RelativePath="CrySkinRigidBasis.cpp">
</File>
<File
RelativePath="CrySkinRigidBasis.h">
</File>
<File
RelativePath="CrySkinTypes.h">
</File>
<Filter
Name="DataSources"
Filter="">
<File
RelativePath="SkinDataSources.cpp">
</File>
<File
RelativePath="SkinDataSources.h">
</File>
</Filter>
</Filter>
<Filter
Name="Front-End"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;h;hpp;hxx;hm;inl;inc">
<File
RelativePath="CalConvertor.h">
</File>
<File
RelativePath="CgfConvertor.cpp">
</File>
<File
RelativePath="CgfConvertor.h">
</File>
<File
RelativePath="CgfUtils.cpp">
</File>
<File
RelativePath="GC_CgfConverter.cpp">
</File>
<File
RelativePath="GC_CgfConverter.h">
</File>
<File
RelativePath="ResourceCompilerPC.cpp">
</File>
<File
RelativePath="ResourceCompilerPC.h">
</File>
<File
RelativePath="stdafx.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Debug64|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release64|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
<File
RelativePath="stdafx.h">
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
<File
RelativePath="ReadMe.txt">
</File>
<File
RelativePath="ResourceCompilerPlugin.def">
</File>
</Filter>
<Filter
Name="Support"
Filter="">
<File
RelativePath="AutoFile.h">
</File>
<File
RelativePath="..\..\GAME01\CryCommon\CryCompiledFile.h">
</File>
<File
RelativePath="MathUtils.h">
</File>
<File
RelativePath="RenderMeshBuilder.cpp">
</File>
<File
RelativePath="RenderMeshBuilder.h">
</File>
<File
RelativePath="TangentSpaceCalculation.h">
</File>
</Filter>
<Filter
Name="Shared with Animation"
Filter="">
<File
RelativePath="BoneLightBindInfo.cpp">
</File>
<File
RelativePath="BoneLightBindInfo.h">
</File>
<File
RelativePath="ChunkFileReader.cpp">
</File>
<File
RelativePath="ChunkFileReader.h">
</File>
<File
RelativePath="Controller.h">
</File>
<File
RelativePath="CryAnimationInfo.h">
</File>
<File
RelativePath="CryBoneDesc.cpp">
</File>
<File
RelativePath="CryBoneDesc.h">
</File>
<File
RelativePath="CryVertexBinding.cpp">
</File>
<File
RelativePath="CryVertexBinding.h">
</File>
<File
RelativePath="FileMapping.cpp">
</File>
<File
RelativePath="FileMapping.h">
</File>
<File
RelativePath="SSEUtils.cpp">
</File>
<File
RelativePath="SSEUtils.h">
</File>
</Filter>
<Filter
Name="NvTriStrip"
Filter="">
<File
RelativePath="NvTriStrip\NvTriStrip.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
<FileConfiguration
Name="Debug64|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
<FileConfiguration
Name="Release64|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
</File>
<File
RelativePath="NvTriStrip\NvTriStrip.h">
</File>
<File
RelativePath="NvTriStrip\NvTriStripObjects.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
<FileConfiguration
Name="Debug64|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
<FileConfiguration
Name="Release64|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"/>
</FileConfiguration>
</File>
<File
RelativePath="NvTriStrip\NvTriStripObjects.h">
</File>
<File
RelativePath="NvTriStrip\RenderPCH.h">
</File>
<File
RelativePath="NvTriStrip\VertexCache.h">
</File>
</Filter>
<Filter
Name="Stencil Shadows"
Filter="">
<File
RelativePath="StencilShadowConnectivity.h">
</File>
<File
RelativePath="StencilShadowConnectivityBuilder.cpp">
</File>
<File
RelativePath="StencilShadowConnectivityBuilder.h">
</File>
</Filter>
<Filter
Name="StatCGFCompiler"
Filter="">
<File
RelativePath="StatCGFCompiler\BaseObj.h">
</File>
<File
RelativePath="StatCGFCompiler\CryStaticModel.cpp">
</File>
<File
RelativePath="StatCGFCompiler\CryStaticModel.h">
</File>
<File
RelativePath="StatCGFCompiler\File.cpp">
</File>
<File
RelativePath="StatCGFCompiler\File.h">
</File>
<File
RelativePath="StatCGFCompiler\Geom.cpp">
</File>
<File
RelativePath="StatCGFCompiler\Geom.h">
</File>
<File
RelativePath="StatCGFCompiler\Helper.cpp">
</File>
<File
RelativePath="StatCGFCompiler\Helper.h">
</File>
<File
RelativePath="StatCGFCompiler\Light.cpp">
</File>
<File
RelativePath="StatCGFCompiler\Light.h">
</File>
<File
RelativePath="StatCGFCompiler\Meshidx.cpp">
</File>
<File
RelativePath="..\CryCommon\MeshIdx.h">
</File>
<File
RelativePath="StatCGFCompiler\Node.cpp">
</File>
<File
RelativePath="StatCGFCompiler\Node.h">
</File>
<File
RelativePath="StatCGFCompiler\StatCGFCompiler.cpp">
</File>
<File
RelativePath="StatCGFCompiler\StatCGFCompiler.h">
</File>
<File
RelativePath="StatCGFCompiler\StatCGFCompilerLB.cpp">
</File>
<File
RelativePath="StatCGFCompiler\StatCGFCompilerLBSerialize.cpp">
</File>
<File
RelativePath="StatCGFCompiler\StatCGFShadowVolume.h">
</File>
<File
RelativePath="StatCGFCompiler\StatCGFShadVol.cpp">
</File>
<File
RelativePath="StatCGFCompiler\StatObjPhysics.cpp">
</File>
</Filter>
<File
RelativePath="..\CryCommon\fSinCos64.lib">
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = "relative:ResourceCompilerPC"
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View File

@@ -0,0 +1,2 @@
EXPORTS
RegisterConvertors @1

View File

@@ -0,0 +1,456 @@
#include "stdafx.h"
#include "SSEUtils.h"
namespace cpu
{
DWORD g_dwFeatures = 0;
DWORD g_dwFeaturesEx = 0;
const char* g_arrCPUCaps[] = {
"FPU", "VME", "DE", "PSE", "TSC", "MSR", "PAE", "MCE", "CX8", "APIC",
"Unknown(10)", "SEP", "MTRR", "PGE", "MCA", "CMOV", "PAT", "PSE-36", "PSN", "CLFSH",
"Unknown(20)", "DS", "ACPI", "MMX", "FXSR", "SSE", "SSE2", "SS", "HTT", "TM",
"Unknown(30)", "PBE"
};
const char* g_arrCPUCapsLong[] = {
"Floating Point Unit On-Chip", "Virtual 8086 Mode Enhancements", "Debugging Extensions", "Page Size Extension", "Time Stamp Counter", "Model Specific Registers RDMSR and WRMSR Instructions", "Physical Address Extension", "Machine Check Exception", "CMPXCHG8B Instruction", "Advanced Programmable Interrupt Controller On-Chip",
"Unknown(10)", "SYSENTER and SYSEXIT Instructions", "Memory Type Range Registers", "Page Directory Entries Global Bit", "Machine Check Architecture", "Conditional Move Instructions", "Page Attribute Table", "32-Bit Page Size Extension", "Processor Serial Number", "CLFLUSH Instruction",
"Unknown(20)", "Debug Store", "Thermal Monitor and Software Controlled Clock Facilities", "MMX Technology", "FXSAVE and FXRSTOR Instructions", "SSE", "SSE2", "Self Snoop", "Hyper-Threading Technology", "Thermal Monitor",
"Unknown(30)", "Pending Break Enable"
};
void logCaps()
{
#ifdef _CRY_ANIMATION_BASE_HEADER_
#ifdef _CPU_X86
g_GetLog()->LogToFile ("CPU capabilities: ");
for (unsigned nCap = 0; nCap < 32; ++nCap)
if (g_dwFeatures&(1<<nCap))
{
#ifdef _DEBUG
g_GetLog()->LogToFilePlus(" %s.", g_arrCPUCapsLong[nCap]);
#else
g_GetLog()->LogToFilePlus(" %s", g_arrCPUCaps[nCap]);
#endif
}
if (has3DNow())
g_GetLog()->LogToFilePlus(" 3DNow!");
#endif
#endif
}
// detects CPU features (SSE) and perhaps sets up some
// pointers to functions
void detect ()
{
#if !defined(LINUX) && defined(_CPU_X86)
__try
{
_asm
{
// 386 processor check
// The AC bit, bit #18, is a new bit introduced in the EFLAGS
// register on the 486 processor to generate alignment
// faults.
// This bit cannot be set on the 386 processor.
pushfd // push original EFLAGS
pop eax // get original EFLAGS
mov ebx, eax // save original EFLAGS
xor eax, 040000h // flip AC bit in EFLAGS
push eax // save new EFLAGS value on stack
popfd // replace current EFLAGS value
pushfd // get new EFLAGS
pop eax // store new EFLAGS in EAX
cmp eax, ebx // can<61>t toggle AC bit, processor=80386
jz label386 // jump if 80386 processor
push ebx
popfd // restore AC bit in EFLAGS
// Checking for ability to set/clear ID flag (Bit 21) in EFLAGS
// which indicates the presence of a processor with the CPUID
// instruction.
pushfd // save EFLAGS to stack
pop eax // store EFLAGS in EAX
mov ebx, eax // save in EBX for testing later
xor eax, 0200000h // flip bit 21 in EFLAGS
push eax // save new EFLAGS value on stack
popfd // replace current EFLAGS value
pushfd // get new EFLAGS
pop eax // store new EFLAGS in EAX
cmp eax, ebx // see if bit 21 has changed
jz labelNoCPUID // CPUID is not present
mov EAX, 1
cpuid
mov g_dwFeatures, EDX
// check for 3DNow!
mov eax, 080000000h // query for extended functions
cpuid // get extended function limit
cmp eax, 080000001h /* functions up to 80000001h must be present */
jb labelNoExtended /* 80000001h is not available */
mov eax, 080000001h /* setup extended function 1 */
cpuid /* call the function */
mov g_dwFeaturesEx, edx /* bit 31 will be set for 3D Now! support*/
labelNoExtended:
test g_dwFeatures, g_featureSSE
jz labelNoSSE
// SSE is present. to check for OS support...
xorps xmm0, xmm0
// if we got here safely after xorps, it only can mean we have SSE
//or g_dwFeatures, g_featureSSE
jmp labelEndDetect
label386:
labelNoCPUID:
labelNoSSE:
labelEndDetect:
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// OS doesn't support some of the instructions we executed..
if(_exception_code () == STATUS_ILLEGAL_INSTRUCTION)
g_dwFeatures &= ~g_featureSSE;
}
if ((g_dwFeatures & (g_featureFXSR|g_featureSSE)) == (g_featureFXSR|g_featureSSE))
{
unsigned nMXCSR;
__try
{
_asm
{
stmxcsr nMXCSR
or nMXCSR, 0x8000
ldmxcsr nMXCSR
or nMXCSR, 0x40
ldmxcsr nMXCSR
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
}
#endif
}
}
#if !defined(LINUX) && defined(_CPU_X86)
// given the array of matrices, calculates the min/max
// of their positions, and puts them into the min and max Vec3d
// NOTE: the matrix array must be aligned on 16-byte boundary
void getBBoxSSE (const Matrix44* pBones, unsigned numBones, CryAABB* pBBox)
{
assert (numBones > 0);
_asm
{
mov EBX, pBones
movaps xmm0, [EBX+0x30]
movaps xmm1, xmm0
mov EDX, pBBox
add EBX, 0x70 // now EBX points to the next bone matrix
mov ECX, numBones
dec ECX
jz label_End
label_Start:
movaps xmm2, [EBX]
minps xmm0, xmm2
maxps xmm1, xmm2
add EBX, 0x40
loop label_Start
label_End:
//SSE_MOVSS(EDX,xmm0)
movss [EDX], xmm0
shufps xmm0,xmm0, 0xE5
movss [EDX+4], xmm0
shufps xmm0,xmm0, 0xE6
movss [EDX+8], xmm0
//SSE_MOVSS(EDX+0x0C,xmm1)
movss [EDX+0x0C], xmm1
shufps xmm1,xmm1, 0xE5
movss [EDX+0x0C+4], xmm1
shufps xmm1,xmm1, 0xE6
movss [EDX+0x0C+8], xmm1
}
}
// given the array of matrices, calculates the min/max
// of their positions, and puts them into the min and max Vec3d
// NOTE: the matrix array must be aligned on 16-byte boundary
void getBBoxSSE (const Matrix44* pBones, const CryBBoxA16* pBoneBBox, unsigned numBones, CryAABB* pBBox)
{
assert (numBones > 0);
_asm
{
mov EBX, pBones
movaps xmm0, [EBX+0x30]
movaps xmm1, xmm0
mov EDX, pBBox
add EBX, 0x70 // now EBX points to the next bone matrix
mov ECX, numBones
dec ECX
jz label_End
label_Start:
movaps xmm2, [EBX]
minps xmm0, xmm2
maxps xmm1, xmm2
add EBX, 0x40
loop label_Start
label_End:
//SSE_MOVSS(EDX,xmm0)
movss [EDX], xmm0
shufps xmm0,xmm0, 0xE5
movss [EDX+4], xmm0
shufps xmm0,xmm0, 0xE6
movss [EDX+8], xmm0
//SSE_MOVSS(EDX+0x0C,xmm1)
movss [EDX+0x0C], xmm1
shufps xmm1,xmm1, 0xE5
movss [EDX+0x0C+4], xmm1
shufps xmm1,xmm1, 0xE6
movss [EDX+0x0C+8], xmm1
}
}
#endif
// packs the array of Vec3dA16 into Vec3d's
// nCount - number of vertices
// pData - [IN] Vec3dA16, [OUT] Vec3d
void packVec3d16 (void* pData, unsigned nCount)
{
#if !defined(LINUX) && defined(_CPU_X86)
_asm
{
mov ESI, pData
mov EDI, ESI
add ESI, 0x10
add EDI, 0xC
mov ECX, nCount
dec ECX
jz endLoop
startLoop:
mov EAX, [ESI]
mov [EDI], EAX
mov EBX, [ESI+4]
mov [EDI+4], EBX
mov EDX, [ESI+8]
mov [EDI+8], EDX
add ESI, 0x10
add EDI, 0xC
loop startLoop
endLoop:
}
#else
float* pTo = (float*)pData + 3;
float* pFrom = (float*)pData + 4;
for (unsigned i = 1; i < nCount; ++i)
{
pTo[0] = pFrom[0];
pTo[1] = pFrom[1];
pTo[2] = pFrom[2];
pTo += 3;
pFrom += 4;
}
#endif
}
#if !defined(LINUX) && defined(_CPU_X86)
__declspec(naked) void PIII_Mult00_4x4_4x4( float *src1, float *src2, float *dst)
{
__asm
{
mov edx, dword ptr [esp+4] ; src1
mov eax, dword ptr [esp+0Ch] ; dst
mov ecx, dword ptr [esp+8] ; src2
movss xmm0, dword ptr [edx]
movaps xmm1, xmmword ptr [ecx]
shufps xmm0, xmm0, 0
movss xmm2, dword ptr [edx+4]
mulps xmm0, xmm1
shufps xmm2, xmm2, 0
movaps xmm3, xmmword ptr [ecx+10h]
movss xmm7, dword ptr [edx+8]
mulps xmm2, xmm3
shufps xmm7, xmm7, 0
addps xmm0, xmm2
movaps xmm4, xmmword ptr [ecx+20h]
movss xmm2, dword ptr [edx+0Ch]
mulps xmm7, xmm4
shufps xmm2, xmm2, 0
addps xmm0, xmm7
movaps xmm5, xmmword ptr [ecx+30h]
movss xmm6, dword ptr [edx+10h]
mulps xmm2, xmm5
movss xmm7, dword ptr [edx+14h]
shufps xmm6, xmm6, 0
addps xmm0, xmm2
shufps xmm7, xmm7, 0
movlps qword ptr [eax], xmm0
movhps qword ptr [eax+8], xmm0
mulps xmm7, xmm3
movss xmm0, dword ptr [edx+18h]
mulps xmm6, xmm1
shufps xmm0, xmm0, 0
addps xmm6, xmm7
mulps xmm0, xmm4
movss xmm2, dword ptr [edx+24h]
addps xmm6, xmm0
movss xmm0, dword ptr [edx+1Ch]
movss xmm7, dword ptr [edx+20h]
shufps xmm0, xmm0, 0
shufps xmm7, xmm7, 0
mulps xmm0, xmm5
mulps xmm7, xmm1
addps xmm6, xmm0
shufps xmm2, xmm2, 0
movlps qword ptr [eax+10h], xmm6
movhps qword ptr [eax+18h], xmm6
mulps xmm2, xmm3
movss xmm6, dword ptr [edx+28h]
addps xmm7, xmm2
shufps xmm6, xmm6, 0
movss xmm2, dword ptr [edx+2Ch]
mulps xmm6, xmm4
shufps xmm2, xmm2, 0
addps xmm7, xmm6
mulps xmm2, xmm5
movss xmm0, dword ptr [edx+34h]
addps xmm7, xmm2
shufps xmm0, xmm0, 0
movlps qword ptr [eax+20h], xmm7
movss xmm2, dword ptr [edx+30h]
movhps qword ptr [eax+28h], xmm7
mulps xmm0, xmm3
shufps xmm2, xmm2, 0
movss xmm6, dword ptr [edx+38h]
mulps xmm2, xmm1
shufps xmm6, xmm6, 0
addps xmm2, xmm0
mulps xmm6, xmm4
movss xmm7, dword ptr [edx+3Ch]
shufps xmm7, xmm7, 0
addps xmm2, xmm6
mulps xmm7, xmm5
addps xmm2, xmm7
movaps xmmword ptr [eax+30h], xmm2
ret
}
}
#endif
void multMatrix(float *product, const float *m1, const float *m2)
{
#if defined(LINUX) || !defined(_CPU_X86)
#define A(row,col) m1[(col<<2)+row]
#define B(row,col) m2[(col<<2)+row]
#define P(row,col) product[(col<<2)+row]
int i;
for (i=0; i<4; i++)
{
float ai0=A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3);
P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0);
P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1);
P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2);
P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3);
}
#undef A
#undef B
#undef P
#else
__asm
{
mov eax, m2;
mov ecx, m1;
mov edx, product;
movss xmm0,dword ptr [eax]
movaps xmm1,xmmword ptr [ecx]
shufps xmm0,xmm0,0
movss xmm2,dword ptr [eax+4]
mulps xmm0,xmm1
shufps xmm2,xmm2,0
movaps xmm3,xmmword ptr [ecx+10h]
movss xmm4,dword ptr [eax+8]
mulps xmm2,xmm3
shufps xmm4,xmm4,0
addps xmm0,xmm2
movaps xmm2,xmmword ptr [ecx+20h]
movss xmm5,dword ptr [eax+0Ch]
mulps xmm4,xmm2
shufps xmm5,xmm5,0
movaps xmm6,xmmword ptr [ecx+30h]
mulps xmm5,xmm6
addps xmm4,xmm5
addps xmm0,xmm4
movaps xmmword ptr [edx],xmm0
movss xmm0,dword ptr [eax+10h]
movss xmm4,dword ptr [eax+14h]
shufps xmm0,xmm0,0
shufps xmm4,xmm4,0
mulps xmm0,xmm1
mulps xmm4,xmm3
movss xmm5,dword ptr [eax+18h]
addps xmm0,xmm4
shufps xmm5,xmm5,0
movss xmm4,dword ptr [eax+1Ch]
mulps xmm5,xmm2
shufps xmm4,xmm4,0
mulps xmm4,xmm6
addps xmm5,xmm4
addps xmm0,xmm5
movaps xmmword ptr [edx+10h],xmm0
movss xmm0,dword ptr [eax+20h]
movss xmm4,dword ptr [eax+24h]
shufps xmm0,xmm0,0
shufps xmm4,xmm4,0
mulps xmm0,xmm1
mulps xmm4,xmm3
movss xmm5,dword ptr [eax+28h]
addps xmm0,xmm4
shufps xmm5,xmm5,0
movss xmm4,dword ptr [eax+2Ch]
mulps xmm5,xmm2
shufps xmm4,xmm4,0
mulps xmm4,xmm6
addps xmm5,xmm4
addps xmm0,xmm5
movaps xmmword ptr [edx+20h],xmm0
movss xmm0,dword ptr [eax+30h]
movss xmm4,dword ptr [eax+34h]
shufps xmm0,xmm0,0
shufps xmm4,xmm4,0
mulps xmm0,xmm1
mulps xmm4,xmm3
movss xmm1,dword ptr [eax+38h]
addps xmm0,xmm4
shufps xmm1,xmm1,0
movss xmm3,dword ptr [eax+3Ch]
mulps xmm1,xmm2
shufps xmm3,xmm3,0
mulps xmm3,xmm6
addps xmm1,xmm3
addps xmm0,xmm1
movaps xmmword ptr [edx+30h],xmm0
}
#endif
}

View File

@@ -0,0 +1,212 @@
#ifndef _CRY_ANIMATION_SSE_UTILS_HDR_
#define _CRY_ANIMATION_SSE_UTILS_HDR_
#include "MathUtils.h"
#ifdef _CPU_X86
// given the array of matrices, calculates the min/max
// of their positions, and puts them into the min and max Vec3d
// NOTE: the matrix array must be aligned on 16-byte boundary
extern void getBBoxSSE (const Matrix44* pBones, unsigned numBones, CryAABB* pBBox);
extern void getBBoxSSE (const Matrix44* pBones, const CryBBoxA16* pBoneBBox, unsigned numBones, CryAABB* pBBox);
#endif
// packs the array of Vec3dA16 into Vec3d's
extern void packVec3d16 (void* pData, unsigned nCount);
namespace cpu
{
// detects CPU features (SSE) and perhaps sets up some
// pointers to functions
void detect ();
// the CPU feature set, as returned by CPUID(1):EDX
extern DWORD g_dwFeatures;
// the extended feature set, as returned by CPUID(0x80000001):EDX
extern DWORD g_dwFeaturesEx;
enum FeaturesEnum
{
// Floating Point Unit On-Chip. The processor contains an x87 FPU.
g_featureFPU = 1,
// Virtual 8086 Mode Enhancements. Virtual 8086 mode enhancements, including
// CR4.VME for controlling the feature, CR4.PVI for protected mode virtual
// interrupts, software interrupt indirection, expansion of the TSS with the software
// indirection bitmap, and EFLAGS.VIF and EFLAGS.VIP flags.
g_featureVME = 1 << 1,
// Debugging Extensions. Support for I/O breakpoints, including CR4.DE for
// controlling the feature, and optional trapping of accesses to DR4 and DR5.
g_featureDE = 1 << 2,
g_featureDebuggingExtensions = g_featureDE,
// Page Size Extension. Large pages of size 4Mbyte are supported, including
// CR4.PSE for controlling the feature, the defined dirty bit in PDE (Page Directory
// Entries), optional reserved bit trapping in CR3, PDEs, and PTEs.
g_featurePSE = 1 << 3,
// Time Stamp Counter. The RDTSC instruction is supported, including CR4.TSD
// for controlling privilege.
g_featureTSC = 1 << 4,
// Model Specific Registers RDMSR and WRMSR Instructions. The RDMSR and
// WRMSR instructions are supported. Some of the MSRs are implementation
// dependent.
g_featureMSR = 1 << 5,
// PAE Physical Address Extension. Physical addresses greater than 32 bits are
// supported: extended page table entry formats, an extra level in the page
// translation tables is defined, 2 Mbyte pages are supported instead of 4 Mbyte
// pages if PAE bit is 1. The actual number of address bits beyond 32 is not defined,
// and is implementation specific.
g_featurePAE = 1 << 6,
// MCE Machine Check Exception. Exception 18 is defined for Machine Checks,
// including CR4.MCE for controlling the feature. This feature does not define the
// model-specific implementations of machine-check error logging, reporting, and
// processor shutdowns. Machine Check exception handlers may have to depend on
// processor version to do model specific processing of the exception, or test for the
// presence of the Machine Check feature.
g_featureMCE = 1 << 7,
// CMPXCHG8B Instruction. The compare-and-exchange 8 bytes (64 bits)
// instruction is supported (implicitly locked and atomic).
g_featureCX8 = 1 << 8,
// APIC On-Chip. The processor contains an Advanced Programmable Interrupt
// Controller (APIC), responding to memory mapped commands in the physical
// address range FFFE0000H to FFFE0FFFH (by default - some processors permit
// the APIC to be relocated).
g_featureAPIC = 1 << 9,
// SYSENTER and SYSEXIT Instructions. The SYSENTER and SYSEXIT and
// associated MSRs are supported.
g_featureSEP = 1 << 11,
// Memory Type Range Registers. MTRRs are supported. The MTRRcap MSR
// contains feature bits that describe what memory types are supported, how many
// variable MTRRs are supported, and whether fixed MTRRs are supported.
g_featureMTRR = 1 << 12,
// PTE Global Bit. The global bit in page directory entries (PDEs) and page table
// entries (PTEs) is supported, indicating TLB entries that are common to different
// processes and need not be flushed. The CR4.PGE bit controls this feature.
g_featurePGE = 1 << 13,
// Machine Check Architecture. The Machine Check Architecture, which provides
// a compatible mechanism for error reporting in P6 family, Pentium 4, and Intel
// Xeon processors, and future processors, is supported. The MCG_CAP MSR
// contains feature bits describing how many banks of error reporting MSRs are
// supported.
g_featureMCA = 1 << 14,
// Conditional Move Instructions. The conditional move instruction CMOV is
// supported. In addition, if x87 FPU is present as indicated by the CPUID.FPU
// feature bit, then the FCOMI and FCMOV instructions are supported
g_featureCMOV = 1 << 15,
//Page Attribute Table. Page Attribute Table is supported. This feature augments
//the Memory Type Range Registers (MTRRs), allowing an operating system to
//specify attributes of memory on a 4K granularity through a linear address.
g_featurePAT = 1 << 16,
//32-Bit Page Size Extension. Extended 4-MByte pages that are capable of
//addressing physical memory beyond 4 GBytes are supported. This feature
//indicates that the upper four bits of the physical address of the 4-MByte page is
//encoded by bits 13-16 of the page directory entry.
g_featurePSE36 = 1 << 17,
// PSN Processor Serial Number. The processor supports the 96-bit processor
// identification number feature and the feature is enabled.
g_featurePSN = 1 << 18,
// CLFLUSH Instruction. CLFLUSH Instruction is supported.
g_featureCLFSH = 1 << 19,
// Reserved
g_featureReserved = 1 << 20,
// DS Debug Store. The processor supports the ability to write debug information into a
// memory resident buffer. This feature is used by the branch trace store (BTS) and
// precise event-based sampling (PEBS) facilities (see Chapter 15, Debugging and
// Performance Monitoring, in the IA-32 Intel Architecture Software Developer<65>s
// Manual, Volume 3).
g_featureDS = 1 << 21,
// Thermal Monitor and Software Controlled Clock Facilities. The processor
// implements internal MSRs that allow processor temperature to be monitored and
// processor performance to be modulated in predefined duty cycles under software
// control.
g_featureACPI = 1 << 22,
// Intel MMX Technology. The processor supports the Intel MMX technology.
g_featureMMX = 1 << 23,
// FXSAVE and FXRSTOR Instructions. The FXSAVE and FXRSTOR instructions
// are supported for fast save and restore of the floating point context. Presence of
// this bit also indicates that CR4.OSFXSR is available for an operating system to
// indicate that it supports the FXSAVE and FXRSTOR instructions
g_featureFXSR = 1 << 24,
// SSE. The processor supports the SSE extensions.
g_featureSSE = 1 << 25,
// SSE2. The processor supports the SSE2 extensions.
g_featureSSE2 = 1 << 26,
// Self Snoop. The processor supports the management of conflicting memory
// types by performing a snoop of its own cache structure for transactions issued to
// the bus
g_featureSS = 1 << 27,
// Hyper-Threading Technology. The processor implements Hyper-Threading
// Technology.
g_featureHTT = 1 << 28,
// TM Thermal Monitor. The processor implements the thermal monitor automatic
// thermal control circuitry (TCC).
g_featureTM = 1 << 29,
// Reserved
g_featureReserved30 = 1 << 30,
// PBE Pending Break Enable. The processor supports the use of the FERR#/PBE# pin
// when the processor is in the stop-clock state (STPCLK# is asserted) to signal the
// processor that an interrupt is pending and that the processor should return to
// normal operation to handle the interrupt. Bit 10 (PBE enable) in the
// IA32_MISC_ENABLE MSR enables this capability.
g_featurePBE = 1 << 31,
// this is 3D Now! instruction set bit
g_featureEx3DNow = 0x80000000
};
inline int hasSSE()
{
#ifdef _AMD64_
return g_featureSSE;
#else
return (g_dwFeatures & g_featureSSE);
#endif
}
// does the machine have the RDTSC instruction?
inline int hasRDTSC()
{
return (g_dwFeatures & g_featureTSC);
}
// does the machine have 3D Now instruction set support?
inline int has3DNow()
{
return (g_dwFeaturesEx & g_featureEx3DNow);
}
extern void logCaps();
}
#endif

View File

@@ -0,0 +1,124 @@
#include "stdafx.h"
#include "RenderMeshBuilder.h"
#include "SkinDataSources.h"
#include "CryBoneDesc.h"
// helper to get order for Vec3d
struct CVec3dOrder: public std::binary_function< Vec3d, Vec3d, bool>
{
bool operator() ( const Vec3d &a, const Vec3d &b ) const
{
// first sort by x
if(a.x<b.x)return(true);
if(a.x>b.x)return(false);
// then by y
if(a.y<b.y)return(true);
if(a.y>b.y)return(false);
// then by z
if(a.z<b.z)return(true);
if(a.z>b.z)return(false);
return(false);
}
};
typedef std::map<Vec3d,unsigned,CVec3dOrder> VertexWelderMap;
CRCSkinVertexSource::CRCSkinVertexSource (const CRenderMeshBuilder& rRendMesh, const CryChunkedFile::MeshDesc& rMeshDesc, Matrix44* pBones):
ICrySkinSource(
&rMeshDesc.arrVertBinds[0],
rMeshDesc.arrVertBinds.size(),
NULL, // m_pVertices
0, // m_numVertices
&rRendMesh.m_arrExtTangents[0],
rRendMesh.m_arrExtTangents.size(),
NULL // m_pExtToIntMapping
)
{
// to get the vertices, we'll have to build the temp buffer holding unique vertices
// this maps the vertex coordinates to the vertex index in the new buffer
//VertexWelderMap mapVertices;
m_arrVerts.resize (rMeshDesc.numVertices());
m_pVertices = &m_arrVerts[0];
m_numVertices = m_arrVerts.size();
// skin the
for (unsigned nVertex = 0; nVertex < rMeshDesc.numVertices(); ++nVertex)
{
Vec3d & p = m_arrVerts[nVertex];
const CryVertexBinding& arrLinks = rMeshDesc.arrVertBinds[nVertex];
if (pBones && !arrLinks.empty())
{
p.x = p.y = p.z = 0;
for (CryVertexBinding::const_iterator itLink = arrLinks.begin(); itLink != arrLinks.end(); ++itLink)
{
Matrix44& matBoneGlobal = pBones[itLink->BoneID];
p += matBoneGlobal.TransformPointOLD(itLink->offset) * itLink->Blending;
}
}
else
p = rMeshDesc.pVertices[nVertex].p;
}
m_arrExtToIntMap.resize (rRendMesh.m_arrExtTangMap.size());
unsigned i;
for (i = 0; i < rRendMesh.m_arrExtTangMap.size(); ++i)
m_arrExtToIntMap[i] = rRendMesh.m_arrExtTangMap[i];
m_pExtToIntMapping = &m_arrExtToIntMap[0];
}
CRCSkinVertexSource::~CRCSkinVertexSource ()
{
}
CRCSkinNormalSource::CRCSkinNormalSource (const CRenderMeshBuilder& rRendMesh, const CryChunkedFile::MeshDesc& rMeshDesc, const std::vector<CryBoneDesc>& arrBones):
ICrySkinSource(
NULL, // m_pLinks {&rMeshDesc.arrVertBinds[0]}
0,// m_numLinks {rMeshDesc.arrVertBinds.size()}
NULL, // m_pVertices
0, // m_numVertices
&rRendMesh.m_arrExtTangents[0],
rRendMesh.m_arrExtTangents.size(),
NULL // m_pExtToIntMapping
)
{
// to get the vertices, we'll have to build the temp buffer holding unique vertices
// this maps the vertex coordinates to the vertex index in the new buffer
//VertexWelderMap mapVertices;
m_arrNormals.resize (rMeshDesc.numVertices());
for (unsigned nVertex = 0; nVertex < rMeshDesc.numVertices(); ++nVertex)
m_arrNormals[nVertex] = rMeshDesc.pVertices[nVertex].n;
m_pVertices = &m_arrNormals[0];
m_numVertices = m_arrNormals.size();
m_arrExtToIntMap.resize (rRendMesh.m_arrExtTangMap.size());
unsigned i;
for (i = 0; i < rRendMesh.m_arrExtTangMap.size(); ++i)
m_arrExtToIntMap[i] = rRendMesh.m_arrExtTangMap[i];
m_pExtToIntMapping = &m_arrExtToIntMap[0];
m_numLinks = rMeshDesc.arrVertBinds.size();
m_arrLinks.resize (m_numLinks);
for (unsigned i = 0; i < m_numVertices; ++i)
{
m_arrLinks[i].resize (1);
unsigned nBone = m_arrLinks[i][0].BoneID = rMeshDesc.arrVertBinds[i][0].BoneID;
m_arrLinks[i][0].Blending = 1;//getLink(0)[0].Blending;
m_arrLinks[i][0].offset = arrBones[nBone].getInvDefGlobal().TransformVectorOLD(rMeshDesc.arrNormals[i]);
}
m_pLinks = &m_arrLinks[0];
}
CRCSkinNormalSource::~CRCSkinNormalSource()
{
}

View File

@@ -0,0 +1,35 @@
// These are the classes that supply information to the CrySkin builders
#ifndef _SKIN_DATA_SOURCES_HDR_
#define _SKIN_DATA_SOURCES_HDR_
#include "CryChunkedFile.h"
#include "CrySkinBuilderBase.h"
class CRCSkinNormalSource: public ICrySkinSource
{
public:
CRCSkinNormalSource (const class CRenderMeshBuilder& rRendMesh, const CryChunkedFile::MeshDesc& rMeshDesc, const std::vector<CryBoneDesc>& arrBones);
~CRCSkinNormalSource ();
protected:
std::vector<Vec3d> m_arrNormals;
std::vector<unsigned> m_arrExtToIntMap;
std::vector<CryVertexBinding> m_arrLinks;
};
class CRCSkinVertexSource: public ICrySkinSource
{
public:
// constructs the vertex source recomputing the actual vertex coordinates with the bones given if any
CRCSkinVertexSource (const class CRenderMeshBuilder& rRendMesh, const CryChunkedFile::MeshDesc& rMeshDesc, Matrix44* pBones = NULL);
~CRCSkinVertexSource ();
protected:
// the vertex buffer - initial pose, no duplicated vertices
std::vector<Vec3d> m_arrVerts;
std::vector<unsigned> m_arrExtToIntMap;
};
#endif

View File

@@ -0,0 +1,42 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: baseobj.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_BASEOBJ_H__2D817DB5_34FA_4C73_8588_867E88C9CFB3__INCLUDED_)
#define AFX_BASEOBJ_H__2D817DB5_34FA_4C73_8588_867E88C9CFB3__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CBaseObj
{
public:
CHUNK_HEADER m_ChunkHeader;
bool m_bBinded;
int m_nUsers;
CBaseObj()
{
memset(&m_ChunkHeader,0,sizeof(m_ChunkHeader));
m_bBinded = false;
m_nUsers = 0;
}
virtual ~CBaseObj(){};
virtual bool Load(class CXFile *f, int pos) { return false; }
virtual void Bind(CBaseObj **all_objects, int n_obj){}
};
#endif // !defined(AFX_BASEOBJ_H__2D817DB5_34FA_4C73_8588_867E88C9CFB3__INCLUDED_)

View File

@@ -0,0 +1,498 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: crystaticmodel.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: load cgf file
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CryStaticModel.h"
#include "baseobj.h"
#include "node.h"
#include "geom.h"
#include "helper.h"
#include "light.h"
#include "file.h"
#include "IRCLog.h"
CryStaticModel::CryStaticModel()
{
memset( this,0,sizeof(*this) );
}
CryStaticModel::~CryStaticModel()
{
for( int i=0; i<m_nNewObjs; i++)
{
CNodeCGF * pNode = (CNodeCGF*)m_ppNewObjs[i];
delete pNode;
}
if(m_ppNewObjs)
free(m_ppNewObjs);
m_ppNewObjs=0;
}
void CryStaticModel::LoadMaterials(CXFile*f, int pos)
{
if(f->FSeek(pos,SEEK_SET))
return;
CHUNK_HEADER ch;
int res = f->FRead(&ch,1,sizeof(ch));
if (ch.ChunkVersion == MTL_CHUNK_DESC_0746::VERSION)
{
f->FSeek(pos,SEEK_SET);
MTL_CHUNK_DESC_0746 chunk;
int res=f->FRead(&chunk,1,sizeof(chunk));
if(res!=sizeof(chunk))
return;
MAT_ENTITY me;
memset(&me, 0, sizeof(MAT_ENTITY));
me.opacity = 1.0f;
me.alpharef = 0;
me.m_New = 2;
strcpy(me.name, chunk.name);
switch (chunk.MtlType)
{
case MTL_STANDARD:
me.IsStdMat = true;
me.col_d = chunk.col_d;
me.col_a = chunk.col_a;
me.col_s = chunk.col_s;
me.specLevel = chunk.specLevel;
me.specShininess = chunk.specShininess*100;
me.opacity = chunk.opacity;
me.selfIllum = chunk.selfIllum;
me.flags = chunk.flags;
if (me.flags & MTLFLAG_CRYSHADER)
me.alpharef = chunk.alphaTest;
me.Dyn_Bounce = chunk.Dyn_Bounce;
me.Dyn_StaticFriction = chunk.Dyn_StaticFriction;
me.Dyn_SlidingFriction = chunk.Dyn_SlidingFriction;
/* //Timur[10/24/2001]
strcpy(me.map_a, chunk.tex_a.name);
strcpy(me.map_d, chunk.tex_d.name);
strcpy(me.map_o, chunk.tex_o.name);
strcpy(me.map_b, chunk.tex_b.name);
strcpy(me.map_s, chunk.tex_s.name);
strcpy(me.map_g, chunk.tex_g.name);
strcpy(me.map_c, chunk.tex_c.name);
strcpy(me.map_e, chunk.tex_rl.name);
strcpy(me.map_rr, chunk.tex_rr.name);
strcpy(me.map_det, chunk.tex_det.name);
*/
me.map_a = chunk.tex_a;
me.map_d = chunk.tex_d;
me.map_o = chunk.tex_o;
me.map_b = chunk.tex_b;
me.map_s = chunk.tex_s;
me.map_g = chunk.tex_g;
me.map_detail = chunk.tex_fl;
me.map_e = chunk.tex_rl;
me.map_subsurf = chunk.tex_subsurf;
me.map_displ = chunk.tex_det;
me.nChildren = chunk.nChildren;
m_lstMaterials.Add(me);
break;
/* case MTL_MULTI:
me.IsStdMat = 0;
me.nChildren = chunk.nChildren;
me.children = new int [chunk.nChildren];
int res=f->FRead(me.children,sizeof(int),chunk.nChildren);
if (res != chunk.nChildren)
return;*/
}
}
else
if (ch.ChunkVersion == MTL_CHUNK_DESC_0745::VERSION)
{
f->FSeek(pos,SEEK_SET);
MTL_CHUNK_DESC_0745 chunk;
int res=f->FRead(&chunk,1,sizeof(chunk));
if(res!=sizeof(chunk))
return;
MAT_ENTITY me;
memset(&me, 0, sizeof(MAT_ENTITY));
me.opacity = 1.0f;
me.alpharef = 0;
me.m_New = 1;
strcpy(me.name, chunk.name);
switch (chunk.MtlType)
{
case MTL_STANDARD:
me.IsStdMat = true;
me.col_d = chunk.col_d;
me.col_a = chunk.col_a;
me.col_s = chunk.col_s;
me.specLevel = chunk.specLevel;
me.specShininess = chunk.specShininess*100;
me.opacity = chunk.opacity;
me.selfIllum = chunk.selfIllum;
me.flags = chunk.flags;
me.Dyn_Bounce = chunk.Dyn_Bounce;
me.Dyn_StaticFriction = chunk.Dyn_StaticFriction;
me.Dyn_SlidingFriction = chunk.Dyn_SlidingFriction;
/* //Timur[10/24/2001]
strcpy(me.map_a, chunk.tex_a.name);
strcpy(me.map_d, chunk.tex_d.name);
strcpy(me.map_o, chunk.tex_o.name);
strcpy(me.map_b, chunk.tex_b.name);
strcpy(me.map_s, chunk.tex_s.name);
strcpy(me.map_g, chunk.tex_g.name);
strcpy(me.map_c, chunk.tex_c.name);
strcpy(me.map_e, chunk.tex_rl.name);
strcpy(me.map_rr, chunk.tex_rr.name);
strcpy(me.map_det, chunk.tex_det.name);
*/
me.map_a = chunk.tex_a;
me.map_d = chunk.tex_d;
me.map_o = chunk.tex_o;
me.map_b = chunk.tex_b;
me.map_s = chunk.tex_s;
me.map_g = chunk.tex_g;
me.map_detail = chunk.tex_c;
me.map_e = chunk.tex_rl;
me.map_subsurf = chunk.tex_subsurf;
me.map_displ = chunk.tex_det;
me.nChildren = chunk.nChildren;
m_lstMaterials.Add(me);
break;
/* case MTL_MULTI:
me.IsStdMat = 0;
me.nChildren = chunk.nChildren;
me.children = new int [chunk.nChildren];
int res=f->FRead(me.children,sizeof(int),chunk.nChildren);
if (res != chunk.nChildren)
return;*/
}
}
else
if (ch.ChunkVersion == MTL_CHUNK_DESC_0744::VERSION)
{
f->FSeek(pos,SEEK_SET);
MTL_CHUNK_DESC_0744 chunk;
int res=f->FRead(&chunk,1,sizeof(chunk));
if(res!=sizeof(chunk))
return;
MAT_ENTITY me;
memset(&me, 0, sizeof(MAT_ENTITY));
me.opacity = 1.0f;
me.alpharef = 0;
strcpy(me.name, chunk.name);
switch (chunk.MtlType)
{
case MTL_STANDARD:
me.IsStdMat = true;
me.col_d = chunk.col_d;
me.col_a = chunk.col_a;
me.col_s = chunk.col_s;
me.Dyn_Bounce = chunk.Dyn_Bounce;
me.Dyn_StaticFriction = chunk.Dyn_StaticFriction;
me.Dyn_SlidingFriction = chunk.Dyn_SlidingFriction;
strcpy(me.map_d.name, chunk.tex_d.name);
strcpy(me.map_o.name, chunk.tex_o.name);
strcpy(me.map_b.name, chunk.tex_b.name);
me.nChildren = chunk.nChildren;
m_lstMaterials.Add(me);
break;
case MTL_MULTI:
me.IsStdMat = 0;
me.nChildren = chunk.nChildren;
me.m_pMaterialChildren = new int [chunk.nChildren];//leak
int res=f->FRead(me.m_pMaterialChildren,sizeof(int),chunk.nChildren);
if (res != chunk.nChildren)
return;
}
}
}
bool CryStaticModel::OnLoadgeom(char * FileName, const char * szGeomName, bool bLoadMats, bool bKeepInLocalSpace)
{
CXFile * f = new CXFile();
// preload all data
if(!f->FLoad(FileName))
{
delete f; return 0;
}
//read the file header
FILE_HEADER fh;
int res = f->FRead(&fh,sizeof(fh),1);
if(res!=1)
return 0;
if(fh.Version != GeomFileVersion)
{
f->FClose(); delete f; f=0;
m_pLog->LogError("CryStaticModel::OnLoadgeom: CGF file version error: %s", FileName);
return 0;
}
if(fh.FileType != FileType_Geom)
{
f->FClose(); delete f; f=0;
m_pLog->LogError("OnLoadgeom: CGF file type error: %s", FileName);
return 0;
}
//read the chunk table
f->FSeek(fh.ChunkTableOffset,SEEK_SET);
int n_chunks=100000;
res = f->FRead(&n_chunks,sizeof(n_chunks),1);
if(res!=1)
return 0;
if(n_chunks>=100000)
{
f->FClose(); delete f; f=0;
m_pLog->LogError("CryStaticModel::OnLoadgeom: File corrupted: %s, (n_chunks>=100000)", FileName);
return 0;
}
CHUNK_HEADER * pChunks;
pChunks=(CHUNK_HEADER *)malloc(sizeof(CHUNK_HEADER)*n_chunks);
assert(pChunks);
res = f->FRead(pChunks,sizeof(CHUNK_HEADER),n_chunks);
if(res!=n_chunks)
return 0;
/////////////////////////////////////////////////////////////////////////////
// Create and load objects
/////////////////////////////////////////////////////////////////////////////
m_ppNewObjs = (CBaseObj **)malloc(n_chunks*sizeof(CBaseObj*));
memset(m_ppNewObjs,0,n_chunks*sizeof(CBaseObj*));
assert(m_ppNewObjs);
m_nNewObjs=0;
int nGeomToLoadID = -1;
int i;
for(i=0;i<n_chunks;i++)
{
switch(pChunks[i].ChunkType)
{
case ChunkType_Node:
m_ppNewObjs[m_nNewObjs]=new CNodeCGF();
break;
case ChunkType_Mesh:
if(!szGeomName || nGeomToLoadID == i)
m_ppNewObjs[m_nNewObjs]=new CGeom();
break;
case ChunkType_Helper:
m_ppNewObjs[m_nNewObjs]=new CHelper();
break;
case ChunkType_Light:
m_ppNewObjs[m_nNewObjs]=new CLight();
break;
case ChunkType_Mtl:
if(bLoadMats)
LoadMaterials(f,pChunks[i].FileOffset);
break;
}
if(m_ppNewObjs[m_nNewObjs])
{
m_ppNewObjs[m_nNewObjs]->Load(f,pChunks[i].FileOffset);
// find chunk id of needed geom
if(pChunks[i].ChunkType == ChunkType_Node)
if(szGeomName && strcmp(szGeomName,((CNodeCGF*)m_ppNewObjs[m_nNewObjs])->m_Chunk.name)==0)
nGeomToLoadID = ((CNodeCGF*)m_ppNewObjs[m_nNewObjs])->m_Chunk.ObjectID;
m_nNewObjs++;
}
}
//Do pointer and name list bindings
for(i=0;i<m_nNewObjs;i++)
{
if(!m_ppNewObjs[i])
continue;
m_ppNewObjs[i]->Bind(m_ppNewObjs, m_nNewObjs);
}
f->FClose(); delete f; f=0;
if(pChunks)
free(pChunks);
pChunks=0;
/////////////////////////////////////////////////////////////////////////////
// Make objects
/////////////////////////////////////////////////////////////////////////////
list2<NAME_ENTITY> lstOtherNames;
for( i=0; i<m_nNewObjs; i++)
{
CNodeCGF * pNode = (CNodeCGF*)m_ppNewObjs[i];
if(pNode->m_ChunkHeader.ChunkType != ChunkType_Node)
continue;
// make list of mesh names
NAME_ENTITY geomname;
strcpy(geomname.name, pNode->m_Chunk.name);
lstOtherNames.Add(geomname);
if(!pNode->m_pObj)
continue;
if(pNode->m_pObj->m_nUsers>1)
m_pLog->Log("WARNING: loading of instances from cgf not supported, geom skipped: %s, %s",FileName, pNode->GetName());
// Accumulate this and all parent nodes transformations
// TODO: get rid of the obsolete CryMatrix here
//CHANGED_BY_IVO
//CryMatrix matNodeMatrix = pNode->m_Chunk.tm;
Matrix44 matNodeMatrix = pNode->m_Chunk.tm;
for(CNodeCGF * pCurNode = pNode->m_pParent; pCurNode; pCurNode = pCurNode->m_pParent)
//CHANGED_BY_IVO
//matNodeMatrix = matNodeMatrix * (CryMatrix&)(pCurNode->m_Chunk.tm);
matNodeMatrix = matNodeMatrix * (pCurNode->m_Chunk.tm);
if(pNode->m_pObj->m_ChunkHeader.ChunkType == ChunkType_Mesh)
if(pNode->m_pObj->m_nUsers<=1)
{ // geoms
// make list of mesh names
NAME_ENTITY geomname;
strcpy(geomname.name, pNode->m_Chunk.name);
m_lstGeomNames.Add(geomname);
lstOtherNames.DeleteLast();
CGeom * pGeom = (CGeom*)pNode->m_pObj;
// transform geometry from this node space into CGFs space
if(!bKeepInLocalSpace)
for(int v=0; v<pGeom->m_Chunk.nVerts; v++)
{
//CHANGED_BY_IVO
//pGeom->m_pVertices[v].p = matNodeMatrix*pGeom->m_pVertices[v].p;
//pGeom->m_pVertices[v].n = matNodeMatrix/pGeom->m_pVertices[v].n;
pGeom->m_pVertices[v].p = matNodeMatrix.TransformPointOLD(pGeom->m_pVertices[v].p);
pGeom->m_pVertices[v].n = matNodeMatrix.TransformVectorOLD(pGeom->m_pVertices[v].n);
}
if(!szGeomName || strcmp(pNode->GetName(),szGeomName)==0)
m_lstGeoms.Add(pGeom);
}
if(pNode->m_pObj->m_ChunkHeader.ChunkType == ChunkType_Light)
{ // make light
CLight * pLight = (CLight*)pNode->m_pObj;
LightInstance inst;
memcpy(&inst.Chunk,&pLight->m_Chunk,sizeof(LIGHT_CHUNK_DESC));
inst.Chunk = pLight->m_Chunk;
if(bKeepInLocalSpace)
//inst.vPos.Set(&pNode->m_Chunk.pos.x);
inst.vPos.Set(pNode->m_Chunk.pos[0],pNode->m_Chunk.pos[1],pNode->m_Chunk.pos[2]);
else
//CHANGED_BY_IVO
//inst.vPos = Vec3d(matNodeMatrix.data[3]);
inst.vPos = matNodeMatrix.GetTranslationOLD();
if(!pNode->m_pParent)
assert( IsEquivalent(pNode->m_Chunk.pos,inst.vPos,VEC_EPSILON) );
strncpy(inst.szName,pNode->m_Chunk.name, sizeof(inst.szName));
// load proj texture
if(inst.Chunk.szLightImage[0])
{
assert(0);
inst.pLightImage = 0;//GetRenderer()->EF_LoadTexture(inst.Chunk.szLightImage, FT_CLAMP, FT2_FORCECUBEMAP, eTT_Cubemap);
if (!inst.pLightImage->IsTextureLoaded())
inst.pLightImage = NULL;
}
else
inst.pLightImage = NULL;
m_lstLights.Add(inst);
}
if(pNode->m_pObj->m_ChunkHeader.ChunkType == ChunkType_Helper)
{ // make helper
CHelper * pHelper = (CHelper*)pNode->m_pObj;
HelperInstance inst;
inst.Chunk = pHelper->m_Chunk;
if(bKeepInLocalSpace)
inst.tMat.SetIdentity();
else
//CHANGED_BY_IVO
//inst.tMat = Matrix(matNodeMatrix.matrix);
inst.tMat = matNodeMatrix;
/*
if(!pNode->m_pParent)
{
assert(inst.vPos == pNode->m_Chunk.pos);
float dot = inst.qRot.Dot(pNode->m_Chunk.rot);
dot=dot;
}
*/
strncpy(inst.szName,pNode->m_Chunk.name, sizeof(inst.szName));
m_lstHelpers.Add(inst);
}
}
m_lstGeomNames.AddList (lstOtherNames);
return 1;
}
/*
CHelperInstance * CryStaticModel::GetHelper(const char * name)
{
for(int i=0; i<m_Helpers.Count(); i++)
{
if(!strcmp(m_Helpers[i].name,name))
return &m_Helpers[i];
}
return 0;
}
*/
// this timer measures the time spent in the CGF Loader
//double g_dTimeLoadCGF;

View File

@@ -0,0 +1,69 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: crystaticmodel.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: cgf file loader
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef CryStaticModel_H
#define CryStaticModel_H
class CBaseObj;
class CGeom;
#include "list2.h"
struct LightInstance
{
LIGHT_CHUNK_DESC Chunk;
Vec3d vPos;
char szName[64];
struct ITexPic * pLightImage;
};
struct HelperInstance
{
HELPER_CHUNK_DESC Chunk;
char szName[64];
Matrix44 tMat;
};
class CXFile;
struct CryStaticModel
{
CryStaticModel();
~CryStaticModel();
char m_FileName[256];
list2<CGeom*> m_lstGeoms;
list2<MAT_ENTITY> m_lstMaterials;
list2<NAME_ENTITY> m_lstGeomNames;
list2<LightInstance> m_lstLights;
list2<HelperInstance> m_lstHelpers;
bool OnLoadgeom(char * filename, const char * geom_name, bool bLoadMats, bool bKeepInLocalSpace);
float m_fBoundingRadius;
float m_fCenterZ;
void LoadMaterials(CXFile*f, int pos);
int m_nNewObjs;
CBaseObj ** m_ppNewObjs;
ILog * m_pLog;
};
// timers that are used for precision very low cost profiling of load times
// this timer measures the time spent in the CGF Loader
//extern double g_dTimeLoadCGF;
#endif // CryStaticModel_H

View File

@@ -0,0 +1,306 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: file.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: ceached file access
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#ifndef GAMECUBE
#include <io.h>
#endif
#ifndef _XBOX
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
#else
#include <xtl.h>
#endif
#include "File.h"
//////////////////////////////////////////////////////////////////////
CXFile::CXFile()
{
m_szFileStart=NULL;
m_nFileSize=0;
m_pCurrPos=0;
m_pEndOfFile=NULL;
m_sLoadedFileName[0]=0;
}
//////////////////////////////////////////////////////////////////////
int CXFile::FRead(void *pDest,int nSize,int nNumElems)
{
int nTotSize=nSize*nNumElems;
char *pTest=m_pCurrPos+nTotSize;
if (pTest>m_pEndOfFile)
return (0);
memcpy(pDest,m_pCurrPos,nTotSize);
m_pCurrPos+=nTotSize;
return (nNumElems);
}
//////////////////////////////////////////////////////////////////////
int CXFile::FSeek(int nOff,int nFrom)
{
if (nFrom==SEEK_SET)
{
m_pCurrPos=m_szFileStart+nOff;
if (m_pCurrPos>m_pEndOfFile)
return (1);
}
return (0);
}
//////////////////////////////////////////////////////////////////////
void CXFile::FClose()
{
if (m_szFileStart)
{
delete [] m_szFileStart;
m_szFileStart=NULL;
}
m_pCurrPos=NULL;
m_nFileSize=0;
m_pEndOfFile=NULL;
m_sLoadedFileName[0]=0;
}
//////////////////////////////////////////////////////////////////////
int CXFile::FLoad(const char * filename)
{
if(!m_szFileStart || strcmp(m_sLoadedFileName,filename)!=0)
{
FClose();
m_nFileSize=LoadInMemory(filename,(void**)&m_szFileStart);
strncpy(m_sLoadedFileName,filename,sizeof(m_sLoadedFileName));
}
m_pCurrPos=m_szFileStart;
m_pEndOfFile=m_szFileStart+m_nFileSize;
return (m_nFileSize);
}
//get filename's extension
//////////////////////////////////////////////////////////////////////
char *CXFile::GetExtension(const char *filename)
{
char *src = (char *)filename+strlen(filename)-1;
while (*src)
{
if (*src == '.')
{
return (++src);
}
src--;
}
return (NULL);
}
//remove extension from filename
//////////////////////////////////////////////////////////////////////
void CXFile::RemoveExtension(char *path)
{
char *src = path+strlen(path)-1;
while (*src)
{
if (*src == '.')
{
*src = 0; // remove extension
return;
}
src--;
}
}
//replace filename extension
//////////////////////////////////////////////////////////////////////
void CXFile::ReplaceExtension(char *path, const char *new_ext)
{
RemoveExtension(path);
strcat(path,".");
strcat(path,new_ext);
}
//check if file exist
//////////////////////////////////////////////////////////////////////
bool CXFile::IsFileExist(const char *filename)
{
return FileExist(filename);
}
//check if file exist
//////////////////////////////////////////////////////////////////////
bool CXFile::FileExist(const char *filename)
{
FILE *fp=fopen(filename,"rb");
if (!fp) return (false);
fclose(fp);
return (true);
}
//get length of the file
//return (-1) if error
//////////////////////////////////////////////////////////////////////
int CXFile::GetLength(const char *filename)
{
FILE *fp=fopen(filename,"rb");
if (!fp) return (-1);
int pos;
int end;
pos = ftell(fp);
fseek(fp, 0, SEEK_END);
end = ftell(fp);
fseek(fp, pos, SEEK_SET);
fclose(fp);
return (end);
}
//tell if filename1 is older than masterfile
//////////////////////////////////////////////////////////////////////
bool CXFile::IsOutOfDate(const char *pFileName1,const char *pMasterFile)
{
FILE *f=fopen(pMasterFile,"rb");
if (f)
fclose(f);
else
return (false);
f=fopen(pFileName1,"rb");
if (f)
fclose(f);
else
return (true);
#ifdef WIN32
HANDLE status1 = CreateFile(pFileName1,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
HANDLE status2 = CreateFile(pMasterFile,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
FILETIME writetime1,writetime2;
GetFileTime(status1,NULL,NULL,&writetime1);
GetFileTime(status2,NULL,NULL,&writetime2);
CloseHandle(status1);
CloseHandle(status2);
if (CompareFileTime(&writetime1,&writetime2)==-1)
return(true);
return (false);
#else
return (false);
#endif
}
//////////////////////////////////////////////////////////////////////
int CXFile::GetWriteTime(const char *pFileName1)
{
FILE *f=fopen(pFileName1,"rb");
if (f)
fclose(f);
else
return (0);
#ifdef WIN32
HANDLE status1 = CreateFile(pFileName1,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
FILETIME writetime1;
memset(&writetime1,0,sizeof(writetime1));
GetFileTime(status1,NULL,NULL,&writetime1);
CloseHandle(status1);
return (writetime1.dwHighDateTime + writetime1.dwLowDateTime);
#else
return (0);
#endif
}
//////////////////////////////////////////////////////////////////////
int CXFile::GetLength(FILE *f)
{
int pos;
int end;
pos = ftell(f);
fseek(f, 0, SEEK_END);
end = ftell(f);
fseek(f, pos, SEEK_SET);
return end;
}
//////////////////////////////////////////////////////////////////////
void CXFile::SafeRead(FILE *f, void *buffer, int count)
{
(fread(buffer, 1, count, f) != (unsigned)count);
}
//////////////////////////////////////////////////////////////////////
int CXFile::LoadInMemory(const char *filename, void **bufferptr)
{
FILE *f = fopen(filename,"rb");
if (!f)
return (0);
int length = CXFile::GetLength(f);
void *buffer = new char[length+1];
SafeRead(f, buffer, length);
fclose(f);
char *bbp=(char *)buffer;
bbp[length]=0; //null terminated
*bufferptr = buffer;
return (length);
}
//////////////////////////////////////////////////////////////////////
void CXFile::GetPath(char *path)
{
char *src = path+strlen(path)-1;
while (*src)
{
if (*src == '\\')
{
src++;
*src = 0; // remove extension
return;
}
src--;
}
}

View File

@@ -0,0 +1,68 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: file.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: cecahed file access
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef PS2
#ifndef FILE_H
#define FILE_H
#define MAX_PATH_LENGTH 512
#include <stdio.h>
class CXFile
{
public:
CXFile();
~CXFile() { FClose(); }
int FRead(void *pDest,int nSize,int nNumElems);
static void SafeRead(FILE *f, void *buffer, int count);
int FSeek(int nOff,int nFrom);
int FLoad(const char *filename);
void FClose();
static bool FileExist(const char *filename);
static bool IsFileExist(const char *filename);
//-1 if file does not exist
static int GetLength(const char *filename);
static int GetLength(FILE *f);
static int LoadInMemory(const char *filename, void **bufferptr);
static bool IsOutOfDate(const char *pFileName1,const char *pMasterFile);
static int GetWriteTime(const char *pFileName1);
//utils
static void RemoveExtension (char *path);
static void ReplaceExtension(char *path, const char *new_ext);
static char *GetExtension (const char *filename);
static char *GetString (FILE *fp,const char *key);
static void GetPath(char *path);
//Tim code
static void SetLanguage (const char *command=NULL);
static char *ConvertFilename(const char *filename);
private:
char *m_szFileStart,*m_pCurrPos,*m_pEndOfFile;
int m_nFileSize;
char m_sLoadedFileName[512];
};
#endif
#else //PS2
#include "..\CryCommon\File.h"
#endif

View File

@@ -0,0 +1,148 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: geom.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: loading geometry from cgf
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Geom.h"
#include "file.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CGeom::CGeom() : CBaseObj()
{
memset(&m_Chunk,0,sizeof(m_Chunk));
m_pVertices =NULL;
m_pFaces =NULL;
// m_pLinks =NULL;
m_pUVs =NULL;
m_sPropstr =NULL;
// bones =NULL;
m_pTexFaces =NULL;
// m_pNumLinks =NULL;
// links =NULL;
m_pVcols =NULL;
// BoneNames =NULL;
}
CGeom::~CGeom()
{
/* if(links)
{
for(int i=0;i<m_Chunk.nVerts;i++) if(links[i]) free(links[i]);
free(links);
}*/
// if(bones) free(bones);
if(m_pFaces) free(m_pFaces);
if(m_pVertices)free(m_pVertices);
if(m_pUVs) free(m_pUVs);
if(m_sPropstr) free(m_sPropstr);
// if(nLinks) free(nLinks);
if(m_pVcols) free(m_pVcols);
if(m_pTexFaces)free(m_pTexFaces);
}
bool CGeom::Load(CXFile *f, int pos)
{
if(f->FSeek(pos,SEEK_SET)) return true;
int res=f->FRead(&m_Chunk,sizeof(m_Chunk),1);
if(res!=1) return true;
m_ChunkHeader=m_Chunk.chdr;
if(m_ChunkHeader.ChunkType != ChunkType_Mesh || m_ChunkHeader.ChunkVersion != MESH_CHUNK_DESC_VERSION)
{
memset(&m_Chunk,0,sizeof(m_Chunk));
return true;
}
//read verts
m_pVertices=(CryVertex*)malloc(sizeof(CryVertex)*m_Chunk.nVerts);
assert(m_pVertices);
res=f->FRead(m_pVertices,sizeof(CryVertex),m_Chunk.nVerts);
if(res!=m_Chunk.nVerts) return true;
//read m_pFaces
m_pFaces=(CryFace*)malloc(sizeof(CryFace)*m_Chunk.nFaces);
assert(m_pFaces);
res=f->FRead(m_pFaces,sizeof(CryFace),m_Chunk.nFaces);
if(res!=m_Chunk.nFaces) return true;
//read tverts
if(m_Chunk.nTVerts)
{
m_pUVs=(CryUV*)malloc(sizeof(CryUV)*m_Chunk.nTVerts);
assert(m_pUVs);
res=f->FRead(m_pUVs,sizeof(CryUV),m_Chunk.nTVerts);
if(res!=m_Chunk.nTVerts) return true;
// flip tex coords (since it was flipped in max?)
for(int t=0; t<m_Chunk.nTVerts; t++)
m_pUVs[t].v = 1.f-m_pUVs[t].v;
//read Tfaces
//if(m_Chunk.nVerts != m_Chunk.nTVerts)
if(m_Chunk.nVerts != 12 || pos != 692)//hack to make old grass objects working
{
m_pTexFaces=(CryTexFace*)malloc(sizeof(CryTexFace)*m_Chunk.nFaces);
assert(m_pTexFaces);
res=f->FRead(m_pTexFaces,sizeof(CryTexFace),m_Chunk.nFaces);
if(res!=m_Chunk.nFaces) return true;
}
}
//read Vertex Colors
if(m_Chunk.HasVertexCol)
{
m_pVcols=(CryIRGB *)malloc(m_Chunk.nVerts*sizeof(CryIRGB));
assert(m_pVcols);
int res=f->FRead(m_pVcols, sizeof(CryIRGB), m_Chunk.nVerts);
/*
for (int k=0;k<m_Chunk.nVerts;k++)
{
m_pVcols[k].r=255;
m_pVcols[k].g=0;
m_pVcols[k].b=0;
} //k
*/
if (res!=m_Chunk.nVerts)
return (true);
}
else
{
m_pVcols=0;
/*
m_pVcols=(CryIRGB *)malloc(m_Chunk.nVerts*sizeof(CryIRGB));
assert(m_pVcols);
for (int k=0;k<m_Chunk.nVerts;k++)
{
m_pVcols[k].r=255;
m_pVcols[k].g=0;
m_pVcols[k].b=0;
} //k
*/
}
return (false);
}

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: geom.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_GEOM_H__D90B8CB0_DAA4_49C7_8D72_4A73FA38E640__INCLUDED_)
#define AFX_GEOM_H__D90B8CB0_DAA4_49C7_8D72_4A73FA38E640__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "baseobj.h"
class CGeom : public CBaseObj
{
public:
CryVertex *m_pVertices;
CryFace *m_pFaces;
CryUV *m_pUVs;
CryTexFace *m_pTexFaces;
CryIRGB *m_pVcols;
// int *m_pNumLinks;
// CryLink ** m_pLinks;
char * m_sPropstr;
MESH_CHUNK_DESC m_Chunk;
CGeom();
virtual ~CGeom();
//from BaseObj
bool Load(CXFile *f, int pos);
MESH_CHUNK_DESC * GetChunk() { return &m_Chunk; }
//CGeom methods
void Deform();
};
#endif // !defined(AFX_GEOM_H__D90B8CB0_DAA4_49C7_8D72_4A73FA38E640__INCLUDED_)

View File

@@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: helper.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: loading helper from cgf
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Helper.h"
#include "file.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CHelper::CHelper() : CBaseObj()
{
memset(&m_Chunk,0,sizeof(m_Chunk));
}
CHelper::~CHelper()
{
}
bool CHelper::Load(CXFile *f, int pos)
{
if(f->FSeek(pos,SEEK_SET)) return true;
int res=f->FRead(&m_Chunk,sizeof(m_Chunk),1);
if(res!=1) return true;
if(m_Chunk.chdr.ChunkType != ChunkType_Helper || m_Chunk.chdr.ChunkVersion != HELPER_CHUNK_DESC_VERSION)
{
memset(&m_Chunk,0,sizeof(m_Chunk));
return true;
}
m_ChunkHeader=m_Chunk.chdr;
return false;
}

View File

@@ -0,0 +1,36 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: helper.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_HELPER_H__3A967DC3_988F_4A67_BC33_AC26573B86FA__INCLUDED_)
#define AFX_HELPER_H__3A967DC3_988F_4A67_BC33_AC26573B86FA__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "BaseObj.h"
class CHelper : public CBaseObj
{
public:
HELPER_CHUNK_DESC m_Chunk;
CHelper();
virtual ~CHelper();
virtual bool Load(CXFile *f, int pos);
};
#endif // !defined(AFX_HELPER_H__3A967DC3_988F_4A67_BC33_AC26573B86FA__INCLUDED_)

View File

@@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: light.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: loading light source from cgf
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Light.h"
#include "file.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CLight::CLight()
{
memset(&m_Chunk,0,sizeof(m_Chunk));
}
CLight::~CLight()
{
}
bool CLight::Load(CXFile *f, int pos)
{
if(f->FSeek(pos,SEEK_SET)) return true;
int res=f->FRead(&m_Chunk,sizeof(m_Chunk),1);
if(res!=1) return true;
if(m_Chunk.chdr.ChunkType != ChunkType_Light || m_Chunk.chdr.ChunkVersion != LIGHT_CHUNK_DESC_VERSION)
{
memset(&m_Chunk,0,sizeof(m_Chunk));
return true;
}
m_ChunkHeader=m_Chunk.chdr;
return false;
}

View File

@@ -0,0 +1,36 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: light.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_LIGHT_H__ACF97045_6471_4C13_BC9F_F26C16A0590E__INCLUDED_)
#define AFX_LIGHT_H__ACF97045_6471_4C13_BC9F_F26C16A0590E__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "BaseObj.h"
class CLight : public CBaseObj
{
public:
LIGHT_CHUNK_DESC m_Chunk;
CLight();
virtual ~CLight();
virtual bool Load(CXFile *f, int pos);
};
#endif // !defined(AFX_LIGHT_H__ACF97045_6471_4C13_BC9F_F26C16A0590E__INCLUDED_)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: node.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: loading node info from cgf
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Node.h"
#include "Geom.h"
#include "file.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CNodeCGF::CNodeCGF() : CBaseObj()
{
m_pObj = NULL;
// m_sPropStr = NULL;
// m_ppChildren = NULL;
// m_pnChildrenIds= NULL;
m_pParent = NULL;
// m_pMtl = NULL;
// m_pConRot = NULL;
// m_pConPos = NULL;
// m_pConScl = NULL;
memset(&m_Chunk,0,sizeof(m_Chunk));
}
CNodeCGF::~CNodeCGF()
{
// if(m_ppChildren) free(m_ppChildren);
// if(m_pnChildrenIds) free(m_pnChildrenIds);
// if(m_sPropStr) free(m_sPropStr);
}
bool CNodeCGF::Load(CXFile *f, int pos)
{
if(f->FSeek(pos,SEEK_SET)) return true;
int res=f->FRead(&m_Chunk,sizeof(m_Chunk),1);
if(res!=1) return true;
if(m_Chunk.chdr.ChunkType != ChunkType_Node || m_Chunk.chdr.ChunkVersion != NODE_CHUNK_DESC_VERSION)
{
memset(&m_Chunk,0,sizeof(m_Chunk));
return true;
}
m_ChunkHeader=m_Chunk.chdr;
// m_NodeMatrix = m_Chunk.tm;
//read propstr
/* if(m_Chunk.PropStrLen)
{
m_sPropStr=(char*)malloc(m_Chunk.PropStrLen+1);
assert(m_sPropStr);
res=f->FRead(m_sPropStr,m_Chunk.PropStrLen,1);
m_sPropStr[m_Chunk.PropStrLen]=0;
if(res!=1) return true;
}*/
//read m_ppChildren
/* if(m_Chunk.nChildren)
{
m_pnChildrenIds = (int*) malloc(m_Chunk.nChildren*sizeof(int));
assert(m_pnChildrenIds);
m_ppChildren = (CNodeCGF **) malloc(m_Chunk.nChildren*sizeof(CNodeCGF*));
assert(m_pnChildrenIds);
memset(m_ppChildren,0,m_Chunk.nChildren*sizeof(CNodeCGF*));
int res=f->FRead(m_pnChildrenIds,sizeof(int),m_Chunk.nChildren);
if(res!=m_Chunk.nChildren) return true;
}*/
return false;
}
void CNodeCGF::Bind(CBaseObj ** all_objects, int n_obj)
{
if(m_bBinded)
return;
for(int i=0;i<n_obj;i++)
{
CBaseObj * o = all_objects[i];
if(!o || o->m_ChunkHeader.ChunkID == -1)
continue;
if(o->m_ChunkHeader.ChunkID == m_Chunk.ObjectID)
{
m_pObj = o;
o->m_nUsers++;
}
else if(o->m_ChunkHeader.ChunkID == m_Chunk.ParentID)
m_pParent=(CNodeCGF *)o; /*
else if(o->m_ChunkHeader.ChunkID == m_Chunk.MatID)
m_pMtl=o;
else if(o->m_ChunkHeader.ChunkID == m_Chunk.pos_cont_id)
m_pConPos=(Controller *)o;
else if(o->m_ChunkHeader.ChunkID == m_Chunk.rot_cont_id)
m_pConRot=(Controller *)o;
else if(o->m_ChunkHeader.ChunkID == m_Chunk.scl_cont_id)
m_pConScl=(Controller *)o;
for(int j=0;j<m_Chunk.nChildren;j++)
{
if(o->m_ChunkHeader.ChunkID == m_pnChildrenIds[j])
m_ppChildren[j]=(CNodeCGF *)o;
}*/
}
m_bBinded = true;
}

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: node.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_NODE_H__8DFD8741_DBA1_4357_9F50_8E37EA039BCB__INCLUDED_)
#define AFX_NODE_H__8DFD8741_DBA1_4357_9F50_8E37EA039BCB__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "BaseObj.h"
class Controller;
class CNodeCGF : public CBaseObj
{
public:
NODE_CHUNK_DESC m_Chunk;
// int *m_pnChildrenIds;
CBaseObj *m_pObj;
/* char *m_sPropStr;
CNodeCGF ** m_ppChildren;*/
CNodeCGF *m_pParent;
/*CBaseObj *m_pMtl;
Controller *m_pConPos,*m_pConRot,*m_pConScl;*/
// CryMatrix m_NodeMatrix;
CNodeCGF();
virtual ~CNodeCGF();
virtual bool Load(CXFile *f, int pos);
virtual void Bind(CBaseObj **all_objects, int n_obj);
char * GetName() { return m_Chunk.name; }
float * GetMatrixData() { return m_Chunk.tm.GetData(); }
};
#endif // !defined(AFX_NODE_H__8DFD8741_DBA1_4357_9F50_8E37EA039BCB__INCLUDED_)

View File

@@ -0,0 +1,362 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: StatCGFCompiler.cpp
// Version: v1.00
// Created: 5/11/2002 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <DbgHelp.h>
#include "ConvertContext.h"
#include "iconfig.h"
#include "StatCGFCompiler.h"
#define ISystem IRCLog
#include "meshidx.h"
#include "statcgfshadvol.h"
CStatCFGCompiler::CStatCFGCompiler(void)
{
}
CStatCFGCompiler::~CStatCFGCompiler(void)
{
}
// function for creating this from outside (without including StatCGFCompiler.h)
IConvertor* NewStatCGFCompiler()
{
return new CStatCFGCompiler();
}
void CStatCFGCompiler::FindDependencies( CIndexedMesh * pIndexedMesh, ConvertContext &cc )
{
cc.pLog->Log("Finding dependencies");
for(int m=0; m<pIndexedMesh->m_lstMatTable.Count(); m++)
{
CMatInfo &ref=pIndexedMesh->m_lstMatTable[m];
const char *szMatName=ref.GetName();
const char *szScriptName=ref.sScriptMaterial;
CString sourceFile = cc.getSourcePath();
cc.pRC->AddDependencyMaterial(sourceFile.GetString(),szMatName,szScriptName); // material name
if(!ref.pMatEnt)continue;
// texture path names
#define ADD_MAP(MAP) if (ref.pMatEnt->map_##MAP.name[0]) cc.pRC->AddDependencyFile(sourceFile.GetString(),ref.pMatEnt->map_##MAP.name);
ADD_MAP(a);
ADD_MAP(d);
ADD_MAP(o);
ADD_MAP(b);
ADD_MAP(s);
ADD_MAP(g);
ADD_MAP(detail);
ADD_MAP(e);
ADD_MAP(subsurf);
ADD_MAP(displ);
#undef ADD_MAP
}
}
FILETIME UnixTimeToFileTime(time_t t)
{
// Note that LONGLONG is a 64-bit value
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
return (FILETIME&)ll;
}
// returns the file modification time
FILETIME GetModificationTime(FILE* hFile)
{
struct _stat st;
_fstat(_fileno(hFile), &st);
#ifdef _DEBUG
const char* szTest = ctime (&st.st_mtime);
#endif
return UnixTimeToFileTime (st.st_mtime);
}
bool CStatCFGCompiler::GetSourceFileTime(const char * szFileName, FILETIME & fileTime)
{
FILE* f = fopen (szFileName, "rb");
if (!f)
return false;
fileTime = GetModificationTime(f);
fclose (f);
return true;
}
void CStatCFGCompiler::GetFileParams( ConvertContext &cc, CString & sGeomName,
bool & bStripify, bool & bLoadAdditinalInfo, bool & bKeepInLocalSpace)
{
cc.config->Get("GeomName",sGeomName); // subobject name
cc.config->Get("Stripify",bStripify); // sort for vertex ceache
cc.config->Get("LoadAdditinalInfo",bLoadAdditinalInfo); // load geom names, helpers, lightsources
cc.config->Get("KeepInLocalSpace",bKeepInLocalSpace); // do not transform vertices by node matrix
}
bool CStatCFGCompiler::Process( ConvertContext &cc )
{
try
{
CString sGeomName;
bool bStripify=0, bLoadAdditinalInfo=0, bKeepInLocalSpace=0;
GetFileParams( cc, sGeomName, bStripify, bLoadAdditinalInfo, bKeepInLocalSpace);
if (!cc.bQuiet)
{
cc.pLog->Log("Conversion params:");
cc.pLog->Log(" GeomName = %s", sGeomName[0] ? sGeomName : "None");
cc.pLog->Log(" Stripify = %s", bStripify ? "Yes" : "No");
cc.pLog->Log(" LoadAdditinalInfo = %s", bLoadAdditinalInfo ? "Yes" : "No");
cc.pLog->Log(" KeepInLocalSpace = %s", bKeepInLocalSpace ? "Yes" : "No");
}
CString sourceFile = cc.getSourcePath();
CString outputFile = cc.getOutputPath();
const char *sInFile = sourceFile.GetString();
FILETIME fileTime;
if(!GetSourceFileTime( sourceFile.GetString(), fileTime))
{
remove( outputFile.GetString() );
return false;
}
// Check if .cga.
if (stricmp(Path::GetExt(cc.sourceFile).GetString(),"cga") == 0)
{
// Load CGA.
ProcessCGA( cc,sourceFile,outputFile,fileTime,bStripify );
}
else
{
// Load Normal CGF.
ProcessCGF( cc,sourceFile,outputFile,fileTime,sGeomName,bStripify,bLoadAdditinalInfo,bKeepInLocalSpace );
}
}
catch(char*)
{
Beep(1000,1000);
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void CStatCFGCompiler::ProcessCGA( ConvertContext &cc,CString &sourceFile,CString &outputFile,FILETIME fileTime,bool bStripify )
{
CString outputFileNoExt = Path::RemoveExtension(cc.sourceFile);
CString outputGeomFile;
int nLoadedTrisCount=0;
CIndexedMesh * pIndexedMesh = new CIndexedMesh( cc.pLog,sourceFile.GetString(),0,&nLoadedTrisCount,true,true );
if (pIndexedMesh)
{
for (int i = 0; i < (int)pIndexedMesh->m_lstGeomNames.size(); i++)
{
CString sOrgGeomName = pIndexedMesh->m_lstGeomNames[i];
CString sGeomName = sOrgGeomName;
sGeomName.Replace( '\\','_' );
sGeomName.Replace( '/','_' );
outputGeomFile.Format( "%s_%s_%d_%d_%d.ccgf",outputFileNoExt.GetString(), sGeomName.GetString(),(int)bStripify,1,1 );
cc.outputFile = outputGeomFile;
outputGeomFile = cc.getOutputPath();
const char *sgeom = outputGeomFile.GetString();
ProcessCGF( cc,sourceFile,outputGeomFile,fileTime,sOrgGeomName,bStripify,true,true );
}
}
delete pIndexedMesh;
}
//////////////////////////////////////////////////////////////////////////
void CStatCFGCompiler::ProcessCGF( ConvertContext &cc,CString &sourceFile,CString &outputFile,FILETIME fileTime,CString sGeomName,
bool bStripify, bool bLoadAdditinalInfo, bool bKeepInLocalSpace )
{
// load source cgf
int nLoadedTrisCount=0;
CIndexedMesh * pIndexedMesh = new CIndexedMesh( cc.pLog, sourceFile.GetString(), sGeomName[0] ? sGeomName.GetString() : 0,
&nLoadedTrisCount, bLoadAdditinalInfo, bKeepInLocalSpace);
pIndexedMesh->CalcTangentSpace();
// if geom name was specified - save empty file to let the engine know that this geom does not exits
// since passing wrong geom name is valid operation
if(!nLoadedTrisCount && !sGeomName[0])
cc.pLog->ThrowError(" No faces found");
// find dependencies (material names, texture path names)
FindDependencies( pIndexedMesh, cc );
// compile data
CSimpleStatObj StatObj( cc.pLog, pIndexedMesh, sourceFile.GetString() );
CSimpleLeafBuffer LeafBuffer(cc.pLog, pIndexedMesh, bStripify,
pIndexedMesh->m_lstGeomNames.Count()>0 && strstr(pIndexedMesh->m_lstGeomNames[0],"cloth")!=0);
CStatCGFShadVol StatCGFShadVol(cc.pLog, pIndexedMesh);
int nPos = 0;
// get data size
if(nLoadedTrisCount)
{
StatObj.Serialize(nPos, 0, true);
LeafBuffer.Serialize(nPos, 0, true, outputFile.GetString() );
StatCGFShadVol.Serialize(nPos, 0, true);
}
// allocate mem buffer
uchar * pData = new uchar[nPos+sizeof(CCGFHeader)];
// make header
CCGFHeader fileHeader;
fileHeader.nDataSize = nPos;
fileHeader.nFacesInCGFNum = nLoadedTrisCount;
fileHeader.SourceFileTime = fileTime;
fileHeader.vBoxMin = pIndexedMesh->m_vBoxMin;
fileHeader.vBoxMax = pIndexedMesh->m_vBoxMax;
strcpy(fileHeader.szVersion,CCGF_FILE_VERSION);
if(StatObj.IsPhysicsExist())
fileHeader.dwFlags |= CCGFHF_PHYSICS_EXIST;
memcpy(pData,&fileHeader,sizeof(CCGFHeader));
nPos = sizeof(fileHeader);
// save to new file
if(nLoadedTrisCount)
{
StatObj.Serialize(nPos, pData, true);
LeafBuffer.Serialize(nPos, pData, true, outputFile.GetString() );
StatCGFShadVol.Serialize(nPos, pData, true);
}
// create folder for object
CString srtDirName = cc.getOutputFolderPath();
int nFind = -1;
while(1)
{
nFind = srtDirName.Find('\\', nFind+1);
if(nFind<0)
break;
CString strSubDirName = srtDirName.Left(nFind);
CreateDirectory(strSubDirName.GetString(),NULL);
}
cc.pLog->Log("Writing CCGF: %s", outputFile.GetString() );
FILE * f = fopen(outputFile.GetString(),"wb");
if(f)
{
size_t nWriten = fwrite(pData,1,nPos,f);
fclose(f);
if(nWriten == nPos)
cc.pLog->Log(" %d bytes saved", nPos);
else
cc.pLog->ThrowError(" Error writing output file");
}
else
cc.pLog->ThrowError(" Error opening output file");
delete pIndexedMesh;
delete pData;
}
////////////////////////////////////////////////////////////
//
// !!! PLZ NEVER CHANGE THIS FILE WITHOUT ASKING VLAD !!!
//
////////////////////////////////////////////////////////////
bool CStatCFGCompiler::GetOutputFile( ConvertContext &cc )
{
bool bStripify=0, bLoadAdditinalInfo=0, bKeepInLocalSpace=0; CString sGeomName;
GetFileParams( cc, sGeomName, bStripify, bLoadAdditinalInfo, bKeepInLocalSpace);
CString outputFileNoExt = Path::ReplaceExtension( cc.sourceFile, "" );
char szCurDir[MAX_PATH]="";
GetCurrentDirectory(MAX_PATH,szCurDir);
CString curFolderName = szCurDir;
cc.outputFolder = cc.masterFolder + CString(CCGF_CACHE_DIR_NAME) + "\\" + cc.outputFolder;
//CString outputDirName=Path::ReplacePath(curFolderName, curFolderName + "\\" + CCGF_CACHE_DIR_NAME, outputFileNoExt);
char szOutputFileName[1024];
sprintf(szOutputFileName,
"%s_%s_%d_%d_%d.ccgf",
outputFileNoExt.GetString(), sGeomName.GetString(),
(int)bStripify, (int)bLoadAdditinalInfo, (int)bKeepInLocalSpace);
//specify output path
cc.outputFile = szOutputFileName;
return true;
}
//////////////////////////////////////////////////////////////////////////
int CStatCFGCompiler::GetNumPlatforms() const
{
return 4;
}
//////////////////////////////////////////////////////////////////////////
Platform CStatCFGCompiler::GetPlatform( int index ) const
{
switch (index)
{
case 0: return PLATFORM_PC;
case 1: return PLATFORM_XBOX;
//case 2: return PLATFORM_PS2;
//case 3: return PLATFORM_GAMECUBE;
};
//assert(0);
return PLATFORM_UNKNOWN;
}
DWORD CStatCFGCompiler::GetTimestamp() const
{
return GetTimestampForLoadedLibrary(g_hInst);
}
CStatCFGCompiler::Error::Error (const char* szFormat, ...)
{
char szBuffer[0x800];
va_list arg;
va_start(arg,szFormat);
_vsnprintf (szBuffer, sizeof(szBuffer), szFormat, arg);
va_end(arg);
this->m_strReason = szBuffer;
}
CStatCFGCompiler::Error::Error (int nCode)
{
char szBuffer[36];
sprintf (szBuffer, "Generic Error #%d", nCode);
this->m_strReason = szBuffer;
}
int CStatCFGCompiler::SetTexType(TextureMap3 *tm)
{
if (tm->type == TEXMAP_CUBIC)
return eTT_Cubemap;
else
if (tm->type == TEXMAP_AUTOCUBIC)
return eTT_AutoCubemap;
return eTT_Base;
}

View File

@@ -0,0 +1,299 @@
#ifndef STAT_CGF_COMPILER
#define STAT_CGF_COMPILER
#include "IConvertor.h"
struct ConvertContext;
class CStatCFGCompiler : public IConvertor
{
public:
class Error
{
public:
Error (int nCode);
Error (const char* szFormat, ...);
const char* c_str()const {return m_strReason.c_str();}
protected:
string m_strReason;
};
CStatCFGCompiler(void);
~CStatCFGCompiler(void);
bool Process( ConvertContext &cc );
void Release() { delete this; };
bool GetOutputFile( ConvertContext &cc );
int GetNumPlatforms() const;
Platform GetPlatform( int index ) const;
virtual int GetNumExt() const { return 2; };
virtual const char* GetExt( int index ) const { return index ? "cga" : "cgf"; };
DWORD GetTimestamp() const;
bool GetSourceFileTime(const char * szFileName, FILETIME & fileTime);
protected:
int SetTexType(struct TextureMap3 *tm);
bool PrepareTexSpaceBasis();
void FindDependencies( CIndexedMesh * pIndexedMesh, ConvertContext &cc );
void GetFileParams( ConvertContext &cc, CString & sGeomName,
bool & bStripify, bool & bLoadAdditinalInfo, bool & bKeepInLocalSpace);
void ProcessCGA( ConvertContext &cc,CString &sourceFile,CString &outputFile,FILETIME fileTime,bool bStripify );
void ProcessCGF( ConvertContext &cc,CString &sourceFile,CString &outputFile,FILETIME fileTime,CString sGeomName,
bool bStripify, bool bLoadAdditinalInfo, bool bKeepInLocalSpace );
};
struct CSimpleREOcLeaf
{
TArray<SMeshFace> * m_Faces;
CMatInfo * m_pChunk;
class CSimpleLeafBuffer * m_pBuffer;
CSimpleREOcLeaf() {memset(this,0,sizeof(CSimpleREOcLeaf));}
~CSimpleREOcLeaf()
{
delete m_Faces;
}
};
struct CBasis
{
CBasis()
{
tangent(0,0,0);
binormal(0,0,0);
tnormal(0,0,0);
}
Vec3d tangent, binormal, tnormal;
};
class CSimpleLeafBuffer
{
public:
CSimpleLeafBuffer(IRCLog * pLog, CIndexedMesh * pIndexedMesh, bool bStripifyAndShareVerts, bool bKeepRemapTable=false);
~CSimpleLeafBuffer();
bool Serialize(int & nPos, uchar * pSerBuf, bool bSave, const char * szFolderName);
// CBasis * m_pBasises;
protected:
void CreateLeafBuffer( CIndexedMesh * pTriData, int Stripify, bool bShareVerts, bool bKeepRemapTable=false );
Vec3d *m_TempNormals;
SMRendTexVert *m_TempTexCoords;
UCol *m_TempColors;
int m_SecVertCount;
list2<CMatInfo> * m_pMats;
IRCLog * m_pLog;
bool compute_tangent( const float * v0, const float * v1, const float * v2,
const float * t0, const float * t1, const float * t2,
Vec3d & tangent, Vec3d & binormal, Vec3d & tnormal, Vec3d & face_normal);
void CompactBuffer(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F * _vbuff, int * _vcount,
list2<unsigned short> * pindices, bool bShareVerts[128], uint *uiInfo, CBasis * pBasises);
list2<unsigned short> *m_pIndices, *m_pIndicesPreStrip;
void *m_pD3DIndBuf;
list2<unsigned short> & GetIndices() { return *m_pIndices; }
CVertexBuffer * m_pSecVertBuffer;
void StripifyMesh(int StripType, CBasis *pTangNonStrip);
void CalcFaceNormals();
bool CreateTangBuffer(CBasis * pBasises);
Vec3d * m_pLoadedColors;
Vec3d m_vBoxMin, m_vBoxMax;
int FindInBuffer(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F &opt, CBasis &origBasis, uint nMatInfo, uint *uiInfo, struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F* _vbuff, CBasis *_vbasis, int _vcount, list2<unsigned short> * pHash, TArray<uint>& ShareNewInfo);
uint * m_arrVertStripMap;
int m_nPrimetiveType;
bool PrepareTexSpaceBasis();
void CorrectTangentBasisesForPolyBump( TangData * pDuplTangData = 0);
CSimpleLeafBuffer *m_pVertexContainer;
CVertexBuffer * m_pVertexBuffer;
uint * m_arrVtxMap; //!< [Anton] mapping table leaf buffer vertex idx->original vertex idx
_inline CSimpleLeafBuffer *GetVertexContainer(void)
{
if (m_pVertexContainer)
return m_pVertexContainer;
return this;
}
byte *GetNormalPtr(int& Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
byte *pData;
SBufInfoTable * pOffs;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
pOffs = &gBufInfoTable[lb->m_pSecVertBuffer->m_vertexformat];
Stride = m_VertexSize[lb->m_pSecVertBuffer->m_vertexformat];
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData;
pOffs = &gBufInfoTable[lb->m_pVertexBuffer->m_vertexformat];
Stride = m_VertexSize[lb->m_pVertexBuffer->m_vertexformat];
}
if (pOffs->OffsNormal)
{
return &pData[Id*Stride+pOffs->OffsNormal];
}
Stride = sizeof(Vec3d);
return (byte*)&lb->m_TempNormals[Id];
}
byte *GetPosPtr(int& Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
byte *pData;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
Stride = m_VertexSize[lb->m_pSecVertBuffer->m_vertexformat];
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData;
Stride = m_VertexSize[lb->m_pVertexBuffer->m_vertexformat];
}
return &pData[Id*Stride];
}
byte *GetBinormalPtr(int& Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
byte *pData;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData;
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_TANGENTS].m_VData;
}
Stride = sizeof(SPipTangents);
return &pData[Id*Stride+12];
}
byte *GetTangentPtr(int& Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
byte *pData;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData;
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_TANGENTS].m_VData;
}
Stride = sizeof(SPipTangents);
return &pData[Id*Stride];
}
byte *GetTNormalPtr(int& Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
byte *pData;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData;
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_TANGENTS].m_VData;
}
Stride = sizeof(SPipTangents);
return &pData[Id*Stride+24];
}
byte *GetColorPtr(int & Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
SBufInfoTable * pOffs;
byte *pData;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
pOffs = &gBufInfoTable[lb->m_pSecVertBuffer->m_vertexformat];
Stride = m_VertexSize[lb->m_pSecVertBuffer->m_vertexformat];
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData;
pOffs = &gBufInfoTable[lb->m_pVertexBuffer->m_vertexformat];
Stride = m_VertexSize[lb->m_pVertexBuffer->m_vertexformat];
}
if (pOffs->OffsColor)
{
return &pData[Id*Stride+pOffs->OffsColor];
}
Stride = sizeof(UCol);
return (byte*)&lb->m_TempColors[Id];
}
byte *GetUVPtr(int & Stride, int Id=0, bool bSys=true)
{
CSimpleLeafBuffer *lb = GetVertexContainer();
SBufInfoTable * pOffs;
byte *pData;
if (bSys)
{
pData = (byte *)lb->m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
pOffs = &gBufInfoTable[lb->m_pSecVertBuffer->m_vertexformat];
Stride = m_VertexSize[m_pSecVertBuffer->m_vertexformat];
}
else
{
pData = (byte *)lb->m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData;
pOffs = &gBufInfoTable[lb->m_pVertexBuffer->m_vertexformat];
Stride = m_VertexSize[m_pVertexBuffer->m_vertexformat];
}
if (pOffs->OffsTC)
{
return &pData[Id*Stride+pOffs->OffsTC];
}
Stride = sizeof(SMRendTexVert);
return (byte*)&lb->m_TempTexCoords[Id];
}
};
class CSimpleStatObj
{
public:
CSimpleStatObj(IRCLog * pLog, CIndexedMesh * pTriData, const char*pGeomName)
{
memset(this,0,sizeof(CSimpleStatObj));
m_pLog = pLog;
m_pTriData = pTriData;
m_pGeomName = (char *)pGeomName;
Physicalize();
InitGeometry();
}
void Serialize(int & nPos, uchar * pSerBuf, bool bSave);
bool IsPhysicsExist();
protected:
int FindInPosBuffer(const Vec3d & opt, Vec3d * _vbuff, int _vcount, list2<int> * pHash);
void CompactPosBuffer(Vec3d * _vbuff, int * _vcount, list2<int> * pindices);
void Physicalize();
void InitGeometry();
IRCLog * m_pLog;
CIndexedMesh * m_pTriData;
char * m_pGeomName;
// output
list2<Vec3d> m_lstProxyVerts[3];
list2<int> m_lstProxyInds[3];
Vec3d m_vProxyBoxMin[3];
Vec3d m_vProxyBoxMax[3];
list2<unsigned char> m_lstProxyFaceMaterials[3];
Vec3d m_vBoxMin, m_vBoxMax;
list2<struct StatHelperInfo> m_lstHelpers;
list2<CDLight> m_lstLSources;
};
#endif

View File

@@ -0,0 +1,913 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: StatCGFCompiler.cpp
// Version: v1.00
// Created: 5/11/2002 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "Dbghelp.h"
#include "FileUtil.h"
#include "PathUtil.h"
#include "..\ResourceCompilerPC.h"
#include "StatCGFCompiler.h"
#include "CryChunkedFile.h"
#include "CryHeaders.h"
#include "NvTriStrip\NvTriStrip.h"
#include "meshidx.h"
#include <IShader.h>
#include "Cry_Geo.h"
CSimpleLeafBuffer::CSimpleLeafBuffer(IRCLog * pLog, CIndexedMesh * pIndexedMesh, bool bStripifyAndShareVerts,bool bKeepRemapTable)
{
memset(this,0,sizeof(CSimpleLeafBuffer));
m_pIndices = new list2<unsigned short>;
m_pIndicesPreStrip = new list2<unsigned short>;
m_pLog = pLog;
CreateLeafBuffer(pIndexedMesh,bStripifyAndShareVerts,bStripifyAndShareVerts,bKeepRemapTable);
}
CSimpleLeafBuffer::~CSimpleLeafBuffer()
{
for (int i=0; m_pMats && i<m_pMats->Count(); i++)
{
delete (CSimpleREOcLeaf*)(m_pMats->Get(i)->pRE);
m_pMats->Get(i)->pRE = 0;
delete m_pMats->Get(i)->pMatEnt;
m_pMats->Get(i)->pMatEnt=0;
delete [] m_pMats->Get(i)->m_pPrimitiveGroups;
m_pMats->Get(i)->m_pPrimitiveGroups=0;
}
delete m_pIndices;
delete m_pIndicesPreStrip;
delete m_TempNormals;
delete m_TempTexCoords;
delete m_TempColors;
delete m_pMats;
delete m_pD3DIndBuf;
delete m_pLoadedColors;
delete m_arrVertStripMap;
if (m_pVertexBuffer)
{
delete m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData;
delete m_pVertexBuffer->m_VS[VSF_TANGENTS].m_VData;
}
if (m_pSecVertBuffer)
{
delete m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
delete m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData;
}
delete m_pVertexContainer;
delete m_pVertexBuffer;
delete m_pSecVertBuffer;
if (m_arrVtxMap)
delete[] m_arrVtxMap;
}
void CSimpleLeafBuffer::CreateLeafBuffer( CIndexedMesh * pTriData, int Stripify, bool bShareVerts, bool bKeepRemapTable )
{
m_pLog->Log("Processing geometry");
int max_vert_num = pTriData->m_nFaceCount*3;
int i;
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F * pVBuff = new struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F[max_vert_num];
CBasis * pTmpTangBasis = new CBasis[max_vert_num];
int buff_vert_count = 0;
uint *uiInfo = new uint[max_vert_num];
uint *piVtxIdx = 0; // [Anton] need this to build the mapping table leafbuffer vtx idx->original vtx idx
if (bKeepRemapTable)
piVtxIdx = new uint[max_vert_num];
m_arrVtxMap = 0;
// . Sort|Group faces by materials
// For each shader (designated by shader_id of an element of m_pFaces)
// there is one list2 in this table. Each list will contain
// set of faces belonging to this shader.
list2<CObjFace*> _hash_table[512];
bool bShareVertsArr[512];
m_pMats = new list2<CMatInfo>;
m_pMats->PreAllocate(pTriData->m_lstMatTable.Count(),pTriData->m_lstMatTable.Count());
*m_pMats = pTriData->m_lstMatTable;
{ // fill the table: one list of faces per one shader
for(int i=0; i<pTriData->m_nFaceCount; i++)
{
CObjFace * pFace = &pTriData->m_pFaces[i];
char szMatName[128];
strncpy(szMatName, m_pMats->GetAt(pFace->shader_id).pMatEnt->name, 128);
strlwr(szMatName);
if(strstr(szMatName,"(nodraw)") || strstr(szMatName,"(no_draw)"))
continue;
assert(pFace->shader_id>=0 && pFace->shader_id<512);
if(pFace->shader_id>=m_pMats->Count())
{
pFace->shader_id=0;
m_pLog->Log("CLeafBuffer::CreateBuffer shader_id of face is out of range");
}
_hash_table[pFace->shader_id].Add(pFace);
}
}
// . Create vertex buffer with sequence of (possibly non-unique) vertices, 3 verts per face
// for each shader..
for (int t = 0; t < m_pMats->Count(); t++)
{
// memorize the starting index of this material's face range
(*m_pMats)[t].nFirstIndexId = buff_vert_count;
// scan through all the faces using the shader #t
for(int i=0; i<_hash_table[t].Count(); ++i)
{
CObjFace * pFace = _hash_table[t][i];
assert(pFace->shader_id == t);
for (int v = 0; v < 3; ++v)
{
if(pTriData->m_pColor)
{ // if color exported - copy from pTriData
pVBuff[buff_vert_count].color.bcolor[0] = pTriData->m_pColor[pFace->v[v]].r;
pVBuff[buff_vert_count].color.bcolor[1] = pTriData->m_pColor[pFace->v[v]].g;
pVBuff[buff_vert_count].color.bcolor[2] = pTriData->m_pColor[pFace->v[v]].b;
pVBuff[buff_vert_count].color.bcolor[3] = 255;
}
else
{
pVBuff[buff_vert_count].color.dcolor = -1;
}
// base tex coord
int tid = pFace->t[v];
if(tid>=0 && tid<pTriData->m_nCoorCount)
{
pVBuff[buff_vert_count].st[0] = pTriData->m_pCoors[pFace->t[v]].s;
pVBuff[buff_vert_count].st[1] = pTriData->m_pCoors[pFace->t[v]].t;
}
else
{
pVBuff[buff_vert_count].st[0] = 0;
pVBuff[buff_vert_count].st[1] = 0;
}
// normal
pVBuff[buff_vert_count].normal = pTriData->m_pNorms[pFace->n[v]];
uiInfo[buff_vert_count] = 0;
// position
pVBuff[buff_vert_count].xyz = pTriData->m_pVerts[pFace->v[v]];
// remember shader id per face to prevent vertex sharing between materials during recompacting
uiInfo[buff_vert_count] |= pFace->shader_id;
bShareVertsArr[pFace->shader_id] = bShareVerts;
// [Anton] keep index list to build mapping table later
if (piVtxIdx)
piVtxIdx[buff_vert_count] = pFace->v[v];
// tang basis
pTmpTangBasis[buff_vert_count] = pTriData->m_pTangBasis[pFace->b[v]];
buff_vert_count++;
}
}
// there are faces belonging to this material(shader) #t, if number of indices > 0
(*m_pMats)[t].nNumIndices = buff_vert_count - (*m_pMats)[t].nFirstIndexId;
_hash_table[t].Reset();
}
// make REs
for (i=0; i<(*m_pMats).Count(); i++)
{
if((*m_pMats)[i].nNumIndices)
{
CSimpleREOcLeaf *re = new CSimpleREOcLeaf; // (CRE OcLeaf *)gRenDev->EF_CreateRE(eDATA_OcLeaf);
re->m_pChunk = &(*m_pMats)[i];
re->m_pBuffer = this;
assert (re->m_pChunk->nNumIndices < 60000);
re->m_pChunk->pRE = (CREOcLeaf*)re;
// always enable sharing if there is 'flareproc' in shader/material name
if (!bShareVertsArr[i] && (*m_pMats)[i].nNumIndices == 6)
{
char nameSh[128];
strncpy(nameSh, (*m_pMats)[i].pMatEnt->name,sizeof(nameSh));
strlwr(nameSh);
bShareVertsArr[i] = strstr(nameSh, "flareproc")!=0;
}
}
}
// . For each (non-unique) vertex calculate the tangent base
/* m_pBasises = buff_vert_count ? new CBasis[buff_vert_count] : 0;
for (int n=0; n<buff_vert_count; n+=3)
{
Vec3d *vN0 = (Vec3d *)(&pVBuff[n+0].nx);
Vec3d *vN1 = (Vec3d *)(&pVBuff[n+1].nx);
Vec3d *vN2 = (Vec3d *)(&pVBuff[n+2].nx);
Vec3d vFaceNormal = *vN0 + *vN1 + *vN2;
vFaceNormal.Normalize();
float *v[3] =
{
(float *)&pVBuff[n+0].x,
(float *)&pVBuff[n+1].x,
(float *)&pVBuff[n+2].x,
};
float *tc[3] =
{
(float *)&pVBuff[n+0].s,
(float *)&pVBuff[n+1].s,
(float *)&pVBuff[n+2].s,
};
compute_tangent(v[0], v[1], v[2], tc[0], tc[1], tc[2], m_pBasises[n+0].tangent, m_pBasises[n+0].binormal, m_pBasises[n+0].tnormal, vFaceNormal);
compute_tangent(v[1], v[2], v[0], tc[1], tc[2], tc[0], m_pBasises[n+1].tangent, m_pBasises[n+1].binormal, m_pBasises[n+1].tnormal, vFaceNormal);
compute_tangent(v[2], v[0], v[1], tc[2], tc[0], tc[1], m_pBasises[n+2].tangent, m_pBasises[n+2].binormal, m_pBasises[n+2].tnormal, vFaceNormal);
}*/
// . Index the mesh (Compact Vertices): detect and delete duplicate vertices
// remove duplicates
if(buff_vert_count)
CompactBuffer(pVBuff, &buff_vert_count, &GetIndices(), bShareVertsArr, uiInfo, pTmpTangBasis );
delete [] uiInfo;
uiInfo=0;
for( i=0; i<GetIndices().Count(); i++ )
{
if(GetIndices()[i]<buff_vert_count)
continue;
m_pLog->ThrowError("CLeafBuffer::CreateBuffer: Indices out of range");
}
if (bKeepRemapTable) // [Anton] build the mapping table leaf buffer vertex index -> original vertex index
{
m_arrVtxMap = new uint[buff_vert_count];
for( i=0; i<GetIndices().Count(); i++ )
m_arrVtxMap[GetIndices()[i]] = piVtxIdx[i];
delete[] piVtxIdx;
}
if(buff_vert_count>65535)
m_pLog->ThrowError("CLeafBuffer::CreateBuffer: Number of vertices in object is more than 65535");
// . Remove degenerated triangles in the generated mesh (GetIndices())
// FIXME: For some reason this optimization doesn't work for Animated objects (Assertion in CryModelState::GenerateRenderArrays)
/*if (!m_sSource || strcmp(m_sSource, "CryModelArray") != 0)
{
// Remove degenerated triangles
list2<ushort> NewIndexes;
for (i=0; i<(*m_pMats).Count(); i++) // each material..
{
CMatInfo *mi = &(*m_pMats)[i];
if (!mi->pRE)
continue;
int nFirstInd = NewIndexes.Count();
for (int j=mi->nFirstIndexId; j<mi->nFirstIndexId+mi->nNumIndices; j+=3)
{
// the face in material #i consists of vertices i0,i1,i2:
int i0 = GetIndices()[j+0];
int i1 = GetIndices()[j+1];
int i2 = GetIndices()[j+2];
// if the face is not degenerated, then add it; otherwise skip and finally it'll be deleted
if (i0!=i1 && i0!=i2 && i1!=i2)
{
NewIndexes.Add(i0);
NewIndexes.Add(i1);
NewIndexes.Add(i2);
}
}
mi->nFirstIndexId = nFirstInd;
mi->nNumIndices = NewIndexes.Count() - nFirstInd;
if (!mi->nNumIndices)
{
mi->pRE->Release();
mi->pRE = NULL;
}
}
GetIndices().Free();
GetIndices().AddList(NewIndexes);
NewIndexes.Free();
}*/
// . Find vertex range (both index and spacial ranges) for each material (needed for rendering)
for (i=0; i<(*m_pMats).Count(); i++)
{
CMatInfo *mi = &(*m_pMats)[i];
if (!mi->pRE)
continue;
if (mi->nNumIndices+mi->nFirstIndexId > GetIndices().Count())
{ assert(0); continue; }
int nMin = 999999;
int nMax = -999999;
Vec3d vMin;
Vec3d vMax;
vMin=SetMaxBB();
vMax=SetMinBB();
for (int j=mi->nFirstIndexId; j<mi->nNumIndices+mi->nFirstIndexId; j++)
{
int ind = GetIndices()[j];
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *pV = &pVBuff[ind];
Vec3d v = pV->xyz;
vMin.CheckMin(v);
vMax.CheckMax(v);
nMin = min(nMin, ind);
nMax = max(nMax, ind);
}
mi->m_vCenter = (vMin + vMax) * 0.5f;
mi->m_fRadius = (vMin - mi->m_vCenter).Length();
mi->nFirstVertId = nMin;
mi->nNumVerts = nMax-nMin+1;
}
// store resulting vertex buffer in system memory
m_SecVertCount = buff_vert_count;
m_pSecVertBuffer = new CVertexBuffer;
m_pSecVertBuffer->m_vertexformat = VERTEX_FORMAT_P3F_N_COL4UB_TEX2F;
if(m_SecVertCount)
m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData = new struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F[m_SecVertCount];
memcpy(m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData, pVBuff, m_SecVertCount*sizeof(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F));
delete [] pVBuff;
pVBuff=0;
*m_pIndicesPreStrip = GetIndices();
if(m_SecVertCount)
{
if (Stripify!=STRIPTYPE_NONE && !bKeepRemapTable)
StripifyMesh(Stripify,pTmpTangBasis);
CalcFaceNormals();
CreateTangBuffer(pTmpTangBasis);
}
delete [] pTmpTangBasis;
pTmpTangBasis=0;
// if colors was loaded - remember for later use
if(pTriData->m_pColor)
{
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F * pSecBuff = (struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *)m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
m_pLoadedColors = new Vec3d[m_SecVertCount];
for(int i=0; i<m_SecVertCount; i++)
{
m_pLoadedColors[i].x = pSecBuff[i].color.bcolor[0];
m_pLoadedColors[i].y = pSecBuff[i].color.bcolor[1];
m_pLoadedColors[i].z = pSecBuff[i].color.bcolor[2];
}
}
m_vBoxMin = pTriData->m_vBoxMin;
m_vBoxMax = pTriData->m_vBoxMax;
}
void CSimpleLeafBuffer::CompactBuffer(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F * _vbuff, int * _vcount,
list2<unsigned short> * pindices, bool bShareVerts[128], uint *uiInfo,
CBasis * pBasises)
{
assert(*_vcount);
if(!*_vcount)
m_pLog->ThrowError("CLeafBuffer::CompactBuffer error");
int vert_num_before = *_vcount;
CBasis *tmp_basis = new CBasis[*_vcount];
SMRendTexVert *tmp_lmtc = NULL;
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F * tmp_buff = new struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F[*_vcount];
unsigned int tmp_count = 0;
pindices->Clear();
TArray<uint> ShareNewInfo;
list2<unsigned short> hash_table[256];//[256];
for(unsigned int v=0; v<(unsigned int)(*_vcount); v++)
{
int nHashInd = (unsigned char)(_vbuff[v].xyz.x*100);
uint nMInfo = uiInfo[v];
uint nMatId = nMInfo & 255;
int find = bShareVerts[nMatId] ? FindInBuffer( _vbuff[v], pBasises[v], nMInfo, uiInfo, tmp_buff, tmp_basis, tmp_count, &hash_table[nHashInd], ShareNewInfo/*[(unsigned char)(_vbuff[v].pos.y*100)]*/) : -1;
if(find<0)
{ // not found
tmp_buff[tmp_count] = _vbuff[v];
tmp_basis[tmp_count] = pBasises[v];
pindices->Add(tmp_count);
ShareNewInfo.AddElem(uiInfo[v]);
hash_table[(unsigned char)(_vbuff[v].xyz.x*100)]/*[(unsigned char)(_vbuff[v].pos.y*100)]*/.Add(tmp_count);
tmp_count++;
}
else
{ // found
pindices->Add(find);
}
}
* _vcount = tmp_count;
memcpy( _vbuff, tmp_buff, tmp_count*sizeof(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F));
delete [] tmp_buff;
// pBasises will contain recompacted tangents now
memcpy( pBasises, tmp_basis, tmp_count*sizeof(CBasis) );
delete [] tmp_basis;
// SAFE_DELETE_ARRAY(pBasises);
int ratio = 100*(*_vcount)/vert_num_before;
m_pLog->Log(" Vert buffer size after compression = %d %s ( %d -> %d )", ratio, "%", vert_num_before, *_vcount);
}
#include "NvTriStrip/NVTriStrip.h"
void CSimpleLeafBuffer::StripifyMesh(int StripType, CBasis *pTangNonStrip)
{
int i;
unsigned int n;
//Log("Stripify mesh...");
////////////////////////////////////////////////////////////////////////////////////////
// Stripping stuff
if (StripType == STRIPTYPE_DEFAULT)
StripType = STRIPTYPE_ONLYLISTS;//CRenderer::CV_r_stripmesh;
if (StripType == STRIPTYPE_NONE)
return;
m_pLog->Log(" Sorting vertices for GPU cache");
// if (gRenDev->GetFeatures() & RFT_HW_GF3)
SetCacheSize(CACHESIZE_GEFORCE3);
// else
// SetCacheSize(CACHESIZE_GEFORCE1_2);
if (StripType == STRIPTYPE_SINGLESTRIP)
SetStitchStrips(true);
else
SetStitchStrips(false);
SetMinStripSize(0);
if (StripType == STRIPTYPE_ONLYLISTS)
{
SetListsOnly(true);
SetStitchStrips(false);
}
else
SetListsOnly(false);
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *pVBOld = (struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *)m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *pVBNew = new struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F [m_SecVertCount];
CBasis *pTangOld = pTangNonStrip;
CBasis *pTangNew = new CBasis[m_SecVertCount];
m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData = pVBNew;
// remember remapping
m_arrVertStripMap = new uint [m_SecVertCount];
memset(m_arrVertStripMap,256,sizeof(uint)*m_SecVertCount);
int vertFirst = 0;
list2<ushort> NewIndexes;
//stripify!
for (i=0; i<(*m_pMats).Count(); i++)
{
CMatInfo *mi = &(*m_pMats)[i];
if (!mi->pRE)
continue;
PrimitiveGroup* pOldPG;
GenerateStrips(&GetIndices()[mi->nFirstIndexId], mi->nNumIndices, &pOldPG, (unsigned short*)&mi->m_dwNumSections);
//remap!
PrimitiveGroup *pg;
RemapIndices(pOldPG, mi->m_dwNumSections, m_SecVertCount, &pg);
mi->m_pPrimitiveGroups = new SPrimitiveGroup[mi->m_dwNumSections];
int nMin = 999999;
int nMax = -999999;
//loop through all indices, copying from oldVB -> newVB
//note that this will do numIndices copies, instead of numVerts copies,
// which is extraneous. Deal with it! ;-)
int nFirstIndex = 0;
mi->nFirstIndexId = NewIndexes.Count();
for(int groupCtr = 0; groupCtr < mi->m_dwNumSections; groupCtr++)
{
mi->m_pPrimitiveGroups[groupCtr].type = pg[groupCtr].type;
mi->m_pPrimitiveGroups[groupCtr].numIndices = pg[groupCtr].numIndices;
mi->m_pPrimitiveGroups[groupCtr].offsIndex = nFirstIndex;
mi->m_pPrimitiveGroups[groupCtr].numTris = 0;
for(unsigned int indexCtr = 0; indexCtr < mi->m_pPrimitiveGroups[groupCtr].numIndices; indexCtr++)
{
//grab old index
int oldVertex = pOldPG[groupCtr].indices[indexCtr];
//grab new index
int newVertex = pg[groupCtr].indices[indexCtr] + vertFirst;
NewIndexes.Add(newVertex);
nMin = min(nMin, newVertex);
nMax = max(nMax, newVertex);
//copy from old -> new vertex buffer
memcpy(&pVBNew[newVertex], &pVBOld[oldVertex], sizeof(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F));
//copy from old -> new tang buffer
memcpy(&pTangNew[newVertex], &pTangOld[oldVertex], sizeof(CBasis));
// if (pVBLMOld)
// memcpy(&pVBLMNew[newVertex], &pVBLMOld[oldVertex], sizeof(SMRendTexVert));
// remember remaping
m_arrVertStripMap[oldVertex] = newVertex;
}
nFirstIndex += mi->m_pPrimitiveGroups[groupCtr].numIndices;
SPrimitiveGroup *pgn = &mi->m_pPrimitiveGroups[groupCtr];
int incr;
switch (pgn->type)
{
case PT_LIST:
incr = 3;
break;
case PT_STRIP:
case PT_FAN:
incr = 1;
break;
}
int offs = pgn->offsIndex;
for (n=0; n<pgn->numIndices-2; n+=incr)
{
int i0, i1, i2;
switch (pgn->type)
{
case PT_LIST:
i0 = pg[groupCtr].indices[offs+n];
i1 = pg[groupCtr].indices[offs+n+1];
i2 = pg[groupCtr].indices[offs+n+2];
break;
case PT_STRIP:
i0 = pg[groupCtr].indices[offs+n];
i1 = pg[groupCtr].indices[offs+n+1];
i2 = pg[groupCtr].indices[offs+n+2];
break;
case PT_FAN:
i0 = pg[groupCtr].indices[offs+0];
i1 = pg[groupCtr].indices[offs+n+1];
i2 = pg[groupCtr].indices[offs+n+2];
break;
}
// ignore degenerate triangle
if (i0==i1 || i0==i2 || i1==i2)
continue;
pgn->numTris++;
}
}
mi->nNumIndices = nFirstIndex;
mi->nFirstVertId = nMin;
mi->nNumVerts = nMax-nMin+1;
vertFirst += mi->nNumVerts;
}
m_nPrimetiveType = R_PRIMV_MULTI_GROUPS;
GetIndices().Free();
GetIndices().AddList(NewIndexes);
delete [] pVBOld;
memcpy(pTangOld,pTangNew,sizeof(CBasis)*m_SecVertCount);
delete [] pTangNew;
}
void CSimpleLeafBuffer::CalcFaceNormals()
{
int i, j;
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *pV = (struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *)m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
if (m_nPrimetiveType != R_PRIMV_MULTI_GROUPS)
{
for (i=0; i<m_pMats->Count(); i++)
{
CMatInfo *mi = m_pMats->Get(i);
CSimpleREOcLeaf * re = (CSimpleREOcLeaf *)mi->pRE;
if (!re)
continue;
if (!re->m_Faces)
re->m_Faces = new TArray<SMeshFace>;
re->m_Faces->Free();
int nOffs = mi->nFirstIndexId;
for(j=0; j<mi->nNumIndices-2; j+=3)
{
unsigned short * face = &GetIndices()[j+nOffs];
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *p0 = &pV[face[0]];
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *p1 = &pV[face[1]];
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *p2 = &pV[face[2]];
Vec3d v0 = p0->xyz;
Vec3d v1 = p1->xyz;
Vec3d v2 = p2->xyz;
Vec3d face_normal = (v0-v1) ^ (v0-v2);
face_normal.Normalize();
SMeshFace fn;
fn.m_Normal = face_normal;
fn.m_Middle = (v0 + v1 + v2) / 3.0f;
re->m_Faces->AddElem(fn);
}
}
}
else
{
// assert(0);
unsigned int n;
for (i=0; i<m_pMats->Count(); i++)
{
CMatInfo *mi = m_pMats->Get(i);
CSimpleREOcLeaf *re = (CSimpleREOcLeaf*)mi->pRE;
if (!re)
continue;
if (!re->m_Faces)
re->m_Faces = new TArray<SMeshFace>;
re->m_Faces->Free();
int nOffs = mi->nFirstIndexId;
for (j=0; j<mi->m_dwNumSections; j++)
{
SPrimitiveGroup *g = &mi->m_pPrimitiveGroups[j];
g->nFirstFace = re->m_Faces->Num();
int incr;
switch (g->type)
{
case PT_LIST:
incr = 3;
break;
case PT_STRIP:
case PT_FAN:
incr = 1;
break;
}
int offs = g->offsIndex + nOffs;
for (n=0; n<g->numIndices-2; n+=incr)
{
int i0, i1, i2;
switch (g->type)
{
case PT_LIST:
i0 = GetIndices()[offs+n];
i1 = GetIndices()[offs+n+1];
i2 = GetIndices()[offs+n+2];
break;
case PT_STRIP:
i0 = GetIndices()[offs+n];
i1 = GetIndices()[offs+n+1];
i2 = GetIndices()[offs+n+2];
break;
case PT_FAN:
i0 = GetIndices()[offs+0];
i1 = GetIndices()[offs+n+1];
i2 = GetIndices()[offs+n+2];
break;
}
// ignore degenerate triangle
if (i0==i1 || i0==i2 || i1==i2)
continue;
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *p0 = &pV[i0];
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *p1 = &pV[i1];
struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F *p2 = &pV[i2];
Vec3d v0 = p0->xyz;
Vec3d v1 = p1->xyz;
Vec3d v2 = p2->xyz;
Vec3d face_normal = (v0-v1) ^ (v0-v2);
face_normal.Normalize();
SMeshFace fn;
fn.m_Normal = face_normal;
fn.m_Middle = (v0 + v1 + v2) / 3.0f;
re->m_Faces->AddElem(fn);
}
}
}
}
}
bool CSimpleLeafBuffer::CreateTangBuffer(CBasis * pBasises)
{
// if (!m_pBasises)
// PrepareTexSpaceBasis();
assert(pBasises);
SAFE_DELETE_ARRAY(m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData);
// if (!m_pBasises)
// return false;
m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData = new SPipTangents[m_SecVertCount];
SPipTangents *tn = (SPipTangents *)m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData;
for (int i=0; i<m_SecVertCount; i++)
{
tn[i].m_Tangent[0] = pBasises[i].tangent[0];
tn[i].m_Tangent[1] = pBasises[i].tangent[1];
tn[i].m_Tangent[2] = pBasises[i].tangent[2];
tn[i].m_Binormal[0] = pBasises[i].binormal[0];
tn[i].m_Binormal[1] = pBasises[i].binormal[1];
tn[i].m_Binormal[2] = pBasises[i].binormal[2];
tn[i].m_TNormal[0] = pBasises[i].tnormal[0];
tn[i].m_TNormal[1] = pBasises[i].tnormal[1];
tn[i].m_TNormal[2] = pBasises[i].tnormal[2];
}
// CorrectTangentBasisesForPolyBump();
// Temporary basis vectors
// delete [] m_pBasises;
// m_pBasises = NULL;
return true;
}
#define PIP_TEX_EPS 0.001f
#define PIP_VER_EPS 0.001f
bool struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F::operator == (struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F & other)
{
assert(this != &other);
return fabs(xyz.x-other.xyz.x)<PIP_VER_EPS && fabs(xyz.y-other.xyz.y)<PIP_VER_EPS && fabs(xyz.z-other.xyz.z)<PIP_VER_EPS &&
fabs(normal.x-other.normal.x)<PIP_VER_EPS && fabs(normal.y-other.normal.y)<PIP_VER_EPS && fabs(normal.z-other.normal.z)<PIP_VER_EPS &&
fabs(st[0]-other.st[0])<PIP_TEX_EPS && fabs(st[1]-other.st[1])<PIP_TEX_EPS &&
(color.dcolor&0xffffff) == (other.color.dcolor&0xffffff);
}
int CSimpleLeafBuffer::FindInBuffer(struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F &opt, CBasis &origBasis, uint nMatInfo, uint *uiInfo, struct_VERTEX_FORMAT_P3F_N_COL4UB_TEX2F* _vbuff, CBasis *_vbasis, int _vcount, list2<unsigned short> * pHash, TArray<uint>& ShareNewInfo)
{
for(int i=0; i<pHash->Count(); i++)
{
int id = (*pHash)[i];
if(_vbuff[id] == opt)
{
if (ShareNewInfo[id] != nMatInfo)
continue;
if (origBasis.binormal.Dot(_vbasis[id].binormal) > 0.5f && origBasis.tangent.Dot(_vbasis[id].tangent) > 0.5f)
return (*pHash)[i];
}
}
return -1;
}
void CSimpleLeafBuffer::CorrectTangentBasisesForPolyBump( TangData * pDuplTangData )
{
TArray <bool> bUsedVerts;
int nBinormalStride=0, nTangentStride=0, nTNormalStride=0;
byte * pBinormal = GetBinormalPtr(nBinormalStride, 0, true);
byte * pTangent = GetTangentPtr(nTangentStride, 0, true);
byte * pTNormal = GetTNormalPtr(nTNormalStride, 0, true);
bUsedVerts.Reserve(GetIndices().Count());
for(int m=0; m<m_pMats->Count(); m++)
{
CMatInfo *pMI = m_pMats->Get(m);
if(!(pMI->m_Flags & MIF_POLYBUMP))
continue; // not polybump
bool bCloneSpace = false;
if (pMI->shaderItem.m_pShaderResources)
{
SRenderShaderResources *sr = pMI->shaderItem.m_pShaderResources;
if (sr->m_Textures[EFTT_BUMP] && sr->m_Textures[EFTT_BUMP]->m_TU.m_ITexPic && bCloneSpace)
continue;
}
if (m_nPrimetiveType != R_PRIMV_MULTI_GROUPS)
{
int nStart = pMI->nFirstIndexId;
int nEnd = nStart + pMI->nNumIndices;
assert(nEnd <= GetIndices().Count());
for( int i = nStart; i<nEnd; i++ )
{
int nVertId = GetIndices()[i];
if (bUsedVerts[nVertId])
continue;
bUsedVerts[nVertId] = true;
Vec3d vBin, vTan, vTnor;
if(pMI->m_Flags & MIF_INVPOLYBUMP)
{
vTan = Vec3d(1,0,0);
vBin = Vec3d(0,1,0);
vTnor = vTan.Cross(vBin);
}
else
{
vTan = Vec3d(-1,0,0);
vBin = Vec3d(0,1,0);
vTnor = vBin.Cross(vTan);
}
if ((UINT_PTR)pBinormal>256 && (UINT_PTR)pTangent>256)
{
Vec3d * vBinorm = (Vec3d *)&pBinormal[nBinormalStride*nVertId];
Vec3d * vTang = (Vec3d *)&pTangent [nTangentStride*nVertId];
Vec3d * vTNormal = (Vec3d *)&pTNormal [nTNormalStride*nVertId];
*vBinorm = vBin;
*vTang = vTan;
*vTNormal = vTnor;
}
if (pDuplTangData)
{
// int sn = pGeomInfo->m_rDupVertToNorVert[nVertId];
// assert(nVertId<pGeomInfo->m_nAllocatedTangNum);
pDuplTangData[nVertId].binormal = vBin;
pDuplTangData[nVertId].tangent = vTan;
pDuplTangData[nVertId].tnormal = vTnor;
}
}
}
else
{
for (int j=0; j<pMI->m_dwNumSections; j++)
{
SPrimitiveGroup *g = &pMI->m_pPrimitiveGroups[j];
int offs = g->offsIndex+pMI->nFirstIndexId;
for (uint n=0; n<g->numIndices; n++)
{
int nVertId = GetIndices()[n+offs];
if (bUsedVerts[nVertId])
continue;
bUsedVerts[nVertId] = true;
Vec3d vBin, vTan, vTnor;
if(pMI->m_Flags & MIF_INVPOLYBUMP)
{
vTan = Vec3d(1,0,0);
vBin = Vec3d(0,1,0);
vTnor = vTan.Cross(vBin);
}
else
{
vTan = Vec3d(-1,0,0);
vBin = Vec3d(0,1,0);
vTnor = vBin.Cross(vTan);
}
if ((UINT_PTR)pBinormal>256 && (UINT_PTR)pTangent>256)
{
Vec3d * vBinorm = (Vec3d *)&pBinormal[nBinormalStride*nVertId];
Vec3d * vTang = (Vec3d *)&pTangent [nTangentStride*nVertId];
Vec3d * vTNormal = (Vec3d *)&pTNormal [nTNormalStride*nVertId];
*vBinorm = vBin;
*vTang = vTan;
*vTNormal = vTnor;
}
if (pDuplTangData)
{
// int sn = pGeomInfo->m_rDupVertToNorVert[nVertId];
// assert(nVertId<pGeomInfo->m_nAllocatedTangNum);
pDuplTangData[nVertId].binormal = vBin;
pDuplTangData[nVertId].tangent = vTan;
pDuplTangData[nVertId].tnormal = vTnor;
}
}
}
}
}
bUsedVerts.Free();
}

View File

@@ -0,0 +1,120 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: LeafBufferSerialize.cpp
// Version: v1.00
// Created: 28/8/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: LeafBuffer serialization
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "Dbghelp.h"
#include "FileUtil.h"
#include "PathUtil.h"
#include "..\ResourceCompilerPC.h"
#include "StatCGFCompiler.h"
#include "CryChunkedFile.h"
#include "CryHeaders.h"
#include "meshidx.h"
#include "IShader.h"
#include "NvTriStrip\NvTriStrip.h"
#include "SerializeBuffer.h"
CryIRGB CF2IRGB(CFColor in)
{
CryIRGB out;
out.r = uchar(in.r*255);
out.g = uchar(in.g*255);
out.b = uchar(in.b*255);
return out;
}
char *SkipPath (char *pathname)
{
char *last;
last = pathname;
while (*pathname)
{
if (*pathname=='/' || *pathname=='\\')
last = pathname+1;
pathname++;
}
return last;
}
int CSimpleLeafBuffer__SetTexType(TextureMap3 *tm)
{
if (tm->type == TEXMAP_CUBIC)
return eTT_Cubemap;
else
if (tm->type == TEXMAP_AUTOCUBIC)
return eTT_AutoCubemap;
return eTT_Base;
}
bool CSimpleLeafBuffer::Serialize(int & nPos, uchar * pSerBuf, bool bSave, const char * szFolderName)
{
assert(bSave);
SaveBuffer("LeafBuffer", 11, pSerBuf, nPos);
SaveBuffer(&m_SecVertCount, sizeof(m_SecVertCount), pSerBuf, nPos);
SaveBuffer( m_arrVertStripMap, m_SecVertCount*sizeof(m_arrVertStripMap[0]), pSerBuf, nPos);
GetIndices().SaveToBuffer(pSerBuf, nPos);
m_pIndicesPreStrip->SaveToBuffer(pSerBuf, nPos);
SaveBuffer(&m_nPrimetiveType, sizeof(m_nPrimetiveType), pSerBuf, nPos);
// assert(m_pBasises==0); // not needed
SaveBuffer( m_pLoadedColors, m_SecVertCount*sizeof(m_pLoadedColors[0]), pSerBuf, nPos);
m_pMats->SaveToBuffer(pSerBuf, nPos); // need to restore
// save shader info
for (int i=0; i<m_pMats->Count(); i++)
{
MAT_ENTITY * pMatEnt = m_pMats->Get(i)->pMatEnt;
SaveBuffer(pMatEnt, sizeof(*pMatEnt), pSerBuf, nPos);
if(m_pMats->GetAt(i).pRE)
{
assert(((CSimpleREOcLeaf*)m_pMats->GetAt(i).pRE)->m_pChunk->nNumIndices);
assert(((CSimpleREOcLeaf*)m_pMats->GetAt(i).pRE)->m_pChunk == m_pMats->Get(i));
}
// save primitive groups
if(m_pMats->GetAt(i).m_dwNumSections)
SaveBuffer((void*)m_pMats->GetAt(i).m_pPrimitiveGroups,
sizeof(SPrimitiveGroup)*m_pMats->GetAt(i).m_dwNumSections,
pSerBuf, nPos);
}
SaveBuffer( m_pSecVertBuffer, sizeof(*m_pSecVertBuffer), pSerBuf, nPos); // need to restore
SaveBuffer( m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData, m_SecVertCount*m_VertexSize[m_pSecVertBuffer->m_vertexformat], pSerBuf, nPos);
SaveBuffer( m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData, m_SecVertCount*sizeof(SPipTangents), pSerBuf, nPos);
assert(!m_pVertexBuffer); // not needed
SaveBuffer(&m_vBoxMax, sizeof(m_vBoxMax), pSerBuf, nPos);
SaveBuffer(&m_vBoxMin, sizeof(m_vBoxMin), pSerBuf, nPos);
// [Anton] m_arrVtxMap serialization
int bHasVtxMap;
if (m_arrVtxMap)
{
SaveBuffer(&(bHasVtxMap=1),sizeof(bHasVtxMap), pSerBuf,nPos);
SaveBuffer(m_arrVtxMap,sizeof(uint)*m_SecVertCount, pSerBuf,nPos);
}
else
SaveBuffer(&(bHasVtxMap=0),sizeof(bHasVtxMap), pSerBuf,nPos);
return 0;
}

View File

@@ -0,0 +1,100 @@
#include "StdAfx.h"
#include "statcgfshadvol.h"
#include "meshidx.h"
#include "IEdgeConnectivityBuilder.h"
#include "..\StencilShadowConnectivity.h"
#include "..\StencilShadowConnectivityBuilder.h"
CStatCGFShadVol::CStatCGFShadVol(ILog * pLog, CIndexedMesh * pIndexedMesh)
{
m_pShadowVolObject = new CShadowVolObject( pLog );
for (int i=0; i<pIndexedMesh->m_nFaceCount; i++)
{
CObjFace *cf=&pIndexedMesh->m_pFaces[i];
for (int v=0; v<3; v++)
{
cf->m_Vecs[v].x=pIndexedMesh->m_pVerts[pIndexedMesh->m_pFaces[i].v[v]].x;
cf->m_Vecs[v].y=pIndexedMesh->m_pVerts[pIndexedMesh->m_pFaces[i].v[v]].y;
cf->m_Vecs[v].z=pIndexedMesh->m_pVerts[pIndexedMesh->m_pFaces[i].v[v]].z;
} //v
//calc plane equation
cf->m_Plane.CalcPlane(cf->m_Vecs[2],cf->m_Vecs[1],cf->m_Vecs[0]);
} //i
//precalc edges
m_pShadowVolObject->CreateConnectivityInfo(pIndexedMesh, pLog);
}
CStatCGFShadVol::~CStatCGFShadVol()
{
delete m_pShadowVolObject;
}
#define FLAG_SKIP_SHADOWVOLUME 1 // todo: share this flag
void CShadowVolObject::CreateConnectivityInfo( CIndexedMesh * pIndexedMesh, ILog * pLog )
{
//list of faces is shared from statobj
m_pFaceList = pIndexedMesh->m_pFaces;
m_nNumFaces = pIndexedMesh->m_nFaceCount;
Vec3d *pVert = pIndexedMesh->m_pVerts;
IEdgeConnectivityBuilder * iBuilder = new CStencilShadowStaticConnectivityBuilder();
iBuilder->Reinit();
assert(iBuilder);
iBuilder->ReserveForTriangles(m_nNumFaces,pIndexedMesh->m_nVertCount);
for(int i=0;i<m_nNumFaces;i++)
{
CObjFace *cf = &m_pFaceList[i];
if(cf->m_dwFlags & FLAG_SKIP_SHADOWVOLUME)
continue;
// with welding
unsigned short a=cf->v[0],b=cf->v[1],c=cf->v[2];
iBuilder->AddTriangleWelded(a,b,c,pVert[a],pVert[b],pVert[c]);
}
m_pEdgeConnectivity = iBuilder->ConstructConnectivity();
#ifdef _DEBUG
if(m_pEdgeConnectivity)
{
DWORD dwVertCount,dwTriCount;
m_pEdgeConnectivity->GetStats(dwVertCount,dwTriCount);
pLog->Log(" StencilEdgeConnectivity Stats:");
pLog->Log(" %d/%d Vertices %d/%d Faces",dwVertCount,pIndexedMesh->m_nVertCount,dwTriCount,m_nNumFaces);
}
#endif
delete iBuilder;
}
CShadowVolObject::~CShadowVolObject()
{
if(m_pEdgeConnectivity)
{
m_pEdgeConnectivity->Release();
m_pEdgeConnectivity=0;
}
}
void CStatCGFShadVol::Serialize(int & nPos, void * pStream, bool bSave)
{
byte* pTarget = pStream ? (byte*)pStream+nPos : NULL;
IStencilShadowConnectivity* pConnectivity = m_pShadowVolObject->GetEdgeConnectivity();
// NOTE: passing a big number is not a good practice here, because in debug mode
// it validates the buffers size and can detect buffer overruns early and painlessly.
// a good practice is passing the actual number of bytes available in ths target buffer
nPos += pConnectivity->Serialize(bSave, pTarget, 100000000);
}

View File

@@ -0,0 +1,81 @@
#pragma once
class CShadowVolObject //: public CVolume
{
public:
// constructor
CShadowVolObject(ILog * pSystem)
{
m_pFaceList=NULL;
m_pReMeshShadow=NULL;
m_pSystemVertexBuffer=NULL;
m_pEdgeConnectivity=0;
m_nNumVertices=0;
m_nNumFaces=0;
m_pSystem = pSystem;
};
ILog * m_pSystem;
//! destructor
~CShadowVolObject();
//!
bool CheckInside(const Vec3d &pos,bool bWorldSpace=true)
{
//temporary return false...will be a cool AI game play feature to know
//if we are in shadows
return (false);
}
//! precalculate the connectivity infos to build the object silouhette
void CreateConnectivityInfo( CIndexedMesh * pIndexedMesh, ILog * pLog );
//! create/update a vertex buffer containing the shadow volume (for static lights)
void RebuildDynamicShadowVolumeBuffer( const CDLight &lSource, struct IVisArea * pVisArea );// lSource has to be object relative
Vec3d * GetSysVertBufer() { return m_pSystemVertexBuffer; }
int GetNumVertices() { return(m_nNumVertices); }
int GetNumFaces() { return(m_nNumFaces); }
// Shader RenderElements for stencil
CRETriMeshShadow * m_pReMeshShadow; //!<
void CheckUnload();
class IStencilShadowConnectivity * GetEdgeConnectivity() { return m_pEdgeConnectivity; }
protected:
//! free the shadow volume buffers
void FreeVertexBuffers();
//list of faces from source objects, its always shared from the stat obj
int m_nNumFaces; //
CObjFace *m_pFaceList; // pointer to MeshIdx faces [0..m_nNumFaces-1]
//list of edges...can be shared from another shadow vol object
IStencilShadowConnectivity *m_pEdgeConnectivity; //!< stored edge connectivity for fast shadow edge extraction, could be 0, call ->Release() to free it
TFixedArray<unsigned short> m_arrIndices; //!<
unsigned m_nNumVertices; //!< number of vertices in SystemBuffer
//!
//! /param nNumIndices
//! /param nNumVertices
void PrepareShadowVolumeVertexBuffer( unsigned nNumIndices, unsigned nNumVertices );
//shadow volume renderer vertex buffer
Vec3d * m_pSystemVertexBuffer;
};
class CStatCGFShadVol
{
public:
CStatCGFShadVol(ILog * pSystem, CIndexedMesh * pMesh);
~CStatCGFShadVol(void);
CShadowVolObject * m_pShadowVolObject;
void Serialize(int & nPos, void * pStream, bool bSave);
};

View File

@@ -0,0 +1,8 @@
#pragma once
class StatCGFShadowVolume
{
public:
StatCGFShadowVolume(void);
~StatCGFShadowVolume(void);
};

View File

@@ -0,0 +1,245 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: statobj.h
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef STAT_OBJ_H
#define STAT_OBJ_H
const int FAR_TEX_COUNT = 24; // number of sprites per object
const int FAR_TEX_ANGLE = (360/FAR_TEX_COUNT);
const int FAR_TEX_SIZE = 64;
const int SHADOW_TEX_SIZE = 256;
class CIndexedMesh;
class CCObject;
#include "../Cry3DEngine/Cry3DEngineBase.h"
#include "list2.h"
struct CStatObjSV;
struct ItShadowVolume;
#include "istatobj.h"
#define STATOBJ_EFT_PLANT (EFT_USER_FIRST+1)
#define STATOBJ_EFT_PLANT_IN_SHADOW (EFT_USER_FIRST+2)
struct CStatObj : public Cry3DEngineBase, IStatObj
{
CStatObj(ISystem * pSystem);
~CStatObj();
CIndexedMesh * m_pTriData;
CIndexedMesh * GetTriData() { return m_pTriData; }
int m_nLoadedTrisCount;
float GetCenterZ() { return m_vBoxMax.z*0.5f; }
const Vec3d GetCenter() { return (m_vBoxMax+m_vBoxMin)*0.5f; }
inline float GetRadius() { return m_fObjectRadius; }
char m_szFolderName[256];
char m_szFileName [256];
char m_szGeomName [256];
bool m_bDefaultObject;
TArray<int> m_lstShaderTemplates;
TArray <SShaderParam> m_ShaderParams;
uint m_arrSpriteTexID[FAR_TEX_COUNT];
float m_fObjectRadius;
void InitParams(float sizeZ);
public:
// Loader
bool LoadObject(const char * szFileName, const char * szGeomName, int Stripify, bool bLoadAdditinalInfo, bool bKeepInLocalSpace, bool bLoadLater = false);
//! Returns script material name
virtual const char * GetScriptMaterialName(int Id=-1);
virtual void Render(const SRendParams & rParams, int nLodLevel=0);
//virtual void RenderModel(const RenderParams *pParams);
virtual void RenderShadowVolumes(const SRendParams *pParams);
//! Refresh object ( reload shaders or/and object geometry )
virtual void Refresh(int nFlags);
virtual bool SetShaderTemplate(int nTemplate, const char *TemplName, const char *ShaderName, bool bOnlyRegister=false);
virtual void SetShaderFloat(const char *Name, float Val);
//virtual void SetRefractFactor(float fRefr) { m_fRefractFactor = fRefr; }
//! set shadow volume
ItShadowVolume *GetShadowVolume() { return (m_pSvObj);}
//! get shadow volume
void SetShadowVolume(ItShadowVolume *pSvObj) { m_pSvObj=pSvObj;}
//Marco's NOTE: NEVER OVERRIDE THESE FLAGS!
//! get flags
int GetFlags() { return (m_dwFlags); }
//! set flags
void SetFlags(int dwFlags) { m_dwFlags=dwFlags; }
//Marco's NOTE: NEVER OVERRIDE THESE FLAGS!
//! get flags
int GetFlags2() { return (m_dwFlags); }
//! set flags
void SetFlags2(int dwFlags) { m_dwFlags2=dwFlags; }
protected:
void Physicalize();
void CreateModelFarImages(int nTexRes);
CLeafBuffer * m_pLeafBuffer;
ItShadowVolume *m_pSvObj;
int m_dwFlags,m_dwFlags2;
public:
CLeafBuffer * GetLeafBuffer() { return m_pLeafBuffer; };
void SetLeafBuffer( CLeafBuffer *buf ) { m_pLeafBuffer = buf; };
Vec3d m_vBoxMin, m_vBoxMax, m_vBoxCenter;//, m_vGeometryAngles;
phys_geometry * m_arrPhysGeomInfo[2];
phys_geometry * GetPhysGeom(int n = 0) { return m_arrPhysGeomInfo[n]; }
const char *GetFolderName() { return (m_szFolderName); }
const char *GetFileName() { return (m_szFileName); }
const char *GetGeoName() { return (m_szGeomName); }
bool IsSameObject(const char * szFileName, const char * szGeomName);
//set object's min/max bbox
void SetBBoxMin(const Vec3d &vBBoxMin) { m_vBoxMin=vBBoxMin; }
void SetBBoxMax(const Vec3d &vBBoxMax) { m_vBoxMax=vBBoxMax; }
Vec3d & GetBoxMin() { return m_vBoxMin; }
Vec3d & GetBoxMax() { return m_vBoxMax; }
int m_nUsers; // reference counter
ShadowMapLightSource * m_pSMLSource;
void MakeShadowMaps(const Vec3d vSunPos);
protected:
void MakeBuffers(bool make_tree, int Stripify, char * szCompiledFileName);
void MakeLeafBuffer( CIndexedMesh *mesh,bool bStripify=false );
void PrepareShadowMaps(const Vec3d & obj_pos, ShadowMapLightSource * pLSource);
public:
// void DrawShadowMapOnTerrain(const Vec3d & pos, const float fScale, float fAlpha, struct IndexedVertexBuffer ** ppShadowGridBuffer, CTerrain * pTerrain, bool bLMapsGeneration);
int GetAllocatedBytes();
// IndexedVertexBuffer * MakeShadowGridBuffer(const Vec3d & pos, const float fScale, ShadowMapFrustum*lf, bool translate_projection, CTerrain * pTerrain);
void AddShadowPoint(int x, int y, list2<struct_VERTEX_FORMAT_P3F> * pList, CTerrain * pTerrain);
void SetCurDynMask(int nCurDynMask);
int FindInPosBuffer(const Vec3d & opt, Vec3d * _vbuff, int _vcount, list2<int> * pHash);
void CompactPosBuffer(Vec3d * _vbuff, int * _vcount, list2<int> * pindices);
virtual Vec3d GetHelperPos(const char * szHelperName);
virtual const char *GetHelperById(int nId, Vec3d & vPos, Matrix * pMat, int * pnType);
virtual const Matrix * GetHelperMatrixByName(const char * szHelperName);
virtual void UpdateCustomLightingSpritesAndShadowMaps(float fStatObjAmbientLevel, int nTexRes);
//float m_fRefractFactor;
float m_fRadiusHors;
float m_fRadiusVert;
// float m_fBending;
// int m_nHideability;
float & GetRadiusVert() { return m_fRadiusVert; }
float & GetRadiusHors() { return m_fRadiusHors; }
virtual void RegisterUser();
virtual void UnregisterUser();
virtual bool IsDefaultObject() { return (m_bDefaultObject); }
virtual bool MakeObjectPicture(UCHAR * pRGBAData, int nWidth);
#define MAX_STATOBJ_LODS_NUM 3
CStatObj * m_arrpLowLODs[MAX_STATOBJ_LODS_NUM];
void LoadLowLODs(int nStripify,bool bLoadAdditinalInfo,bool bKeepInLocalSpace);
int m_nLoadedLodsNum;
// virtual int GetHideability() { return m_nHideability; }
// virtual void SetHideability(int hideability);
bool IsSpritesCreated() { return m_arrSpriteTexID[0]>0; }
float GetDistFromPoint(const Vec3d & vPoint);
int GetLoadedTrisCount() { return m_nLoadedTrisCount; }
list2<Vec3d> m_lstOcclVolVerts;
list2<int> m_lstOcclVolInds;
virtual bool GetOcclusionVolume(list2<Vec3d> * & plstOcclVolVerts, list2<int> * & plstOcclVolInds)
{
plstOcclVolVerts = &m_lstOcclVolVerts;
plstOcclVolInds = &m_lstOcclVolInds;
return m_lstOcclVolInds.Count() >= 3;
}
list2<struct HelperInfo> m_lstHelpers;
list2<CDLight> m_lstLSources;
bool Serialize(int & nPos, uchar * pSerBuf, bool bSave, char * szFolderName);
virtual void FreeTriData();
// virtual void SetBending(float fBending);
virtual const CDLight * GetLightSources(int nId);
void RenderDebugInfo(const SRendParams & rParams, const class CCObject * pObj);
void DrawMatrix(const Matrix & pMat);
//! Release method.
void Release() { delete this; }
void GetMemoryUsage(class ICrySizer* pSizer);
int GetMemoryUsage();
void SpawnParticles( ParticleParams & SpawnParticleParams, const Matrix & matWorldSpace, bool bOnlyUpLookingFaces );
// connectivity object that gets pre-computed for each model once and then
// used to extract edge topology by the stencil shadowing module
class IStencilShadowConnectivity* m_pStencilShadowConnectivity;
// returns the cached connectivity object for stencil shadows
class IStencilShadowConnectivity* getStencilShadowConnectivity( );
//////////////////////////////////////////////////////////////////////////
// builds the connectivity object for stencil shadows
// PARAMETERS:
// iEdgeBuilder - the builder to use to create the connectivity info
// pbCastShadow - the array of flags, 1 flag per 1 material, if the flag is true, then this material casts shadow, otherwise not
// numMaterials - number of items in the pbCastShadow array
void buildStencilShadowConnectivity (class IEdgeConnectivityBuilder *inpEdgeCon, const bool* pbCastShadow, unsigned numMaterials);
void RenderShadowVolumes (const SRendParams *rParams, int nLimitLOD);
void ShutDown();
void Init();
// loading state
int m_nStripify;
bool m_bLoadAdditinalInfo;
bool m_bKeepInLocalSpace;
int m_nLastRendFrameId;
bool m_bStreamable;
static float m_fStreamingTimePerFrame;
int m_nSpriteTexRes;
};
#endif // STAT_OBJ_H

View File

@@ -0,0 +1,768 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: statobjconstr.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: loading
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "StatObj.h"
#include "MeshIdx.h"
#include "../RenderDll/Common/shadow_renderer.h"
#include <irenderer.h>
#include <I3dIndoorEngine.h>
#include <CrySizer.h>
//#define USE_CCGF
float CStatObj::m_fStreamingTimePerFrame=0;
void CStatObj::Refresh(int nFlags)
{
if(nFlags & FRO_GEOMETRY)
{
bool bSpritesWasCreated = IsSpritesCreated();
ShutDown();
Init();
bool bRes = LoadObject(m_szFileName, m_szGeomName[0] ? m_szGeomName : 0, m_nStripify, m_bLoadAdditinalInfo, m_bKeepInLocalSpace);
if(bRes && bSpritesWasCreated)
{
Vec3d vColor = Get3DEngine()->GetAmbientColorFromPosition(Vec3d(-1000,-1000,-1000));
UpdateCustomLightingSpritesAndShadowMaps(vColor.x, m_nSpriteTexRes);
}
if(!bRes)
{ // load default in case of error
ShutDown();
Init();
LoadObject("Objects\\default.cgf", 0, m_nStripify, m_bLoadAdditinalInfo, m_bKeepInLocalSpace);
}
return;
}
if (nFlags & (FRO_TEXTURES | FRO_SHADERS))
{
CLeafBuffer *lb = m_pLeafBuffer;
for (int i=0; i<lb->m_pMats->Count(); i++)
{
IShader *e = (*lb->m_pMats)[i].shaderItem.m_pShader;
if (e && (*lb->m_pMats)[i].pRE && (*lb->m_pMats)[i].nNumIndices)
e->Reload(nFlags);
}
}
}
bool CStatObj::LoadObject(const char * szFileName,
const char*szGeomName,
int nStripify,
bool bLoadAdditinalInfo,
bool bKeepInLocalSpace,
bool bLoadLater)
{
if(!szFileName[0])
{
GetLog()->Log("Error: CStatObj::LoadObject: szFileName not specified");
return 0;
}
m_nStripify = nStripify;
m_bLoadAdditinalInfo = bLoadAdditinalInfo;
m_bKeepInLocalSpace = bKeepInLocalSpace;
m_bStreamable = bLoadLater;
if(bLoadLater)
{ // define fake bbox
Init();
m_vBoxMin = Vec3d(-1.f,-1.f,-1.f);
m_vBoxMax = Vec3d( 1.f, 1.f, 1.f);
m_vBoxCenter = Vec3d(0,0,0);
m_fRadiusHors = m_fRadiusVert = 1.f;
// remember names
strcpy(m_szFileName,szFileName);
if(szGeomName)
strcpy(m_szGeomName,szGeomName);
else
m_szGeomName[0]=0;
strcpy(m_szFolderName,szFileName);
while(m_szFolderName[0])
{ // make folder name
if(m_szFolderName[strlen(m_szFolderName)-1] == '\\' || m_szFolderName[strlen(m_szFolderName)-1] == '/')
{ m_szFolderName[strlen(m_szFolderName)-1]=0; break; }
m_szFolderName[strlen(m_szFolderName)-1]=0;
}
m_nLoadedTrisCount = 0;
return true;
}
FILE * f = 0;
#ifdef USE_CCGF
char szCompiledFileNameFull[512];
{
char szCompiledFileName[512]="";
strcpy(szCompiledFileName,szFileName);
while(strstr(szCompiledFileName,"\\") || strstr(szCompiledFileName,"/"))
strcpy(szCompiledFileName,szCompiledFileName+1);
while(strstr(szCompiledFileName,"."))
szCompiledFileName[strlen(szCompiledFileName)-1]=0;
strcat( szCompiledFileName, "_" );
strcat( szCompiledFileName, szGeomName ? szGeomName : "NoGeom" );
strcat( szCompiledFileName, ".ccgf" );
snprintf(szCompiledFileNameFull, "CCGF\\%s", szCompiledFileName);
}
f = fopen(szCompiledFileNameFull, "rb");
#endif // USE_CCGF
if(!f || szGeomName)
{ // compile object and save to disk
strcpy(m_szFileName,szFileName);
if(szGeomName)
strcpy(m_szGeomName,szGeomName);
else
m_szGeomName[0]=0;
strcpy(m_szFolderName,szFileName);
while(m_szFolderName[0])
{ // make folder name
if(m_szFolderName[strlen(m_szFolderName)-1] == '\\' || m_szFolderName[strlen(m_szFolderName)-1] == '/')
{ m_szFolderName[strlen(m_szFolderName)-1]=0; break; }
m_szFolderName[strlen(m_szFolderName)-1]=0;
}
m_nLoadedTrisCount = 0;
m_pTriData = new CIndexedMesh( m_pSystem, szFileName, szGeomName, &m_nLoadedTrisCount, bLoadAdditinalInfo, bKeepInLocalSpace );
if(!m_nLoadedTrisCount)
{
if(!szGeomName)
return false;
int i;
for(i=0; i<m_pTriData->m_lstGeomNames.Count(); i++)
if(strcmp(m_pTriData->m_lstGeomNames[i],szGeomName)==0)
break;
if(i>=m_pTriData->m_lstGeomNames.Count())
return false;
}
m_vBoxMin = m_pTriData->m_vBoxMin;
m_vBoxMax = m_pTriData->m_vBoxMax;
m_vBoxCenter = (m_vBoxMax+m_vBoxMin)/2;
// copy helpers
m_lstHelpers.AddList(*m_pTriData->GetHelpers());
// copy lsources
for(int i=0; i<m_pTriData->GetLightSourcesList()->Count(); i++)
m_lstLSources.Add(*m_pTriData->GetLightSourcesList()->GetAt(i));
InitParams(m_pTriData->m_vBoxMax.z - m_pTriData->m_vBoxMin.z);
Physicalize(); // can change some indices/faces
// create vert buffers
if(m_nLoadedTrisCount>30000)
GetLog()->UpdateLoadingScreen(" Indexing huge vertex buffer ...");
MakeBuffers(szGeomName!=0, nStripify, 0);
for (int i=0; m_pLeafBuffer && m_pLeafBuffer->m_pMats && i<m_pLeafBuffer->m_pMats->Count(); i++)
m_lstShaderTemplates.Add(-1);
if(m_nLoadedTrisCount>30000)
GetLog()->UpdateLoadingScreen(" Indexed OK");
#ifdef USE_CCGF
delete m_pTriData;
m_pTriData=0;
CreateDirectory("CCGF", 0);
// Save to file
int nPos = 0;
Serialize(nPos, 0, true, m_szFolderName);
uchar * pData = new uchar[nPos];
nPos=0;
Serialize(nPos, pData, true, m_szFolderName);
f = fopen(szCompiledFileNameFull,"wb");
if(f)
fwrite(pData,1,nPos,f);
delete pData;
}
else
{ // load ready object from disk
GetLog()->UpdateLoadingScreen("Loading compiled object: %s", szCompiledFileNameFull);
fseek(f,0,SEEK_END);
int nSize = ftell(f);
fseek(f,0,SEEK_SET);
uchar * pData = new uchar[nSize];
int nReadedBytes = fread(pData,1,nSize,f);
if(nReadedBytes != nSize)
GetConsole()->Exit("Error: CStatObj::LoadObject: Error reading ccfg: %s", szCompiledFileNameFull);
nSize=0;
Serialize(nSize, pData, false, m_szFolderName);
assert(nReadedBytes == nSize);
delete pData;
#endif // USE_CCGF
}
if(f)
fclose(f);
// if(!szGeomName) // m_pTriData is needed only for indoors
// FreeTriData();
// buildStencilShadowConnectivity (Get3DEngine()->GetNewStaticConnectivityBuilder(), 0, 0);
return true;
}
void CStatObj::FreeTriData()
{
delete m_pTriData;
m_pTriData=0;
}
const char * CStatObj::GetScriptMaterialName(int Id)
{
CLeafBuffer *lb = m_pLeafBuffer;
if (Id < 0)
{
for (int i=0; i<lb->m_pMats->Count(); i++)
{
if ((*lb->m_pMats)[i].sScriptMaterial[0])
return (*lb->m_pMats)[i].sScriptMaterial;
}
return NULL;
}
else
if (Id < lb->m_pMats->Count() && (*lb->m_pMats)[Id].sScriptMaterial[0])
return (*lb->m_pMats)[Id].sScriptMaterial;
return NULL;
}
void CStatObj::InitParams(float sizeZ)
{
m_fObjectRadius = GetDistance(m_vBoxMin, m_vBoxMax)/2;
// calc vert/horis radiuses
/* float dxh = GetBoxMax().x - GetBoxMin().x;
float dyh = GetBoxMax().y - GetBoxMin().y;
m_fRadiusHors = sqrtf(dxh*dxh+dyh*dyh)/2;
m_fRadiusVert = GetBoxMax().z/2;*/
float dxh = (float)max( fabs(GetBoxMax().x), fabs(GetBoxMin().x));
float dyh = (float)max( fabs(GetBoxMax().y), fabs(GetBoxMin().y));
m_fRadiusHors = (float)sqrt(dxh*dxh+dyh*dyh);
m_fRadiusVert = 0.01f + (GetBoxMax().z - GetBoxMin().z)*0.5f;
}
CStatObj::CStatObj(ISystem * pSystem)
{
m_pSystem = pSystem;
m_nUsers = 0; // referense counter
m_nStripify=0;
m_bLoadAdditinalInfo=false;
m_bKeepInLocalSpace=false;
m_bStreamable=false;
m_nSpriteTexRes=0;
ZeroStruct( m_szFolderName );
ZeroStruct( m_szFileName );
ZeroStruct( m_szGeomName );
m_pStencilShadowConnectivity=0;
m_nLastRendFrameId = 0;
Init();
}
void CStatObj::Init()
{
m_pTriData = 0;
m_nLoadedTrisCount = 0;
m_fObjectRadius = 0;
m_pSvObj=NULL;
m_dwFlags=m_dwFlags2=0;
ZeroStruct( m_arrSpriteTexID );
m_vBoxMin.Set(0,0,0);
m_vBoxMax.Set(0,0,0);
m_vBoxCenter.Set(0,0,0);
memset(m_arrPhysGeomInfo, 0, sizeof(m_arrPhysGeomInfo));
m_pSMLSource = 0;
m_pLeafBuffer = 0;//GetRenderer()->CreateLe afBuffer("StatObj");
m_bDefaultObject=false;
memset(m_arrpLowLODs,0,sizeof(m_arrpLowLODs));
m_nLoadedLodsNum=1;
}
CStatObj::~CStatObj()
{
ShutDown();
}
void CStatObj::ShutDown()
{
if(!m_pSystem)
return;
if(m_pTriData)
m_pTriData->FreeLMInfo();
delete m_pTriData;
m_pTriData = 0;
for(int n=0; n<2; n++)
if(m_arrPhysGeomInfo[n])
GetPhysicalWorld()->GetGeomManager()->UnregisterGeometry(m_arrPhysGeomInfo[n]);
if(m_pSMLSource && m_pSMLSource->m_LightFrustums.Count() && m_pSMLSource->m_LightFrustums[0].pModelsList)
delete m_pSMLSource->m_LightFrustums[0].pModelsList;
delete m_pSMLSource;
if(m_pLeafBuffer && !m_pLeafBuffer->m_bMaterialsWasCreatedInRenderer)
{
for (int i=0; i<(*m_pLeafBuffer->m_pMats).Count(); i++)
{
if((*m_pLeafBuffer->m_pMats)[i].pRE)
(*m_pLeafBuffer->m_pMats)[i].pRE->Release();
}
delete m_pLeafBuffer->m_pMats;
m_pLeafBuffer->m_pMats=0;
}
GetRenderer()->DeleteLeafBuffer(m_pLeafBuffer);
m_pLeafBuffer=0;
for(int i=0; i<FAR_TEX_COUNT; i++)
if(m_arrSpriteTexID[i])
GetRenderer()->RemoveTexture(m_arrSpriteTexID[i]);
if (m_pSvObj)
{
m_pSvObj->Release();
m_pSvObj=NULL;
}
for(int i=0; i<MAX_STATOBJ_LODS_NUM; i++)
delete m_arrpLowLODs[i];
m_ShaderParams.Free();
}
/*
void CStatObj::BuildOcTree()
{
if(!m_pTriData->m_nFaceCount || m_pTriData->m_nFaceCount<=2)
return;
CBox parent_box( m_pTriData->m_vBoxMin, m_pTriData->m_vBoxMax );
parent_box.max += 0.01f;
parent_box.min +=-0.01f;
CObjFace ** allFaces = new CObjFace *[m_pTriData->m_nFaceCount];
for(int f=0; f<m_pTriData->m_nFaceCount; f++)
{
m_pTriData->m_pFaces[f].m_vCenter =
(Vec3d(&m_pTriData->m_pVerts[m_pTriData->m_pFaces[f].v[0]].x) +
Vec3d(&m_pTriData->m_pVerts[m_pTriData->m_pFaces[f].v[1]].x) +
Vec3d(&m_pTriData->m_pVerts[m_pTriData->m_pFaces[f].v[2]].x))/3.f;
allFaces[f] = &m_pTriData->m_pFaces[f];
}
const int max_tris_in_leaf = 2000;
const float leaf_min_size = stricmp(m_szGeomName,"sector_0") ? 16.f : 32.f;
text_to_log(" Generating octree ... ");
m_pOcTree = new octree_node( &parent_box, allFaces, triData->m_nFaceCount, triData, leaf_min_size, max_tris_in_leaf, 2);
text_to_log_plus("%d leafs created", octree_node::static_current_leaf_id);
m_pOcTree->update_bbox(triData);
delete [] allFaces;
}*/
//float MakeBuffersTime = 0;
void CStatObj::MakeBuffers(bool make_tree, int nStripify, char * szCompiledFileName)
{
// float fTimeStart = GetTimer()->GetAsyncCurTime();
/*
FILE * f = fopen(szCompiledFileName,"rb");
if(f)
{
fseek(f,0,SEEK_END);
int nSize = ftell(f);
fseek(f,0,SEEK_SET);
uchar * pData = new uchar[nSize];
int nReadedBytes = fread(pData,1,nSize,f);
m_pLeafBuffer->Serialize(nSize, pData, false, m_szFolderName, m_nEFT_Flags);
delete pData;
}
else*/
{
assert(!m_pLeafBuffer);
m_pLeafBuffer = GetRenderer()->CreateLeafBuffer(eBT_Static,"StatObj");
m_pLeafBuffer->m_pMats = new list2<CMatInfo>;
m_pLeafBuffer->m_pMats->AddList(m_pTriData->m_lstMatTable);
if(m_pTriData->m_nFaceCount)
{
// m_pLeafBuffer->CreateBuffer(m_pTriData, nStripify, true);
m_pLeafBuffer->CreateBuffer(m_pTriData, STRIPTYPE_NONE, false ); // no sorting for lightmaps
}
/*
int nSize = 0;
m_pLeafBuffer->Serialize(nSize, 0, true, m_szFolderName, m_nEFT_Flags);
uchar * pData = new uchar[nSize];
m_pLeafBuffer->Serialize(nSize, pData, true, m_szFolderName, m_nEFT_Flags);
f = fopen(szCompiledFileName,"wb");
fwrite(pData,1,nSize,f);
delete pData;*/
}
//fclose(f);
// MakeBuffersTime += (GetTimer()->GetAsyncCurTime() - fTimeStart);
}
//////////////////////////////////////////////////////////////////////////
void CStatObj::MakeLeafBuffer( CIndexedMesh *mesh,bool bStripify )
{
m_pTriData = mesh;
if (m_pLeafBuffer)
{
// Delete old leaf buffer.
GetRenderer()->DeleteLeafBuffer(m_pLeafBuffer);
}
m_pLeafBuffer = GetRenderer()->CreateLeafBuffer(eBT_Static,"StatObj");
m_pLeafBuffer->m_pMats = new list2<CMatInfo>;
m_pLeafBuffer->m_pMats->AddList(m_pTriData->m_lstMatTable);
if(m_pTriData->m_nFaceCount)
{
// m_pLeafBuffer->CreateBuffer(m_pTriData, (bStripify)?1:0, true );
m_pLeafBuffer->CreateBuffer(m_pTriData, STRIPTYPE_NONE, false ); // no sorting for lightmaps
}
}
//////////////////////////////////////////////////////////////////////////
CStatObj::GetAllocatedBytes()
{
int size = sizeof(*this) + m_pTriData ? m_pTriData->GetAllocatedBytes() : 0;
// for(int i=0; i<MAX_TREE_LEAFS_NUM; i++)
{
size += m_pLeafBuffer->GetAllocatedBytes(false);
}
return size;
}
///////////////////////////////////////////////////////////////////////////////////////
Vec3d CStatObj::GetHelperPos(const char * szHelperName)
{
for(int i=0; i<m_lstHelpers.Count(); i++)
if(!strcmp(m_lstHelpers[i].sName,szHelperName))
return m_lstHelpers[i].tMat.GetTranslation();
return Vec3d(0,0,0);
}
///////////////////////////////////////////////////////////////////////////////////////
const Matrix * CStatObj::GetHelperMatrixByName(const char * szHelperName)
{
for(int i=0; i<m_lstHelpers.Count(); i++)
if(!strcmp(m_lstHelpers[i].sName,szHelperName))
return &(m_lstHelpers[i].tMat);
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////
const char *CStatObj::GetHelperById(int nId, Vec3d & vPos, Matrix * pMat, int * pnType)
{
if ( nId >= m_lstHelpers.Count() || nId<0 )
return (NULL);
vPos = m_lstHelpers[nId].tMat.GetTranslation();
if(pnType)
*pnType = m_lstHelpers[nId].nType;
if(pMat)
*pMat = m_lstHelpers[nId].tMat;
return (m_lstHelpers[nId].sName);
}
/*
bool CStatObj::GetHelper(int id, char * szHelperName, int nMaxHelperNameSize, Vec3d * pPos, Vec3d * pRot)
{
if(id<0 || id>=triData->m_Helpers.Count())
return false;
strncpy(szHelperName, triData->m_Helpers[id].name, nMaxHelperNameSize);
*pPos = triData->m_Helpers[id].pos;
*pRot = triData->m_Helpers[id].rot;
return true;
} */
void CStatObj::UpdateCustomLightingSpritesAndShadowMaps(float fStatObjAmbientLevel, int nTexRes)
{
m_nSpriteTexRes = nTexRes;
Vec3d vLight = m_pSystem->GetI3DEngine()->GetSunPosition();
vLight.Normalize();
// Vec3d vColor = m_pSystem->GetI3DEngine()->GetWorldColor();
float fSize = m_vBoxMax.z - m_vBoxMin.z;
// update lighting for full lod and lower lods
m_pLeafBuffer->UpdateCustomLighting( vLight, fSize, fStatObjAmbientLevel );
int nLowestLod=0;
for(int nLodLevel=1; nLodLevel<MAX_STATOBJ_LODS_NUM; nLodLevel++)
if(m_arrpLowLODs[nLodLevel])
{
m_arrpLowLODs[nLodLevel]->GetLeafBuffer()->UpdateCustomLighting( vLight, fSize, fStatObjAmbientLevel );
nLowestLod = nLodLevel;
}
// make sprites
if(nLowestLod)
{
// clear sprites in full lod
for(int i=0; i<FAR_TEX_COUNT; i++)
if(m_arrSpriteTexID[i])
{
GetRenderer()->RemoveTexture(m_arrSpriteTexID[i]);
m_arrSpriteTexID[i]=0;
}
// make new sprites in low lod
m_arrpLowLODs[nLowestLod]->CreateModelFarImages(nTexRes); // use lowest lod if present
// move sprite id from low inro into full lod
memcpy(m_arrSpriteTexID, m_arrpLowLODs[nLowestLod]->m_arrSpriteTexID, sizeof(m_arrSpriteTexID));
memset(m_arrpLowLODs[nLowestLod]->m_arrSpriteTexID, 0, sizeof(m_arrpLowLODs[nLowestLod]->m_arrSpriteTexID));
}
else
CreateModelFarImages(nTexRes);
MakeShadowMaps(vLight);
// if(m_pTriData && !m_pTriData->m_lstLSources.Count())
// FreeTriData();
}
/*
const char * CStatObj::GetPhysMaterialName(int nMatID)
{
if(m_pLeafBuffer && m_pLeafBuffer->m_pMats && nMatID < m_pLeafBuffer->m_pMats->Count())
return m_pLeafBuffer->m_pMats->Get(nMatID)->szPhysMat;
return 0;
}
bool CStatObj::SetPhysMaterialName(int nMatID, const char * szPhysMatName)
{
if(m_pLeafBuffer && m_pLeafBuffer->m_pMats && nMatID < m_pLeafBuffer->m_pMats->Count())
{
strncpy(m_pLeafBuffer->m_pMats->Get(nMatID)->szPhysMat, szPhysMatName, sizeof(m_pLeafBuffer->m_pMats->Get(nMatID)->szPhysMat));
return true;
}
return false;
}
*/
void CStatObj::RegisterUser()
{
m_nUsers++;
}
void CStatObj::UnregisterUser()
{
m_nUsers--;
}
void CStatObj::LoadLowLODs(int nStripify,bool bLoadAdditinalInfo,bool bKeepInLocalSpace)
{
if(m_szGeomName[0])
return;
m_nLoadedLodsNum = 1;
for(int nLodLevel=1; nLodLevel<MAX_STATOBJ_LODS_NUM; nLodLevel++)
{
// make lod file name
char sLodFileName[512];
strncpy(sLodFileName, m_szFileName, sizeof(m_szFileName));
sLodFileName[strlen(sLodFileName)-4]=0;
strcat(sLodFileName,"_lod");
char sLodNum[8];
ltoa(nLodLevel,sLodNum,10);
strcat(sLodFileName,sLodNum);
strcat(sLodFileName,".cgf");
// try to load
m_arrpLowLODs[nLodLevel] = new CStatObj(m_pSystem);
bool bRes = fxopen(sLodFileName,"r") &&
m_arrpLowLODs[nLodLevel]->LoadObject(sLodFileName, 0, nStripify, bLoadAdditinalInfo, bKeepInLocalSpace);
if(!bRes || m_arrpLowLODs[nLodLevel]->m_nLoadedTrisCount > m_nLoadedTrisCount / 1.8f)
{
if(bRes)
GetLog()->Log("Error: CStatObj::LoadLowLODs: Low lod model contains too many polygons (more than half of original model, loading skipped): %s", sLodFileName);
delete m_arrpLowLODs[nLodLevel];
m_arrpLowLODs[nLodLevel]=0;
break;
}
m_nLoadedLodsNum++;
}
}
float CStatObj::GetDistFromPoint(const Vec3d & vPoint)
{
float fMinDist = 4096;
for(int v=0; v<m_pTriData->m_nVertCount; v++)
{
float fDist = GetDistance(m_pTriData->m_pVerts[v],vPoint);
if(fDist < fMinDist)
fMinDist = fDist;
}
return fMinDist;
}
bool CStatObj::IsSameObject(const char * szFileName, const char * szGeomName)
{
// cmp object names
if (szGeomName)
{
if(stricmp(szGeomName,m_szGeomName)!=0)
return false;
}
// Normilize file name
char szFileNameNorm[MAX_PATH_LENGTH]="";
char *pszDest = szFileNameNorm;
const char *pszSource = szFileName;
while (*pszSource)
{
if (*pszSource=='/')
*pszDest++='\\';
else
*pszDest++=*pszSource;
pszSource++;
}
*pszDest=0;
// cmp file names
if(stricmp(szFileNameNorm,m_szFileName)!=0)
return false;
return true;
}
/*
// SetHideability and SetBending will be removed from here
#include "objman.h"
#include "3dengine.h"
void CStatObj::SetHideability(int nHideability)
{
m_nHideability = nHideability;
list2<StatInstGroup> & TypeList = ((C3DEngine*)Get3DEngine())->GetObjManager()->m_lstStaticTypes;
for(int i=0; i<TypeList.Count(); i++)
if(TypeList[i].pStatObj == this)
TypeList[i].bHideability = (nHideability!=0);
}
void CStatObj::SetBending(float fBending)
{
m_fBending = fBending;
list2<StatInstGroup> & TypeList = ((C3DEngine*)Get3DEngine())->GetObjManager()->m_lstStaticTypes;
for(int i=0; i<TypeList.Count(); i++)
if(TypeList[i].pStatObj == this)
TypeList[i].fBending = fBending;
}
*/
void CStatObj::GetMemoryUsage(ICrySizer* pSizer)
{
pSizer->AddObject(this,GetMemoryUsage());
}
int CStatObj::GetMemoryUsage()
{
int nSize=0;
for(int i=0; i<MAX_STATOBJ_LODS_NUM; i++)
if(m_arrpLowLODs[i])
nSize += m_arrpLowLODs[i]->GetMemoryUsage();
nSize += m_lstHelpers.GetMemoryUsage();
nSize += m_lstLSources.GetMemoryUsage();
nSize += m_lstOcclVolInds.GetMemoryUsage();
nSize += m_lstOcclVolVerts.GetMemoryUsage();
nSize += m_lstShaderTemplates.GetMemoryUsage();
nSize += m_pSMLSource ? sizeof(*m_pSMLSource) : 0;
nSize += m_pSvObj ? m_pSvObj->GetMemoryUsage() : 0;
nSize += m_pTriData ? m_pTriData->GetMemoryUsage() : 0;
nSize += m_ShaderParams.GetMemoryUsage() + m_ShaderParams.Num()*sizeof(SShaderParam);
return nSize;
}

View File

@@ -0,0 +1,265 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: statobjphys.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: make physical representation
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "StatObj.h"
#include "MeshIdx.h"
#include "3dengine.h"
/////////////////////////////////////////////////////////////////////////////////////
// Buffer optimizer
/////////////////////////////////////////////////////////////////////////////////////
int CStatObj::FindInPosBuffer(const Vec3d & opt, Vec3d * _vbuff, int _vcount, list2<int> * pHash)
{
for(int i=0; i<pHash->Count(); i++)
{
if(
IsEquivalent(*((Vec3d*)(&_vbuff[(*pHash)[i]].x)), *((Vec3d*)(&opt.x)), VEC_EPSILON)
)
return (*pHash)[i];
}
return -1;
}
void CStatObj::CompactPosBuffer(Vec3d * _vbuff, int * _vcount, list2<int> * pindices)
{
int before = *_vcount; assert(before);
if(!before)
GetConsole()->Exit("Error: CStatObj::CompactPosBuffer: Input vertex count is zero");
Vec3d * tmp_buff = new Vec3d[*_vcount];
int tmp_count = 0;
pindices->Clear();
list2<int> pos_hash_table[256];//[256];
for(uint v=0; v<(uint)(*_vcount); v++)
{
list2<int> * pHash = &pos_hash_table[(unsigned char)(_vbuff[v].x*100)];//[(unsigned char)(_vbuff[v].y*100)];
int find = FindInPosBuffer( _vbuff[v], tmp_buff, tmp_count, pHash);
if(find<0)
{
tmp_buff[tmp_count] = _vbuff[v];
pindices->Add(tmp_count);
pos_hash_table[(unsigned char)(_vbuff[v].x*100)]/*[(unsigned char)(_vbuff[v].y*100)]*/.Add(tmp_count);
tmp_count++;
}
else
{
int u = (uint)find;
pindices->Add(u);
}
}
* _vcount = tmp_count;
memcpy( _vbuff, tmp_buff, tmp_count*sizeof(Vec3d));
delete [] tmp_buff;
}
// This function prepares 3 additional meshes:
// usual physical representation(mat_phys or bounce koeff),
// obstruct physical representation(mat_obstruct)
// and occlusion volume(mat_occl).
// Register physical stuff in physics engine.
void CStatObj::Physicalize()
{
bool bShowINfo = (m_pTriData->m_nFaceCount>10000);
if(bShowINfo)
GetLog()->UpdateLoadingScreen(" Creating buffer for physics ...");
// get phys material id's from game code
IPhysMaterialEnumerator * pPhysMaterialEnumerator = ((C3DEngine*)Get3DEngine())->m_pPhysMaterialEnumerator;
int i=0;
for(i=0; pPhysMaterialEnumerator && i<m_pTriData->m_lstMatTable.Count(); i++)
m_pTriData->m_lstMatTable[i].nGamePhysMatId = pPhysMaterialEnumerator->EnumPhysMaterial(m_pTriData->m_lstMatTable[i].sScriptMaterial);
// find mat id's
int nPhysMatID = -1;
int nObstrMatID = -1;
int nOcclMatID = -1;
{
// find phys material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_phys"))
{
nPhysMatID = m;
break;
}
// find obstruct material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_obstruct"))
{
nObstrMatID = m;
break;
}
// find occlusion material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_occl"))
{
nOcclMatID = m;
break;
}
}
#define MESH_PHYSIC 0
#define MESH_OBSTRUCT 1
#define MESH_OCCLUSION 2
for(int nMesh = 0; nMesh<=2; nMesh++)
{ // fill physics indices
list2<int> lstPhysIndices;
list2<unsigned char> lstFaceMaterials;
if(nMesh == MESH_PHYSIC)
{ // find all physicalized faces
for(i=0; i<m_pTriData->m_nFaceCount; i++)
{
if( ((m_pTriData->m_lstMatTable[m_pTriData->m_pFaces[i].shader_id].m_Flags & MIF_PHYSIC) && nPhysMatID<0) ||
m_pTriData->m_pFaces[i].shader_id == nPhysMatID )
{
for(int v=0; v<3; v++)
lstPhysIndices.Add(m_pTriData->m_pFaces[i].v[v]);
lstFaceMaterials.Add(m_pTriData->m_lstMatTable[m_pTriData->m_pFaces[i].shader_id].nGamePhysMatId);
if(m_pTriData->m_pFaces[i].shader_id == nPhysMatID)
{ // remove face from list (it's not needed for rendering)
if(m_pTriData->m_nFaceCount>1)
m_pTriData->m_pFaces[i] = m_pTriData->m_pFaces[m_pTriData->m_nFaceCount-1];
m_pTriData->m_nFaceCount--;
i--;
}
}
}
}
else if(nMesh == MESH_OBSTRUCT)
{ // find all obstruct faces
if(nObstrMatID>=0)
{ // find all obstruct faces
for(i=0; i<m_pTriData->m_nFaceCount; i++)
{
if(m_pTriData->m_pFaces[i].shader_id == nObstrMatID)
{
for(int v=0; v<3; v++)
lstPhysIndices.Add(m_pTriData->m_pFaces[i].v[v]);
lstFaceMaterials.Add(m_pTriData->m_lstMatTable[m_pTriData->m_pFaces[i].shader_id].nGamePhysMatId);
// remove face from list (it's not needed for rendering)
if(m_pTriData->m_nFaceCount>1)
m_pTriData->m_pFaces[i] = m_pTriData->m_pFaces[m_pTriData->m_nFaceCount-1];
m_pTriData->m_nFaceCount--;
i--;
}
}
}
}
else if(nMesh == MESH_OCCLUSION)
{
if(nOcclMatID>=0)
{ // find all occlusion faces
for(i=0; i<m_pTriData->m_nFaceCount; i++)
{
if(m_pTriData->m_pFaces[i].shader_id == nOcclMatID)
{
for(int v=0; v<3; v++)
lstPhysIndices.Add(m_pTriData->m_pFaces[i].v[v]);
lstFaceMaterials.Add(m_pTriData->m_lstMatTable[m_pTriData->m_pFaces[i].shader_id].nGamePhysMatId);
// remove face from list (it's not needed for rendering)
if(m_pTriData->m_nFaceCount>1)
m_pTriData->m_pFaces[i] = m_pTriData->m_pFaces[m_pTriData->m_nFaceCount-1];
m_pTriData->m_nFaceCount--;
i--;
}
}
}
}
if(lstPhysIndices.Count())
{
Vec3d * pExVerts = new Vec3d[lstPhysIndices.Count()];
for(i=0; i<lstPhysIndices.Count();i++)
pExVerts[i] = m_pTriData->m_pVerts[lstPhysIndices[i]];
if(bShowINfo)
GetLog()->UpdateLoadingScreen(" Compacting buffer ...");
int init_count = lstPhysIndices.Count();
CompactPosBuffer(pExVerts, &init_count, &lstPhysIndices);
if(bShowINfo)
GetLog()->UpdateLoadingScreen(" Creating OBB tree ...");
if(GetPhysicalWorld() && (nMesh==MESH_PHYSIC || nMesh==MESH_OBSTRUCT))
{
IGeomManager *pGeoman = GetPhysicalWorld()->GetGeomManager();
Vec3d ptmin=pExVerts[0],ptmax=pExVerts[0],sz;
for(int i=1;i<lstPhysIndices.Count();i++)
{
ptmin.x = min(ptmin.x,pExVerts[i].x);
ptmax.x = max(ptmax.x,pExVerts[i].x);
ptmin.y = min(ptmin.y,pExVerts[i].y);
ptmax.y = max(ptmax.y,pExVerts[i].y);
ptmin.z = min(ptmin.z,pExVerts[i].z);
ptmax.z = max(ptmax.z,pExVerts[i].z);
}
int nMinTrisPerNode=2, nMaxTrisPerNode=4;
sz = ptmax-ptmin;
int flags = mesh_multicontact1 | mesh_uchar_ids;
float tol = 0.05f;
flags |= lstPhysIndices.Count()<=60 ? mesh_SingleBB : mesh_OBB|mesh_AABB;
if (strstr(m_szGeomName,"wheel"))
{
flags |= mesh_approx_cylinder;
tol = 1.0f;
} //else
//flags |= mesh_approx_box;
if (lstPhysIndices.Count()<600 && max(max(sz.x,sz.y),sz.z)>6) // make more dense OBBs for large (wrt terrain grid) objects
nMinTrisPerNode = nMaxTrisPerNode = 1;
m_arrPhysGeomInfo[nMesh] = pGeoman->RegisterGeometry(pGeoman->CreateMesh((vectorf*)&pExVerts[0], &lstPhysIndices[0],
(short*)&lstFaceMaterials[0], lstPhysIndices.Count()/3, flags, true, true, tol, nMinTrisPerNode,nMaxTrisPerNode, 2.5f));
}
if(nOcclMatID>=0 && nMesh==MESH_OCCLUSION)
{
m_lstOcclVolVerts.AddList(pExVerts,init_count);
m_lstOcclVolInds.AddList(lstPhysIndices);
}
delete [] pExVerts;
}
}
if(bShowINfo)
GetLog()->UpdateLoadingScreenPlus("ok");
#undef MESH_PHYSIC
#undef MESH_OBSTRUCT
#undef MESH_OCCLUSION
}

View File

@@ -0,0 +1,353 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: StatCGFCompiler.cpp
// Version: v1.00
// Created: 5/11/2002 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "StatCGFCompiler.h"
#include "MeshIdx.h"
#include "SerializeBuffer.h"
#define MESH_PHYSIC 0
#define MESH_OBSTRUCT 1
#define MESH_OCCLUSION 2
/////////////////////////////////////////////////////////////////////////////////////
// Buffer optimizer
/////////////////////////////////////////////////////////////////////////////////////
int CSimpleStatObj::FindInPosBuffer(const Vec3d & opt, Vec3d * _vbuff, int _vcount, list2<int> * pHash)
{
for(int i=0; i<pHash->Count(); i++)
{
if(
IsEquivalent(*((Vec3d*)(&_vbuff[(*pHash)[i]].x)), *((Vec3d*)(&opt.x)), VEC_EPSILON)
)
return (*pHash)[i];
}
return -1;
}
void CSimpleStatObj::CompactPosBuffer(Vec3d * _vbuff, int * _vcount, list2<int> * pindices)
{
int before = *_vcount; assert(before);
if(!before)
m_pLog->ThrowError("Error: CSimpleStatObj::CompactPosBuffer: Input vertex count is zero");
Vec3d * tmp_buff = new Vec3d[*_vcount];
int tmp_count = 0;
pindices->Clear();
list2<int> pos_hash_table[256];//[256];
for(uint v=0; v<(uint)(*_vcount); v++)
{
list2<int> * pHash = &pos_hash_table[(unsigned char)(_vbuff[v].x*100)];//[(unsigned char)(_vbuff[v].y*100)];
int find = FindInPosBuffer( _vbuff[v], tmp_buff, tmp_count, pHash);
if(find<0)
{
tmp_buff[tmp_count] = _vbuff[v];
pindices->Add(tmp_count);
pos_hash_table[(unsigned char)(_vbuff[v].x*100)]/*[(unsigned char)(_vbuff[v].y*100)]*/.Add(tmp_count);
tmp_count++;
}
else
{
int u = (uint)find;
pindices->Add(u);
}
}
* _vcount = tmp_count;
memcpy( _vbuff, tmp_buff, tmp_count*sizeof(Vec3d));
delete [] tmp_buff;
}
// This function prepares 3 additional meshes:
// usual physical representation(mat_phys or bounce koeff),
// obstruct physical representation(mat_obstruct)
// and occlusion volume(mat_occl).
// Register physical stuff in physics engine.
void CSimpleStatObj::Physicalize()
{
bool bShowINfo = (m_pTriData->m_nFaceCount>10000);
if(bShowINfo)
m_pLog->Log(" Creating buffer for physics ...");
// get phys material id's from game code
/* IPhysMaterialEnumerator * pPhysMaterialEnumerator = ((C3DEngine*)Get3DEngine())->m_pPhysMaterialEnumerator;
int i=0;
for(i=0; pPhysMaterialEnumerator && i<m_pTriData->m_lstMatTable.Count(); i++)
m_pTriData->m_lstMatTable[i].nGamePhysMatId = pPhysMaterialEnumerator->EnumPhysMaterial(m_pTriData->m_lstMatTable[i].sScriptMaterial);
*/
// find mat id's
int nPhysMatID = -1;
int nObstrMatID = -1;
int nOcclMatID = -1;
int nLeavesMatID= -1;
{
// find phys material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_phys"))
{
nPhysMatID = m;
break;
}
// find obstruct material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_obstruct"))
{
nObstrMatID = m;
break;
}
// find leaves material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_leaves"))
{
nLeavesMatID = m;
break;
}
// find occlusion material id
for(int m=0; m<m_pTriData->m_lstMatTable.Count(); m++)
if(strstr(m_pTriData->m_lstMatTable[m].sScriptMaterial,"mat_occl"))
{
nOcclMatID = m;
break;
}
}
for(int nMesh = 0; nMesh<=2; nMesh++)
{ // fill physics indices
list2<int> lstPhysIndices;
list2<unsigned char> lstFaceMaterials;
if(nMesh == MESH_PHYSIC)
{ // find all physicalized faces
for(int i=0; i<m_pTriData->m_nFaceCount; i++)
{
if( ((m_pTriData->m_lstMatTable[m_pTriData->m_pFaces[i].shader_id].m_Flags & MIF_PHYSIC) && nPhysMatID<0) ||
m_pTriData->m_pFaces[i].shader_id == nPhysMatID )
{
for(int v=0; v<3; v++)
lstPhysIndices.Add(m_pTriData->m_pFaces[i].v[v]);
lstFaceMaterials.Add((unsigned char)m_pTriData->m_pFaces[i].shader_id);
if(m_pTriData->m_pFaces[i].shader_id == nPhysMatID)
{ // remove face from list (it's not needed for rendering)
if(m_pTriData->m_nFaceCount>1)
m_pTriData->m_pFaces[i] = m_pTriData->m_pFaces[m_pTriData->m_nFaceCount-1];
m_pTriData->m_nFaceCount--;
i--;
}
}
}
}
else if(nMesh == MESH_OBSTRUCT)
{ // find all obstruct faces
if(nObstrMatID>=0 || nLeavesMatID>=0)
{ // find all obstruct faces
for(int i=0; i<m_pTriData->m_nFaceCount; i++)
{
if( m_pTriData->m_pFaces[i].shader_id == nObstrMatID ||
m_pTriData->m_pFaces[i].shader_id == nLeavesMatID )
{
for(int v=0; v<3; v++)
lstPhysIndices.Add(m_pTriData->m_pFaces[i].v[v]);
lstFaceMaterials.Add((unsigned char)m_pTriData->m_pFaces[i].shader_id);
if(m_pTriData->m_pFaces[i].shader_id == nObstrMatID)
{ // remove face from list (it's not needed for rendering)
if(m_pTriData->m_nFaceCount>1)
m_pTriData->m_pFaces[i] = m_pTriData->m_pFaces[m_pTriData->m_nFaceCount-1];
m_pTriData->m_nFaceCount--;
i--;
}
}
}
}
}
else if(nMesh == MESH_OCCLUSION)
{
if(nOcclMatID>=0)
{ // find all occlusion faces
for(int i=0; i<m_pTriData->m_nFaceCount; i++)
{
if(m_pTriData->m_pFaces[i].shader_id == nOcclMatID)
{
for(int v=0; v<3; v++)
lstPhysIndices.Add(m_pTriData->m_pFaces[i].v[v]);
lstFaceMaterials.Add((unsigned char)m_pTriData->m_pFaces[i].shader_id);
// remove face from list (it's not needed for rendering)
if(m_pTriData->m_nFaceCount>1)
m_pTriData->m_pFaces[i] = m_pTriData->m_pFaces[m_pTriData->m_nFaceCount-1];
m_pTriData->m_nFaceCount--;
i--;
}
}
}
}
if(lstPhysIndices.Count())// && lstPhysIndices.Count()<600) // 200 tris limit for physics set by Cevat
{
Vec3d * pExVerts;
int init_count;
if (m_pTriData->m_lstGeomNames.Count()>0 && strstr(m_pTriData->m_lstGeomNames[0],"cloth")!=0)
{
pExVerts = m_pTriData->m_pVerts;
init_count = m_pTriData->m_nVertCount;
}
else
{
pExVerts = new Vec3d[lstPhysIndices.Count()];
for(int i=0; i<lstPhysIndices.Count();i++)
pExVerts[i] = m_pTriData->m_pVerts[lstPhysIndices[i]];
if(bShowINfo)
m_pLog->Log(" Compacting buffer ...");
init_count = lstPhysIndices.Count();
CompactPosBuffer(pExVerts, &init_count, &lstPhysIndices);
}
if(bShowINfo)
m_pLog->Log(" Creating OBB tree ...");
if(/*GetPhysicalWorld() && */(nMesh==MESH_PHYSIC || nMesh==MESH_OBSTRUCT))
{
// IGeomManager *pGeoman = GetPhysicalWorld()->GetGeomManager();
Vec3d ptmin=pExVerts[0],ptmax=pExVerts[0],sz;
for(int i=1;i<init_count;i++)
{
ptmin.x = min(ptmin.x,pExVerts[i].x);
ptmax.x = max(ptmax.x,pExVerts[i].x);
ptmin.y = min(ptmin.y,pExVerts[i].y);
ptmax.y = max(ptmax.y,pExVerts[i].y);
ptmin.z = min(ptmin.z,pExVerts[i].z);
ptmax.z = max(ptmax.z,pExVerts[i].z);
}
int nMinTrisPerNode=2, nMaxTrisPerNode=4;
sz = ptmax-ptmin;
int flags = mesh_multicontact1 | mesh_uchar_ids;
float tol = 0.05f;
flags |= lstPhysIndices.Count()<=60 ? mesh_SingleBB : mesh_OBB|mesh_AABB;
if (strstr(m_pGeomName,"wheel"))
{
flags |= mesh_approx_cylinder;
tol = 1.0f;
} else
flags |= mesh_approx_box | mesh_approx_sphere;
if (lstPhysIndices.Count()<600 && max(max(sz.x,sz.y),sz.z)>6) // make more dense OBBs for large (wrt terrain grid) objects
nMinTrisPerNode = nMaxTrisPerNode = 1;
// assert(0);
// serialize data here
///// m_arrPhysGeomInfo[nMesh] = pGeoman->RegisterGeometry(pGeoman->CreateMesh((vectorf*)
// &pExVerts[0], &lstPhysIndices[0],
///// (short*)&lstFaceMaterials[0],
// lstPhysIndices.Count()/3,
// flags,
// true,
// true,
// tol,
// nMinTrisPerNode,
// nMaxTrisPerNode,
///// 2.5f));
m_lstProxyVerts[nMesh].AddList(pExVerts,init_count);
m_lstProxyInds[nMesh].AddList(lstPhysIndices);
m_vProxyBoxMin[nMesh] = ptmin;
m_vProxyBoxMax[nMesh] = ptmax;
m_lstProxyFaceMaterials[nMesh].AddList(lstFaceMaterials);
}
if(nOcclMatID>=0 && nMesh==MESH_OCCLUSION)
{
m_lstProxyVerts[MESH_OCCLUSION].AddList(pExVerts,init_count);
m_lstProxyInds[MESH_OCCLUSION].AddList(lstPhysIndices);
}
if (pExVerts!=m_pTriData->m_pVerts)
delete [] pExVerts;
}
else if(lstPhysIndices.Count())
{
m_pLog->Log("Error: CSimpleStatObj::Physicalize: proxy geometry contains more than 200 polygons - skipped");
}
}
if(bShowINfo)
m_pLog->LogPlus("ok");
}
void CSimpleStatObj::InitGeometry()
{
// copy helpers
m_lstHelpers.AddList(*m_pTriData->GetHelpers());
// copy lsources
for(int i=0; i<m_pTriData->GetLightSourcesList()->Count(); i++)
m_lstLSources.Add(*m_pTriData->GetLightSourcesList()->GetAt(i));
m_vBoxMin = m_pTriData->m_vBoxMin;
m_vBoxMax = m_pTriData->m_vBoxMax;
}
void CSimpleStatObj::Serialize(int & nPos, uchar * pSerBuf, bool bSave)
{
assert(bSave);
SaveBuffer("StatObj", 8, pSerBuf, nPos);
for(int i=0; i<3; i++)
{
m_lstProxyVerts[i].SaveToBuffer(pSerBuf,nPos);
m_lstProxyInds[i].SaveToBuffer(pSerBuf,nPos);
SaveBuffer(m_vProxyBoxMin[i], sizeof(m_vProxyBoxMin[i]), pSerBuf, nPos);
SaveBuffer(m_vProxyBoxMax[i], sizeof(m_vProxyBoxMax[i]), pSerBuf, nPos);
m_lstProxyFaceMaterials[i].SaveToBuffer(pSerBuf,nPos);
}
SaveBuffer(&m_vBoxMin, sizeof(m_vBoxMin), pSerBuf, nPos);
SaveBuffer(&m_vBoxMax, sizeof(m_vBoxMax), pSerBuf, nPos);
m_lstHelpers.SaveToBuffer(pSerBuf,nPos);
m_lstLSources.SaveToBuffer(pSerBuf,nPos);
}
bool CSimpleStatObj::IsPhysicsExist()
{
return (m_lstProxyInds[MESH_PHYSIC].Count() || m_lstProxyInds[MESH_OBSTRUCT].Count());
}
//"Objects\Natural\Bushes\beach_bush_yellow.cgf" /GeomName= /Stripify=1 /LoadAdditinalInfo=0 /KeepInLocalSpace=0 /StaticCGF=1 /refresh

View File

@@ -0,0 +1,680 @@
////////////////////////////////////////////////////////////////////////////////////////////////
//
// bm
//
////////////////////////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "StatCGFCompiler.h"
#define EPS 0.00001f
// from nvidia kitchen
bool CSimpleLeafBuffer::compute_tangent( const float * v0, const float * v1, const float * v2,
const float * t0, const float * t1, const float * t2,
Vec3d & tangent, Vec3d & binormal, Vec3d & tnormal, Vec3d & face_normal)
{
Vec3d cp;
Vec3d e0;
Vec3d e1;
tangent = Vec3d(0,0,1);
binormal = Vec3d(0,1,0);
tnormal = Vec3d(1,0,0);
// x
e0[0] = v1[0] - v0[0];
e0[1] = t1[0] - t0[0];
e0[2] = t1[1] - t0[1];
e1[0] = v2[0] - v0[0];
e1[1] = t2[0] - t0[0];
e1[2] = t2[1] - t0[1];
cp = e0.Cross(e1);
if ( fabs(cp[0]) > EPS )
{
tangent[0] = -cp[1] / cp[0];
binormal[0] = -cp[2] / cp[0];
}
// y
e0[0] = v1[1] - v0[1];
e0[1] = t1[0] - t0[0];
e0[2] = t1[1] - t0[1];
e1[0] = v2[1] - v0[1];
e1[1] = t2[0] - t0[0];
e1[2] = t2[1] - t0[1];
cp = e0.Cross(e1);
if ( fabs(cp[0]) > EPS )
{
tangent[1] = -cp[1] / cp[0];
binormal[1] = -cp[2] / cp[0];
}
// z
e0[0] = v1[2] - v0[2];
e0[1] = t1[0] - t0[0];
e0[2] = t1[1] - t0[1];
e1[0] = v2[2] - v0[2];
e1[1] = t2[0] - t0[0];
e1[2] = t2[1] - t0[1];
cp = e0.Cross(e1);
if ( fabs(cp[0]) > EPS )
{
tangent[2] = -cp[1] / cp[0];
binormal[2] = -cp[2] / cp[0];
}
tangent.Normalize();
binormal.Normalize();
tnormal = tangent.Cross(binormal);
tnormal.Normalize();
// Gram-Schmidt orthogonalization process for B
// compute the cross product B=NxT to obtain
// an orthogonal basis
//binormal = tnormal.Cross(tangent);
if (tnormal.Dot(face_normal) < 0)
tnormal = -tnormal;
return true;
}
#include "NvTriStrip/NVTriStrip.h"
static _inline int sGetHash(float *v, float *n)
{
return (int)(v[0]*64.0f+v[1]*64.0f+v[2]*64.0f+n[0]*2.0f+n[1]*2.0f+n[2]*2.0f) & 511;
}
static _inline void sAddToHash(TArray<unsigned short>& hash, unsigned short ind)
{
int i;
for (i=0; i<hash.Num(); i++)
{
if (hash[i] == ind)
return;
}
hash.AddElem(ind);
}
static void sGetObjectSpaceVectors( Vec3d &outvA, Vec3d &outvB, Vec3d &outvC )
{
outvA = Vec3d(1,0,0);
outvB = Vec3d(0,1,0);
outvC = Vec3d(0,0,1);
}
/*
float sCalcAngle( Vec3d &invA, Vec3d &invB )
{
float LengthQ = invA.Length() * invB.Length();
if(LengthQ < 0.01f)
LengthQ = 0.01f;
return acosf(invA.Dot(invB) / LengthQ);
}
*/
// orthogonalize the base vectors
//! /param v0 input [0..2] position vertex 1
//! /param v1 input [0..2] position vertex 2
//! /param v2 input [0..2] position vertex 3
//! /param t0 input [0..1] texture coordinate vertex 1
//! /param t1 input [0..1] texture coordinate vertex 2
//! /param t2 input [0..1] texture coordinate vertex 3
//! /param tangent output vector 1
//! /param binormal output vector 2
//! /param tnormal output vector 3
/*void CSimpleLeafBuffer::compute_tangent_base_CS( const float *v0, const float *v1, const float *v2,
const float *t0, const float *t1, const float *t2,
Vec3d &tangent, Vec3d &binormal, Vec3d &tnormal )
{
float fA[2]={ t1[0]-t0[0], t1[1]-t0[1] }, fB[2]={ t2[0]-t0[0], t2[1]-t0[1] };
float fOrientation = fA[0]*fB[1]-fA[1]*fB[0];
Vec3d vfNormal = Vec3d(0,0,1);
compute_tangent_CS(v0,v1,v2,t0,t1,t2,tangent,binormal,tnormal,vfNormal);
// make sure they are orthogonal
tnormal = tangent.Cross(binormal);
tnormal.Normalize();
binormal = tnormal.Cross(tangent);
binormal.Normalize();
if(fOrientation < 0)
tnormal = -tnormal;
} */
#define TV_EPS 0.001f
#define TN_EPS 0.0001f
bool CSimpleLeafBuffer::PrepareTexSpaceBasis()
{
int hash;
int i, j;
// allocate if not ready
// process faces
byte *pD = (byte *)m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData;
SBufInfoTable *pOffs = &gBufInfoTable[m_pSecVertBuffer->m_vertexformat];
int Stride = m_VertexSize[m_pSecVertBuffer->m_vertexformat]; // position stride
// Get pointers to positions, TexCoords and Normals
byte *verts = pD;
int StrN; // Normals Stride
byte *norms;
int StrTC; // Tex coords stride
byte *tc0;
if (pOffs->OffsNormal)
{
norms = &pD[pOffs->OffsNormal];
StrN = Stride;
}
else
{
norms = (byte *)this->m_TempNormals;
StrN = sizeof(Vec3d);
}
if (pOffs->OffsTC)
{
tc0 = &pD[pOffs->OffsTC];
StrTC = Stride;
}
else
{
tc0 = (byte *)this->m_TempTexCoords;
StrTC = sizeof(SMRendTexVert);
}
// If there is no tex coords or normals ignore this mesh
if (!tc0 || !norms)
return false;
if(!m_pBasises)
m_pBasises = new CBasis[m_SecVertCount];
memset(m_pBasises, 0, sizeof(CBasis)*m_SecVertCount);
// Indices hash table for smoothing of vectors between different materials
TArray<unsigned short> hash_table[2][512]; // 0. for TS; 1. for CS
// array for avoid smooothing multiple times
TArray<bool> bUsed;
bUsed.Create(m_SecVertCount);
// Clone space map
/* CPBCloneMapDest *pCM = NULL;
bool bCM = false;*/
bool bSpace[2];
// Init hash tables
for (i=0; i<2; i++)
{
bSpace[i] = false;
for (j=0; j<512; j++)
{
hash_table[i][j].Free();
}
}
// Non-optimized geometry case (usually in Debug mode)
if (m_nPrimetiveType != R_PRIMV_MULTI_GROUPS)
{
// All materials
for (int nm=0; nm<m_pMats->Count(); nm++)
{
CMatInfo *mi = m_pMats->Get(nm);
/* if (pCM)
{
delete pCM;
pCM = NULL;
}
*/ /*
if (mi->shaderItem.m_pShaderResources)
{
STexPic *pBumpTex = mi->shaderItem.m_pShaderResources->m_Textures[EFTT_BUMP].m_TU.m_TexPic;
// If clone space texture is present
if (pBumpTex && (pBumpTex->m_Flags2 & FT2_CLONESPACE))
{
char name[128];
strcpy(name, pBumpTex->m_SourceName.c_str());
StripExtension(name, name);
AddExtension(name, ".cln");
FILE *fp = iSystem->GetIPak()->FOpen (name, "rb");
if (fp)
{
iSystem->GetIPak()->FClose(fp);
pCM = new CPBCloneMapDest;
pCM->LoadCloneMap(name);
bCM = true;
}
}
else // otherwise if it's object-space polybump ignore this material
if (pBumpTex && (pBumpTex->m_Flags2 & FT2_POLYBUMP))
continue;
}*/
// For clone-space - another hash index for smoothing
int nIndHash = 0;//(pCM != 0) ? 1 : 0;
if (mi->nNumIndices)
bSpace[nIndHash] = true;
for(i=0; i<mi->nNumIndices-2; i+=3)
{
unsigned short * face = &GetIndices()[i+mi->nFirstIndexId];
float *n[3] =
{
(float *)&norms[face[0]*StrN],
(float *)&norms[face[1]*StrN],
(float *)&norms[face[2]*StrN],
};
float *v[3] =
{
(float *)&verts[face[0]*Stride],
(float *)&verts[face[1]*Stride],
(float *)&verts[face[2]*Stride],
};
float *tc[3] =
{
(float *)&tc0[face[0]*StrTC],
(float *)&tc0[face[1]*StrTC],
(float *)&tc0[face[2]*StrTC],
};
// Place indices to hash (for future smoothing)
hash = sGetHash(v[0], n[0]);
sAddToHash(hash_table[nIndHash][hash], face[0]);
hash = sGetHash(v[1], n[1]);
sAddToHash(hash_table[nIndHash][hash], face[1]);
hash = sGetHash(v[2], n[2]);
sAddToHash(hash_table[nIndHash][hash], face[2]);
// Compute the face normal based on vertex normals
Vec3d face_normal;
face_normal.x = n[0][0] + n[1][0] + n[2][0];
face_normal.y = n[0][1] + n[1][1] + n[2][1];
face_normal.z = n[0][2] + n[1][2] + n[2][2];
face_normal.Normalize();
// If we have clone-space
{
Vec3d tangents[3];
Vec3d binormals[3];
Vec3d tnormals[3];
// compute tangent vectors
compute_tangent(v[0], v[1], v[2], tc[0], tc[1], tc[2], tangents[0], binormals[0], tnormals[0], face_normal);
compute_tangent(v[1], v[2], v[0], tc[1], tc[2], tc[0], tangents[1], binormals[1], tnormals[1], face_normal);
compute_tangent(v[2], v[0], v[1], tc[2], tc[0], tc[1], tangents[2], binormals[2], tnormals[2], face_normal);
// accumulate
for(j=0; j<3; j++)
{
m_pBasises[face[j]].tangent += tangents [j];
m_pBasises[face[j]].binormal += binormals[j];
m_pBasises[face[j]].tnormal += tnormals[j];
}
}
}
}
// smooth tangent vectors between different materials with the same positions and normals
if (1)//CRenderer::CV_r_smoothtangents)
{
// Shared indices array for smoothing
TArray<int> Inds;
Inds.Create(32);
// Smooth separatelly for tangent-space and clone-space
for (int nn=0; nn<2; nn++)
{
// If this space wasn't used ignore it
if (!bSpace[nn])
continue;
for (i=0; i<m_SecVertCount; i++)
{
// if this vertex was already used ignore it
if (bUsed[i])
continue;
bUsed[i] = true;
Inds.SetUse(0);
Inds.AddElem(i);
// Get position and normal for the current index i
float *v = (float *)&verts[i*Stride];
float *n = (float *)&norms[i*StrN];
hash = sGetHash(v, n);
for (j=0; j<hash_table[nn][hash].Num(); j++)
{
int m = hash_table[nn][hash][j];
if (m == i)
continue;
// Get position and normal for the new index m
float *v1 = (float *)&verts[m*Stride];
float *n1 = (float *)&norms[m*StrN];
// If position and normal are the same take this index int account
if (fabs(v1[0]-v[0])<TV_EPS && fabs(v1[1]-v[1])<TV_EPS && fabs(v1[2]-v[2])<TV_EPS && fabs(n1[0]-n[0])<TN_EPS && fabs(n1[1]-n[1])<TN_EPS && fabs(n1[2]-n[2])<TN_EPS)
{
// Check angle between tangent vectors to avoid degenerated tangent vectors
Vec3d tang, tang1;
tang = m_pBasises[m].binormal;
tang.NormalizeFast();
tang1 = m_pBasises[i].binormal;
tang1.NormalizeFast();
float dot = tang.Dot(tang1);
if (dot > 0.5f)
{
tang = m_pBasises[m].tangent;
tang.NormalizeFast();
tang1 = m_pBasises[i].tangent;
tang1.NormalizeFast();
dot = tang.Dot(tang1);
if (dot > 0.5f)
{
tang = m_pBasises[m].tnormal;
tang.NormalizeFast();
tang1 = m_pBasises[i].tnormal;
tang1.NormalizeFast();
float dot = tang.Dot(tang1);
if (dot > 0.5f)
Inds.AddElem(m); // Add the new index to the shared indices list
}
}
}
}
// If we have more then one shared index smooth vectors between them
if (Inds.Num() > 1)
{
Vec3d tang = m_pBasises[Inds[0]].tangent;
Vec3d binorm = m_pBasises[Inds[0]].binormal;
Vec3d tnorm = m_pBasises[Inds[0]].tnormal;
for (j=1; j<Inds.Num(); j++)
{
int m = Inds[j];
bUsed[m] = true;
tang += m_pBasises[m].tangent;
binorm += m_pBasises[m].binormal;
tnorm += m_pBasises[m].tnormal;
}
for (j=0; j<Inds.Num(); j++)
{
int m = Inds[j];
m_pBasises[m].tangent = tang;
m_pBasises[m].binormal = binorm;
m_pBasises[m].tnormal = tnorm;
}
}
}
}
}
}
else // Optimized geometry (Stripified)
{
unsigned int n;
for (int nm=0; nm<m_pMats->Count(); nm++)
{
CMatInfo *mi = m_pMats->Get(nm);
/* if (pCM)
{
delete pCM;
pCM = NULL;
}*//*
if (mi->shaderItem.m_pShaderResources)
{
STexPic *pBumpTex = mi->shaderItem.m_pShaderResources->m_Textures[EFTT_BUMP].m_TU.m_TexPic;
// If we have clone-space texture
if (pBumpTex && (pBumpTex->m_Flags2 & FT2_CLONESPACE))
{
char name[128];
strcpy(name, pBumpTex->m_SourceName.c_str());
StripExtension(name, name);
AddExtension(name, ".cln");
FILE *fp = iSystem->GetIPak()->FOpen (name, "rb");
if (fp)
{
iSystem->GetIPak()->FClose(fp);
pCM = new CPBCloneMapDest;
pCM->LoadCloneMap(name);
bCM = true;
}
}
else // if it's object-space bump ignore this material
if (pBumpTex && (pBumpTex->m_Flags2 & FT2_POLYBUMP))
continue;
} */
// For clone-space - another hash index for smoothing
int nIndHash = 0;//(pCM != 0) ? 1 : 0;
if (mi->nNumIndices)
bSpace[nIndHash] = true;
int nOffs = mi->nFirstIndexId;
for (j=0; j<mi->m_dwNumSections; j++)
{
SPrimitiveGroup *g = &mi->m_pPrimitiveGroups[j];
int incr;
switch (g->type)
{
case PT_LIST:
incr = 3;
break;
case PT_STRIP:
case PT_FAN:
incr = 1;
break;
}
int offs = g->offsIndex + nOffs;
for (n=0; n<g->numIndices-2; n+=incr)
{
int i0, i1, i2;
switch (g->type)
{
case PT_LIST:
i0 = GetIndices()[offs+n];
i1 = GetIndices()[offs+n+1];
i2 = GetIndices()[offs+n+2];
break;
case PT_STRIP:
i0 = GetIndices()[offs+n];
i1 = GetIndices()[offs+n+1];
i2 = GetIndices()[offs+n+2];
break;
case PT_FAN:
i0 = GetIndices()[offs+0];
i1 = GetIndices()[offs+n+1];
i2 = GetIndices()[offs+n+2];
break;
}
// ignore degenerate triangle
if (i0==i1 || i0==i2 || i1==i2)
continue;
float *n[3] =
{
(float *)&norms[i0*StrN],
(float *)&norms[i1*StrN],
(float *)&norms[i2*StrN],
};
float *v[3] =
{
(float *)&verts[i0*Stride],
(float *)&verts[i1*Stride],
(float *)&verts[i2*Stride],
};
float *tc[3] =
{
(float *)&tc0[i0*StrTC],
(float *)&tc0[i1*StrTC],
(float *)&tc0[i2*StrTC],
};
// Place indices to hash (for future smoothing)
hash = sGetHash(v[0], n[0]);
sAddToHash(hash_table[nIndHash][hash], i0);
hash = sGetHash(v[1], n[1]);
sAddToHash(hash_table[nIndHash][hash], i1);
hash = sGetHash(v[2], n[2]);
sAddToHash(hash_table[nIndHash][hash], i2);
// Compute the face normal based on vertex normals
Vec3d face_normal;
face_normal.x = n[0][0] + n[1][0] + n[2][0];
face_normal.y = n[0][1] + n[1][1] + n[2][1];
face_normal.z = n[0][2] + n[1][2] + n[2][2];
//Vec3d::Normalize(face_normal);
face_normal.Normalize();
{
Vec3d tangents[3];
Vec3d binormals[3];
Vec3d tnormals[3];
compute_tangent(v[0], v[1], v[2], tc[0], tc[1], tc[2], tangents[0], binormals[0], tnormals[0], face_normal);
compute_tangent(v[1], v[2], v[0], tc[1], tc[2], tc[0], tangents[1], binormals[1], tnormals[1], face_normal);
compute_tangent(v[2], v[0], v[1], tc[2], tc[0], tc[1], tangents[2], binormals[2], tnormals[2], face_normal);
// accumulate
m_pBasises[i0].tangent += tangents [0];
m_pBasises[i0].binormal += binormals[0];
m_pBasises[i0].tnormal += tnormals[0];
m_pBasises[i1].tangent += tangents [1];
m_pBasises[i1].binormal += binormals[1];
m_pBasises[i1].tnormal += tnormals[1];
m_pBasises[i2].tangent += tangents [2];
m_pBasises[i2].binormal += binormals[2];
m_pBasises[i2].tnormal += tnormals[2];
}
}
}
}
// smooth tangent vectors between different materials with the same positions and normals
if (1)//CRenderer::CV_r_smoothtangents)
{
// Shared indices array for smoothing
TArray<int> Inds;
Inds.Create(32);
// Smooth separatelly for tangent-space and clone-space
for (int nn=0; nn<2; nn++)
{
// If this space wasn't used ignore it
if (!bSpace[nn])
continue;
for (i=0; i<m_SecVertCount; i++)
{
// if this vertex was already used ignore it
if (bUsed[i])
continue;
bUsed[i] = true;
Inds.SetUse(0);
Inds.AddElem(i);
// Get position and normal for the current index i
float *v = (float *)&verts[i*Stride];
float *n = (float *)&norms[i*StrN];
hash = sGetHash(v, n);
for (j=0; j<hash_table[nn][hash].Num(); j++)
{
int m = hash_table[nn][hash][j];
// If it's the same index ignore it
if (m == i)
continue;
// Get position and normal for the tested index m
float *v1 = (float *)&verts[m*Stride];
float *n1 = (float *)&norms[m*StrN];
// If position and normal are the same take this index int account
if (fabs(v1[0]-v[0])<TV_EPS && fabs(v1[1]-v[1])<TV_EPS && fabs(v1[2]-v[2])<TV_EPS && fabs(n1[0]-n[0])<TN_EPS && fabs(n1[1]-n[1])<TN_EPS && fabs(n1[2]-n[2])<TN_EPS)
{
// Check angle between tangent vectors to avoid degenerated tangent vectors
Vec3d tang, tang1;
tang = m_pBasises[m].binormal;
tang.NormalizeFast();
tang1 = m_pBasises[i].binormal;
tang1.NormalizeFast();
if (tang.Dot(tang1) > 0.5f)
{
tang = m_pBasises[m].tangent;
tang.NormalizeFast();
tang1 = m_pBasises[i].tangent;
tang1.NormalizeFast();
if (tang.Dot(tang1) > 0.5f)
{
tang = m_pBasises[m].tnormal;
tang.NormalizeFast();
tang1 = m_pBasises[i].tnormal;
tang1.NormalizeFast();
if (tang.Dot(tang1) > 0.5f)
Inds.AddElem(m); // Add the new index to the shared indices list
}
}
}
}
// If we have more then one shared index smooth vectors between them
if (Inds.Num() > 1)
{
Vec3d tang = m_pBasises[Inds[0]].tangent;
Vec3d binorm = m_pBasises[Inds[0]].binormal;
Vec3d tnorm = m_pBasises[Inds[0]].tnormal;
for (j=1; j<Inds.Num(); j++)
{
int m = Inds[j];
bUsed[m] = true;
tang += m_pBasises[m].tangent;
binorm += m_pBasises[m].binormal;
tnorm += m_pBasises[m].tnormal;
}
for (j=0; j<Inds.Num(); j++)
{
int m = Inds[j];
m_pBasises[m].tangent = tang;
m_pBasises[m].binormal = binorm;
m_pBasises[m].tnormal = tnorm;
}
}
}
}
}
}
// normalize
for(int v=0; v<m_SecVertCount; v++)
{
m_pBasises[v].tangent.Normalize();
m_pBasises[v].binormal.Normalize();
m_pBasises[v].tnormal.Normalize();
// if we have CV_r_unifytangentnormals (set by default) use Normals from vertex normal as Tangent normal
// and orthonormalize tangent vectors
if (1)//CRenderer::CV_r_unifytangentnormals && !bCM)
{
Vec3d *n = (Vec3d *)&norms[v*StrN];
m_pBasises[v].tnormal = *n;
Vec3d bin = m_pBasises[v].binormal;
Vec3d tan = m_pBasises[v].tangent;
m_pBasises[v].tangent = m_pBasises[v].tnormal.Cross(m_pBasises[v].binormal);
if (m_pBasises[v].tangent.Dot(tan) < 0.0f)
m_pBasises[v].tangent = -m_pBasises[v].tangent;
m_pBasises[v].binormal = m_pBasises[v].tnormal.Cross(m_pBasises[v].tangent);
if (m_pBasises[v].binormal.Dot(bin) < 0.0f)
m_pBasises[v].binormal = -m_pBasises[v].binormal;
m_pBasises[v].tangent.Normalize();
m_pBasises[v].binormal.Normalize();
}
}
return true;
}

View File

@@ -0,0 +1,805 @@
//////////////////////////////////////////////////////////////////////
//
// CryEngine Source code
//
// File:StencilShadowConnectivity.h
// Declaration of class CStencilShadowConnectivity
//
// History:
// -:Created by Sergiy Migdalskiy <sergiy@crytek.de>
// 08/30/2002 include .cpp in header files, move to CryCommon, use in Cry3DEngine (Martin Mittring)
//
//////////////////////////////////////////////////////////////////////
// class CStencilShadowConnectivity
//
// This is pre-computed data for a model that's gonna cast stencil
// shadows, and whose vertices may change position ****, but the topology
// cannot change.
//
// Ok, to put it clear: for each model, you need one instance of this
// class initialized once. The model is allowed to deform, but is not
// allowed to change topology (add or remove faces as well as re-assign
// face vertices or even change face orientation).
//
// NOTE: The class is optimized for use with manifold geometry.
// This means you have to provide a model with unified normals and non-
// exploded triangles.
//
//
// IMPLEMENTATION NOTES should be in
// StencilShadowConnectivity.cpp
//
#pragma once
#ifndef _STENCIL_SHADOW_CONNECTIVITY_HDR_
#define _STENCIL_SHADOW_CONNECTIVITY_HDR_
#include <IEdgeConnectivityBuilder.h> // IStencilShadowConnectivity
// the empty connectivity info:
class CStencilShadowConnectivityNull:public IStencilShadowConnectivity
{
public:
//! don't forget to call Release for freeing the memory resources
virtual void Release( void ) {delete this;}
//! to keep the interface small and the access fast
//! /return pointer to the internal memory representation (only used within module 3DEngine)
const virtual CStencilShadowConnectivity *GetInternalRepresentation( void ) const {return NULL;}
//! for debugging and profiling
//! /param outVertexCount
//! /param outTriangleCount
virtual void GetStats( DWORD &outVertexCount, DWORD &outTriangleCount ) {outVertexCount = outTriangleCount = 0;}
//! Memorize the vertex buffer in this connectivity object. This is needed if a static object doesn't need this info
virtual void SetVertices(const Vec3d* pVertices, unsigned numVertices) {assert(0);}
//! Serializes this object to the given memory block; if it's NULL, only returns
//! the number of bytes required. If it's not NULL, returns the number of bytes written
//! (0 means error, insufficient space)
virtual unsigned Serialize (bool bSave, void* pStream, unsigned nSize, IMiniLog* pWarningLog = NULL)
{
return 0;
}
//! Calculates the size of this object
virtual void GetMemoryUsage (ICrySizer* pSizer) {}
#ifdef WIN32
//! /param szFilename filename with path (or relative) and extension
//! /return true=success, false otherwise
virtual bool DebugConnectivityInfo( const char *szFilename ){}
#endif
};
#ifdef WIN64
#pragma warning( push ) //AMD Port
#pragma warning( disable : 4267 )
#endif
class CStencilShadowConnectivity : public IStencilShadowConnectivity
{
public:
//! number of vertices in the array referenced by the mesh
//! /return vertex count
unsigned numVertices() const
{
return m_numVertices;
}
//! number of edges in the array of edges
//! /return edge count
unsigned numEdges() const
{
return m_numEdges;
}
//! number of orphaned (open) edges
//! /return orphaned (open) count
virtual unsigned numOrphanEdges() const
{
return m_numOrphanEdges;
}
//! total number of faces
//! /return face count
unsigned numFaces ()const
{
return m_numFaces;
}
// -----------------
// This is the basic edge representation: two vertex indices.
// This is capable of being sorted.
struct BasicEdge
{
//! default constructor
BasicEdge () {}
//! constructor
//! /param nV0 start vertex index
//! /param nV1 end vertex index
BasicEdge (vindex nV0, vindex nV1)
{
m_nVertex[0] = nV0;
m_nVertex[1] = nV1;
}
//! start and end vertex index of the edge
vindex m_nVertex[2];
// alphabet sorting implementation
//! /param rEdge right side of the operator term
inline bool operator < (const BasicEdge& rEdge)const
{
if (m_nVertex[0] < rEdge.m_nVertex[0])
return true;
else
if (m_nVertex[0] == rEdge.m_nVertex[0])
return m_nVertex[1] < rEdge.m_nVertex[1];
else
return false;
}
//! get teh start or end vertex index
//! /param nIndex 0/1
inline unsigned operator [] (int nIndex)const
{
assert(nIndex==0 || nIndex==1);
return m_nVertex[nIndex];
}
//! copy the basic edge, designed for ancestors
//! /param beEdge
void setBasicEdge (const BasicEdge& beEdge)
{
m_nVertex[0] = beEdge.m_nVertex[0];
m_nVertex[1] = beEdge.m_nVertex[1];
}
};
// -----------------
// connection of the edge to the face: may contain, depending on the implementation,
// the face index, pointer to it, vertex, pointer to it
struct EdgeFace
{
//! default constructor
EdgeFace (){}
//! constructor
//! /param nFace face index
//! /param nVertex vertex index
EdgeFace (vindex nFace, vindex nVertex): m_nFace(nFace), m_nVertex(nVertex) {}
//! /return face index
inline vindex getFaceIndex ()const {return m_nFace;}
//! /return vertex index
inline vindex getVertexIndex() const {return m_nVertex;}
vindex m_nFace; //!< face index
vindex m_nVertex; //!< face vertex
};
// -----------------
// This is a primary connectivity edge data structure:
// the basic edge (start and end vertices, inherited from BasicEdge)
// and the opposite face edges
class Edge: public BasicEdge
{
//! this and opposite face 3rd vertex.
//! in "this" face, the edge goes CCW (natural) direction [0],
//! and in "opposite" face it goes CW (reverse) direction [1]
EdgeFace m_Face[2];
public:
//! default constructor
Edge () {}
//! constructor
//! /param be edge datastructure
//! /param ef0 in this triangle the edge goes CCW (natural) direction
//! /param ef1 in this triangle the edge goes CW (reverse) direction
Edge (const BasicEdge& be, const EdgeFace& ef0, const EdgeFace& ef1):
BasicEdge(be)
{
m_Face[0] = ef0;
m_Face[1] = ef1;
}
//! /param nFace 0/1
//! /return reference to the face data structure
const EdgeFace& getFace(int nFace) const
{
assert(nFace==0 || nFace==1);
return m_Face[nFace];
}
//! /param nFace 0/1
//! /param ef reference to the edge datastructure
void setFace (int nFace, const EdgeFace& ef)
{
assert(nFace==0 || nFace==1);
m_Face[nFace] = ef;
}
};
// -----------------
// orphan edge - an edge with only one face attached to it (boundary)
class OrphanEdge: public BasicEdge
{
EdgeFace m_Face; //!<
public:
//! default constructor
OrphanEdge () {}
// constructor
OrphanEdge (const BasicEdge& be, const EdgeFace& ef):
BasicEdge (be),
m_Face (ef)
{}
const EdgeFace& getFace()const {return m_Face;}
void setFace (const EdgeFace& ef) {m_Face = ef;}
};
// -----------------
// face - 3 vertices
class Face
{
public:
vindex m_nVertex[3]; //!<
//! default constructur
Face () {}
//! constructor
//! /param nV0 first vertex index
//! /param nV1 second vertex index
//! /param nV2 thired vertex index
Face (vindex nV0, vindex nV1, vindex nV2)
{
m_nVertex[0] = nV0;
m_nVertex[1] = nV1;
m_nVertex[2] = nV2;
}
//! get the index of one triangle vertex
//! /param nVtx 0/1/2
//! /return index of that triangle vertex
const vindex getVertex (int nVtx) const
{
assert(nVtx==0 || nVtx==1 || nVtx==2);
return m_nVertex[nVtx];
}
//! set the index of one triangle vertex
//! /param nVtx 0/1/2
//! /param nIndex index of the triangle vertex no <nVtx>
void setVertex (int nVtx, vindex nIndex)
{
assert(nVtx==0 || nVtx==1 || nVtx==2);
m_nVertex[nVtx] = nIndex;
}
};
// -----------------
//! /aparam nEdge 0..m_numEdges-1
//! /return the nEdge'th edge
const Edge& getEdge (int nEdge)const
{
assert(m_pEdges);
assert(nEdge>=0);
assert((unsigned)nEdge<m_numEdges);
return m_pEdges[nEdge];
}
// returns the nEdge's orphaned edge
//! /param nEdge 0..m_numOrphanEdges-1
//! /return
const OrphanEdge& getOrphanEdge(int nEdge)const
{
assert(m_pOrphanEdges);
assert(nEdge>=0);
assert((unsigned)nEdge<m_numOrphanEdges);
return m_pOrphanEdges[nEdge];
}
//! returns the nFace's face
//! /param 0..m_numFaces-1
//! /return reference to the face
const Face& getFace (int nFace) const
{
assert(m_pFaces);
assert(nFace>=0);
assert((unsigned)nFace<m_numFaces);
return m_pFaces[nFace];
}
// copies the array of referred faces into internal array
// this is specially optimized for faster cooperation with the Builder
// The number of faces is determined from the earlier given edges, so
// NOTE: becareful to call the SetOrphanEdges BEFORE THIS FUNCTION
// DO NOT call twice, this is only designed to be called once after construction
void SetFaces (const std::vector<Face>& arrFaces)
{
assert (!m_pFaces && m_numFaces <= arrFaces.size());
if (!arrFaces.empty())
{
m_pFaces = new Face[m_numFaces = arrFaces.size()];
for (unsigned i = 0; i < m_numFaces; ++i)
m_pFaces[i] = arrFaces[i];
}
}
// destruct the object (it's only constructed by the Builder)
// the virtual ensures that the function releases the object in the same module where it was allocated:
// because the VTable was also allocated in the same module
virtual void Release ()
{
assert(this);
// this is a counterpart to the new that's called within the
// CStencilShadowConnectivityBuilder::ConstructConnectivity() method
delete this;
}
const virtual CStencilShadowConnectivity *GetInternalRepresentation( void ) const
{
return(this);
}
// from IStencilShadowConnectivity
virtual bool DebugConnectivityInfo( const char *szFilename )
{
// used only for debugging
FILE *out=fopen(szFilename,"w");
if(!out)return(false);
fprintf(out,"%d Edges:\n",m_numEdges);
for(unsigned i=0;i<m_numEdges;i++)
{
fprintf(out," face={%d,%d}, vertex={%d,%d}\n",
(m_pEdges[i].getFace(0)).getFaceIndex(),
(m_pEdges[i].getFace(1)).getFaceIndex(),
m_pEdges[i].m_nVertex[0],
m_pEdges[i].m_nVertex[1]);
}
fprintf(out,"%d OrphanEdges:\n",m_numOrphanEdges);
for(unsigned i=0;i<m_numOrphanEdges;i++)
#if !defined(LINUX)
fprintf(out," face={%d}, vertex={%d,%d}\n",m_pOrphanEdges[i].getFace(),m_pOrphanEdges[i].m_nVertex[0],m_pOrphanEdges[i].m_nVertex[1]);
#else
fprintf(out,", vertex={%d,%d}\n",m_pOrphanEdges[i].m_nVertex[0],m_pOrphanEdges[i].m_nVertex[1]);//face info does not compile, no time to make it more correct here
#endif//LINUX
fprintf(out,"%d Vertices:\n",m_numVertices);
fprintf(out,"Faces:\n",m_numFaces);
for(unsigned i=0;i<m_numFaces;i++)
fprintf(out," vertex indices={%d,%d,%d}\n",m_pFaces[i].getVertex(0),m_pFaces[i].getVertex(1),m_pFaces[i].getVertex(2));
fclose(out);
return(true);
}
//! constructor, only makes an empty object that's good for deserializing only
CStencilShadowConnectivity ()
{
m_numEdges = m_numOrphanEdges = m_numVertices = m_numFaces = 0;
m_pOrphanEdges = NULL;
m_pFaces = NULL;
m_pEdges = NULL;
m_pPlanes = NULL;
m_pVertices = NULL;
}
struct Plane
{
Vec3d vNormal;
float fDistance;
Plane(){}
Plane (const Vec3d& v0, const Vec3d& v1, const Vec3d& v2)
{
vNormal = (v1-v0)^(v2-v0);
fDistance = (v0*vNormal);
}
float apply (const Vec3d& v)const
{
return vNormal * v - fDistance;
}
};
//! Calculates the size of this object
void GetMemoryUsage(ICrySizer* pSizer)
{
pSizer->AddObject(this,Serialize (true, NULL,0));
}
protected:
//! constructor is only called within 3DEngine
//! /param pEdges - array of nNumEdges edges
//! /param nNumEdges
CStencilShadowConnectivity( const std::vector<Edge>& arrEdges)
{
m_pOrphanEdges=NULL;
m_pFaces=NULL;
m_numOrphanEdges=0;
// find the max vertex index to put it into m_nVertices
m_numVertices = 0;
m_numFaces = 0;
for (std::vector<Edge>::const_iterator it = arrEdges.begin(); it != arrEdges.end(); ++it)
{
UseBasicEdge (*it);
UseEdgeFace (it->getFace(0));
UseEdgeFace (it->getFace(1));
}
m_numEdges = arrEdges.size();
if(m_numEdges)
{
m_pEdges = new Edge[m_numEdges];
for (unsigned i = 0; i < m_numEdges; ++i)
m_pEdges[i] = arrEdges[i];
}
else
m_pEdges=0;
m_pPlanes = NULL;
m_pVertices = NULL;
}
void SetPlanes (const Plane* pPlanes, unsigned numPlanes)
{
if (m_pPlanes)
delete [] m_pPlanes;
if (pPlanes)
{
assert (numPlanes >= m_numFaces);
m_pPlanes = new Plane[m_numFaces];
memcpy (m_pPlanes, pPlanes, sizeof(Plane) * m_numFaces);
}
else
m_pPlanes = NULL;
}
void SetVertices(const Vec3d* pVertices, unsigned numVertices)
{
if (m_pVertices)
delete[]m_pVertices;
if (pVertices)
{
assert (numVertices >= m_numVertices);
m_pVertices = new Vec3d[m_numVertices];
memcpy (m_pVertices, pVertices, sizeof(Vec3d)*m_numVertices);
}
else
m_pVertices = NULL;
}
// remaps all vertex indices and memorizes the passed "new" vertex array (in new indexation)
void SetRemapVertices (const vindex*pMap, unsigned numMapEntries, const Vec3d* pNewVertices, unsigned numNewVertices)
{
if (m_pVertices)
delete[]m_pVertices;
assert (pNewVertices);
// we collected information about the vertices before . The number of used vertices is in
// m_numVertices. We assume the passed new vertex map can not make this number larger, because
// its sole purpose is optimization of number of vertex indices used (for storing the vertex info inside)
assert (numNewVertices <= m_numVertices);
m_numVertices = numNewVertices;
m_pVertices = new Vec3d[numNewVertices];
memcpy (m_pVertices, pNewVertices, sizeof(Vec3d)*numNewVertices);
remapVertexIndices (pMap, numMapEntries);
}
// can be destructed only from within Release()
virtual ~CStencilShadowConnectivity(void)
{
if (m_pEdges) delete[] m_pEdges;
if (m_pOrphanEdges) delete[] m_pOrphanEdges;
if (m_pFaces) delete[] m_pFaces;
if (m_pPlanes) delete[] m_pPlanes;
if (m_pVertices) delete[] m_pVertices;
}
// makes sure that the given vertex index fits into the range 0..m_nVertices
// by expanding (increasing) m_nVertices
void UseVertex(vindex nVertex)
{
if (nVertex >= m_numVertices)
m_numVertices = nVertex + 1;
}
// makes sure that the given face index fits into the range 0..m_nFaces
// by expanding (increasing) m_nFaces
void UseFace (vindex nFace)
{
if (nFace >= m_numFaces)
m_numFaces = nFace + 1;
}
// copies the list of orphaned edges into internal array
// this is specially optimized for faster cooperation with the Builder
// DO NOT call twice, this is only designed to be called once after construction
typedef std::map<BasicEdge, EdgeFace> OrphanEdgeMap;
// copies the list of orphaned edges into internal array
// this is specially optimized for faster cooperation with the Builder
// DO NOT call twice, this is only designed to be called once after construction
void SetOrphanEdges (const OrphanEdgeMap& mapOrphanEdge)
{
m_numOrphanEdges = mapOrphanEdge.size();
m_pOrphanEdges = new OrphanEdge[m_numOrphanEdges];
OrphanEdge* pEdge = m_pOrphanEdges;
// copy the content of the orphan edge map into the array
for (OrphanEdgeMap::const_iterator it = mapOrphanEdge.begin(); it != mapOrphanEdge.end(); ++it)
{
pEdge->setBasicEdge (it->first);
pEdge->setFace (it->second);
UseBasicEdge (*pEdge);
UseEdgeFace (pEdge->getFace());
++pEdge;
}
}
// makes sure that the vertices/faces referenced by the edge are in account by the m_numFaces and m_numVertices
void UseBasicEdge (const BasicEdge& beEdge)
{
UseVertex(beEdge[0]);
UseVertex(beEdge[1]);
}
// makes sure that the vertices/faces referenced by the edge are in account by the m_numFaces and m_numVertices
void UseEdgeFace (const EdgeFace& efEdge)
{
UseFace (efEdge.getFaceIndex());
UseVertex(efEdge.getVertexIndex());
}
//! for debugging and profiling
//! /param outVertexCount
//! /param outTriangleCount
virtual void GetStats( DWORD &outVertexCount, DWORD &outTriangleCount )
{
outVertexCount=m_numVertices;
outTriangleCount=m_numFaces;
}
public:
// the version of the stream
enum {g_nSerialVersion = 2};
//! Deserializes this object. Returns the number of bytes read. 0 means error
//! pWarningLog is used to put warnings about deserialized connectivity
unsigned Deserialize (void *pStream, unsigned nSize, IMiniLog* pWarningLog = NULL)
{
unsigned* pHeader = (unsigned*)pStream;
if (nSize < 5 * sizeof(unsigned))
return 0; // we should have the header in place
if (*(pHeader++) != g_nSerialVersion) // version is incompatible
return 0;
m_numEdges = *(pHeader++);
m_numOrphanEdges = *(pHeader++);
m_numVertices = *(pHeader++);
m_numFaces = *(pHeader++);
unsigned numPlanes = *(pHeader++);
assert (numPlanes == 0 || numPlanes == m_numFaces);
unsigned numVertices = *(pHeader++);
assert (numVertices == 0 || numVertices == m_numVertices);
unsigned nRequiredSize =
sizeof(unsigned) +
sizeof(m_numEdges) + m_numEdges * sizeof(Edge) +
sizeof(m_numOrphanEdges) + m_numOrphanEdges * sizeof(OrphanEdge) +
sizeof(m_numVertices) +
sizeof(m_numFaces) + m_numFaces * sizeof(Face) +
sizeof(numPlanes) + numPlanes * sizeof(Plane)+
sizeof(numVertices) + numVertices * sizeof(Vec3d);
if(m_pEdges) delete[] m_pEdges;
if(m_pOrphanEdges) delete[] m_pOrphanEdges;
if(m_pFaces) delete[] m_pFaces;
if(m_pPlanes) delete[] m_pPlanes;
if (m_pVertices) delete []m_pVertices;
if (nSize < nRequiredSize)
{
m_pEdges = NULL;
m_pOrphanEdges = NULL;
m_pFaces = NULL;
m_numEdges = 0;
m_numOrphanEdges = 0;
m_numVertices = 0;
m_numFaces = 0;
// incompatible stream
return 0;
}
m_pEdges = new Edge [m_numEdges];
m_pOrphanEdges = new OrphanEdge [m_numOrphanEdges];
m_pFaces = new Face [m_numFaces];
m_pPlanes = numPlanes?new Plane[numPlanes]:NULL;
m_pVertices = numVertices?new Vec3d[numVertices]:NULL;
Edge* pEdgeData = (Edge*)pHeader;
memcpy (m_pEdges, pEdgeData, m_numEdges*sizeof(Edge));
OrphanEdge* pOrphanEdgeData = (OrphanEdge*)(pEdgeData + m_numEdges);
memcpy (m_pOrphanEdges, pOrphanEdgeData, m_numOrphanEdges*sizeof(OrphanEdge));
Face* pFaceData = (Face*)(pOrphanEdgeData + m_numOrphanEdges);
memcpy (m_pFaces, pFaceData, m_numFaces * sizeof(Face));
Plane* pPlanes = (Plane*)(pFaceData+m_numFaces);
if (numPlanes)
memcpy (m_pPlanes, pPlanes, numPlanes* sizeof(Plane));
Vec3d* pVertices = (Vec3d*)(pPlanes + numPlanes);
if (numVertices)
memcpy (m_pVertices, pVertices, sizeof(Vec3d)*numVertices);
#if !defined(LINUX)
if (m_numOrphanEdges && pWarningLog)
pWarningLog->LogWarning("Connectivity warning: there are %d open edges (%d closed, %d faces, %d planes, %d vertices)", m_numOrphanEdges, m_numEdges, m_numFaces, numPlanes, numVertices);
#endif
return nRequiredSize;
}
//! Serializes this object to the given memory block; if it's NULL, only returns
//! the number of bytes required. If it's not NULL, returns the number of bytes written
//! (0 means error, insufficient space)
unsigned Serialize (bool bSave, void* pStream, unsigned nSize, IMiniLog* pWarningLog = NULL)
{
if (!bSave)
return Deserialize (pStream, nSize, pWarningLog);
// calculate the required size of the buffer
unsigned
numPlanes = m_pPlanes?m_numFaces:0,
numVertices = m_pVertices?m_numVertices:0,
nRequiredSize =
sizeof(unsigned) +
sizeof(m_numEdges) + m_numEdges * sizeof(Edge) +
sizeof(m_numOrphanEdges) + m_numOrphanEdges * sizeof(OrphanEdge) +
sizeof(m_numVertices) +
sizeof(m_numFaces) + m_numFaces * sizeof(Face) +
sizeof(numPlanes) + numPlanes * sizeof(Plane)+
sizeof(numVertices) + numVertices * sizeof(Vec3d);
if (pStream)
{
if (nSize < nRequiredSize)
return 0; // insufficient space in the pStream buffer
unsigned* pHeader = (unsigned*)pStream;
*(pHeader++) = g_nSerialVersion; // version of the stream
*(pHeader++) = m_numEdges;
*(pHeader++) = m_numOrphanEdges;
*(pHeader++) = m_numVertices;
*(pHeader++) = m_numFaces;
*(pHeader++) = numPlanes;
*(pHeader++) = numVertices;
Edge* pEdgeData = (Edge*)pHeader;
memcpy (pEdgeData, m_pEdges, m_numEdges*sizeof(Edge));
OrphanEdge* pOrphanEdgeData = (OrphanEdge*)(pEdgeData + m_numEdges);
memcpy (pOrphanEdgeData, m_pOrphanEdges, m_numOrphanEdges*sizeof(OrphanEdge));
Face* pFaceData = (Face*)(pOrphanEdgeData + m_numOrphanEdges);
memcpy (pFaceData, m_pFaces, m_numFaces * sizeof(Face));
Plane* pPlanes = (Plane*)(pFaceData+m_numFaces);
if (m_pPlanes)
memcpy (pPlanes, m_pPlanes, numPlanes * sizeof(Plane));
Vec3d* pVertices = (Vec3d*)(pPlanes+numPlanes);
if (m_pVertices)
memcpy (pVertices, m_pVertices, numVertices * sizeof(Vec3d));
}
return nRequiredSize;
}
// the plane equation, x*n+d == 0
bool hasPlanes() const {return m_pPlanes != NULL;}
const Plane&getPlane (unsigned i)const {assert(i<m_numFaces && m_pPlanes);return m_pPlanes[i];}
const Vec3d* getVertices() const {return m_pVertices;}
bool IsStandalone() const {return m_pPlanes != NULL && m_pVertices!= NULL;}
void remapVertexIndices(const vindex*pMap, unsigned nMapSize)
{
unsigned i;
#define REMAP(X) assert((X) < nMapSize);\
(X) = pMap[X];\
assert ((vindex)(X) != (vindex)-1 && (X) < m_numVertices)
for(i=0;i<m_numEdges;i++)
{
REMAP(m_pEdges[i].m_nVertex[0]);
REMAP(m_pEdges[i].m_nVertex[1]);
}
for(i=0;i<m_numOrphanEdges;i++)
{
REMAP(m_pOrphanEdges[i].m_nVertex[0]);
REMAP(m_pOrphanEdges[i].m_nVertex[1]);
}
for(i=0;i<m_numFaces;i++)
{
REMAP(m_pFaces[i].m_nVertex[0]);
REMAP(m_pFaces[i].m_nVertex[1]);
REMAP(m_pFaces[i].m_nVertex[2]);
}
#undef REMAP
}
private:
friend class CStencilShadowConnectivityBuilder;
friend class CStencilShadowStaticConnectivityBuilder;
// the array of normal edges (with two adjacent faces)
unsigned m_numEdges; //!<
Edge* m_pEdges; //!<
// the array of orphaned edges (with only one face attached)
unsigned m_numOrphanEdges; //!<
OrphanEdge* m_pOrphanEdges; //!<
private:
// number of vertices in the array referenced by the mesh
unsigned m_numVertices; //!<
// the faces
unsigned m_numFaces; //!< number of faces in the array referenced by the mesh
Face* m_pFaces; //!<
Plane* m_pPlanes; //!< if not NULL, then use these normals instead of computing them on the fly; 1 face - 1 normal
Vec3d* m_pVertices;
};
#ifdef WIN64
#pragma warning( pop ) //AMD Port
#endif
#endif // _STENCIL_SHADOW_CONNECTIVITY_HDR_

View File

@@ -0,0 +1,279 @@
#include "stdafx.h" // precompiled header
#include "StencilShadowConnectivity.h"
#include "StencilShadowConnectivityBuilder.h"
#ifdef WIN64
#pragma warning( push ) //AMD Port
#pragma warning( disable : 4267 )
#endif
// creates an empty mesh connectivity
CStencilShadowConnectivityBuilder::CStencilShadowConnectivityBuilder( void )
#ifdef DEBUG_STD_CONTAINERS
:m_mapSingleEdges("CStencilShadowConnectivityBuilder.SingleEdges")
,m_vDoubleEdges ("CStencilShadowConnectivityBuilder.DoubleEdges")
,m_vFaces("CStencilShadowConnectivityBuilder.Faces")
,m_mVertexWelder("CStencilShadowConnectivityBuilder.VertexWelder")
#endif
{
Reinit();
}
CStencilShadowConnectivityBuilder::~CStencilShadowConnectivityBuilder( void )
{
}
void CStencilShadowConnectivityBuilder::Reinit( void )
{
m_mapSingleEdges.clear();
m_vDoubleEdges.clear();
m_vFaces.clear();
m_mVertexWelder.clear();
m_dwNumUncompressedVertices=0;
// m_fWeldTolerance = 0;
}
///////////////////////////////////////////////////////////////////////////////////
// adds a single triangle to the mesh
// the tirangle is defined by three vertices, in counter-clockwise order
// these vertex indices will be used later when accessing the array of
// deformed character/model vertices to determine the shadow volume boundary
///////////////////////////////////////////////////////////////////////////////////
void CStencilShadowConnectivityBuilder::AddTriangle( unsigned short nV0, unsigned short nV1, unsigned short nV2 )
{
unsigned nNumFaces = m_vFaces.size();
AddNewEdge (BasicEdge(nV0, nV1), EdgeFace(nNumFaces, nV2));
AddNewEdge (BasicEdge(nV1, nV2), EdgeFace(nNumFaces, nV0));
AddNewEdge (BasicEdge(nV2, nV0), EdgeFace(nNumFaces, nV1));
m_vFaces.push_back(Face (nV0, nV1, nV2));
}
// this will try to find a close match to the given vertex, and
// if found, return its index (the actual index of the vertex that's very close or
// coincide with v in space). Otherwise, creates a new vertex reference in the map
// and returns the index nNewVertex
unsigned CStencilShadowConnectivityBuilder::WeldVertex (const Vec3d& v, unsigned nNewVertex)
{
// The easiest way: just find it directly
VertexWelderMap::iterator it;
it = m_mVertexWelder.find(v);
if (it != m_mVertexWelder.end())
return it->second;
else
{
/*
// scan and find some very close vertex
if (m_fWeldTolerance > 0)
for (it = m_mVertexWelder.begin(); it != m_mVertexWelder.end(); ++it)
{
if ((it->first-v).len2() < m_fWeldTolerance)
return it->second;
}
*/
m_mVertexWelder.insert (VertexWelderMap::value_type(v, nNewVertex));
return nNewVertex;
}
}
///////////////////////////////////////////////////////////////////////////////////
// Adds the triangle with the given vertex indices and vertex coordinates
// to the list of triangles that will cast shadows. Welds the vertices based on their
// coordinates
void CStencilShadowConnectivityBuilder::AddTriangleWelded(
unsigned short nV0, unsigned short nV1, unsigned short nV2,
const Vec3d &vV0, const Vec3d &vV1, const Vec3d &vV2 )
{
nV0=WeldVertex(vV0,nV0);
nV1=WeldVertex(vV1,nV1);
nV2=WeldVertex(vV2,nV2);
AddTriangle(nV0,nV1,nV2);
}
//////////////////////////////////////////////////////////////////////////////////////
// add a new edge, if there is no complementary single edge;
// otherwise, withdraw the edge from the list of single edges and add to double edges
// PARAMETERS:
// edge - start and end vertices of the edge going CCW along the face
// efFace - the opposite vertex/face the edge belongs to
//////////////////////////////////////////////////////////////////////////////////////
void CStencilShadowConnectivityBuilder::AddNewEdge (BasicEdge eEdge, EdgeFace efFace)
{
// first, try to find the complementary
BasicEdge eComplementary (eEdge[1], eEdge[0]);
SingleEdgeMap::iterator itComplementary = m_mapSingleEdges.find (eComplementary);
if (itComplementary == m_mapSingleEdges.end())
{
// complementeary edge not found. Add a new single edge
m_mapSingleEdges.insert (SingleEdgeMap::value_type(eEdge, efFace));
// nVEdgeG for nFaceVertex[1] is unknown and doesn't matter at the moment
}
else
{
// we found the complementary edge
Edge edgeNewDouble (eComplementary, itComplementary->second, efFace);
m_vDoubleEdges.push_back (edgeNewDouble);
m_mapSingleEdges.erase (itComplementary);
}
}
///////////////////////////////////////////////////////////////////////////////////
// constructs/compiles the optimum representation of the connectivity
// to be used in run-time
// WARNING: use Release method to dispose the connectivity object
///////////////////////////////////////////////////////////////////////////////////
IStencilShadowConnectivity *CStencilShadowConnectivityBuilder::ConstructConnectivity( void )
{
CStencilShadowConnectivity* pConnectivity = new CStencilShadowConnectivity(m_vDoubleEdges);
assert(pConnectivity); // low memeory?
pConnectivity->SetOrphanEdges(m_mapSingleEdges);
pConnectivity->SetFaces(m_vFaces);
// find all the used vertices
FaceArray::const_iterator pFace = m_vFaces.begin();
DWORD nFaceCount = m_vFaces.size();
#if 0
// test for serialization function
unsigned nBufSize = pConnectivity->Serialize(true, NULL, 0);
void* pBuf = malloc (nBufSize);
unsigned nSaved = pConnectivity->Serialize(true, pBuf, nBufSize);
assert (nSaved == nBufSize);
pConnectivity->Release();
pConnectivity = new CStencilShadowConnectivity ();
unsigned nLoaded = pConnectivity->Serialize(false, pBuf, nBufSize);
assert (nLoaded == nBufSize);
free (pBuf);
#endif
return((IStencilShadowConnectivity *)pConnectivity);
}
// returns the number of single (with no pair faces found) or orphaned edges
unsigned CStencilShadowConnectivityBuilder::numOrphanedEdges ()const
{
return(m_mapSingleEdges.size());
}
//! Returns the list of faces for orphaned edges into the given buffer;
//! For each orphaned edge, one face will be returned; some faces may be duplicated
void CStencilShadowConnectivityBuilder::getOrphanedEdgeFaces (unsigned* pBuffer)
{
for (SingleEdgeMap::iterator it = m_mapSingleEdges.begin(); it != m_mapSingleEdges.end(); ++it)
*(pBuffer++) = it->second.m_nFace;
}
// reserves space for the given number of triangles that are to be added
void CStencilShadowConnectivityBuilder::ReserveForTriangles( unsigned nNumTriangles, unsigned innNumVertices )
{
m_vDoubleEdges.reserve(nNumTriangles*3/2);
m_vFaces.reserve(nNumTriangles);
m_dwNumUncompressedVertices=innNumVertices;
}
CStencilShadowStaticConnectivityBuilder::CStencilShadowStaticConnectivityBuilder()
#ifdef DEBUG_STD_CONTAINERS
: m_vPlanes("CStencilShadowStaticConnectivityBuilder.Planes")
#endif
{
}
CStencilShadowStaticConnectivityBuilder::~CStencilShadowStaticConnectivityBuilder()
{
}
// reserves space for the given number of triangles that are to be added
void CStencilShadowStaticConnectivityBuilder::ReserveForTriangles( unsigned nNumTriangles, unsigned innNumVertices )
{
CStencilShadowConnectivityBuilder::ReserveForTriangles (nNumTriangles, innNumVertices);
m_vPlanes.reserve (nNumTriangles);
}
//! return to the state right after construction
void CStencilShadowStaticConnectivityBuilder::Reinit( void )
{
CStencilShadowConnectivityBuilder::Reinit();
m_vPlanes.clear();
}
//////////////////////////////////////////////////////////////////////////
// Adds the triangle with the given vertex indices and vertex coordinates
// to the list of triangles that will cast shadows. Welds the vertices based on their
// coordinates. Calculates and remembers the normals for each triangle
void CStencilShadowStaticConnectivityBuilder::AddTriangleWelded(
vindex nV0, vindex nV1, vindex nV2,
const Vec3d &vV0, const Vec3d &vV1, const Vec3d &vV2 )
{
CStencilShadowConnectivityBuilder::AddTriangleWelded (nV0, nV1, nV2, vV0, vV1, vV2);
m_vPlanes.push_back(Plane (vV0,vV1,vV2));
}
// constructs/compiles the optimum representation of the connectivity
// to be used in run-time
// WARNING: use Release method to dispose the connectivity object
//! /param inpVertexBuf vertex position buffer to check for solvable open edges (2 vertices with same position)
//! /return interface pointer, could be 0
class IStencilShadowConnectivity *CStencilShadowStaticConnectivityBuilder::ConstructConnectivity ()
{
if (m_vPlanes.empty())
return new CStencilShadowConnectivity();
CStencilShadowConnectivity * pConnectivity = (CStencilShadowConnectivity *)CStencilShadowConnectivityBuilder::ConstructConnectivity()->GetInternalRepresentation();
pConnectivity->SetPlanes(&m_vPlanes[0], m_vPlanes.size());
SetVertices (pConnectivity);
return pConnectivity;
};
//////////////////////////////////////////////////////////////////////////
// WARNING: This is only to be called when the whole construction process
// is finished, to modify already created connectivity
// Goes through all vertices used by the connectivity and constructs a continuous
// array out of them; reindexes the vertices in the connectivity object
// to use these vertices; puts the new vertex array into the connectivity object
void CStencilShadowStaticConnectivityBuilder::SetVertices (CStencilShadowConnectivity * pConnectivity)
{
// scan through all used vertices and put them into continuous array
std::vector<Vec3d> arrNewVertices; // the vertices array in new indexation
std::vector<CStencilShadowConnectivity::vindex> arrOldToNewMap; // the mapping old->new indexation
arrNewVertices.reserve (pConnectivity->numVertices());
arrOldToNewMap.resize (pConnectivity->numVertices(),-1);
for (VertexWelderMap::const_iterator it = m_mVertexWelder.begin(); it != m_mVertexWelder.end(); ++it)
{
#if 1
// this will remap the vertex indices
arrOldToNewMap[it->second] = arrNewVertices.size();
arrNewVertices.push_back(it->first);
#else
// this will not change indexation
arrOldToNewMap[it->second] = it->second;
if (arrNewVertices.size() <= it->second)
arrNewVertices.resize (it->second+1, Vec3d(0,0,0));
arrNewVertices[it->second] = it->first;
#endif
}
// now renumber the vertices
pConnectivity->SetRemapVertices(&arrOldToNewMap[0], arrOldToNewMap.size(), &arrNewVertices[0], arrNewVertices.size());
}
#ifdef WIN64
#pragma warning( pop ) //AMD Port
#endif

View File

@@ -0,0 +1,210 @@
//////////////////////////////////////////////////////////////////////
//
// CryEngine Source code
//
// File:StencilShadowConnectivityBuilder.h
// Declaration of class CStencilShadowConnectivityBuilder
//
// History:
// -:Created by Sergiy Migdalskiy <sergiy@crytek.de>
//
//////////////////////////////////////////////////////////////////////
// class CStencilShadowConnectivityBuilder
//
// This class is used to pre-compute the data for a model that's gonna cast stencil
// shadows, and whose vertices may change position, but the topology
// cannot change.
//
// Ok, to put it clear: for each model, you need one instance of
// class CStencilShadowConnectivity. To create that instance, use
// this class: create an object, push the mesh of the model into it,
// then ask this class to give out the CStencilShadowConnectivity.
//
// CStencilShadowConnectivity is build for maximum run-time performance and
// minimal memory footprint, whilst this class is build for convenient
// pre-calculations and flexibility.
//
// The model is allowed to deform, but is not
// allowed to change topology (add or remove faces as well as re-assign
// face vertices or even change face orientation).
/////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION NOTES should be in
// StencilShadowConnectivityBuilder.cpp
//
#pragma once
#ifndef _STENCIL_SHADOW_CONNECTIVITY_BUILDER_HDR_
#define _STENCIL_SHADOW_CONNECTIVITY_BUILDER_HDR_
// the connectivity class is required for the declarations
// of the Face, Edge, EdgeFace and BasicEdge structures which are common for both
// the builder and the built class
#include "IEdgeConnectivityBuilder.h" // IEdgeConnectivityBuilder
#include "StencilShadowConnectivity.h" // CStencilShadowConnectivity
#include <map> // STL map<>
#include <functional>
#include <algorithm>
class CStencilShadowConnectivityBuilder : public IEdgeConnectivityBuilder
{
public:
typedef IStencilShadowConnectivity::vindex vindex;
//! default constructor
//! creates an empty mesh connectivity
CStencilShadowConnectivityBuilder(void);
//! destructor
virtual ~CStencilShadowConnectivityBuilder(void);
//! return to the state right after construction
virtual void Reinit( void );
// sets the maximum distance between two points that are allowed to be welded
//virtual void SetWeldTolerance (float fTolerance) {m_fWeldTolerance = fTolerance*fTolerance;}
// reserves space for the given number of triangles that are to be added
//! /param nNumTriangles 0..
//! /param innNumVertices 0..
virtual void ReserveForTriangles( unsigned nNumTriangles, unsigned innNumVertices );
// adds a single triangle to the mesh
// the triangle is defined by three vertices, in counter-clockwise order
// these vertex indices will be used later when accessing the array of
// deformed character/model vertices to determine the shadow volume boundary
//! /param nV0 vertex index one 0..0xffff
//! /param nV1 vertex index two 0..0xffff
//! /param nV2 vertex index three 0..0xffff
//! /param inpVertexPos pointer to the vertex array (to remove vertices on same position)
virtual void AddTriangle( vindex nV0, vindex nV1, vindex nV2 );
//!
//! /param nV0 vertex index one 0..0xffff
//! /param nV1 vertex index two 0..0xffff
//! /param nV2 vertex index three 0..0xffff
//! /param vV0 original vertex one position
//! /param vV1 original vertex two position
//! /param vV2 original vertex three position
//! slower but with the auto weld feature (if there are vertices with the same position your result is smaller and therefore faster)
virtual void AddTriangleWelded( vindex nV0, vindex nV1, vindex nV2, const Vec3d &vV0, const Vec3d &vV1, const Vec3d &vV2 );
// constructs/compiles the optimum representation of the connectivity
// to be used in run-time
// WARNING: use Release method to dispose the connectivity object
//! /param inpVertexBuf vertex position buffer to check for solvable open edges (2 vertices with same position)
//! /return interface pointer, could be 0
virtual class IStencilShadowConnectivity *ConstructConnectivity( void );
// returns the number of single (with no pair faces found) or orphaned edges
// /return 0..
virtual unsigned numOrphanedEdges ()const;
//! Returns the list of faces for orphaned edges into the given buffer;
//! For each orphaned edge, one face will be returned; some faces may be duplicated
virtual void getOrphanedEdgeFaces (unsigned* pBuffer);
protected:
// for the descriptions of the following shared structures, refer to
// File:StencilShadowConnectivity.h
typedef CStencilShadowConnectivity::BasicEdge BasicEdge;
typedef CStencilShadowConnectivity::Edge Edge;
typedef CStencilShadowConnectivity::EdgeFace EdgeFace;
typedef CStencilShadowConnectivity::Face Face;
// map of single edges - edge with only one face attached
typedef std::map<BasicEdge, EdgeFace> SingleEdgeMap;
SingleEdgeMap m_mapSingleEdges; //!<
// array of double-edges - edges with both faces attached/found
typedef std::vector<Edge> DoubleEdgeArray;
DoubleEdgeArray m_vDoubleEdges; //!<
// triangles added (used to extract index to reference edge to faces, and keep the topology)
typedef std::vector<Face> FaceArray;
FaceArray m_vFaces; //!<
DWORD m_dwNumUncompressedVertices; //!<
// helper to get order for Vec3d
struct CVec3dOrder: public std::binary_function< Vec3d, Vec3d, bool>
{
bool operator() ( const Vec3d &a, const Vec3d &b ) const
{
// first sort by x
if(a.x<b.x)return(true);
if(a.x>b.x)return(false);
// then by y
if(a.y<b.y)return(true);
if(a.y>b.y)return(false);
// then by z
if(a.z<b.z)return(true);
if(a.z>b.z)return(false);
return(false);
}
};
typedef std::map<Vec3d,unsigned,CVec3dOrder> VertexWelderMap;
VertexWelderMap m_mVertexWelder; //!< used for AddTriangleWelded
// this will try to find a close match to the given vertex, and
// if found, return its index (the actual index of the vertex that's very close or
// coincide with v in space). Otherwise, creates a new vertex reference in the map
// and returns the index nNewVertex
unsigned WeldVertex (const Vec3d& v, unsigned nNewVertex);
//float m_fWeldTolerance;
// this is 1 + the max index of the vertex in the original mesh vertex array, that
// is used by the currently being built connectivity
//unsigned m_numOrigMeshVerticesUsed; // this is unneeded because connectivity calculates this itself
protected:
// adds a new single edge, or finds an adjacent edge and puts the double-edge on record
// add a new edge, if there is no complementary single edge;
// otherwise, withdraw the edge from the list of single edges and add to double edges
//! /param eEdge
//! /param efFace
void AddNewEdge (BasicEdge eEdge, EdgeFace efFace);
};
// this is the builder of connectivity that can be used for static objects
// (with non-changing face normals)
class CStencilShadowStaticConnectivityBuilder :public CStencilShadowConnectivityBuilder
{
public:
CStencilShadowStaticConnectivityBuilder();
virtual ~CStencilShadowStaticConnectivityBuilder();
void AddTriangleWelded( vindex nV0, vindex nV1, vindex nV2, const Vec3d &vV0, const Vec3d &vV1, const Vec3d &vV2 );
//! return to the state right after construction
virtual void Reinit( void );
// reserves space for the given number of triangles that are to be added
//! /param nNumTriangles 0..
//! /param innNumVertices 0..
virtual void ReserveForTriangles( unsigned nNumTriangles, unsigned innNumVertices );
// constructs/compiles the optimum representation of the connectivity
// to be used in run-time
// WARNING: use Release method to dispose the connectivity object
//! /param inpVertexBuf vertex position buffer to check for solvable open edges (2 vertices with same position)
//! /return interface pointer, could be 0
virtual class IStencilShadowConnectivity *ConstructConnectivity( void );
protected:
// WARNING: This is only to be called when the whole construction process
// is finished, to modify already created connectivity
// Goes through all vertices used by the connectivity and constructs a continuous
// array out of them; reindexes the vertices in the connectivity object
// to use these vertices; puts the new vertex array into the connectivity object
void SetVertices (CStencilShadowConnectivity * pConnectivity);
typedef CStencilShadowConnectivity::Plane Plane;
std::vector<Plane> m_vPlanes;
};
#endif // _STENCIL_SHADOW_CONNECTIVITY_BUILDER_HDR_

View File

@@ -0,0 +1,612 @@
//////////////////////////////////////////////////////////////////////
//
// Crytek SuperFramework Source code
//
// (c) by Crytek GmbH 2003. All rights reserved.
// Used with permission to distribute for non-commercial purposes.
//
//
// File:TangentSpaceCalculation.h
// Description: calculated the tangent space base vector for a given mesh
// Dependencies: none
// Documentation: "How to calculate tangent base vectors.doc"
//
// Usage:
// implement the proxy class: CTriangleInputProxy
// instance the proxy class: CTriangleInputProxy MyProxy(MyData);
// instance the calculation helper: CTangentSpaceCalculation<MyProxy> MyTangent;
// do the calculation: MyTangent.CalculateTangentSpace(MyProxy);
// get the data back: MyTangent.GetTriangleIndices(), MyTangent.GetBase()
//
// History:
// - 12/07/2002: Created by Martin Mittring as part of CryEngine
// - 08/18/2003: MM improved stability (no illegal floats) with bad input data
// - 08/19/2003: MM added check for input data problems (DebugMesh() is deactivated by default)
// - 10/02/2003: MM removed rundundant code
//
//////////////////////////////////////////////////////////////////////
#pragma once
#include <vector> // STL vector<>
#include <map> // STL map<,,> multimap<>
#define BASEMATRIXMERGEBIAS 0.9f
/* // use this as reference
class CTriangleInputProxy
{
public:
//! /return 0..
DWORD GetTriangleCount( void ) const;
//! /param indwTriNo 0..
//! /param outdwPos
//! /param outdwNorm
//! /param outdwUV
void GetTriangleIndices( const DWORD indwTriNo, DWORD outdwPos[3], DWORD outdwNorm[3], DWORD outdwUV[3] ) const;
//! /param indwPos 0..
//! /param outfPos
void GetPos( const DWORD indwPos, float outfPos[3] ) const;
//! /param indwPos 0..
//! /param outfUV
void GetUV( const DWORD indwPos, float outfUV[2] ) const;
};
*/
// ---------------------------------------------------------------------------------------------------------------
// InputProxy - use CTriangleInputProxy as reference
template <class InputProxy>
class CTangentSpaceCalculation
{
public:
// IN ------------------------------------------------
//! /param inInput - the normals are only used as smoothing input - we calculate the normals ourself
void CalculateTangentSpace( const InputProxy &inInput );
// OUT -----------------------------------------------
//! /return the number of base vectors that are stored 0..
size_t GetBaseCount( void );
//!
//! /param indwTriNo 0..
//! /param outdwBase
void GetTriangleBaseIndices( const DWORD indwTriNo, DWORD outdwBase[3] );
//! returns a orthogonal base (perpendicular and normalized)
//! /param indwPos 0..
//! /param outU in object/worldspace
//! /param outV in object/worldspace
//! /param outN in object/worldspace
void GetBase( const DWORD indwPos, float outU[3], float outV[3], float outN[3] );
private: // -----------------------------------------------------------------
class CVec2
{
public:
float x,y;
CVec2(){}
CVec2(float fXval, float fYval) { x=fXval; y=fYval; }
friend CVec2 operator - (const CVec2 &vec1, const CVec2 &vec2) { return CVec2(vec1.x-vec2.x, vec1.y-vec2.y); }
operator float * () { return((float *)this); }
};
class CVec3
{
public:
float x,y,z;
CVec3(){}
CVec3(float fXval, float fYval, float fZval) { x=fXval; y=fYval; z=fZval; }
friend CVec3 operator - (const CVec3 &vec1, const CVec3 &vec2) { return CVec3(vec1.x-vec2.x, vec1.y-vec2.y, vec1.z-vec2.z); }
friend CVec3 operator - (const CVec3 &vec1) { return CVec3(-vec1.x, -vec1.y, -vec1.z); }
friend CVec3 operator + (const CVec3 &vec1, const CVec3 &vec2) { return CVec3(vec1.x+vec2.x, vec1.y+vec2.y, vec1.z+vec2.z); }
friend CVec3 operator * (const CVec3 &vec1, const float fVal) { return CVec3(vec1.x*fVal, vec1.y*fVal, vec1.z*fVal); }
friend float operator * (const CVec3 &vec1, const CVec3 &vec2) { return( vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z); }
operator float * () { return((float *)this); }
void Negate() { x=-x;y=-y;z=-z; }
friend CVec3 normalize( const CVec3 &vec ) { CVec3 ret; float fLen=length(vec); if(fLen<0.00001f)return(vec); fLen=1.0f/fLen;ret.x=vec.x*fLen;ret.y=vec.y*fLen;ret.z=vec.z*fLen;return(ret); }
friend CVec3 cross( const CVec3 &vec1, const CVec3 &vec2 ) { return CVec3(vec1.y*vec2.z-vec1.z*vec2.y, vec1.z*vec2.x-vec1.x*vec2.z, vec1.x*vec2.y-vec1.y*vec2.x); }
friend float length( const CVec3 &invA ) { return (float)sqrt(invA.x*invA.x+invA.y*invA.y+invA.z*invA.z); }
friend float CalcAngleBetween( const CVec3 &invA, const CVec3 &invB )
{
float LengthQ=length(invA)*length(invB);
if(LengthQ<0.0001f)LengthQ=0.0001f; // to prevent division by zero
float f=(invA*invB)/LengthQ;
if(f>1.0f)f=1.0f; // acos need input in the range [-1..1]
else if(f<-1.0f)f=-1.0f; //
float fRet=(float)acos(f); // cosf is not avaiable on every plattform
return(fRet);
}
friend bool IsZero( const CVec3 &invA ) { return(invA.x==0.0f && invA.y==0.0f && invA.z==0.0f); }
friend bool IsNormalized( const CVec3 &invA ) { float f=length(invA);return(f>=0.95f && f<=1.05f); }
};
class CBaseIndex
{
public:
DWORD m_dwPosNo; //!< 0..
DWORD m_dwNormNo; //!< 0..
};
// helper to get order for CVertexLoadHelper
struct CBaseIndexOrder: public std::binary_function< CBaseIndex, CBaseIndex, bool>
{
bool operator() ( const CBaseIndex &a, const CBaseIndex &b ) const
{
// first sort by position
if(a.m_dwPosNo<b.m_dwPosNo)return(true);
if(a.m_dwPosNo>b.m_dwPosNo)return(false);
// then by normal
if(a.m_dwNormNo<b.m_dwNormNo)return(true);
if(a.m_dwNormNo>b.m_dwNormNo)return(false);
return(false);
}
};
class CBase33
{
public:
CBase33() { }
CBase33(CVec3 Uval, CVec3 Vval, CVec3 Nval) { u=Uval; v=Vval; n=Nval; }
CVec3 u; //!<
CVec3 v; //!<
CVec3 n; //!< is part of the tangent base but can be used also as vertex normal
};
class CTriBaseIndex
{
public:
DWORD p[3]; //!< index in m_BaseVectors
};
// output data -----------------------------------------------------------------------------------
std::vector<CTriBaseIndex> m_TriBaseAssigment; //!< [0..dwTriangleCount]
std::vector<CBase33> m_BaseVectors; //!< [0..] generated output data
//! /param indwPosNo
//! /param indwNormNo
CBase33 &GetBase( std::multimap<CBaseIndex,DWORD,CBaseIndexOrder> &inMap, const DWORD indwPosNo, const DWORD indwNormNo );
private:
//! creates, copies or returns a reference
//! /param inMap
//! /param indwPosNo
//! /param indwNormNo
//! /param inU weighted
//! /param inV weighted
//! /param inN normalized
DWORD AddUV2Base( std::multimap<CBaseIndex,DWORD,CBaseIndexOrder> &inMap, const DWORD indwPosNo, const DWORD indwNormNo, const CVec3 &inU, const CVec3 &inV, const CVec3 &inNormN );
//! /param inMap
//! /param indwPosNo
//! /param indwNormNo
//! /param inNormal weighted normal
void AddNormal2Base( std::multimap<CBaseIndex,DWORD,CBaseIndexOrder> &inMap, const DWORD indwPosNo, const DWORD indwNormNo, const CVec3 &inNormal );
//! this code was heavly tested with external test app by SergiyM and MartinM
//! rotates the input vector with the rotation to-from
//! /param vFrom has to be normalized
//! /param vTo has to be normalized
//! /param vInput
static CVec3 Rotate( const CVec3 &vFrom, const CVec3 &vTo, const CVec3 &vInput )
{
// no mesh is perfect
// assert(IsNormalized(vFrom));
// no mesh is perfect
// assert(IsNormalized(vTo));
CVec3 vRotAxis=cross(vFrom,vTo); // rotation axis
float fSin=length(vRotAxis);
float fCos=vFrom*vTo;
if(fSin<0.00001f) // no rotation
return(vInput);
vRotAxis=vRotAxis*(1.0f/fSin); // normalize
CVec3 vFrom90deg=normalize(cross(vRotAxis,vFrom)); // perpendicular to vRotAxis and vFrom90deg
// Base is vFrom,vFrom90deg,vRotAxis
float fXInPlane=vFrom*vInput;
float fYInPlane=vFrom90deg*vInput;
CVec3 a=vFrom*(fXInPlane*fCos-fYInPlane*fSin);
CVec3 b=vFrom90deg*(fXInPlane*fSin+fYInPlane*fCos);
CVec3 c=vRotAxis*(vRotAxis*vInput);
return( a + b + c );
}
void DebugMesh( const InputProxy &inInput ) const;
};
// ---------------------------------------------------------------------------------------------------------------
template <class InputProxy>
void CTangentSpaceCalculation<InputProxy>::DebugMesh( const InputProxy &inInput ) const
{
DWORD dwTriCount=inInput.GetTriangleCount();
// search for polygons that use the same indices (input data problems)
for(DWORD a=0;a<dwTriCount;a++)
{
DWORD dwAPos[3],dwANorm[3],dwAUV[3];
inInput.GetTriangleIndices(a,dwAPos,dwANorm,dwAUV);
for(DWORD b=a+1;b<dwTriCount;b++)
{
DWORD dwBPos[3],dwBNorm[3],dwBUV[3];
inInput.GetTriangleIndices(b,dwBPos,dwBNorm,dwBUV);
assert(!( dwAPos[0]==dwBPos[0] && dwAPos[1]==dwBPos[1] && dwAPos[2]==dwBPos[2] ));
assert(!( dwAPos[1]==dwBPos[0] && dwAPos[2]==dwBPos[1] && dwAPos[0]==dwBPos[2] ));
assert(!( dwAPos[2]==dwBPos[0] && dwAPos[0]==dwBPos[1] && dwAPos[1]==dwBPos[2] ));
assert(!( dwAPos[1]==dwBPos[0] && dwAPos[0]==dwBPos[1] && dwAPos[2]==dwBPos[2] ));
assert(!( dwAPos[2]==dwBPos[0] && dwAPos[1]==dwBPos[1] && dwAPos[0]==dwBPos[2] ));
assert(!( dwAPos[0]==dwBPos[0] && dwAPos[2]==dwBPos[1] && dwAPos[1]==dwBPos[2] ));
}
}
}
template <class InputProxy>
void CTangentSpaceCalculation<InputProxy>::CalculateTangentSpace( const InputProxy &inInput )
{
DWORD dwTriCount=inInput.GetTriangleCount();
// clear result
m_BaseVectors.clear();
m_TriBaseAssigment.clear();
m_TriBaseAssigment.reserve(dwTriCount);
assert(m_BaseVectors.size()==0);
assert(m_TriBaseAssigment.size()==0);
std::multimap<CBaseIndex,DWORD,CBaseIndexOrder> mBaseMap; // second=index into m_BaseVectors, generated output data
std::vector<CBase33> vTriangleBase; // base vectors per triangle
// DebugMesh(inInput); // only for debugging - slow
// calculate the base vectors per triangle -------------------------------------------
{
for(DWORD i=0;i<dwTriCount;i++)
{
// get data from caller ---------------------------
DWORD dwPos[3],dwNorm[3],dwUV[3];
inInput.GetTriangleIndices(i,dwPos,dwNorm,dwUV);
CVec3 vPos[3];
CVec2 vUV[3];
for(int e=0;e<3;e++)
{
inInput.GetPos(dwPos[e],vPos[e]);
inInput.GetUV(dwUV[e],vUV[e]);
}
// calculate tangent vectors ---------------------------
CVec3 vA=vPos[1]-vPos[0];
CVec3 vB=vPos[2]-vPos[0];
/*
char str[2024];
sprintf(str,"in: vA=(%.3f %.3f %.3f) vB=(%.3f %.3f %.3f)\n",vA.x,vA.y,vA.z,vA.x,vA.y,vA.z);
OutputDebugString(str);
*/
float fDeltaU1=vUV[1].x-vUV[0].x;
float fDeltaU2=vUV[2].x-vUV[0].x;
float fDeltaV1=vUV[1].y-vUV[0].y;
float fDeltaV2=vUV[2].y-vUV[0].y;
float div =(fDeltaU1*fDeltaV2-fDeltaU2*fDeltaV1);
CVec3 vU,vV,vN=normalize(cross(vA,vB));
if(div!=0.0)
{
// area(u1*v2-u2*v1)/2
float fAreaMul2=fabsf(fDeltaU1*fDeltaV2-fDeltaU2*fDeltaV1); // weight the tangent vectors by the UV triangles area size (fix problems with base UV assignment)
float a = fDeltaV2/div;
float b = -fDeltaV1/div;
float c = -fDeltaU2/div;
float d = fDeltaU1/div;
vU=normalize(vA*a+vB*b)*fAreaMul2;
vV=normalize(vA*c+vB*d)*fAreaMul2;
}
else { vU=CVec3(0,0,0);vV=CVec3(0,0,0); }
vTriangleBase.push_back(CBase33(vU,vV,vN));
}
}
// distribute the normals to the vertices
{
// we create a new tangent base for every vertex index that has a different normal (later we split further for mirrored use)
// and sum the base vectors (weighted by angle and mirrored if necessary)
for(DWORD i=0;i<dwTriCount;i++)
{
DWORD e;
// get data from caller ---------------------------
DWORD dwPos[3],dwNorm[3],dwUV[3];
inInput.GetTriangleIndices(i,dwPos,dwNorm,dwUV);
CBase33 TriBase=vTriangleBase[i];
CVec3 vPos[3];
for(e=0;e<3;e++) inInput.GetPos(dwPos[e],vPos[e]);
// for each triangle vertex
for(e=0;e<3;e++)
{
float fWeight=CalcAngleBetween( vPos[(e+2)%3]-vPos[e],vPos[(e+1)%3]-vPos[e] ); // weight by angle to fix the L-Shape problem
// float fWeight=length(cross( vPos[(e+2)%3]-vPos[e],vPos[(e+1)%3]-vPos[e] )); // weight by area, that does not fix the L-Shape problem 100%
if(fWeight<=0.0f)
fWeight=0.0001f;
AddNormal2Base(mBaseMap,dwPos[e],dwNorm[e],TriBase.n*fWeight);
}
}
}
// distribute the uv vectors to the vertices
{
// we create a new tangent base for every vertex index that has a different normal
// if the base vectors does'nt fit we split as well
for(DWORD i=0;i<dwTriCount;i++)
{
DWORD e;
// get data from caller ---------------------------
DWORD dwPos[3],dwNorm[3],dwUV[3];
CTriBaseIndex Indx;
inInput.GetTriangleIndices(i,dwPos,dwNorm,dwUV);
CBase33 TriBase=vTriangleBase[i];
CVec3 vPos[3];
for(e=0;e<3;e++) inInput.GetPos(dwPos[e],vPos[e]);
// for each triangle vertex
for(e=0;e<3;e++)
{
float fWeight=CalcAngleBetween( vPos[(e+2)%3]-vPos[e],vPos[(e+1)%3]-vPos[e] ); // weight by angle to fix the L-Shape problem
// float fWeight=length(cross( vPos[(e+2)%3]-vPos[e],vPos[(e+1)%3]-vPos[e] )); // weight by area, that does not fix the L-Shape problem 100%
Indx.p[e]=AddUV2Base(mBaseMap,dwPos[e],dwNorm[e],TriBase.u*fWeight,TriBase.v*fWeight,normalize(TriBase.n));
}
m_TriBaseAssigment.push_back(Indx);
}
}
// orthogonalize the base vectors per vertex -------------------------------------------
{
std::vector<CBase33>::iterator it;
for(it=m_BaseVectors.begin();it!=m_BaseVectors.end();++it)
{
CBase33 &ref=(*it);
// rotate u and v in n plane
{
// (N is dominating, U and V equal weighted)
CVec3 vUout,vVout,vNout;
vNout=normalize(ref.n);
vUout = ref.u - vNout * (vNout*ref.u); // project u in n plane
vVout = ref.v - vNout * (vNout*ref.v); // project v in n plane
ref.u=normalize(vUout);ref.v=normalize(vVout);ref.n=vNout;
assert(ref.u.x>=-1 && ref.u.x<=1);
assert(ref.u.y>=-1 && ref.u.y<=1);
assert(ref.u.z>=-1 && ref.u.z<=1);
assert(ref.v.x>=-1 && ref.v.x<=1);
assert(ref.v.y>=-1 && ref.v.y<=1);
assert(ref.v.z>=-1 && ref.v.z<=1);
assert(ref.n.x>=-1 && ref.n.x<=1);
assert(ref.n.y>=-1 && ref.n.y<=1);
assert(ref.n.z>=-1 && ref.n.z<=1);
}
}
}
}
template <class InputProxy>
DWORD CTangentSpaceCalculation<InputProxy>::AddUV2Base( std::multimap<CBaseIndex,DWORD,CBaseIndexOrder> &inMap,
const DWORD indwPosNo, const DWORD indwNormNo, const CVec3 &inU, const CVec3 &inV, const CVec3 &inNormN )
{
// no mesh is perfect
// assert(IsNormalized(inNormN));
CBaseIndex Indx;
Indx.m_dwPosNo=indwPosNo;
Indx.m_dwNormNo=indwNormNo;
std::multimap<CBaseIndex,DWORD,CBaseIndexOrder>::iterator iFind,iFindEnd;
iFind = inMap.lower_bound(Indx);
assert(iFind!=inMap.end());
CVec3 vNormal=m_BaseVectors[(*iFind).second].n;
iFindEnd = inMap.upper_bound(Indx);
DWORD dwBaseUVIndex=0xffffffff; // init with not found
bool bParity=(cross(inU,inV)*inNormN>0.0f);
for(;iFind!=iFindEnd;++iFind)
{
CBase33 &refFound=m_BaseVectors[(*iFind).second];
if(!IsZero(refFound.u))
{
bool bParityRef=(cross(refFound.u,refFound.v)*refFound.n>0.0f);
bool bParityCheck=(bParityRef==bParity);
if(!bParityCheck)continue;
// bool bHalfAngleCheck=normalize(inU+inV) * normalize(refFound.u+refFound.v) > 0.0f;
CVec3 vRotHalf=Rotate(normalize(refFound.n),inNormN,normalize(refFound.u+refFound.v));
bool bHalfAngleCheck=normalize(inU+inV) * vRotHalf > 0.0f;
// // bool bHalfAngleCheck=normalize(normalize(inU)+normalize(inV)) * normalize(normalize(refFound.u)+normalize(refFound.v)) > 0.0f;
if(!bHalfAngleCheck)continue;
}
dwBaseUVIndex=(*iFind).second;break;
}
if(dwBaseUVIndex==0xffffffff) // not found
{
// otherwise create a new base
CBase33 Base( CVec3(0,0,0), CVec3(0,0,0), vNormal );
dwBaseUVIndex = m_BaseVectors.size();
inMap.insert( std::pair<CBaseIndex,DWORD>(Indx,dwBaseUVIndex) );
m_BaseVectors.push_back(Base);
}
CBase33 &refBaseUV=m_BaseVectors[dwBaseUVIndex];
refBaseUV.u=refBaseUV.u+inU;
refBaseUV.v=refBaseUV.v+inV;
//no mesh is perfect
if(inU.x!=0.0f || inU.y!=0.0f || inU.z!=0.0f)
assert(refBaseUV.u.x!=0.0f || refBaseUV.u.y!=0.0f || refBaseUV.u.z!=0.0f);
// no mesh is perfect
if(inV.x!=0.0f || inV.y!=0.0f || inV.z!=0.0f)
assert(refBaseUV.v.x!=0.0f || refBaseUV.v.y!=0.0f || refBaseUV.v.z!=0.0f);
return(dwBaseUVIndex);
}
template <class InputProxy>
void CTangentSpaceCalculation<InputProxy>::AddNormal2Base( std::multimap<CBaseIndex,DWORD,CBaseIndexOrder> &inMap,
const DWORD indwPosNo, const DWORD indwNormNo, const CVec3 &inNormal )
{
CBaseIndex Indx;
Indx.m_dwPosNo=indwPosNo;
Indx.m_dwNormNo=indwNormNo;
std::multimap<CBaseIndex,DWORD,CBaseIndexOrder>::iterator iFind = inMap.find(Indx);
DWORD dwBaseNIndex;
if(iFind!=inMap.end()) // found
{
// resuse the existing one
dwBaseNIndex=(*iFind).second;
}
else
{
// otherwise create a new base
CBase33 Base( CVec3(0,0,0), CVec3(0,0,0), CVec3(0,0,0) );
dwBaseNIndex=m_BaseVectors.size();
inMap.insert( std::pair<CBaseIndex,DWORD>(Indx,dwBaseNIndex) );
m_BaseVectors.push_back(Base);
}
CBase33 &refBaseN=m_BaseVectors[dwBaseNIndex];
refBaseN.n=refBaseN.n+inNormal;
}
template <class InputProxy>
void CTangentSpaceCalculation<InputProxy>::GetBase( const DWORD indwPos, float outU[3], float outV[3], float outN[3] )
{
CBase33 &base=m_BaseVectors[indwPos];
outU[0]=base.u.x;
outV[0]=base.v.x;
outN[0]=base.n.x;
outU[1]=base.u.y;
outV[1]=base.v.y;
outN[1]=base.n.y;
outU[2]=base.u.z;
outV[2]=base.v.z;
outN[2]=base.n.z;
}
template <class InputProxy>
void CTangentSpaceCalculation<InputProxy>::GetTriangleBaseIndices( const DWORD indwTriNo, DWORD outdwBase[3] )
{
assert(indwTriNo<m_TriBaseAssigment.size());
CTriBaseIndex &indx=m_TriBaseAssigment[indwTriNo];
for(DWORD i=0;i<3;i++) outdwBase[i]=indx.p[i];
}
template <class InputProxy>
size_t CTangentSpaceCalculation<InputProxy>::GetBaseCount( void )
{
return(m_BaseVectors.size());
}

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ResourceCompilerPC.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -0,0 +1,58 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#define NOT_USE_CRY_MEMORY_MANAGER
#include <platform.h>
// Windows Header Files:
#ifdef WIN64
#include "PortableString.h"
typedef CPortableString CString;
#else
#include <atlbase.h>
#include <atlstr.h>
#endif
#include <assert.h>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <tchar.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <Cry_Math.h>
#include <IRenderer.h>
#include <LeafBuffer.h>
#include <RendElement.h>
#include <IShader.h>
#include <VertexFormats.h>
#include <CryHeaders.h>
#include <TArrays.h>
#include <primitives.h>
#include <physinterface.h>
#include <CrySizer.h>
// TODO: reference additional headers your program requires here
#ifndef SIZEOF_ARRAY
#define SIZEOF_ARRAY(arr) (sizeof(arr)/sizeof((arr)[0]))
#include "ResourceCompilerPC.h"
#include "float.h"
#include "list2.h"
#ifndef WIN64
#include "Dbghelp.h"
#endif
#include "FileUtil.h"
#include "PathUtil.h"
#include "IRCLog.h"
#include "CryChunkedFile.h"
#endif