Initial revision of 3DS MAX exporter for PMD and PSA files.
This was SVN commit r314.
This commit is contained in:
parent
52f5b707ee
commit
210d77c9b3
125
source/tools/pmdexp/DllEntry.cpp
Executable file
125
source/tools/pmdexp/DllEntry.cpp
Executable file
@ -0,0 +1,125 @@
|
||||
/**********************************************************************
|
||||
*<
|
||||
FILE: DllEntry.cpp
|
||||
|
||||
DESCRIPTION: Contains the Dll Entry stuff
|
||||
|
||||
CREATED BY:
|
||||
|
||||
HISTORY:
|
||||
|
||||
*> Copyright (c) 2000, All Rights Reserved.
|
||||
**********************************************************************/
|
||||
#include "PMDExp.h"
|
||||
#include "PSAExp.h"
|
||||
#include "PSProp.h"
|
||||
#include "MaxInc.h"
|
||||
|
||||
|
||||
#define PMDEXP_CLASS_ID Class_ID(0x71d92656, 0x136330c5)
|
||||
#define PSAEXP_CLASS_ID Class_ID(0x6cf86c73, 0x54e0844)
|
||||
|
||||
HINSTANCE hInstance;
|
||||
static int controlsInit = FALSE;
|
||||
|
||||
TCHAR* GetString(int id)
|
||||
{
|
||||
static TCHAR buf[256];
|
||||
|
||||
if (hInstance) {
|
||||
if (!LoadString(hInstance, id, buf, sizeof(buf))) {
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// PMDExpClassDesc: required class to expose PMDExp to MAX
|
||||
class PMDExpClassDesc : public ClassDesc2
|
||||
{
|
||||
public:
|
||||
int IsPublic() { return TRUE; }
|
||||
void * Create(BOOL loading = FALSE) { return new PMDExp(); }
|
||||
const TCHAR * ClassName() { return GetString(IDS_PSA_CLASS_NAME); }
|
||||
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
|
||||
Class_ID ClassID() { return PMDEXP_CLASS_ID; }
|
||||
const TCHAR* Category() { return GetString(IDS_CATEGORY); }
|
||||
|
||||
const TCHAR* InternalName() { return _T("PMDExp"); }
|
||||
HINSTANCE HInstance() { return hInstance; }
|
||||
|
||||
};
|
||||
|
||||
static PMDExpClassDesc PMDExpDesc;
|
||||
ClassDesc2* GetPMDExpDesc() { return &PMDExpDesc; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// PSAExpClassDesc: required class to expose PSAExp to MAX
|
||||
class PSAExpClassDesc : public ClassDesc2
|
||||
{
|
||||
public:
|
||||
int IsPublic() { return TRUE; }
|
||||
void * Create(BOOL loading = FALSE) { return new PSAExp(); }
|
||||
const TCHAR * ClassName() { return GetString(IDS_PSA_CLASS_NAME); }
|
||||
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
|
||||
Class_ID ClassID() { return PSAEXP_CLASS_ID; }
|
||||
const TCHAR* Category() { return GetString(IDS_CATEGORY); }
|
||||
|
||||
const TCHAR* InternalName() { return _T("PSAExp"); }
|
||||
HINSTANCE HInstance() { return hInstance; }
|
||||
|
||||
};
|
||||
|
||||
static PSAExpClassDesc PSAExpDesc;
|
||||
ClassDesc2* GetPSAExpDesc() { return &PSAExpDesc; }
|
||||
|
||||
|
||||
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved)
|
||||
{
|
||||
hInstance = hinstDLL; // Hang on to this DLL's instance handle.
|
||||
|
||||
if (!controlsInit) {
|
||||
controlsInit = TRUE;
|
||||
InitCustomControls(hInstance); // Initialize MAX's custom controls
|
||||
InitCommonControls(); // Initialize Win95 controls
|
||||
}
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
|
||||
__declspec(dllexport) const TCHAR* LibDescription()
|
||||
{
|
||||
return GetString(IDS_LIBDESCRIPTION);
|
||||
}
|
||||
|
||||
__declspec(dllexport) int LibNumberClasses()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ClassDesc* LibClassDesc(int i)
|
||||
{
|
||||
switch(i) {
|
||||
case 0: return GetPMDExpDesc();
|
||||
case 1: return GetPSAExpDesc();
|
||||
case 2: return GetPSPropDesc();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) ULONG LibVersion()
|
||||
{
|
||||
return VERSION_3DSMAX;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) ULONG CanAutoDefer()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
463
source/tools/pmdexp/ExpMesh.cpp
Executable file
463
source/tools/pmdexp/ExpMesh.cpp
Executable file
@ -0,0 +1,463 @@
|
||||
#include "ExpMesh.h"
|
||||
#include "ExpUtil.h"
|
||||
#include "ExpSkeleton.h"
|
||||
#include "VertexTree.h"
|
||||
#include "VNormal.h"
|
||||
|
||||
#include "phyexp.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TMNegParity: return whether the given matrix has a negative scale or not
|
||||
inline bool TMNegParity(Matrix3 &Mat)
|
||||
{
|
||||
return (DotProd(CrossProd(Mat.GetRow(0),Mat.GetRow(1)),Mat.GetRow(2)) < 0.0) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetTriObjectFromNode: return a pointer to a TriObject
|
||||
// given an INode or return NULL if the node cannot be converted
|
||||
// to a TriObject
|
||||
static TriObject* GetTriObjectFromNode(INode* node,bool& deleteIt)
|
||||
{
|
||||
deleteIt = false;
|
||||
Object* obj=node->EvalWorldState(0).obj;
|
||||
if (obj) {
|
||||
if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
|
||||
TriObject *tri=(TriObject*) obj->ConvertToType(0,Class_ID(TRIOBJ_CLASS_ID,0));
|
||||
|
||||
// note that the TriObject should only be deleted if the pointer to it is
|
||||
// not equal to the object pointer that called ConvertToType()
|
||||
if (obj != tri) {
|
||||
deleteIt = true;
|
||||
}
|
||||
return tri;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetPhysiqueFromNode: return a pointer to a Physique modifier found
|
||||
// in the modifier stack of the given INode, or return NULL is no Physique
|
||||
// modifier can be found
|
||||
static Modifier* GetPhysiqueFromNode(INode* node)
|
||||
{
|
||||
// Get object from node. Abort if no object.
|
||||
Object* object=node->GetObjectRef();
|
||||
if (!object) return 0;
|
||||
|
||||
// ss derived object?
|
||||
if (object->SuperClassID()==GEN_DERIVOB_CLASS_ID) {
|
||||
// yes -> cast
|
||||
IDerivedObject* derivedObj=static_cast<IDerivedObject*>(object);
|
||||
|
||||
// iterate over all entries of the modifier stack
|
||||
int modStackIndex=0;
|
||||
while (modStackIndex<derivedObj->NumModifiers()) {
|
||||
|
||||
// get current modifier
|
||||
Modifier* modifier=derivedObj->GetModifier(modStackIndex);
|
||||
Class_ID clsid=modifier->ClassID();
|
||||
|
||||
// check if it's a Physique
|
||||
if (modifier->ClassID()==Class_ID(PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B)) {
|
||||
// yes -> return it
|
||||
return modifier;
|
||||
}
|
||||
|
||||
// advance to next modifier stack entry
|
||||
modStackIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// not found ..
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PMDExpMesh::PMDExpMesh(INode* node,const ExpSkeleton* skeleton)
|
||||
: m_Node(node), m_Skeleton(skeleton)
|
||||
{
|
||||
}
|
||||
|
||||
bool PMDExpMesh::IsMesh(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// InsertBlend: insert the given bone/weight blend into the given blend set
|
||||
void PMDExpMesh::InsertBlend(unsigned char bone,float weight,SVertexBlend& blend)
|
||||
{
|
||||
// get position where this blend should be inserted
|
||||
int i=0;
|
||||
while (weight<blend.m_Weight[i]) i++;
|
||||
if (i>=SVertexBlend::SIZE) {
|
||||
// all other blends carry greater weight; reject this blend
|
||||
return;
|
||||
}
|
||||
|
||||
for (int j=SVertexBlend::SIZE-2;j>=i;j--) {
|
||||
blend.m_Bone[j+1]=blend.m_Bone[j];
|
||||
blend.m_Weight[j+1]=blend.m_Weight[j];
|
||||
}
|
||||
|
||||
blend.m_Bone[i]=bone;
|
||||
blend.m_Weight[i]=weight;
|
||||
}
|
||||
|
||||
void PMDExpMesh::BuildVertexBlends(Mesh& mesh,VBlendList& blends)
|
||||
{
|
||||
// allocate blends
|
||||
int numVerts=mesh.getNumVerts();
|
||||
blends.resize(mesh.getNumVerts());
|
||||
|
||||
// set all blends to null
|
||||
for (int i=0;i<numVerts;i++) {
|
||||
memset(blends[i].m_Bone,0xff,sizeof(blends[i].m_Bone));
|
||||
memset(blends[i].m_Weight,0,sizeof(blends[i].m_Weight));
|
||||
}
|
||||
|
||||
// query presence of physique modifier first
|
||||
Modifier* physique=GetPhysiqueFromNode(m_Node);
|
||||
if (!physique) {
|
||||
// no Physique, no blends: no support for Skin at present
|
||||
// LogError("No physique export");
|
||||
return;
|
||||
}
|
||||
|
||||
// get physique export interface
|
||||
IPhysiqueExport *phyExport=(IPhysiqueExport*) physique->GetInterface(I_PHYINTERFACE);
|
||||
if(phyExport) {
|
||||
// get ModContext interface from export interface for this INode
|
||||
IPhyContextExport *contextExport=(IPhyContextExport*) phyExport->GetContextInterface(m_Node);
|
||||
if(contextExport) {
|
||||
// export all the vertices as if they are rigid ..
|
||||
contextExport->ConvertToRigid(TRUE);
|
||||
// .. and disable blending
|
||||
contextExport->AllowBlending(FALSE);
|
||||
|
||||
// check number of vertices assigned to physique matches mesh total
|
||||
if (contextExport->GetNumberVertices()!=mesh.getNumVerts()) {
|
||||
// LogError("vtx mismatch!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i=0;i<numVerts;i++) {
|
||||
|
||||
SVertexBlend& blend=blends[i];
|
||||
|
||||
// get export interface for this vertex
|
||||
IPhyVertexExport* vertexexport=contextExport->GetVertexInterface(i);
|
||||
if (vertexexport) {
|
||||
|
||||
// get type of vertex
|
||||
int type=vertexexport->GetVertexType();
|
||||
|
||||
if (type==RIGID_TYPE) {
|
||||
IPhyRigidVertex* rigidVertex=(IPhyRigidVertex*) vertexexport;
|
||||
|
||||
// get attached bone
|
||||
INode* boneNode=rigidVertex->GetNode();
|
||||
if (boneNode) {
|
||||
// store blend
|
||||
blend.m_Bone[0]=m_Skeleton->FindBoneByNode(boneNode);
|
||||
|
||||
if (blend.m_Bone[0]==0xff) {
|
||||
// LogError("Failed to find bonenode %s (0x%p)\n",boneNode->GetName(),boneNode);
|
||||
}
|
||||
blend.m_Weight[0]=1.0f;
|
||||
}
|
||||
} else {
|
||||
// must be RIGID_BLENDED_TYPE vertex
|
||||
IPhyBlendedRigidVertex* blendedVertex=(IPhyBlendedRigidVertex*) vertexexport;
|
||||
|
||||
// get number of nodes affecting vertex; clamp to SVertexBlend::SIZE
|
||||
int numBones=blendedVertex->GetNumberNodes();
|
||||
|
||||
for (int boneindex=0;boneindex<numBones;boneindex++) {
|
||||
|
||||
// get weight of boneindex'th bone
|
||||
float boneWeight=blendedVertex->GetWeight(boneindex);
|
||||
if (boneWeight>0.001f) {
|
||||
// get boneindex'th bone
|
||||
INode* boneNode=blendedVertex->GetNode(boneindex);
|
||||
// store blend - (need to check for prior bones using same bone?)
|
||||
unsigned char bone=m_Skeleton->FindBoneByNode(boneNode);
|
||||
if (bone==0xff) {
|
||||
//LogError("Failed to find bonenode %s (0x%p)\n",boneNode->GetName(),boneNode);
|
||||
} else {
|
||||
InsertBlend(bone,boneWeight,blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (blend.m_Bone[0]==0xff) {
|
||||
// ugh - all bones must have zero weight .. use the root bone with a weight of 1?
|
||||
blend.m_Bone[0]=0;
|
||||
blend.m_Weight[0]=1;
|
||||
}
|
||||
#else
|
||||
if (blend.m_Bone[0]==0xff) {
|
||||
// ugh - all bones must have zero weight .. try again and assign all bones equal weight
|
||||
// TODO, RC 13/03/04 - log warning for this; most likely user error in enveloping/bone assignment
|
||||
for (boneindex=0;boneindex<numBones;boneindex++) {
|
||||
|
||||
float boneWeight=1.0f;
|
||||
|
||||
// get boneindex'th bone
|
||||
INode* boneNode=blendedVertex->GetNode(boneindex);
|
||||
// store blend - (need to check for prior bones using same bone?)
|
||||
unsigned char bone=m_Skeleton->FindBoneByNode(boneNode);
|
||||
if (bone==0xff) {
|
||||
//LogError("Failed to find bonenode %s (0x%p)\n",boneNode->GetName(),boneNode);
|
||||
} else {
|
||||
InsertBlend(bone,boneWeight,blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// normalise bone weights of this blend such that they sum to 1
|
||||
int j;
|
||||
float totalweight=0;
|
||||
for (j=0;j<SVertexBlend::SIZE && blend.m_Bone[j]!=0xff;j++) {
|
||||
totalweight+=blend.m_Weight[j];
|
||||
}
|
||||
for (j=0;j<SVertexBlend::SIZE && blend.m_Bone[j]!=0xff;j++) {
|
||||
blend.m_Weight[j]/=totalweight;
|
||||
}
|
||||
}
|
||||
|
||||
// done with this vertex
|
||||
contextExport->ReleaseVertexInterface(vertexexport);
|
||||
} else {
|
||||
//LogError("No vertexexport interface!\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//LogError("No contextexport interface!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static CVector3D SkinVertex(const ExpSkeleton* skeleton,const CVector3D& pos,const SVertexBlend& blend)
|
||||
{
|
||||
CVector3D result(0,0,0);
|
||||
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
|
||||
const CMatrix3D& m=skeleton->m_Bones[blend.m_Bone[i]]->m_Transform;
|
||||
CVector3D tmp=m.Transform(pos);
|
||||
result+=tmp*blend.m_Weight[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CVector3D SkinNormal(const ExpSkeleton* skeleton,const CVector3D& pos,const SVertexBlend& blend)
|
||||
{
|
||||
CMatrix3D minv,minvtrans;
|
||||
CVector3D result(0,0,0);
|
||||
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
|
||||
const CMatrix3D& m=skeleton->m_Bones[blend.m_Bone[i]]->m_Transform;
|
||||
m.GetInverse(minv);
|
||||
minv.GetTranspose(minvtrans);
|
||||
CVector3D tmp=minvtrans.Transform(pos);
|
||||
result+=tmp*blend.m_Weight[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CVector3D UnskinPoint(const ExpSkeleton* skeleton,const CVector3D& pos,const SVertexBlend& blend)
|
||||
{
|
||||
CMatrix3D m,minv;
|
||||
m.SetZero();
|
||||
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
|
||||
const CMatrix3D& m2=skeleton->m_Bones[blend.m_Bone[i]]->m_Transform;
|
||||
m+=m2*blend.m_Weight[i];
|
||||
}
|
||||
m.GetInverse(minv);
|
||||
|
||||
return minv.Transform(pos);
|
||||
}
|
||||
|
||||
static CVector3D UnskinNormal(const ExpSkeleton* skeleton,const CVector3D& nrm,const SVertexBlend& blend)
|
||||
{
|
||||
CMatrix3D m,minv,minvtrans;
|
||||
m.SetZero();
|
||||
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
|
||||
const CMatrix3D& m2=skeleton->m_Bones[blend.m_Bone[i]]->m_Transform;
|
||||
m+=m2*blend.m_Weight[i];
|
||||
}
|
||||
m.GetInverse(minv);
|
||||
minv.GetTranspose(minvtrans);
|
||||
minvtrans.GetInverse(m);
|
||||
|
||||
return m.Rotate(nrm);
|
||||
}
|
||||
|
||||
CVector3D* PMDExpMesh::BuildVertexPoints(Mesh& mesh,VBlendList& vblends)
|
||||
{
|
||||
// get transform to world space
|
||||
Matrix3 tm=m_Node->GetObjectTM(0);
|
||||
bool negScale=TMNegParity(tm);
|
||||
|
||||
int numverts=mesh.getNumVerts();
|
||||
CVector3D* transPts=new CVector3D[numverts];
|
||||
|
||||
// transform each vertex and store in output array
|
||||
for (int i=0;i<numverts;i++) {
|
||||
Point3 maxpos=mesh.getVert(i)*tm;
|
||||
transPts[i]=CVector3D(maxpos.x,maxpos.z,maxpos.y);
|
||||
}
|
||||
|
||||
if (m_Skeleton) {
|
||||
BuildVertexBlends(mesh,vblends);
|
||||
for (i=0;i<numverts;i++) {
|
||||
transPts[i]=UnskinPoint(m_Skeleton,transPts[i],vblends[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return transPts;
|
||||
}
|
||||
|
||||
VNormal* PMDExpMesh::BuildVertexNormals(Mesh& mesh,VBlendList& vblends)
|
||||
{
|
||||
// get transform to world space
|
||||
Matrix3 tm=m_Node->GetObjectTM(0);
|
||||
bool negScale=TMNegParity(tm);
|
||||
|
||||
// allocate basis vectors for dot product lighting
|
||||
VNormal* vnormals=new VNormal[mesh.getNumVerts()];
|
||||
|
||||
for (u32 i=0;i<mesh.getNumFaces();++i) {
|
||||
|
||||
// build face normal
|
||||
Point3 P0=mesh.getVert(mesh.faces[i].v[0])*tm;
|
||||
Point3 P1=mesh.getVert(mesh.faces[i].v[1])*tm;
|
||||
Point3 P2=mesh.getVert(mesh.faces[i].v[2])*tm;
|
||||
|
||||
Point3 e0=P1-P0;
|
||||
Point3 e1=P2-P0;
|
||||
Point3 faceNormal=e0^e1;
|
||||
if (negScale) faceNormal*=-1;
|
||||
|
||||
CVector3D N(faceNormal.x,faceNormal.z,faceNormal.y);
|
||||
N.Normalize();
|
||||
|
||||
// accumulate normal
|
||||
vnormals[mesh.faces[i].v[0]].add(N,mesh.faces[i].smGroup);
|
||||
vnormals[mesh.faces[i].v[1]].add(N,mesh.faces[i].smGroup);
|
||||
vnormals[mesh.faces[i].v[2]].add(N,mesh.faces[i].smGroup);
|
||||
}
|
||||
|
||||
for (i=0;i<mesh.getNumVerts();i++) {
|
||||
if (m_Skeleton) {
|
||||
VNormal* vnorm=&vnormals[i];
|
||||
vnorm->normalize();
|
||||
while (vnorm) {
|
||||
vnorm->_normal=UnskinNormal(m_Skeleton,vnorm->_normal,vblends[i]);
|
||||
vnorm=vnorm->next;
|
||||
}
|
||||
}
|
||||
vnormals[i].normalize();
|
||||
}
|
||||
|
||||
// .. and return them
|
||||
return vnormals;
|
||||
}
|
||||
|
||||
void PMDExpMesh::BuildVerticesAndFaces(Mesh& mesh,VertexList& vertices,CVector3D* vpoints,VBlendList& vblends,VNormal* vnormals,FaceList& faces)
|
||||
{
|
||||
// assume worst case and reserve enough space up front ..
|
||||
u32 numFaces=mesh.getNumFaces();
|
||||
vertices.reserve(numFaces*3);
|
||||
|
||||
// initialise outgoing face array
|
||||
faces.resize(numFaces);
|
||||
|
||||
// create a vertex tree setup to fill unique vertices into outgoing array
|
||||
VertexTree<UniqueVertexCmp> vtxtree(vertices);
|
||||
|
||||
// get transform to world space
|
||||
Matrix3 tm=m_Node->GetObjectTM(0);
|
||||
bool negScale=TMNegParity(tm);
|
||||
|
||||
// iterate through faces
|
||||
for (u32 i=0;i<numFaces;++i) {
|
||||
|
||||
for (int k=0;k<3;++k) {
|
||||
ExpVertex vtx;
|
||||
vtx.m_Index=mesh.faces[i].v[k];
|
||||
|
||||
// get position
|
||||
vtx.m_Pos=vpoints[vtx.m_Index];
|
||||
|
||||
// get UVs
|
||||
if (mesh.getNumTVerts()) {
|
||||
const TVFace& tvface=mesh.tvFace[i];
|
||||
const UVVert& uv=mesh.getTVert(tvface.t[k]);
|
||||
vtx.m_UVs[0]=uv.x;
|
||||
vtx.m_UVs[1]=uv.y;
|
||||
} else {
|
||||
vtx.m_UVs[0]=0;
|
||||
vtx.m_UVs[1]=0;
|
||||
}
|
||||
|
||||
VNormal* vnrm=vnormals[mesh.faces[i].v[k]].get(mesh.faces[i].smGroup);
|
||||
vtx.m_Normal=vnrm ? vnrm->_normal : CVector3D(0,0,0);
|
||||
faces[i].m_V[k]=vtxtree.insert(vtx);
|
||||
}
|
||||
|
||||
if (negScale) {
|
||||
int t=faces[i].m_V[1];
|
||||
faces[i].m_V[1]=faces[i].m_V[2];
|
||||
faces[i].m_V[2]=t;
|
||||
}
|
||||
|
||||
faces[i].m_MAXindex=i;
|
||||
faces[i].m_Smooth=mesh.faces[i].smGroup;
|
||||
}
|
||||
}
|
||||
|
||||
ExpMesh* PMDExpMesh::Build()
|
||||
{
|
||||
// get mesh from node
|
||||
bool delTriObj;
|
||||
TriObject* triObj=GetTriObjectFromNode(m_Node,delTriObj);
|
||||
Mesh& mesh=triObj->mesh;
|
||||
|
||||
ExpMesh* expmesh=new ExpMesh;
|
||||
|
||||
// build vertex positions/blends from given mesh data
|
||||
CVector3D* vpoints=BuildVertexPoints(mesh,expmesh->m_Blends);
|
||||
|
||||
// build vertex normals from mesh data and vertex positions
|
||||
VNormal* vnormals=BuildVertexNormals(mesh,expmesh->m_Blends);
|
||||
|
||||
// build face and vertex lists
|
||||
BuildVerticesAndFaces(mesh,expmesh->m_Vertices,vpoints,expmesh->m_Blends,vnormals,expmesh->m_Faces);
|
||||
|
||||
// clean up ..
|
||||
delete[] vnormals;
|
||||
delete[] vpoints;
|
||||
if (delTriObj) {
|
||||
triObj->DeleteThis();
|
||||
}
|
||||
|
||||
return expmesh;
|
||||
}
|
||||
|
||||
|
81
source/tools/pmdexp/ExpMesh.h
Executable file
81
source/tools/pmdexp/ExpMesh.h
Executable file
@ -0,0 +1,81 @@
|
||||
#ifndef __EXPMESH_H
|
||||
#define __EXPMESH_H
|
||||
|
||||
#include <vector>
|
||||
#include "MaxInc.h"
|
||||
#include "lib\types.h"
|
||||
#include "Vector3D.h"
|
||||
|
||||
#include "ExpVertex.h"
|
||||
|
||||
class VNormal;
|
||||
class ExpSkeleton;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ExpFace: face declaration used in building mesh geometry
|
||||
struct ExpFace {
|
||||
// vertex indices
|
||||
u32 m_V[3];
|
||||
// index of this face in max's face list (necessary, since extra
|
||||
// faces may be created in fixing t-junctions, but it's still
|
||||
// necessary to map back to MAX for eg. getting material on face)
|
||||
u32 m_MAXindex;
|
||||
// smoothing group
|
||||
u32 m_Smooth;
|
||||
// face normal
|
||||
CVector3D m_Normal;
|
||||
// face area
|
||||
float m_Area;
|
||||
};
|
||||
|
||||
// handy typedefs
|
||||
typedef std::vector<u32> IndexList;
|
||||
typedef std::vector<CVector3D> PointList;
|
||||
typedef std::vector<ExpFace> FaceList;
|
||||
typedef std::vector<ExpVertex> VertexList;
|
||||
typedef std::vector<SVertexBlend> VBlendList;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ExpMesh: mesh type used in building mesh geometry
|
||||
class ExpMesh
|
||||
{
|
||||
public:
|
||||
// list of faces in mesh
|
||||
FaceList m_Faces;
|
||||
// list of vertices used by mesh
|
||||
VertexList m_Vertices;
|
||||
// list of vertex blends used by mesh
|
||||
VBlendList m_Blends;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// PMDExpMesh: class used for building output meshes
|
||||
class PMDExpMesh
|
||||
{
|
||||
public:
|
||||
PMDExpMesh(INode* node,const ExpSkeleton* skeleton);
|
||||
|
||||
static bool IsMesh(Object* obj);
|
||||
ExpMesh* Build();
|
||||
|
||||
|
||||
private:
|
||||
// the node we're constructing the mesh from
|
||||
INode* m_Node;
|
||||
// the skeleton attached to the mesh, if any
|
||||
const ExpSkeleton* m_Skeleton;
|
||||
|
||||
// construct list of vertices and faces used by mesh
|
||||
void BuildVerticesAndFaces(Mesh& mesh,VertexList& vertices,CVector3D* vpoints,VBlendList& vblends,VNormal* vnormals,FaceList& faces);
|
||||
// build and return vertex normals, accounting for smoothing groups
|
||||
VNormal* BuildVertexNormals(Mesh& mesh,VBlendList& vblends);
|
||||
// build and return vertex blends (determined by Physique), if any
|
||||
void BuildVertexBlends(Mesh& mesh,VBlendList& blends);
|
||||
// insert the given bone/weight blend into the given blend set
|
||||
void InsertBlend(unsigned char bone,float weight,SVertexBlend& blend);
|
||||
|
||||
CVector3D* BuildVertexPoints(Mesh& mesh,VBlendList& vblends);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
44
source/tools/pmdexp/ExpProp.cpp
Executable file
44
source/tools/pmdexp/ExpProp.cpp
Executable file
@ -0,0 +1,44 @@
|
||||
#include "PSProp.h"
|
||||
#include "ExpProp.h"
|
||||
#include "ExpUtil.h"
|
||||
#include "decomp.h"
|
||||
|
||||
PMDExpProp::PMDExpProp(INode* node) : m_Node(node)
|
||||
{
|
||||
}
|
||||
|
||||
bool PMDExpProp::IsProp(Object* obj)
|
||||
{
|
||||
return obj->ClassID()==PSPROP_CLASS_ID ? true : false;
|
||||
}
|
||||
|
||||
ExpProp* PMDExpProp::Build()
|
||||
{
|
||||
ExpProp* prop=new ExpProp;
|
||||
prop->m_Name=m_Node->GetName();
|
||||
prop->m_Parent=m_Node->GetParentNode();
|
||||
|
||||
// build local transformation matrix
|
||||
INode *parent;
|
||||
Matrix3 parentTM, nodeTM, localTM;
|
||||
nodeTM = m_Node->GetNodeTM(0);
|
||||
parent = m_Node->GetParentNode();
|
||||
parentTM = parent->GetNodeTM(0);
|
||||
localTM = nodeTM*Inverse(parentTM);
|
||||
|
||||
// decompose it to get translation and rotation
|
||||
AffineParts parts;
|
||||
decomp_affine(localTM,&parts);
|
||||
|
||||
// get translation from affine parts
|
||||
MAXtoGL(parts.t);
|
||||
prop->m_Position=CVector3D(parts.t.x,parts.t.y,parts.t.z);
|
||||
|
||||
// get rotation from affine parts
|
||||
prop->m_Rotation.m_V.X=parts.q.x;
|
||||
prop->m_Rotation.m_V.Y=parts.q.z;
|
||||
prop->m_Rotation.m_V.Z=parts.q.y;
|
||||
prop->m_Rotation.m_W=parts.q.w;
|
||||
|
||||
return prop;
|
||||
}
|
38
source/tools/pmdexp/ExpProp.h
Executable file
38
source/tools/pmdexp/ExpProp.h
Executable file
@ -0,0 +1,38 @@
|
||||
#ifndef _EXPPROP_H
|
||||
#define _EXPPROP_H
|
||||
|
||||
#include "CStr.h"
|
||||
#include "Vector3D.h"
|
||||
#include "Quaternion.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ExpProp: prop object used on export
|
||||
class ExpProp
|
||||
{
|
||||
public:
|
||||
// name of prop
|
||||
CStr m_Name;
|
||||
// position relative to parent
|
||||
CVector3D m_Position;
|
||||
// rotation relative to parent
|
||||
CQuaternion m_Rotation;
|
||||
// parent node
|
||||
INode* m_Parent;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// PMDExpProp: class used for building output props
|
||||
class PMDExpProp
|
||||
{
|
||||
public:
|
||||
PMDExpProp(INode* node);
|
||||
|
||||
static bool IsProp(Object* obj);
|
||||
ExpProp* Build();
|
||||
|
||||
private:
|
||||
// the node we're constructing the prop from
|
||||
INode* m_Node;
|
||||
};
|
||||
|
||||
#endif
|
173
source/tools/pmdexp/ExpSkeleton.cpp
Executable file
173
source/tools/pmdexp/ExpSkeleton.cpp
Executable file
@ -0,0 +1,173 @@
|
||||
#include "ExpSkeleton.h"
|
||||
#include "ExpUtil.h"
|
||||
#include "bipexp.h"
|
||||
#include "decomp.h"
|
||||
#include "ModelDef.h"
|
||||
#include "SkeletonAnimDef.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// ExpSkeleton constructor
|
||||
ExpSkeleton::ExpSkeleton()
|
||||
{
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// ExpSkeleton destructor
|
||||
ExpSkeleton::~ExpSkeleton()
|
||||
{
|
||||
for (int i=0;i<m_Bones.size();i++) {
|
||||
delete m_Bones[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// IsFootprints: return true if given node is biped footprints, else false
|
||||
bool ExpSkeleton::IsFootprints(INode* node)
|
||||
{
|
||||
// get nodes transform control
|
||||
Control* c=node->GetTMController();
|
||||
|
||||
// check classID for footprints
|
||||
return (c && (c->ClassID()==FOOTPRINT_CLASS_ID)) ? true : false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// IsBone: return true if given node is a skeletal biped bone, else false
|
||||
bool ExpSkeleton::IsBone(INode* node)
|
||||
{
|
||||
// get nodes transform control
|
||||
Control* c=node->GetTMController();
|
||||
|
||||
// check classID for either a root or a child bone
|
||||
return (c && (c->ClassID()==BIPSLAVE_CONTROL_CLASS_ID || c->ClassID()==BIPBODY_CONTROL_CLASS_ID)) ? true : false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// IsSkeleton: return true if given node is the root of a skeleton, else false
|
||||
bool ExpSkeleton::IsSkeletonRoot(INode* node)
|
||||
{
|
||||
// get nodes transform control
|
||||
Control* c=node->GetTMController();
|
||||
|
||||
// check for biped root
|
||||
return (c && c->ClassID()==BIPBODY_CONTROL_CLASS_ID) ? true : false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildSkeletons: build all skeletons from given root node
|
||||
void ExpSkeleton::BuildSkeletons(INode* node,std::vector<ExpSkeleton*>& skeletons)
|
||||
{
|
||||
if (IsSkeletonRoot(node)) {
|
||||
// build skeleton from this node - Build traverses all children
|
||||
// of given node
|
||||
ExpSkeleton* skeleton=new ExpSkeleton;
|
||||
skeleton->Build(node,0);
|
||||
skeletons.push_back(skeleton);
|
||||
} else {
|
||||
// traverse into children as this object wasn't a skeleton root
|
||||
for (int i=0;i<node->NumberOfChildren();i++) {
|
||||
BuildSkeletons(node->GetChildNode(i),skeletons);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FindBoneByNode: search skeleton looking for matching bone using given node;
|
||||
// return index of given node in the skeleton, 0xff if not found
|
||||
u8 ExpSkeleton::FindBoneByNode(INode* boneNode) const
|
||||
{
|
||||
if (!boneNode) return 0xff;
|
||||
|
||||
for (int j=0;j<m_Bones.size();j++) {
|
||||
ExpBone* bone=m_Bones[j];
|
||||
if (bone->m_Node==boneNode) return j;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Build: traverse node heirachy adding bones to skeleton
|
||||
void ExpSkeleton::Build(INode* node,ExpBone* parent)
|
||||
{
|
||||
if (IsBone(node)) {
|
||||
|
||||
// build bone
|
||||
ExpBone* bone=new ExpBone;
|
||||
bone->m_Node=node;
|
||||
bone->m_Parent=parent;
|
||||
|
||||
// get bone's translation and rotation relative to root at time 0
|
||||
GetBoneTransformComponents(node,0,bone->m_Translation,bone->m_Rotation);
|
||||
|
||||
// build transform from bone to object space
|
||||
CMatrix3D m;
|
||||
bone->m_Transform.SetIdentity();
|
||||
bone->m_Transform.Rotate(bone->m_Rotation);
|
||||
bone->m_Transform.Translate(bone->m_Translation);
|
||||
|
||||
// add bone to parent's list of children and complete bone list
|
||||
if (parent) parent->m_Children.push_back(bone);
|
||||
m_Bones.push_back(bone);
|
||||
|
||||
char buf[256];
|
||||
sprintf(buf,"bonenode 0x%p (%s) added\n",node,node->GetName());
|
||||
OutputDebugString(buf);
|
||||
|
||||
for (int i=0;i<node->NumberOfChildren();i++) {
|
||||
Build(node->GetChildNode(i),bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildAnimation: build and return a complete animation for this skeleton over
|
||||
// given time range
|
||||
CSkeletonAnimDef* ExpSkeleton::BuildAnimation(TimeValue start,TimeValue end,float rate)
|
||||
{
|
||||
CSkeletonAnimDef* anim=new CSkeletonAnimDef;
|
||||
anim->m_Name="God Knows";
|
||||
anim->m_NumFrames=1+(end-start)/rate;
|
||||
anim->m_NumKeys=m_Bones.size();
|
||||
anim->m_FrameTime=rate;
|
||||
anim->m_Keys=new CSkeletonAnimDef::Key[anim->m_NumFrames*anim->m_NumKeys];
|
||||
|
||||
u32 counter=0;
|
||||
TimeValue t=start;
|
||||
while (t<end) {
|
||||
|
||||
for (int i=0;i<m_Bones.size();i++) {
|
||||
CSkeletonAnimDef::Key& key=anim->m_Keys[counter++];
|
||||
GetBoneTransformComponents(m_Bones[i]->m_Node,t,key.m_Translation,key.m_Rotation);
|
||||
}
|
||||
t+=rate;
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// GetBoneTransformComponents: calculate the translation and rotation
|
||||
// values of the given bone, relative to the root bone, at the given time
|
||||
void ExpSkeleton::GetBoneTransformComponents(INode* node,TimeValue t,
|
||||
CVector3D& trans,CQuaternion& rot)
|
||||
{
|
||||
Matrix3 nodeTM=node->GetNodeTM(t);
|
||||
nodeTM.NoScale();
|
||||
|
||||
// decompose it to get translation and rotation
|
||||
AffineParts parts;
|
||||
decomp_affine(nodeTM,&parts);
|
||||
|
||||
// get translation from affine parts
|
||||
MAXtoGL(parts.t);
|
||||
trans=CVector3D(parts.t.x,parts.t.y,parts.t.z);
|
||||
|
||||
// get rotation from affine parts
|
||||
rot.m_V.X=parts.q.x;
|
||||
rot.m_V.Y=parts.q.z;
|
||||
rot.m_V.Z=parts.q.y;
|
||||
rot.m_W=parts.q.w;
|
||||
}
|
74
source/tools/pmdexp/ExpSkeleton.h
Executable file
74
source/tools/pmdexp/ExpSkeleton.h
Executable file
@ -0,0 +1,74 @@
|
||||
#ifndef __EXPSKELETON_H
|
||||
#define __EXPSKELETON_H
|
||||
|
||||
#include <vector>
|
||||
#include "MaxInc.h"
|
||||
#include "res/res.h"
|
||||
#include "Matrix3D.h"
|
||||
#include "Vector3D.h"
|
||||
#include "Quaternion.h"
|
||||
|
||||
class CSkeleton;
|
||||
class CSkeletonAnimDef;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ExpBone: bone type used during export
|
||||
class ExpBone
|
||||
{
|
||||
public:
|
||||
// MAX bone node
|
||||
INode* m_Node;
|
||||
// transform from object to this bones space at time 0
|
||||
CMatrix3D m_Transform;
|
||||
// translation relative to root bone at time 0
|
||||
CVector3D m_Translation;
|
||||
// rotation relative to root bone at time 0
|
||||
CQuaternion m_Rotation;
|
||||
// parent of this bone; 0 for root bone
|
||||
ExpBone* m_Parent;
|
||||
// children of this bone
|
||||
std::vector<ExpBone*> m_Children;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ExpSkeleton:
|
||||
class ExpSkeleton
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// static methods for skeleton creation, destruction and type queries:
|
||||
|
||||
// build all skeletons from given root node
|
||||
static void BuildSkeletons(INode* node,std::vector<ExpSkeleton*>& skeletons);
|
||||
// return true if given node is a skeletal biped bone, else false
|
||||
static bool IsBone(INode* node);
|
||||
// return true if given node is biped footprints, else false
|
||||
static bool IsFootprints(INode* node);
|
||||
// return true if given node is the root of a skeleton, else false
|
||||
static bool IsSkeletonRoot(INode* node);
|
||||
|
||||
public:
|
||||
// constructor, destructor
|
||||
ExpSkeleton();
|
||||
~ExpSkeleton();
|
||||
|
||||
// search skeleton looking for matching bone using given node;
|
||||
// return index of given node in the skeleton, 0xff if not found
|
||||
u8 FindBoneByNode(INode* boneNode) const;
|
||||
|
||||
// build and return a complete animation for this skeleton over given time range
|
||||
CSkeletonAnimDef* BuildAnimation(TimeValue start,TimeValue end,float rate);
|
||||
|
||||
private:
|
||||
// traverse node heirachy adding bones to skeleton
|
||||
void Build(INode* node,ExpBone* parent=0);
|
||||
// calculate the translation and rotation values of the given bone,
|
||||
// relative to the root bone, at the given time
|
||||
void GetBoneTransformComponents(INode* node,TimeValue t,CVector3D& trans,CQuaternion& rot);
|
||||
|
||||
public:
|
||||
// list of bones in skeleton; root bone is m_Bones[0]
|
||||
std::vector<ExpBone*> m_Bones;
|
||||
};
|
||||
|
||||
#endif
|
12
source/tools/pmdexp/ExpUtil.cpp
Executable file
12
source/tools/pmdexp/ExpUtil.cpp
Executable file
@ -0,0 +1,12 @@
|
||||
#include "MaxInc.h"
|
||||
#include "ExpUtil.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// MAXtoGL: convert point from MAX's coordinate system to that used
|
||||
// within the engine
|
||||
void MAXtoGL(Point3 &pnt)
|
||||
{
|
||||
float tmp = pnt.y;
|
||||
pnt.y = pnt.z;
|
||||
pnt.z = tmp;
|
||||
}
|
9
source/tools/pmdexp/ExpUtil.h
Executable file
9
source/tools/pmdexp/ExpUtil.h
Executable file
@ -0,0 +1,9 @@
|
||||
#ifndef _EXPUTIL_H
|
||||
#define _EXPUTIL_H
|
||||
|
||||
class Point3;
|
||||
|
||||
extern void MAXtoGL(Point3 &pnt);
|
||||
|
||||
|
||||
#endif
|
22
source/tools/pmdexp/ExpVertex.h
Executable file
22
source/tools/pmdexp/ExpVertex.h
Executable file
@ -0,0 +1,22 @@
|
||||
#ifndef __EXPVERTEX_H
|
||||
#define __EXPVERTEX_H
|
||||
|
||||
#include "lib\types.h"
|
||||
#include "Vector3D.h"
|
||||
#include "ModelDef.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ExpVertex: vertex type used in building mesh geometry
|
||||
struct ExpVertex {
|
||||
// index into original meshes point list
|
||||
unsigned int m_Index;
|
||||
// object space position
|
||||
CVector3D m_Pos;
|
||||
// object space normal
|
||||
CVector3D m_Normal;
|
||||
// uv coordinates
|
||||
float m_UVs[2];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
15
source/tools/pmdexp/MaxInc.h
Executable file
15
source/tools/pmdexp/MaxInc.h
Executable file
@ -0,0 +1,15 @@
|
||||
#ifndef _MAXINC_H
|
||||
#define _MAXINC_H
|
||||
|
||||
#define timeval MAX_timeval
|
||||
//#define ERROR MAX_ERROR
|
||||
|
||||
#include "Max.h"
|
||||
#include "iparamb2.h"
|
||||
#include "iparamm2.h"
|
||||
#include "istdplug.h"
|
||||
#include "resource.h"
|
||||
|
||||
#undef timeval
|
||||
|
||||
#endif
|
276
source/tools/pmdexp/PMDExp.cpp
Executable file
276
source/tools/pmdexp/PMDExp.cpp
Executable file
@ -0,0 +1,276 @@
|
||||
#include "PMDExp.h"
|
||||
#include "ExpMesh.h"
|
||||
#include "ExpProp.h"
|
||||
#include "ExpSkeleton.h"
|
||||
|
||||
#undef PI
|
||||
#include "ModelDef.h"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// PMDExp constructor
|
||||
PMDExp::PMDExp()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// PMDExp destructor
|
||||
PMDExp::~PMDExp()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ExtCount: return the number of file name extensions supported
|
||||
// by the plug-in.
|
||||
int PMDExp::ExtCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Ext: return the ith file name extension
|
||||
const TCHAR* PMDExp::Ext(int n)
|
||||
{
|
||||
return _T("pmd");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LongDesc: return long ASCII description
|
||||
const TCHAR* PMDExp::LongDesc()
|
||||
{
|
||||
return _T("Prometheus Model Data");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ShortDesc: return short ASCII description
|
||||
const TCHAR* PMDExp::ShortDesc()
|
||||
{
|
||||
return _T("Prometheus Model");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// AuthorName: return author name
|
||||
const TCHAR* PMDExp::AuthorName()
|
||||
{
|
||||
return _T("Rich Cross");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CopyrightMessage: return copyright message
|
||||
const TCHAR* PMDExp::CopyrightMessage()
|
||||
{
|
||||
return _T("(c) Wildfire Games 2004");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// OtherMessage1: return some other message (or don't, in this case)
|
||||
const TCHAR* PMDExp::OtherMessage1()
|
||||
{
|
||||
return _T("");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// OtherMessage2: return some other message (or don't, in this case)
|
||||
const TCHAR* PMDExp::OtherMessage2()
|
||||
{
|
||||
return _T("");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Version: return version number * 100 (i.e. v3.01 = 301)
|
||||
unsigned int PMDExp::Version()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ShowAbout: show an about box (or don't, in this case)
|
||||
void PMDExp::ShowAbout(HWND hWnd)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// SupportsOptions: return true for each option supported by each
|
||||
// extension the exporter supports
|
||||
BOOL PMDExp::SupportsOptions(int ext, DWORD options)
|
||||
{
|
||||
// return TRUE to indicate export selected supported
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// DoExport: actually perform the export to the given filename
|
||||
int PMDExp::DoExport(const TCHAR *name,ExpInterface *ei,Interface *ip, BOOL suppressPrompts, DWORD options)
|
||||
{
|
||||
// result of the export: assume we'll fail somewhere along the way
|
||||
BOOL res=FALSE;
|
||||
|
||||
// save off the interface ptr
|
||||
m_IP=ip;
|
||||
|
||||
// save off the options
|
||||
m_Options=options;
|
||||
|
||||
// build any skeletons in MAXs heirarchy before going any further
|
||||
std::vector<ExpSkeleton*> skeletons;
|
||||
ExpSkeleton::BuildSkeletons(m_IP->GetRootNode(),skeletons);
|
||||
if (skeletons.size()>1) {
|
||||
MessageBox(GetActiveWindow(),"Found more than one skeleton in scene","Error",MB_OK);
|
||||
} else {
|
||||
// build list of meshes and props from nodes in MAXs heirarchy
|
||||
std::vector<ExpMesh*> meshes;
|
||||
std::vector<ExpProp*> props;
|
||||
ExpSkeleton* skeleton=skeletons.size()>0 ? skeletons[0] : 0;
|
||||
BuildOutputList(m_IP->GetRootNode(),skeleton,meshes,props);
|
||||
if (meshes.size()==0) {
|
||||
// hmm .. nothing there?
|
||||
MessageBox(GetActiveWindow(),"Failed to find any meshes to export","Error",MB_OK);
|
||||
} else {
|
||||
// build a model from the node tree and any skeleton in scene
|
||||
CModelDef* model=BuildModel(meshes,props,skeleton);
|
||||
if (!model) {
|
||||
MessageBox(GetActiveWindow(),"Failed to create model","Error",MB_OK);
|
||||
} else {
|
||||
try {
|
||||
CModelDef::Save(name,model);
|
||||
res=TRUE;
|
||||
} catch (...) {
|
||||
res=FALSE;
|
||||
}
|
||||
|
||||
MessageBox(GetActiveWindow(),res ? "Export Complete" : "Error saving model",res ? "Info" : "Error",MB_OK);
|
||||
}
|
||||
|
||||
for (int i=0;i<meshes.size();i++) {
|
||||
delete meshes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up
|
||||
for (int i=0;i<skeletons.size();i++) {
|
||||
delete skeletons[i];
|
||||
}
|
||||
|
||||
// return result
|
||||
return res;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildOutputList: traverse heirarchy collecting meshes/props
|
||||
void PMDExp::BuildOutputList(INode* node,ExpSkeleton* skeleton,
|
||||
std::vector<ExpMesh*>& meshes,std::vector<ExpProp*>& props)
|
||||
{
|
||||
// get object attached to node
|
||||
ObjectState os=node->EvalWorldState(0);
|
||||
if (os.obj) {
|
||||
// handle export selected
|
||||
if ((m_Options & SCENE_EXPORT_SELECTED) && !node->Selected()) {
|
||||
// ignore ..
|
||||
} else {
|
||||
// mesh?
|
||||
bool isMesh=true; // assume so
|
||||
if (isMesh && !PMDExpMesh::IsMesh(os.obj)) isMesh=false;
|
||||
if (isMesh && ExpSkeleton::IsBone(node)) isMesh=false;
|
||||
if (isMesh && ExpSkeleton::IsFootprints(node)) isMesh=false;
|
||||
|
||||
if (isMesh) {
|
||||
PMDExpMesh meshbuilder(node,skeleton);
|
||||
ExpMesh* mesh=meshbuilder.Build();
|
||||
meshes.push_back(mesh);
|
||||
} else {
|
||||
// not a mesh - a prop?
|
||||
if (PMDExpProp::IsProp(os.obj)) {
|
||||
PMDExpProp propbuilder(node);
|
||||
ExpProp* prop=propbuilder.Build();
|
||||
props.push_back(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// traverse into children
|
||||
for (int i=0;i<node->NumberOfChildren();i++) {
|
||||
BuildOutputList(node->GetChildNode(i),skeleton,meshes,props);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildModel: weld together given list of meshes, and attach props; return
|
||||
// result as a CModelDef
|
||||
CModelDef* PMDExp::BuildModel(std::vector<ExpMesh*>& meshes,std::vector<ExpProp*>& props,
|
||||
ExpSkeleton* skeleton)
|
||||
{
|
||||
SVertexBlend nullBlend;
|
||||
memset(nullBlend.m_Bone,0xff,sizeof(nullBlend.m_Bone));
|
||||
memset(nullBlend.m_Weight,0,sizeof(nullBlend.m_Weight));
|
||||
|
||||
// sum up total number of vertices and faces
|
||||
u32 totalVerts=0,totalFaces=0;
|
||||
for (int i=0;i<meshes.size();i++) {
|
||||
totalVerts+=meshes[i]->m_Vertices.size();
|
||||
totalFaces+=meshes[i]->m_Faces.size();
|
||||
}
|
||||
|
||||
CModelDef* mdl=new CModelDef;
|
||||
|
||||
mdl->m_NumVertices=totalVerts;
|
||||
mdl->m_pVertices=new SModelVertex[totalVerts];
|
||||
|
||||
mdl->m_NumFaces=totalFaces;
|
||||
mdl->m_pFaces=new SModelFace[totalFaces];
|
||||
|
||||
mdl->m_NumBones=skeleton ? skeleton->m_Bones.size() : 0;
|
||||
mdl->m_Bones=new CBoneState[mdl->m_NumBones];
|
||||
|
||||
// build vertices
|
||||
int vcount=0;
|
||||
for (i=0;i<meshes.size();i++) {
|
||||
const VertexList& verts=meshes[i]->m_Vertices;
|
||||
const VBlendList& blends=meshes[i]->m_Blends;
|
||||
for (int j=0;j<verts.size();j++) {
|
||||
mdl->m_pVertices[vcount].m_Coords=verts[j].m_Pos;
|
||||
mdl->m_pVertices[vcount].m_Norm=verts[j].m_Normal;
|
||||
mdl->m_pVertices[vcount].m_U=verts[j].m_UVs[0];
|
||||
mdl->m_pVertices[vcount].m_V=verts[j].m_UVs[1];
|
||||
mdl->m_pVertices[vcount].m_Blend=blends.size()>0 ? blends[verts[j].m_Index] : nullBlend;
|
||||
vcount++;
|
||||
}
|
||||
}
|
||||
|
||||
// build faces
|
||||
int fcount=0;
|
||||
int offset=0;
|
||||
for (i=0;i<meshes.size();i++) {
|
||||
const FaceList& faces=meshes[i]->m_Faces;
|
||||
for (int j=0;j<faces.size();j++) {
|
||||
mdl->m_pFaces[fcount].m_Verts[0]=faces[j].m_V[0]+offset;
|
||||
mdl->m_pFaces[fcount].m_Verts[1]=faces[j].m_V[1]+offset;
|
||||
mdl->m_pFaces[fcount].m_Verts[2]=faces[j].m_V[2]+offset;
|
||||
fcount++;
|
||||
}
|
||||
offset+=meshes[i]->m_Vertices.size();
|
||||
}
|
||||
|
||||
// build bones
|
||||
for (i=0;i<mdl->m_NumBones;i++) {
|
||||
mdl->m_Bones[i].m_Translation=skeleton->m_Bones[i]->m_Translation;
|
||||
mdl->m_Bones[i].m_Rotation=skeleton->m_Bones[i]->m_Rotation;
|
||||
}
|
||||
|
||||
// attach props
|
||||
mdl->m_NumPropPoints=props.size();
|
||||
mdl->m_PropPoints=new SPropPoint[mdl->m_NumPropPoints];
|
||||
for (i=0;i<mdl->m_NumPropPoints;i++) {
|
||||
mdl->m_PropPoints[i].m_Name=props[i]->m_Name;
|
||||
mdl->m_PropPoints[i].m_Position=props[i]->m_Position;
|
||||
mdl->m_PropPoints[i].m_Rotation=props[i]->m_Rotation;
|
||||
mdl->m_PropPoints[i].m_BoneIndex=skeleton ? skeleton->FindBoneByNode(props[i]->m_Parent) : 0xff;
|
||||
}
|
||||
|
||||
// all done
|
||||
return mdl;
|
||||
}
|
||||
|
||||
|
144
source/tools/pmdexp/PMDExp.dsw
Executable file
144
source/tools/pmdexp/PMDExp.dsw
Executable file
@ -0,0 +1,144 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "DyMaxLight"=..\dymaxlight\DyMaxLight.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/tools/max/dymaxlight", ZVAAAAAA
|
||||
..\dymaxlight
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "arbvp_c"=..\..\arbvp_c\arbvp_c.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/tools/arbvp_c", LRBAAAAA
|
||||
..\..\arbvp_c
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "common"=..\..\..\common\common.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/common", LVAAAAAA
|
||||
..\..\..\common
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "dy"=..\..\..\dy\dy.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/dy", NVAAAAAA
|
||||
..\..\..\dy
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "dyexp"=.\dyexp.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/tools/max/dyexp", YVAAAAAA
|
||||
.
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name dy
|
||||
End Project Dependency
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name DyMaxLight
|
||||
End Project Dependency
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name common
|
||||
End Project Dependency
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name filesys
|
||||
End Project Dependency
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name luacore
|
||||
End Project Dependency
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "filesys"=..\..\..\filesys\filesys.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/filesys", RSBAAAAA
|
||||
..\..\..\filesys
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "luacore"=..\..\..\lua\luacore\luacore.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/dev/src/lua/luacore", RVBAAAAA
|
||||
..\..\..\lua\luacore
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
53
source/tools/pmdexp/PMDExp.h
Executable file
53
source/tools/pmdexp/PMDExp.h
Executable file
@ -0,0 +1,53 @@
|
||||
#ifndef __PMDEXP_H
|
||||
#define __PMDEXP_H
|
||||
|
||||
// necessary includes
|
||||
#include "MaxInc.h"
|
||||
#include <vector>
|
||||
|
||||
// necessary declarations
|
||||
class ExpMesh;
|
||||
class ExpProp;
|
||||
class ExpSkeleton;
|
||||
class CModelDef;
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// PMDExp:
|
||||
class PMDExp : public SceneExport
|
||||
{
|
||||
public:
|
||||
PMDExp();
|
||||
~PMDExp();
|
||||
|
||||
// standard stuff that Max requires
|
||||
int ExtCount(); // Number of extensions supported
|
||||
const TCHAR* Ext(int n); // Extension #n (i.e. "3DS")
|
||||
const TCHAR* LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File")
|
||||
const TCHAR* ShortDesc(); // Short ASCII description (i.e. "3D Studio")
|
||||
const TCHAR* AuthorName(); // ASCII Author name
|
||||
const TCHAR* CopyrightMessage(); // ASCII Copyright message
|
||||
const TCHAR* OtherMessage1(); // Other message #1
|
||||
const TCHAR* OtherMessage2(); // Other message #2
|
||||
unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301)
|
||||
void ShowAbout(HWND hWnd); // Show DLL's "About..." box
|
||||
|
||||
BOOL SupportsOptions(int ext, DWORD options);
|
||||
int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
|
||||
|
||||
private:
|
||||
// BuildOutputList: traverse heirarchy collecting meshes/props
|
||||
void BuildOutputList(INode* node,ExpSkeleton* skeleton,
|
||||
std::vector<ExpMesh*>& meshes,std::vector<ExpProp*>& props);
|
||||
// WeldMeshes: weld together given list of meshes; return result as a CModelDef
|
||||
CModelDef* BuildModel(std::vector<ExpMesh*>& meshes,std::vector<ExpProp*>& props,ExpSkeleton* skeleton);
|
||||
|
||||
// pointer to MAXs Interface object
|
||||
Interface* m_IP;
|
||||
// handle to the export parameters window
|
||||
HWND m_Params;
|
||||
// export options
|
||||
DWORD m_Options;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
BIN
source/tools/pmdexp/PMDExp.opt
Executable file
BIN
source/tools/pmdexp/PMDExp.opt
Executable file
Binary file not shown.
1
source/tools/pmdexp/PMDExp.pchs
Executable file
1
source/tools/pmdexp/PMDExp.pchs
Executable file
@ -0,0 +1 @@
|
||||
35
|
168
source/tools/pmdexp/PMDExp.rc
Executable file
168
source/tools/pmdexp/PMDExp.rc
Executable file
@ -0,0 +1,168 @@
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_NEW_POINTPARAM DIALOG DISCARDABLE 0, 0, 108, 110
|
||||
STYLE WS_CHILD | WS_VISIBLE
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
CONTROL "Axis Tripod",IDC_POINT_AXIS,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,12,24,53,10
|
||||
CONTROL "",IDC_POINT_SIZE,"CustEdit",WS_TABSTOP,49,63,28,10
|
||||
RTEXT "Size:",IDC_STATIC,19,63,27,8
|
||||
CONTROL "",IDC_POINT_SIZESPIN,"SpinnerControl",0x0,78,63,7,10
|
||||
CONTROL "Center Marker",IDC_POINT_MARKER,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,12,12,63,10
|
||||
CONTROL "Cross",IDC_POINT_CROSS,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,12,36,33,10
|
||||
CONTROL "Box",IDC_POINT_BOX,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,12,48,33,10
|
||||
GROUPBOX "Display:",IDC_STATIC,5,1,98,105
|
||||
CONTROL "Constant Screen Size",IDC_POINT_SCREENSIZE,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,12,78,84,10
|
||||
CONTROL "Draw On Top",IDC_POINT_DRAWONTOP,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,12,91,59,10
|
||||
END
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,0,0,0
|
||||
PRODUCTVERSION 5,0,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "TECH: \0"
|
||||
VALUE "CompanyName", "\0"
|
||||
VALUE "FileDescription", "PMD File Export\0"
|
||||
VALUE "FileVersion", "5.0.0.0\0"
|
||||
VALUE "InternalName", "PMDExp\0"
|
||||
VALUE "LegalCopyright", "\0"
|
||||
VALUE "LegalTrademarks", "3D Studio MAX, Biped, Character Studio, Heidi, Kinetix and Physique are registered trademarks and 3ds max, combustion, Discreet, DWG Unplugged, DXF, FLI and FLC are trademarks of Autodesk, Inc.\0"
|
||||
VALUE "OriginalFilename", "PMDExp.dle\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "3ds max\0"
|
||||
VALUE "ProductVersion", "5.0.0.0\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
IDS_LIBDESCRIPTION "Prometheus Export Tools"
|
||||
IDS_CATEGORY "Exporters"
|
||||
IDS_PMD_CLASS_NAME "PMDExp"
|
||||
IDS_PARAMS "Parameters"
|
||||
IDS_PSA_CLASS_NAME "PSAExp"
|
||||
IDS_DB_POINT "PSProp"
|
||||
IDS_DB_POINTHELPER_CLASS "Prop Point"
|
||||
IDS_DB_POINT_CLASS "Prop Point"
|
||||
END
|
||||
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
IDS_POINT_PARAMS "Parameters"
|
||||
IDS_POINT_SIZE "Size"
|
||||
IDS_POINT_CENTERMARKER "Center Marker"
|
||||
IDS_POINT_AXISTRIPOD "Axis Tripod"
|
||||
IDS_POINT_CROSS "Cross"
|
||||
IDS_POINT_BOX "Box"
|
||||
IDS_POINT_SCREENSIZE "Constant Screen Size"
|
||||
IDS_POINT_HELPER_NAME "PSProp"
|
||||
IDS_POINT_DRAWONTOP "Draw On Top"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
8
source/tools/pmdexp/PMDExp5.def
Executable file
8
source/tools/pmdexp/PMDExp5.def
Executable file
@ -0,0 +1,8 @@
|
||||
LIBRARY pmdexp5
|
||||
EXPORTS
|
||||
LibDescription @1
|
||||
LibNumberClasses @2
|
||||
LibClassDesc @3
|
||||
LibVersion @4
|
||||
SECTIONS
|
||||
.data READ WRITE
|
189
source/tools/pmdexp/PMDExp5.dsp
Executable file
189
source/tools/pmdexp/PMDExp5.dsp
Executable file
@ -0,0 +1,189 @@
|
||||
# Microsoft Developer Studio Project File - Name="PMDExp5" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=PMDExp5 - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "PMDExp5.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "PMDExp5.mak" CFG="PMDExp5 - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "PMDExp5 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "PMDExp5 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""$/dev/dylan/plugins/PMDExp", YVAAAAAA"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "PMDExp5 - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "ReleaseR5"
|
||||
# PROP Intermediate_Dir "ReleaseR5"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
|
||||
# ADD CPP /nologo /G5 /MT /W3 /GX /I "d:\3dsmax5\maxsdk\include" /I "..\\" /I "..\lib" /I "..\ps" /I "..\simulation" /I "..\terrain" /D "NDEBUG" /D "_USRDLL" /D "_3DSMAX_" /D "_WINDOWS" /D "WIN32" /D "_NO_WINMAIN_" /FD /LD /c
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x809 /d "NDEBUG"
|
||||
# ADD RSC /l 0x809 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
|
||||
# ADD LINK32 pslib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib /nologo /base:"0x50810000" /subsystem:windows /dll /machine:I386 /out:"d:\3dsmax5\plugins\pmdexp5.dle" /libpath:"d:\3dsmax5\maxsdk\lib" /libpath:"..\libs" /release
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ELSEIF "$(CFG)" == "PMDExp5 - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "DebugR5"
|
||||
# PROP Intermediate_Dir "DebugR5"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /G6 /MTd /W3 /GX /ZI /Od /I "d:\3dsmax5\maxsdk\include" /I "..\\" /I "..\lib" /I "..\ps" /I "..\simulation" /I "..\terrain" /D "_DEBUG" /D "_USRDLL" /D "_3DSMAX_" /D "_WINDOWS" /D "WIN32" /D "_NO_WINMAIN_" /FD /LD /c
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x809 /d "_DEBUG"
|
||||
# ADD RSC /l 0x809 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 pslib_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib /nologo /base:"0x50810000" /subsystem:windows /dll /map /debug /machine:I386 /out:"d:\3dsmax5\plugins\pmdexp5.dle" /pdbtype:sept /libpath:"d:\3dsmax5\maxsdk\lib" /libpath:"..\libs"
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "PMDExp5 - Win32 Release"
|
||||
# Name "PMDExp5 - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\DllEntry.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpMesh.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpSkeleton.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpUtil.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp.rc
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp5.def
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PSAExp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PSProp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\VNormal.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpMesh.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpSkeleton.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpUtil.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpVertex.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PSAExp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PSProp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\resource.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\Tree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\VertexTree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\VNormal.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
8
source/tools/pmdexp/PMDExp6.def
Executable file
8
source/tools/pmdexp/PMDExp6.def
Executable file
@ -0,0 +1,8 @@
|
||||
LIBRARY pmdexp6
|
||||
EXPORTS
|
||||
LibDescription @1
|
||||
LibNumberClasses @2
|
||||
LibClassDesc @3
|
||||
LibVersion @4
|
||||
SECTIONS
|
||||
.data READ WRITE
|
193
source/tools/pmdexp/PMDExp6.dsp
Executable file
193
source/tools/pmdexp/PMDExp6.dsp
Executable file
@ -0,0 +1,193 @@
|
||||
# Microsoft Developer Studio Project File - Name="PMDExp6" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=PMDExp6 - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "PMDExp6.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "PMDExp6.mak" CFG="PMDExp6 - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "PMDExp6 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "PMDExp6 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""$/dev/dylan/plugins/PMDExp", YVAAAAAA"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "PMDExp6 - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "ReleaseR6"
|
||||
# PROP Intermediate_Dir "ReleaseR6"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
|
||||
# ADD CPP /nologo /G5 /MT /W3 /GX /I "d:\3dsmax6\maxsdk\include" /I "..\\" /I "..\lib" /I "..\ps" /I "..\simulation" /I "..\terrain" /D "NDEBUG" /D "_USRDLL" /D "_3DSMAX_" /D "_WINDOWS" /D "WIN32" /D "_NO_WINMAIN_" /FD /LD /c
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x809 /d "NDEBUG"
|
||||
# ADD RSC /l 0x809 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
|
||||
# ADD LINK32 pslib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib /nologo /base:"0x50810000" /entry:"" /subsystem:windows /dll /machine:I386 /out:"d:\3dsmax6\plugins\pmdexp6.dle" /libpath:"d:\3dsmax6\maxsdk\lib" /libpath:"..\libs" /release
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ELSEIF "$(CFG)" == "PMDExp6 - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "DebugR6"
|
||||
# PROP Intermediate_Dir "DebugR6"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /ZI /Od /I "d:\3dsmax6\maxsdk\include" /I "..\\" /I "..\lib" /I "..\ps" /I "..\simulation" /I "..\terrain" /D "_DEBUG" /D "_USRDLL" /D "_3DSMAX_" /D "_WINDOWS" /D "WIN32" /D "_NO_WINMAIN_" /FD /LD /c
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x809 /d "_DEBUG"
|
||||
# ADD RSC /l 0x809 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 pslib_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib /nologo /base:"0x50810000" /subsystem:windows /dll /debug /machine:I386 /out:"d:\3dsmax6\plugins\pmdexp6.dle" /pdbtype:sept /libpath:"d:\3dsmax6\maxsdk\lib" /libpath:"..\libs"
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "PMDExp6 - Win32 Release"
|
||||
# Name "PMDExp6 - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\DllEntry.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpMesh.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpProp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpSkeleton.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpUtil.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp.rc
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp6.def
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PSAExp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PSProp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\VNormal.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpMesh.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpProp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpSkeleton.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpUtil.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ExpVertex.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\MaxInc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\PMDExp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\resource.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\Tree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\VertexTree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\VNormal.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
143
source/tools/pmdexp/PSAExp.cpp
Executable file
143
source/tools/pmdexp/PSAExp.cpp
Executable file
@ -0,0 +1,143 @@
|
||||
#include "PSAExp.h"
|
||||
#include "ExpMesh.h"
|
||||
#include "ExpSkeleton.h"
|
||||
|
||||
#undef PI
|
||||
#include "ModelDef.h"
|
||||
#include "SkeletonAnim.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// PSAExp constructor
|
||||
PSAExp::PSAExp()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// PSAExp destructor
|
||||
PSAExp::~PSAExp()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ExtCount: return the number of file name extensions supported
|
||||
// by the plug-in.
|
||||
int PSAExp::ExtCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Ext: return the ith file name extension
|
||||
const TCHAR* PSAExp::Ext(int n)
|
||||
{
|
||||
return _T("psa");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LongDesc: return long ASCII description
|
||||
const TCHAR* PSAExp::LongDesc()
|
||||
{
|
||||
return _T("Prometheus Skeleton Anim");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ShortDesc: return short ASCII description
|
||||
const TCHAR* PSAExp::ShortDesc()
|
||||
{
|
||||
return _T("Prometheus Anim");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// AuthorName: return author name
|
||||
const TCHAR* PSAExp::AuthorName()
|
||||
{
|
||||
return _T("Rich Cross");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CopyrightMessage: return copyright message
|
||||
const TCHAR* PSAExp::CopyrightMessage()
|
||||
{
|
||||
return _T("(c) Wildfire Games 2004");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// OtherMessage1: return some other message (or don't, in this case)
|
||||
const TCHAR* PSAExp::OtherMessage1()
|
||||
{
|
||||
return _T("");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// OtherMessage2: return some other message (or don't, in this case)
|
||||
const TCHAR* PSAExp::OtherMessage2()
|
||||
{
|
||||
return _T("");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Version: return version number * 100 (i.e. v3.01 = 301)
|
||||
unsigned int PSAExp::Version()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ShowAbout: show an about box (or don't, in this case)
|
||||
void PSAExp::ShowAbout(HWND hWnd)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// SupportsOptions: return true for each option supported by each
|
||||
// extension the exporter supports
|
||||
BOOL PSAExp::SupportsOptions(int ext, DWORD options)
|
||||
{
|
||||
// return TRUE to indicate export selected supported
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// DoExport: actually perform the export to the given filename
|
||||
int PSAExp::DoExport(const TCHAR *name,ExpInterface *ei,Interface *ip, BOOL suppressPrompts, DWORD options)
|
||||
{
|
||||
// result of the export: assume we'll fail somewhere along the way
|
||||
BOOL res=FALSE;
|
||||
|
||||
// save off the interface ptr
|
||||
m_IP=ip;
|
||||
|
||||
// save off the options
|
||||
m_Options=options;
|
||||
|
||||
// build any skeletons in MAXs heirarchy before going any further
|
||||
std::vector<ExpSkeleton*> skeletons;
|
||||
ExpSkeleton::BuildSkeletons(m_IP->GetRootNode(),skeletons);
|
||||
if (skeletons.size()>1) {
|
||||
MessageBox(GetActiveWindow(),"Found more than one skeleton in scene","Error",MB_OK);
|
||||
} else if (skeletons.size()==0) {
|
||||
MessageBox(GetActiveWindow(),"No skeletons found in scene","Error",MB_OK);
|
||||
} else {
|
||||
// build an animation from first skeleton
|
||||
ExpSkeleton* skeleton=skeletons[0];
|
||||
Interval interval=m_IP->GetAnimRange();
|
||||
CSkeletonAnimDef* anim=skeleton->BuildAnimation(interval.Start(),interval.End(),1000/30);
|
||||
try {
|
||||
CSkeletonAnimDef::Save(name,anim);
|
||||
res=TRUE;
|
||||
} catch (...) {
|
||||
res=FALSE;
|
||||
}
|
||||
|
||||
MessageBox(GetActiveWindow(),res ? "Export Complete" : "Error saving model",res ? "Info" : "Error",MB_OK);
|
||||
}
|
||||
|
||||
// clean up
|
||||
for (int i=0;i<skeletons.size();i++) {
|
||||
delete skeletons[i];
|
||||
}
|
||||
|
||||
// return result
|
||||
return res;
|
||||
}
|
||||
|
49
source/tools/pmdexp/PSAExp.h
Executable file
49
source/tools/pmdexp/PSAExp.h
Executable file
@ -0,0 +1,49 @@
|
||||
#ifndef __PSAEXP_H
|
||||
#define __PSAEXP_H
|
||||
|
||||
// necessary includes
|
||||
#include "MaxInc.h"
|
||||
#include <vector>
|
||||
|
||||
// necessary declarations
|
||||
class ExpMesh;
|
||||
class ExpSkeleton;
|
||||
class CModelDef;
|
||||
class CSkeleton;
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// PSAExp:
|
||||
class PSAExp : public SceneExport
|
||||
{
|
||||
public:
|
||||
PSAExp();
|
||||
~PSAExp();
|
||||
|
||||
// standard stuff that Max requires
|
||||
int ExtCount(); // Number of extensions supported
|
||||
const TCHAR* Ext(int n); // Extension #n (i.e. "3DS")
|
||||
const TCHAR* LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File")
|
||||
const TCHAR* ShortDesc(); // Short ASCII description (i.e. "3D Studio")
|
||||
const TCHAR* AuthorName(); // ASCII Author name
|
||||
const TCHAR* CopyrightMessage(); // ASCII Copyright message
|
||||
const TCHAR* OtherMessage1(); // Other message #1
|
||||
const TCHAR* OtherMessage2(); // Other message #2
|
||||
unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301)
|
||||
void ShowAbout(HWND hWnd); // Show DLL's "About..." box
|
||||
|
||||
BOOL SupportsOptions(int ext, DWORD options);
|
||||
int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
|
||||
|
||||
private:
|
||||
// pointer to MAXs Interface object
|
||||
Interface* m_IP;
|
||||
// handle to the export parameters window
|
||||
HWND m_Params;
|
||||
// export options
|
||||
DWORD m_Options;
|
||||
// list of all skeletons found in scene
|
||||
std::vector<ExpSkeleton*> m_Skeletons;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
924
source/tools/pmdexp/PSProp.cpp
Executable file
924
source/tools/pmdexp/PSProp.cpp
Executable file
@ -0,0 +1,924 @@
|
||||
/**********************************************************************
|
||||
*<
|
||||
FILE: pthelp.cpp
|
||||
|
||||
DESCRIPTION: A point helper implementation
|
||||
|
||||
CREATED BY:
|
||||
|
||||
HISTORY: created 14 July 1995
|
||||
|
||||
*> Copyright (c) 1995, All Rights Reserved.
|
||||
**********************************************************************/
|
||||
|
||||
#include "PSProp.h"
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
|
||||
// in prim.cpp - The dll instance handle
|
||||
extern HINSTANCE hInstance;
|
||||
|
||||
#define AXIS_LENGTH 20.0f
|
||||
#define ZFACT (float).005;
|
||||
|
||||
void AxisViewportRect(ViewExp *vpt, const Matrix3 &tm, float length, Rect *rect);
|
||||
void DrawAxis(ViewExp *vpt, const Matrix3 &tm, float length, BOOL screenSize);
|
||||
Box3 GetAxisBox(ViewExp *vpt, const Matrix3 &tm,float length,int resetTM);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// PSPropClassDesc : required class to expose PSProp to MAX
|
||||
class PSPropClassDesc : public ClassDesc2
|
||||
{
|
||||
public:
|
||||
int IsPublic() { return 1; }
|
||||
void * Create(BOOL loading = FALSE) { return new PSPropObject; }
|
||||
const TCHAR * ClassName() { return GetString(IDS_DB_POINT_CLASS); }
|
||||
SClass_ID SuperClassID() { return HELPER_CLASS_ID; }
|
||||
Class_ID ClassID() { return PSPROP_CLASS_ID; }
|
||||
const TCHAR* Category() { return _T("PS Helpers"); }
|
||||
|
||||
const TCHAR* InternalName() {return _T("PSProp");}
|
||||
HINSTANCE HInstance() {return hInstance;}
|
||||
};
|
||||
|
||||
PSPropClassDesc pointHelpObjDesc;
|
||||
ClassDesc* GetPSPropDesc() { return &pointHelpObjDesc; }
|
||||
|
||||
|
||||
// class variable for point class.
|
||||
IObjParam *PSPropObject::ip = NULL;
|
||||
PSPropObject *PSPropObject::editOb = NULL;
|
||||
|
||||
|
||||
//HWND PSPropObject::hParams = NULL;
|
||||
//IObjParam *PSPropObject::iObjParams;
|
||||
|
||||
//int PSPropObject::dlgShowAxis = TRUE;
|
||||
//float PSPropObject::dlgAxisLength = AXIS_LENGTH;
|
||||
|
||||
void resetPointParams()
|
||||
{
|
||||
//PSPropObject::dlgShowAxis = TRUE;
|
||||
//PSPropObject::dlgAxisLength = AXIS_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define PBLOCK_REF_NO 0
|
||||
|
||||
// The following two enums are transfered to the istdplug.h by AG: 01/20/2002
|
||||
// in order to access the parameters for use in Spline IK Control modifier
|
||||
// and the Spline IK Solver
|
||||
|
||||
// block IDs
|
||||
//enum { pointobj_params, };
|
||||
|
||||
// pointobj_params IDs
|
||||
|
||||
// enum {
|
||||
// pointobj_size, pointobj_centermarker, pointobj_axistripod,
|
||||
// pointobj_cross, pointobj_box, pointobj_screensize, pointobj_drawontop };
|
||||
|
||||
// per instance block
|
||||
static ParamBlockDesc2 pointobj_param_blk(
|
||||
|
||||
pointobj_params, _T("PointObjectParameters"), 0, &pointHelpObjDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, PBLOCK_REF_NO,
|
||||
|
||||
//rollout
|
||||
IDD_NEW_POINTPARAM, IDS_POINT_PARAMS, 0, 0, NULL,
|
||||
|
||||
// params
|
||||
pointobj_size, _T("size"), TYPE_FLOAT, P_ANIMATABLE, IDS_POINT_SIZE,
|
||||
p_default, 20.0,
|
||||
p_ms_default, 20.0,
|
||||
p_range, 0.0f, float(1.0E30),
|
||||
p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE, IDC_POINT_SIZE, IDC_POINT_SIZESPIN, SPIN_AUTOSCALE,
|
||||
end,
|
||||
|
||||
|
||||
pointobj_centermarker, _T("centermarker"), TYPE_BOOL, P_ANIMATABLE, IDS_POINT_CENTERMARKER,
|
||||
p_default, FALSE,
|
||||
p_ui, TYPE_SINGLECHEKBOX, IDC_POINT_MARKER,
|
||||
end,
|
||||
|
||||
pointobj_axistripod, _T("axistripod"), TYPE_BOOL, P_ANIMATABLE, IDS_POINT_AXISTRIPOD,
|
||||
p_default, FALSE,
|
||||
p_ui, TYPE_SINGLECHEKBOX, IDC_POINT_AXIS,
|
||||
end,
|
||||
|
||||
pointobj_cross, _T("cross"), TYPE_BOOL, P_ANIMATABLE, IDS_POINT_CROSS,
|
||||
p_default, TRUE,
|
||||
p_ui, TYPE_SINGLECHEKBOX, IDC_POINT_CROSS,
|
||||
end,
|
||||
|
||||
pointobj_box, _T("box"), TYPE_BOOL, P_ANIMATABLE, IDS_POINT_BOX,
|
||||
p_default, FALSE,
|
||||
p_ui, TYPE_SINGLECHEKBOX, IDC_POINT_BOX,
|
||||
end,
|
||||
|
||||
pointobj_screensize, _T("constantscreensize"), TYPE_BOOL, P_ANIMATABLE, IDS_POINT_SCREENSIZE,
|
||||
p_default, FALSE,
|
||||
p_ui, TYPE_SINGLECHEKBOX, IDC_POINT_SCREENSIZE,
|
||||
end,
|
||||
|
||||
pointobj_drawontop, _T("drawontop"), TYPE_BOOL, P_ANIMATABLE, IDS_POINT_DRAWONTOP,
|
||||
p_default, FALSE,
|
||||
p_ui, TYPE_SINGLECHEKBOX, IDC_POINT_DRAWONTOP,
|
||||
end,
|
||||
|
||||
end
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
INT_PTR CALLBACK PointParamProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
PSPropObject *po = (PSPropObject*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
|
||||
if (!po && msg!=WM_INITDIALOG) return FALSE;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG: {
|
||||
po = (PSPropObject*)lParam;
|
||||
SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam);
|
||||
CheckDlgButton(hWnd,IDC_SHOWAXIS,po->showAxis);
|
||||
|
||||
ISpinnerControl *spin =
|
||||
GetISpinner(GetDlgItem(hWnd,IDC_AXISLENGHSPIN));
|
||||
spin->SetLimits(10,1000,FALSE);
|
||||
spin->SetScale(0.1f);
|
||||
spin->SetValue(po->axisLength,FALSE);
|
||||
spin->LinkToEdit(GetDlgItem(hWnd,IDC_AXISLENGTH),EDITTYPE_FLOAT);
|
||||
ReleaseISpinner(spin);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case CC_SPINNER_CHANGE: {
|
||||
ISpinnerControl *spin = (ISpinnerControl*)lParam;
|
||||
po->axisLength = spin->GetFVal();
|
||||
po->NotifyDependents(FOREVER,PART_OBJ,REFMSG_CHANGE);
|
||||
po->iObjParams->RedrawViews(po->iObjParams->GetTime());
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case IDC_SHOWAXIS:
|
||||
po->showAxis = IsDlgButtonChecked(hWnd,IDC_SHOWAXIS);
|
||||
po->NotifyDependents(FOREVER,PART_OBJ,REFMSG_CHANGE);
|
||||
po->iObjParams->RedrawViews(po->iObjParams->GetTime());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
*/
|
||||
|
||||
void PSPropObject::BeginEditParams(
|
||||
IObjParam *ip, ULONG flags,Animatable *prev)
|
||||
{
|
||||
this->ip = ip;
|
||||
editOb = this;
|
||||
pointHelpObjDesc.BeginEditParams(ip, this, flags, prev);
|
||||
|
||||
/*
|
||||
iObjParams = ip;
|
||||
if (!hParams) {
|
||||
hParams = ip->AddRollupPage(
|
||||
hInstance,
|
||||
MAKEINTRESOURCE(IDD_POINTPARAM),
|
||||
PointParamProc,
|
||||
GetString(IDS_DB_PARAMETERS),
|
||||
(LPARAM)this );
|
||||
ip->RegisterDlgWnd(hParams);
|
||||
} else {
|
||||
SetWindowLongPtr(hParams,GWLP_USERDATA,(LONG_PTR)this);
|
||||
CheckDlgButton(hParams,IDC_SHOWAXIS,showAxis);
|
||||
ISpinnerControl *spin =
|
||||
GetISpinner(GetDlgItem(hParams,IDC_AXISLENGHSPIN));
|
||||
spin->SetValue(axisLength,FALSE);
|
||||
ReleaseISpinner(spin);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void PSPropObject::EndEditParams(
|
||||
IObjParam *ip, ULONG flags,Animatable *next)
|
||||
{
|
||||
editOb = NULL;
|
||||
this->ip = NULL;
|
||||
pointHelpObjDesc.EndEditParams(ip, this, flags, next);
|
||||
ClearAFlag(A_OBJ_CREATING);
|
||||
|
||||
/*
|
||||
dlgShowAxis = IsDlgButtonChecked(hParams, IDC_SHOWAXIS );
|
||||
ISpinnerControl *spin = GetISpinner(GetDlgItem(hParams,IDC_AXISLENGHSPIN));
|
||||
dlgAxisLength = spin->GetFVal();
|
||||
ReleaseISpinner(spin);
|
||||
if (flags&END_EDIT_REMOVEUI) {
|
||||
ip->UnRegisterDlgWnd(hParams);
|
||||
ip->DeleteRollupPage(hParams);
|
||||
hParams = NULL;
|
||||
} else {
|
||||
SetWindowLongPtr(hParams,GWLP_USERDATA,0);
|
||||
}
|
||||
iObjParams = NULL;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
PSPropObject::PSPropObject()
|
||||
{
|
||||
pointHelpObjDesc.MakeAutoParamBlocks(this);
|
||||
showAxis = TRUE; //dlgShowAxis;
|
||||
axisLength = 10.0f; //dlgAxisLength;
|
||||
suspendSnap = FALSE;
|
||||
SetAFlag(A_OBJ_CREATING);
|
||||
}
|
||||
|
||||
PSPropObject::~PSPropObject()
|
||||
{
|
||||
DeleteAllRefsFromMe();
|
||||
}
|
||||
|
||||
IParamArray *PSPropObject::GetParamBlock()
|
||||
{
|
||||
return (IParamArray*)pblock2;
|
||||
}
|
||||
|
||||
int PSPropObject::GetParamBlockIndex(int id)
|
||||
{
|
||||
if (pblock2 && id>=0 && id<pblock2->NumParams()) return id;
|
||||
else return -1;
|
||||
}
|
||||
|
||||
|
||||
class PointHelpObjCreateCallBack: public CreateMouseCallBack {
|
||||
PSPropObject *ob;
|
||||
public:
|
||||
int proc( ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat );
|
||||
void SetObj(PSPropObject *obj) { ob = obj; }
|
||||
};
|
||||
|
||||
int PointHelpObjCreateCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ) {
|
||||
|
||||
#ifdef _OSNAP
|
||||
if (msg == MOUSE_FREEMOVE)
|
||||
{
|
||||
#ifdef _3D_CREATE
|
||||
vpt->SnapPreview(m,m,NULL, SNAP_IN_3D);
|
||||
#else
|
||||
vpt->SnapPreview(m,m,NULL, SNAP_IN_PLANE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (msg==MOUSE_POINT||msg==MOUSE_MOVE) {
|
||||
switch(point) {
|
||||
case 0: {
|
||||
|
||||
// Find the node and plug in the wire color
|
||||
ULONG handle;
|
||||
ob->NotifyDependents(FOREVER, (PartID)&handle, REFMSG_GET_NODE_HANDLE);
|
||||
INode *node;
|
||||
node = GetCOREInterface()->GetINodeByHandle(handle);
|
||||
if (node) {
|
||||
Point3 color(0,0,1);// = GetUIColor(COLOR_POINT_OBJ);
|
||||
node->SetWireColor(RGB(color.x*255.0f, color.y*255.0f, color.z*255.0f));
|
||||
}
|
||||
|
||||
ob->suspendSnap = TRUE;
|
||||
#ifdef _3D_CREATE
|
||||
mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_3D));
|
||||
#else
|
||||
mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
#ifdef _3D_CREATE
|
||||
mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_3D));
|
||||
#else
|
||||
mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
|
||||
#endif
|
||||
if (msg==MOUSE_POINT) {
|
||||
ob->suspendSnap = FALSE;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else
|
||||
if (msg == MOUSE_ABORT) {
|
||||
return CREATE_ABORT;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PointHelpObjCreateCallBack pointHelpCreateCB;
|
||||
|
||||
CreateMouseCallBack* PSPropObject::GetCreateMouseCallBack() {
|
||||
pointHelpCreateCB.SetObj(this);
|
||||
return(&pointHelpCreateCB);
|
||||
}
|
||||
|
||||
void PSPropObject::SetExtendedDisplay(int flags)
|
||||
{
|
||||
extDispFlags = flags;
|
||||
}
|
||||
|
||||
|
||||
void PSPropObject::GetLocalBoundBox(
|
||||
TimeValue t, INode* inode, ViewExp* vpt, Box3& box )
|
||||
{
|
||||
Matrix3 tm = inode->GetObjectTM(t);
|
||||
|
||||
float size;
|
||||
int screenSize;
|
||||
pblock2->GetValue(pointobj_size, t, size, FOREVER);
|
||||
pblock2->GetValue(pointobj_screensize, t, screenSize, FOREVER);
|
||||
|
||||
float zoom = 1.0f;
|
||||
if (screenSize) {
|
||||
zoom = vpt->GetScreenScaleFactor(tm.GetTrans())*ZFACT;
|
||||
}
|
||||
if (zoom==0.0f) zoom = 1.0f;
|
||||
|
||||
size *= zoom;
|
||||
box = Box3(Point3(0,0,0), Point3(0,0,0));
|
||||
box += Point3(size*0.5f, 0.0f, 0.0f);
|
||||
box += Point3( 0.0f, size*0.5f, 0.0f);
|
||||
box += Point3( 0.0f, 0.0f, size*0.5f);
|
||||
box += Point3(-size*0.5f, 0.0f, 0.0f);
|
||||
box += Point3( 0.0f, -size*0.5f, 0.0f);
|
||||
box += Point3( 0.0f, 0.0f, -size*0.5f);
|
||||
|
||||
box.EnlargeBy(10.0f/zoom);
|
||||
|
||||
/*
|
||||
if (showAxis)
|
||||
box = GetAxisBox(vpt,tm,showAxis?axisLength:0.0f, TRUE);
|
||||
else
|
||||
box = Box3(Point3(0,0,0), Point3(0,0,0));
|
||||
*/
|
||||
}
|
||||
|
||||
void PSPropObject::GetWorldBoundBox(
|
||||
TimeValue t, INode* inode, ViewExp* vpt, Box3& box )
|
||||
{
|
||||
Matrix3 tm;
|
||||
tm = inode->GetObjectTM(t);
|
||||
Box3 lbox;
|
||||
|
||||
GetLocalBoundBox(t, inode, vpt, lbox);
|
||||
box = Box3(tm.GetTrans(), tm.GetTrans());
|
||||
for (int i=0; i<8; i++) {
|
||||
box += lbox * tm;
|
||||
}
|
||||
/*
|
||||
if(!(extDispFlags & EXT_DISP_ZOOM_EXT) && showAxis)
|
||||
box = GetAxisBox(vpt,tm,showAxis?axisLength:0.0f, FALSE);
|
||||
else
|
||||
box = Box3(tm.GetTrans(), tm.GetTrans());
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void PSPropObject::Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt)
|
||||
{
|
||||
if(suspendSnap)
|
||||
return;
|
||||
|
||||
Matrix3 tm = inode->GetObjectTM(t);
|
||||
GraphicsWindow *gw = vpt->getGW();
|
||||
gw->setTransform(tm);
|
||||
|
||||
Matrix3 invPlane = Inverse(snap->plane);
|
||||
|
||||
// Make sure the vertex priority is active and at least as important as the best snap so far
|
||||
if(snap->vertPriority > 0 && snap->vertPriority <= snap->priority) {
|
||||
Point2 fp = Point2((float)p->x, (float)p->y);
|
||||
Point2 screen2;
|
||||
IPoint3 pt3;
|
||||
|
||||
Point3 thePoint(0,0,0);
|
||||
// If constrained to the plane, make sure this point is in it!
|
||||
if(snap->snapType == SNAP_2D || snap->flags & SNAP_IN_PLANE) {
|
||||
Point3 test = thePoint * tm * invPlane;
|
||||
if(fabs(test.z) > 0.0001) // Is it in the plane (within reason)?
|
||||
return;
|
||||
}
|
||||
gw->wTransPoint(&thePoint,&pt3);
|
||||
screen2.x = (float)pt3.x;
|
||||
screen2.y = (float)pt3.y;
|
||||
|
||||
// Are we within the snap radius?
|
||||
int len = (int)Length(screen2 - fp);
|
||||
if(len <= snap->strength) {
|
||||
// Is this priority better than the best so far?
|
||||
if(snap->vertPriority < snap->priority) {
|
||||
snap->priority = snap->vertPriority;
|
||||
snap->bestWorld = thePoint * tm;
|
||||
snap->bestScreen = screen2;
|
||||
snap->bestDist = len;
|
||||
}
|
||||
else
|
||||
if(len < snap->bestDist) {
|
||||
snap->priority = snap->vertPriority;
|
||||
snap->bestWorld = thePoint * tm;
|
||||
snap->bestScreen = screen2;
|
||||
snap->bestDist = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int PSPropObject::DrawAndHit(TimeValue t, INode *inode, ViewExp *vpt)
|
||||
{
|
||||
float size;
|
||||
int centerMarker, axisTripod, cross, box, screenSize, drawOnTop;
|
||||
|
||||
// Color color = inode->GetWireColor();
|
||||
|
||||
Interval ivalid = FOREVER;
|
||||
pblock2->GetValue(pointobj_size, t, size, ivalid);
|
||||
pblock2->GetValue(pointobj_centermarker, t, centerMarker, ivalid);
|
||||
pblock2->GetValue(pointobj_axistripod, t, axisTripod, ivalid);
|
||||
pblock2->GetValue(pointobj_cross, t, cross, ivalid);
|
||||
pblock2->GetValue(pointobj_box, t, box, ivalid);
|
||||
pblock2->GetValue(pointobj_screensize, t, screenSize, ivalid);
|
||||
pblock2->GetValue(pointobj_drawontop, t, drawOnTop, ivalid);
|
||||
|
||||
Matrix3 tm(1);
|
||||
Point3 pt(0,0,0);
|
||||
Point3 pts[5];
|
||||
|
||||
vpt->getGW()->setTransform(tm);
|
||||
tm = inode->GetObjectTM(t);
|
||||
|
||||
int limits = vpt->getGW()->getRndLimits();
|
||||
if (drawOnTop) vpt->getGW()->setRndLimits(limits & ~GW_Z_BUFFER);
|
||||
|
||||
if (inode->Selected()) {
|
||||
vpt->getGW()->setColor( TEXT_COLOR, GetUIColor(COLOR_SELECTION) );
|
||||
vpt->getGW()->setColor( LINE_COLOR, GetUIColor(COLOR_SELECTION) );
|
||||
} else if (!inode->IsFrozen() && !inode->Dependent()) {
|
||||
//vpt->getGW()->setColor( TEXT_COLOR, GetUIColor(COLOR_POINT_AXES) );
|
||||
//vpt->getGW()->setColor( LINE_COLOR, GetUIColor(COLOR_POINT_AXES) );
|
||||
vpt->getGW()->setColor( TEXT_COLOR, Color(0,0,1));
|
||||
vpt->getGW()->setColor( LINE_COLOR, Color(0,0,1));
|
||||
}
|
||||
|
||||
if (axisTripod) {
|
||||
DrawAxis(vpt, tm, size, screenSize);
|
||||
}
|
||||
|
||||
size *= 0.5f;
|
||||
|
||||
float zoom = vpt->GetScreenScaleFactor(tm.GetTrans())*ZFACT;
|
||||
if (screenSize) {
|
||||
tm.Scale(Point3(zoom,zoom,zoom));
|
||||
}
|
||||
|
||||
vpt->getGW()->setTransform(tm);
|
||||
|
||||
if (!inode->IsFrozen() && !inode->Dependent() && !inode->Selected()) {
|
||||
//vpt->getGW()->setColor(LINE_COLOR, GetUIColor(COLOR_POINT_OBJ));
|
||||
vpt->getGW()->setColor( LINE_COLOR, Color(0,0,1));
|
||||
}
|
||||
|
||||
if (centerMarker) {
|
||||
vpt->getGW()->marker(&pt,X_MRKR);
|
||||
}
|
||||
|
||||
if (cross) {
|
||||
// X
|
||||
pts[0] = Point3(-size, 0.0f, 0.0f); pts[1] = Point3(size, 0.0f, 0.0f);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
|
||||
// Y
|
||||
pts[0] = Point3(0.0f, -size, 0.0f); pts[1] = Point3(0.0f, size, 0.0f);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
|
||||
// Z
|
||||
pts[0] = Point3(0.0f, 0.0f, -size); pts[1] = Point3(0.0f, 0.0f, size);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
if (box) {
|
||||
|
||||
// Make the box half the size
|
||||
size = size * 0.5f;
|
||||
|
||||
// Bottom
|
||||
pts[0] = Point3(-size, -size, -size);
|
||||
pts[1] = Point3(-size, size, -size);
|
||||
pts[2] = Point3( size, size, -size);
|
||||
pts[3] = Point3( size, -size, -size);
|
||||
vpt->getGW()->polyline(4, pts, NULL, NULL, TRUE, NULL);
|
||||
|
||||
// Top
|
||||
pts[0] = Point3(-size, -size, size);
|
||||
pts[1] = Point3(-size, size, size);
|
||||
pts[2] = Point3( size, size, size);
|
||||
pts[3] = Point3( size, -size, size);
|
||||
vpt->getGW()->polyline(4, pts, NULL, NULL, TRUE, NULL);
|
||||
|
||||
// Sides
|
||||
pts[0] = Point3(-size, -size, -size);
|
||||
pts[1] = Point3(-size, -size, size);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
|
||||
pts[0] = Point3(-size, size, -size);
|
||||
pts[1] = Point3(-size, size, size);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
|
||||
pts[0] = Point3( size, size, -size);
|
||||
pts[1] = Point3( size, size, size);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
|
||||
pts[0] = Point3( size, -size, -size);
|
||||
pts[1] = Point3( size, -size, size);
|
||||
vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
vpt->getGW()->setRndLimits(limits);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PSPropObject::HitTest(
|
||||
TimeValue t, INode *inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt)
|
||||
{
|
||||
Matrix3 tm(1);
|
||||
HitRegion hitRegion;
|
||||
DWORD savedLimits;
|
||||
Point3 pt(0,0,0);
|
||||
|
||||
vpt->getGW()->setTransform(tm);
|
||||
GraphicsWindow *gw = vpt->getGW();
|
||||
Material *mtl = gw->getMaterial();
|
||||
|
||||
tm = inode->GetObjectTM(t);
|
||||
MakeHitRegion(hitRegion, type, crossing, 4, p);
|
||||
|
||||
gw->setRndLimits(((savedLimits = gw->getRndLimits())|GW_PICK)&~GW_ILLUM);
|
||||
gw->setHitRegion(&hitRegion);
|
||||
gw->clearHitCode();
|
||||
|
||||
DrawAndHit(t, inode, vpt);
|
||||
|
||||
/*
|
||||
if (showAxis) {
|
||||
DrawAxis(vpt,tm,axisLength,screenSize);
|
||||
}
|
||||
vpt->getGW()->setTransform(tm);
|
||||
vpt->getGW()->marker(&pt,X_MRKR);
|
||||
*/
|
||||
|
||||
gw->setRndLimits(savedLimits);
|
||||
|
||||
if((hitRegion.type != POINT_RGN) && !hitRegion.crossing)
|
||||
return TRUE;
|
||||
return gw->checkHitCode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
int PSPropObject::Display(
|
||||
TimeValue t, INode* inode, ViewExp *vpt, int flags)
|
||||
{
|
||||
DrawAndHit(t, inode, vpt);
|
||||
/*
|
||||
Matrix3 tm(1);
|
||||
Point3 pt(0,0,0);
|
||||
|
||||
vpt->getGW()->setTransform(tm);
|
||||
tm = inode->GetObjectTM(t);
|
||||
|
||||
if (showAxis) {
|
||||
DrawAxis(vpt,tm,axisLength,inode->Selected(),inode->IsFrozen());
|
||||
}
|
||||
|
||||
|
||||
vpt->getGW()->setTransform(tm);
|
||||
if(!inode->IsFrozen())
|
||||
vpt->getGW()->setColor(LINE_COLOR,GetUIColor(COLOR_POINT_OBJ));
|
||||
vpt->getGW()->marker(&pt,X_MRKR);
|
||||
*/
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Reference Managment:
|
||||
//
|
||||
|
||||
// This is only called if the object MAKES references to other things.
|
||||
RefResult PSPropObject::NotifyRefChanged(
|
||||
Interval changeInt, RefTargetHandle hTarget,
|
||||
PartID& partID, RefMessage message )
|
||||
{
|
||||
switch (message) {
|
||||
case REFMSG_CHANGE:
|
||||
if (editOb==this) InvalidateUI();
|
||||
break;
|
||||
}
|
||||
return(REF_SUCCEED);
|
||||
}
|
||||
|
||||
void PSPropObject::InvalidateUI()
|
||||
{
|
||||
pointobj_param_blk.InvalidateUI(pblock2->LastNotifyParamID());
|
||||
}
|
||||
|
||||
Interval PSPropObject::ObjectValidity(TimeValue t)
|
||||
{
|
||||
float size;
|
||||
int centerMarker, axisTripod, cross, box, screenSize, drawOnTop;
|
||||
|
||||
Interval ivalid = FOREVER;
|
||||
pblock2->GetValue(pointobj_size, t, size, ivalid);
|
||||
pblock2->GetValue(pointobj_centermarker, t, centerMarker, ivalid);
|
||||
pblock2->GetValue(pointobj_axistripod, t, axisTripod, ivalid);
|
||||
pblock2->GetValue(pointobj_cross, t, cross, ivalid);
|
||||
pblock2->GetValue(pointobj_box, t, box, ivalid);
|
||||
pblock2->GetValue(pointobj_screensize, t, screenSize, ivalid);
|
||||
pblock2->GetValue(pointobj_drawontop, t, drawOnTop, ivalid);
|
||||
|
||||
return ivalid;
|
||||
}
|
||||
|
||||
ObjectState PSPropObject::Eval(TimeValue t)
|
||||
{
|
||||
return ObjectState(this);
|
||||
}
|
||||
|
||||
RefTargetHandle PSPropObject::Clone(RemapDir& remap)
|
||||
{
|
||||
PSPropObject* newob = new PSPropObject();
|
||||
newob->showAxis = showAxis;
|
||||
newob->axisLength = axisLength;
|
||||
newob->ReplaceReference(0, pblock2->Clone(remap));
|
||||
BaseClone(this, newob, remap);
|
||||
return(newob);
|
||||
}
|
||||
|
||||
|
||||
void PSPropObject::UpdateParamblockFromVars()
|
||||
{
|
||||
SuspendAnimate();
|
||||
AnimateOff();
|
||||
|
||||
pblock2->SetValue(pointobj_size, TimeValue(0), axisLength);
|
||||
pblock2->SetValue(pointobj_centermarker, TimeValue(0), TRUE);
|
||||
pblock2->SetValue(pointobj_axistripod, TimeValue(0), showAxis);
|
||||
pblock2->SetValue(pointobj_cross, TimeValue(0), FALSE);
|
||||
pblock2->SetValue(pointobj_box, TimeValue(0), FALSE);
|
||||
pblock2->SetValue(pointobj_screensize, TimeValue(0), TRUE);
|
||||
|
||||
ResumeAnimate();
|
||||
}
|
||||
|
||||
|
||||
class PointHelperPostLoadCallback : public PostLoadCallback {
|
||||
public:
|
||||
PSPropObject *pobj;
|
||||
|
||||
PointHelperPostLoadCallback(PSPropObject *p) {pobj=p;}
|
||||
void proc(ILoad *iload) {
|
||||
pobj->UpdateParamblockFromVars();
|
||||
}
|
||||
};
|
||||
|
||||
#define SHOW_AXIS_CHUNK 0x0100
|
||||
#define AXIS_LENGTH_CHUNK 0x0110
|
||||
#define POINT_HELPER_R4_CHUNKID 0x0120 // new version of point helper for R4 (updated to use PB2)
|
||||
|
||||
IOResult PSPropObject::Load(ILoad *iload)
|
||||
{
|
||||
ULONG nb;
|
||||
IOResult res = IO_OK;
|
||||
BOOL oldVersion = TRUE;
|
||||
|
||||
while (IO_OK==(res=iload->OpenChunk())) {
|
||||
switch (iload->CurChunkID()) {
|
||||
|
||||
case SHOW_AXIS_CHUNK:
|
||||
res = iload->Read(&showAxis,sizeof(showAxis),&nb);
|
||||
break;
|
||||
|
||||
case AXIS_LENGTH_CHUNK:
|
||||
res = iload->Read(&axisLength,sizeof(axisLength),&nb);
|
||||
break;
|
||||
|
||||
case POINT_HELPER_R4_CHUNKID:
|
||||
oldVersion = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
res = iload->CloseChunk();
|
||||
if (res!=IO_OK) return res;
|
||||
}
|
||||
|
||||
if (oldVersion) {
|
||||
iload->RegisterPostLoadCallback(new PointHelperPostLoadCallback(this));
|
||||
}
|
||||
|
||||
return IO_OK;
|
||||
}
|
||||
|
||||
IOResult PSPropObject::Save(ISave *isave)
|
||||
{
|
||||
/*
|
||||
isave->BeginChunk(SHOW_AXIS_CHUNK);
|
||||
isave->Write(&showAxis,sizeof(showAxis),&nb);
|
||||
isave->EndChunk();
|
||||
|
||||
isave->BeginChunk(AXIS_LENGTH_CHUNK);
|
||||
isave->Write(&axisLength,sizeof(axisLength),&nb);
|
||||
isave->EndChunk();
|
||||
*/
|
||||
|
||||
isave->BeginChunk(POINT_HELPER_R4_CHUNKID);
|
||||
isave->EndChunk();
|
||||
|
||||
return IO_OK;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
//
|
||||
// Stole this from scene.cpp
|
||||
// Probably couldn't hurt to make an API...
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
void Text( ViewExp *vpt, TCHAR *str, Point3 &pt )
|
||||
{
|
||||
vpt->getGW()->text( &pt, str );
|
||||
}
|
||||
|
||||
static void DrawAnAxis( ViewExp *vpt, Point3 axis )
|
||||
{
|
||||
Point3 v1, v2, v[3];
|
||||
v1 = axis * (float)0.9;
|
||||
if ( axis.x != 0.0 || axis.y != 0.0 ) {
|
||||
v2 = Point3( axis.y, -axis.x, axis.z ) * (float)0.1;
|
||||
} else {
|
||||
v2 = Point3( axis.x, axis.z, -axis.y ) * (float)0.1;
|
||||
}
|
||||
|
||||
v[0] = Point3(0.0,0.0,0.0);
|
||||
v[1] = axis;
|
||||
vpt->getGW()->polyline( 2, v, NULL, NULL, FALSE, NULL );
|
||||
v[0] = axis;
|
||||
v[1] = v1+v2;
|
||||
vpt->getGW()->polyline( 2, v, NULL, NULL, FALSE, NULL );
|
||||
v[0] = axis;
|
||||
v[1] = v1-v2;
|
||||
vpt->getGW()->polyline( 2, v, NULL, NULL, FALSE, NULL );
|
||||
}
|
||||
|
||||
|
||||
void DrawAxis(ViewExp *vpt, const Matrix3 &tm, float length, BOOL screenSize)
|
||||
{
|
||||
Matrix3 tmn = tm;
|
||||
float zoom;
|
||||
|
||||
// Get width of viewport in world units: --DS
|
||||
zoom = vpt->GetScreenScaleFactor(tmn.GetTrans())*ZFACT;
|
||||
|
||||
if (screenSize) {
|
||||
tmn.Scale( Point3(zoom,zoom,zoom) );
|
||||
}
|
||||
vpt->getGW()->setTransform( tmn );
|
||||
|
||||
Text( vpt, _T("x"), Point3(length,0.0f,0.0f) );
|
||||
DrawAnAxis( vpt, Point3(length,0.0f,0.0f) );
|
||||
|
||||
Text( vpt, _T("y"), Point3(0.0f,length,0.0f) );
|
||||
DrawAnAxis( vpt, Point3(0.0f,length,0.0f) );
|
||||
|
||||
Text( vpt, _T("z"), Point3(0.0f,0.0f,length) );
|
||||
DrawAnAxis( vpt, Point3(0.0f,0.0f,length) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--- RB 7/17/2000: the code below seems to be unused ---------------------------------------------------
|
||||
|
||||
|
||||
Box3 GetAxisBox(ViewExp *vpt, const Matrix3 &tm,float length,int resetTM)
|
||||
{
|
||||
Matrix3 tmn = tm;
|
||||
Box3 box;
|
||||
float zoom;
|
||||
|
||||
// Get width of viewport in world units: --DS
|
||||
zoom = vpt->GetScreenScaleFactor(tmn.GetTrans())*ZFACT;
|
||||
if (zoom==0.0f) zoom = 1.0f;
|
||||
// tmn.Scale(Point3(zoom,zoom,zoom));
|
||||
length *= zoom;
|
||||
if(resetTM)
|
||||
tmn.IdentityMatrix();
|
||||
|
||||
box += Point3(0.0f,0.0f,0.0f) * tmn;
|
||||
box += Point3(length,0.0f,0.0f) * tmn;
|
||||
box += Point3(0.0f,length,0.0f) * tmn;
|
||||
box += Point3(0.0f,0.0f,length) * tmn;
|
||||
box += Point3(-length/5.f,0.0f,0.0f) * tmn;
|
||||
box += Point3(0.0f,-length/5.f,0.0f) * tmn;
|
||||
box += Point3(0.0f,0.0f,-length/5.0f) * tmn;
|
||||
box.EnlargeBy(10.0f/zoom);
|
||||
return box;
|
||||
}
|
||||
|
||||
|
||||
inline void EnlargeRectIPoint3( RECT *rect, IPoint3& pt )
|
||||
{
|
||||
if ( pt.x < rect->left ) rect->left = pt.x;
|
||||
if ( pt.x > rect->right ) rect->right = pt.x;
|
||||
if ( pt.y < rect->top ) rect->top = pt.y;
|
||||
if ( pt.y > rect->bottom ) rect->bottom = pt.y;
|
||||
}
|
||||
|
||||
// This is a guess - need to find real w/h.
|
||||
#define FONT_HEIGHT 11
|
||||
#define FONT_WIDTH 9
|
||||
|
||||
|
||||
static void AxisRect( GraphicsWindow *gw, Point3 axis, Rect *rect )
|
||||
{
|
||||
Point3 v1, v2, v;
|
||||
IPoint3 iv;
|
||||
v1 = axis * (float)0.9;
|
||||
if ( axis.x != 0.0 || axis.y != 0.0 ) {
|
||||
v2 = Point3( axis.y, -axis.x, axis.z ) * (float)0.1;
|
||||
} else {
|
||||
v2 = Point3( axis.x, axis.z, -axis.y ) * (float)0.1;
|
||||
}
|
||||
v = axis;
|
||||
gw->wTransPoint( &v, &iv );
|
||||
EnlargeRectIPoint3( rect, iv);
|
||||
|
||||
iv.x += FONT_WIDTH;
|
||||
iv.y -= FONT_HEIGHT;
|
||||
EnlargeRectIPoint3( rect, iv);
|
||||
|
||||
v = v1+v2;
|
||||
gw->wTransPoint( &v, &iv );
|
||||
EnlargeRectIPoint3( rect, iv);
|
||||
v = v1-v2;
|
||||
gw->wTransPoint( &v, &iv );
|
||||
EnlargeRectIPoint3( rect, iv);
|
||||
}
|
||||
|
||||
|
||||
void AxisViewportRect(ViewExp *vpt, const Matrix3 &tm, float length, Rect *rect)
|
||||
{
|
||||
Matrix3 tmn = tm;
|
||||
float zoom;
|
||||
IPoint3 wpt;
|
||||
Point3 pt;
|
||||
GraphicsWindow *gw = vpt->getGW();
|
||||
|
||||
// Get width of viewport in world units: --DS
|
||||
zoom = vpt->GetScreenScaleFactor(tmn.GetTrans())*ZFACT;
|
||||
|
||||
tmn.Scale( Point3(zoom,zoom,zoom) );
|
||||
gw->setTransform( tmn );
|
||||
pt = Point3(0.0f, 0.0f, 0.0f);
|
||||
gw->wTransPoint( &pt, &wpt );
|
||||
rect->left = rect->right = wpt.x;
|
||||
rect->top = rect->bottom = wpt.y;
|
||||
|
||||
AxisRect( gw, Point3(length,0.0f,0.0f),rect );
|
||||
AxisRect( gw, Point3(0.0f,length,0.0f),rect );
|
||||
AxisRect( gw, Point3(0.0f,0.0f,length),rect );
|
||||
|
||||
rect->right += 2;
|
||||
rect->bottom += 2;
|
||||
rect->left -= 2;
|
||||
rect->top -= 2;
|
||||
}
|
||||
|
||||
|
||||
|
95
source/tools/pmdexp/PSProp.h
Executable file
95
source/tools/pmdexp/PSProp.h
Executable file
@ -0,0 +1,95 @@
|
||||
#ifndef _PSPROP_H
|
||||
#define _PSPROP_H
|
||||
|
||||
#include "MaxInc.h"
|
||||
|
||||
TCHAR *GetString(int id);
|
||||
|
||||
#define PSPROP_CLASS_ID Class_ID(0x353f201d, 0x3d01408d)
|
||||
|
||||
extern ClassDesc* GetPSPropDesc();
|
||||
|
||||
class PSPropObject : public HelperObject
|
||||
{
|
||||
public:
|
||||
static IObjParam *ip;
|
||||
static PSPropObject *editOb;
|
||||
IParamBlock2 *pblock2;
|
||||
|
||||
// Class vars
|
||||
/*
|
||||
static HWND hParams;
|
||||
static IObjParam *iObjParams;
|
||||
static int dlgShowAxis;
|
||||
static float dlgAxisLength;
|
||||
*/
|
||||
|
||||
// Snap suspension flag (TRUE during creation only)
|
||||
BOOL suspendSnap;
|
||||
|
||||
// Old params... these are for loading old files only. Params are now stored in pb2.
|
||||
BOOL showAxis;
|
||||
float axisLength;
|
||||
|
||||
// For use by display system
|
||||
int extDispFlags;
|
||||
|
||||
// inherited virtual methods for Reference-management
|
||||
RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget,
|
||||
PartID& partID, RefMessage message );
|
||||
|
||||
PSPropObject();
|
||||
~PSPropObject();
|
||||
|
||||
// From BaseObject
|
||||
int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt);
|
||||
void Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt);
|
||||
void SetExtendedDisplay(int flags);
|
||||
int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags);
|
||||
CreateMouseCallBack* GetCreateMouseCallBack();
|
||||
void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev);
|
||||
void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
|
||||
TCHAR *GetObjectName() {return GetString(IDS_POINT_HELPER_NAME);}
|
||||
|
||||
// From Object
|
||||
ObjectState Eval(TimeValue time);
|
||||
void InitNodeName(TSTR& s) { s = GetString(IDS_DB_POINT); }
|
||||
ObjectHandle ApplyTransform(Matrix3& matrix) {return this;}
|
||||
int CanConvertToType(Class_ID obtype) {return FALSE;}
|
||||
Object* ConvertToType(TimeValue t, Class_ID obtype) {assert(0);return NULL;}
|
||||
void GetWorldBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
|
||||
void GetLocalBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
|
||||
int DoOwnSelectHilite() { return 1; }
|
||||
Interval ObjectValidity(TimeValue t);
|
||||
int UsesWireColor() {return TRUE;}
|
||||
|
||||
// Animatable methods
|
||||
void DeleteThis() { delete this; }
|
||||
Class_ID ClassID() { return PSPROP_CLASS_ID; }
|
||||
void GetClassName(TSTR& s) { s = TSTR(GetString(IDS_DB_POINTHELPER_CLASS)); }
|
||||
int IsKeyable(){ return 0;}
|
||||
int NumSubs() { return 1; }
|
||||
Animatable* SubAnim(int i) { return pblock2; }
|
||||
TSTR SubAnimName(int i) { return TSTR(_T("Parameters"));}
|
||||
IParamArray *GetParamBlock();
|
||||
int GetParamBlockIndex(int id);
|
||||
int NumParamBlocks() { return 1; }
|
||||
IParamBlock2* GetParamBlock(int i) { return pblock2; }
|
||||
IParamBlock2* GetParamBlockByID(short id) { return pblock2; }
|
||||
|
||||
// From ref
|
||||
RefTargetHandle Clone(RemapDir& remap = NoRemap());
|
||||
IOResult Load(ILoad *iload);
|
||||
IOResult Save(ISave *isave);
|
||||
int NumRefs() {return 1;}
|
||||
RefTargetHandle GetReference(int i) {return pblock2;}
|
||||
void SetReference(int i, RefTargetHandle rtarg) {pblock2=(IParamBlock2*)rtarg;}
|
||||
|
||||
// Local methods
|
||||
void InvalidateUI();
|
||||
void UpdateParamblockFromVars();
|
||||
int DrawAndHit(TimeValue t, INode *inode, ViewExp *vpt);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
212
source/tools/pmdexp/Tree.h
Executable file
212
source/tools/pmdexp/Tree.h
Executable file
@ -0,0 +1,212 @@
|
||||
#ifndef __TREE_H
|
||||
#define __TREE_H
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Tree: template class to build a binary tree of elements of class T
|
||||
template <class T,class Cmp>
|
||||
class Tree
|
||||
{
|
||||
public:
|
||||
template <class T>
|
||||
class Node {
|
||||
public:
|
||||
Node<T>* _Next;
|
||||
|
||||
public:
|
||||
Node() : _Left(0), _Right(0) {}
|
||||
|
||||
int _Index;
|
||||
T _Element;
|
||||
Node<T>* _Left;
|
||||
Node<T>* _Right;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
Tree();
|
||||
~Tree();
|
||||
|
||||
void Clear();
|
||||
|
||||
int Find(T& element);
|
||||
int Find(Node<T>* _node,T& _vtx);
|
||||
|
||||
int Add(T& element);
|
||||
int Add(Node<T>* _node,T& _vtx,int _index);
|
||||
|
||||
// total nodes currently in tree
|
||||
int Entries() { return _Size; }
|
||||
|
||||
void Reserve(int count) { _AllocatedNodes.reserve(count); }
|
||||
|
||||
T& operator[](int index) { return _AllocatedNodes[index]->_Element; }
|
||||
|
||||
private:
|
||||
// the root node of the tree
|
||||
Node<T>* _Root;
|
||||
// all allocated nodes in tree
|
||||
std::vector<Node<T>*> _AllocatedNodes;
|
||||
// size of tree
|
||||
int _Size;
|
||||
// the comparison function used to compare two elements in the tree
|
||||
Cmp _Cmp;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Default Tree constructor - create tree with no allocated nodes
|
||||
//***************************************************************************
|
||||
template <class T,class Cmp>
|
||||
Tree<T,Cmp>::Tree()
|
||||
{
|
||||
_Root=0;
|
||||
_Size=0;
|
||||
}
|
||||
|
||||
|
||||
//***************************************************************************
|
||||
// Tree destructor
|
||||
//***************************************************************************
|
||||
template <class T,class Cmp>
|
||||
Tree<T,Cmp>::~Tree()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
template <class T,class Cmp>
|
||||
void Tree<T,Cmp>::Clear()
|
||||
{
|
||||
for (int i=0;i<_AllocatedNodes.Entries();i++) {
|
||||
_NodePile.Release(_AllocatedNodes[i]);
|
||||
}
|
||||
_AllocatedNodes.SetSize(0);
|
||||
_Root=0;
|
||||
_Size=0;
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
// Add : insert an element into the tree; return the index of the treenode
|
||||
// at which the element was added, or, if an identical element was already
|
||||
// in the tree, return it's index
|
||||
//***************************************************************************
|
||||
template <class T,class Cmp>
|
||||
int Tree<T,Cmp>::Add(T& element)
|
||||
{
|
||||
if (_Root) {
|
||||
int index=Add(_Root,element,_Size);
|
||||
if (index==_Size) {
|
||||
// element added to tree
|
||||
_Size++;
|
||||
} else {
|
||||
// element not added
|
||||
}
|
||||
return index;
|
||||
} else {
|
||||
_Root=_NodePile.Allocate();
|
||||
_AllocatedNodes.Add(_Root);
|
||||
_Root->_Element=element;
|
||||
_Root->_Index=0;
|
||||
_Root->_Left=0;
|
||||
_Root->_Right=0;
|
||||
_Size++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//***************************************************************************
|
||||
// Add : insert an element into the given node; return the index of the
|
||||
// treenode at which the element was added, or, if an identical element was
|
||||
// already in the tree, return it's index
|
||||
//***************************************************************************
|
||||
template <class T,class Cmp>
|
||||
int Tree<T,Cmp>::Add(Node<T>* _node,T& element,int _index)
|
||||
{
|
||||
// compare given element with element at given node
|
||||
int cmp=_Cmp.compare(_node->_Element,element);
|
||||
if (cmp==0) {
|
||||
// identical - return index of this node
|
||||
return _node->_Index;
|
||||
} else {
|
||||
if (cmp==-1) {
|
||||
// this node less than new node
|
||||
if (_node->_Left) {
|
||||
// send down left tree
|
||||
return Add(_node->_Left,element,_index);
|
||||
} else {
|
||||
// no left node - create one
|
||||
_node->_Left=_NodePile.Allocate();
|
||||
_AllocatedNodes.Add(_node->_Left);
|
||||
_node->_Left->_Element=element;
|
||||
_node->_Left->_Index=_index;
|
||||
_node->_Left->_Left=0;
|
||||
_node->_Left->_Right=0;
|
||||
return _index;
|
||||
}
|
||||
} else {
|
||||
// this node greater than new node
|
||||
if (_node->_Right) {
|
||||
// send down right tree
|
||||
return Add(_node->_Right,element,_index);
|
||||
} else {
|
||||
// no right node - create one
|
||||
_node->_Right=_NodePile.Allocate();
|
||||
_AllocatedNodes.Add(_node->_Right);
|
||||
_node->_Right->_Element=element;
|
||||
_node->_Right->_Index=_index;
|
||||
_node->_Right->_Left=0;
|
||||
_node->_Right->_Right=0;
|
||||
return _index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
// Find: try and find a matching element in the tree; return the index of the
|
||||
// treenode at which a match was found, or -1 if no match found
|
||||
//***************************************************************************
|
||||
template <class T,class Cmp>
|
||||
int Tree<T,Cmp>::Find(T& element)
|
||||
{
|
||||
return _Root ? Find(_Root,element) : -1;
|
||||
}
|
||||
|
||||
|
||||
//***************************************************************************
|
||||
// Find: try and find a matching element in the given node; return the index
|
||||
// of the treenode at which a match was found, or -1 if no match found
|
||||
//***************************************************************************
|
||||
template <class T,class Cmp>
|
||||
int Tree<T,Cmp>::Find(Node<T>* _node,T& element)
|
||||
{
|
||||
// compare given element with element at given node
|
||||
int cmp=_Cmp.compare(_node->_Element,element);
|
||||
if (cmp==0) {
|
||||
// identical - return index of this node
|
||||
return _node->_Index;
|
||||
} else {
|
||||
if (cmp==-1) {
|
||||
// this node less than new node
|
||||
if (_node->_Left) {
|
||||
// send down left tree
|
||||
return Find(_node->_Left,element);
|
||||
} else {
|
||||
// no left node - no match on this subtree
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// this node greater than new node
|
||||
if (_node->_Right) {
|
||||
// send down right tree
|
||||
return Find(_node->_Right,element);
|
||||
} else {
|
||||
// no left node - no match on this subtree
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
56
source/tools/pmdexp/VNormal.cpp
Executable file
56
source/tools/pmdexp/VNormal.cpp
Executable file
@ -0,0 +1,56 @@
|
||||
#include "MaxInc.h"
|
||||
#include "VNormal.h"
|
||||
|
||||
|
||||
|
||||
void VNormal::add(CVector3D& n,unsigned int s)
|
||||
{
|
||||
if (!(s&smooth) && init) {
|
||||
if (next) {
|
||||
next->add(n,s);
|
||||
} else {
|
||||
next=new VNormal(n,s);
|
||||
}
|
||||
} else {
|
||||
_normal+=n;
|
||||
smooth|=s;
|
||||
init=true;
|
||||
}
|
||||
}
|
||||
|
||||
VNormal* VNormal::get(unsigned int s)
|
||||
{
|
||||
if (smooth&s || !next) {
|
||||
return this;
|
||||
} else {
|
||||
return next->get(s);
|
||||
}
|
||||
}
|
||||
|
||||
void VNormal::get(unsigned int s,CVector3D& normal)
|
||||
{
|
||||
if (smooth&s || !next) {
|
||||
normal=_normal;
|
||||
} else {
|
||||
next->get(s,normal);
|
||||
}
|
||||
}
|
||||
|
||||
void VNormal::normalize()
|
||||
{
|
||||
VNormal *ptr = next, *prev = this;
|
||||
while (ptr) {
|
||||
if (ptr->smooth&smooth) {
|
||||
_normal += ptr->_normal;
|
||||
prev->next = ptr->next;
|
||||
delete ptr;
|
||||
ptr = prev->next;
|
||||
} else {
|
||||
prev = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
|
||||
_normal.Normalize();
|
||||
if (next) next->normalize();
|
||||
}
|
36
source/tools/pmdexp/VNormal.h
Executable file
36
source/tools/pmdexp/VNormal.h
Executable file
@ -0,0 +1,36 @@
|
||||
#ifndef __VNORMAL_H
|
||||
#define __VNORMAL_H
|
||||
|
||||
#include "Vector3D.h"
|
||||
|
||||
class VNormal
|
||||
{
|
||||
public:
|
||||
CVector3D _normal;
|
||||
unsigned int smooth;
|
||||
VNormal *next;
|
||||
bool init;
|
||||
|
||||
VNormal() {
|
||||
smooth=0;
|
||||
next=0;
|
||||
init=false;
|
||||
_normal=CVector3D(0,0,0);
|
||||
}
|
||||
|
||||
VNormal(CVector3D& n,unsigned int s) {
|
||||
next=0;
|
||||
init=true;
|
||||
_normal=n;
|
||||
smooth=s;
|
||||
}
|
||||
|
||||
~VNormal() {delete next;}
|
||||
void add(CVector3D &n,unsigned int s);
|
||||
VNormal* get(unsigned int s);
|
||||
void get(unsigned int s,CVector3D& normal);
|
||||
void normalize();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
158
source/tools/pmdexp/VertexTree.h
Executable file
158
source/tools/pmdexp/VertexTree.h
Executable file
@ -0,0 +1,158 @@
|
||||
#ifndef __VERTEXTREE_H
|
||||
#define __VERTEXTREE_H
|
||||
|
||||
// necessary includes
|
||||
#include "ExpVertex.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// VertexTree: template tree class for building unique vertices in varying fashions; the
|
||||
// template parameter Cmp specifies a function which compares (and possibly modifies)
|
||||
// two vertices
|
||||
// FIXME: ugh .. modifying a vertex already in the tree may cause it to be in the
|
||||
// wrong position in the tree
|
||||
template <class Cmp>
|
||||
class VertexTree
|
||||
{
|
||||
private:
|
||||
struct Node {
|
||||
Node(int index,ExpVertex& vtx) : _index(index), _vertex(vtx), _left(0), _right(0) {}
|
||||
|
||||
// index into the output _vertices array
|
||||
int _index;
|
||||
// reference to actual vertex on the node (vertex itself in _vertices array)
|
||||
ExpVertex& _vertex;
|
||||
// children
|
||||
Node* _left;
|
||||
Node* _right;
|
||||
};
|
||||
|
||||
public:
|
||||
VertexTree(VertexList& vertices) : _vertices(vertices), _treeroot(0) {}
|
||||
|
||||
int insert(const ExpVertex& vtx) {
|
||||
// copy incoming vertex in case the comparison function wants to modify
|
||||
// it before storing it ..
|
||||
ExpVertex copy=vtx;
|
||||
if (_treeroot) {
|
||||
return insert(_treeroot,copy);
|
||||
} else {
|
||||
_vertices.push_back(copy);
|
||||
_treeroot=new Node(0,_vertices.back());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int insert(Node* node,ExpVertex& vtx) {
|
||||
// compare given element with element at given node
|
||||
Cmp compareFn;
|
||||
int cmp=compareFn(node->_vertex,vtx);
|
||||
if (cmp==0) {
|
||||
// matching vertex found
|
||||
return node->_index;
|
||||
} else {
|
||||
if (cmp==-1) {
|
||||
// this node less than new node
|
||||
if (node->_left) {
|
||||
// send down left tree
|
||||
return insert(node->_left,vtx);
|
||||
} else {
|
||||
// no left node - create one
|
||||
_vertices.push_back(vtx);
|
||||
node->_left=new Node(_vertices.size()-1,_vertices.back());
|
||||
return _vertices.size()-1;
|
||||
}
|
||||
} else {
|
||||
// this node greater than new node
|
||||
if (node->_right) {
|
||||
// send down right tree
|
||||
return insert(node->_right,vtx);
|
||||
} else {
|
||||
// no right node - create one
|
||||
_vertices.push_back(vtx);
|
||||
node->_right=new Node(_vertices.size()-1,_vertices.back());
|
||||
return _vertices.size()-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Node* _treeroot;
|
||||
VertexList& _vertices;
|
||||
};
|
||||
|
||||
|
||||
class UniqueVertexCmp {
|
||||
public:
|
||||
int operator()(ExpVertex& left,ExpVertex& right) {
|
||||
// check distance between two vertices ..
|
||||
CVector3D vec3=left.m_Pos-right.m_Pos;
|
||||
if (vec3.GetLength()>0.0001f) {
|
||||
// vertices too far apart to weld .. sort on x,y,z
|
||||
if (left.m_Pos[0]<right.m_Pos[0]) {
|
||||
return -1;
|
||||
} else if (left.m_Pos[0]>right.m_Pos[0]) {
|
||||
return 1;
|
||||
} else {
|
||||
if (left.m_Pos[1]<right.m_Pos[1]) {
|
||||
return -1;
|
||||
} else if (left.m_Pos[1]>right.m_Pos[1]) {
|
||||
return 1;
|
||||
} else {
|
||||
if (left.m_Pos[2]<right.m_Pos[2]) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// weld two points together ..
|
||||
right.m_Pos=left.m_Pos;
|
||||
|
||||
// .. and now compare by texcoords
|
||||
CVector3D vec2(left.m_UVs[0]-right.m_UVs[0],left.m_UVs[1]-right.m_UVs[1],0);
|
||||
if (vec2.GetLength()>0.0001f) {
|
||||
// uvs too far apart to weld .. sort on u,v
|
||||
if (left.m_UVs[0]<right.m_UVs[0]) {
|
||||
return -1;
|
||||
} else if (left.m_UVs[0]>right.m_UVs[0]) {
|
||||
return 1;
|
||||
} else {
|
||||
if (left.m_UVs[1]<right.m_UVs[1]) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// weld uvs
|
||||
right.m_UVs[0]=left.m_UVs[0];
|
||||
right.m_UVs[1]=left.m_UVs[1];
|
||||
|
||||
// compare normals
|
||||
if (left.m_Normal[0]<right.m_Normal[0]) {
|
||||
return -1;
|
||||
} else if (left.m_Normal[0]>right.m_Normal[0]) {
|
||||
return 1;
|
||||
} else {
|
||||
if (left.m_Normal[1]<right.m_Normal[1]) {
|
||||
return -1;
|
||||
} else if (left.m_Normal[1]>right.m_Normal[1]) {
|
||||
return 1;
|
||||
} else {
|
||||
if (left.m_Normal[2]<right.m_Normal[2]) {
|
||||
return -1;
|
||||
} else if (left.m_Normal[2]>right.m_Normal[2]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
5
source/tools/pmdexp/mssccprj.scc
Executable file
5
source/tools/pmdexp/mssccprj.scc
Executable file
@ -0,0 +1,5 @@
|
||||
SCC = This is a Source Code Control file
|
||||
|
||||
[dyexp.dsp]
|
||||
SCC_Aux_Path = "D:\Program Files\Microsoft Visual Studio\Common\VSS\data"
|
||||
SCC_Project_Name = "$/dev/src/tools/max/dyexp", YVAAAAAA
|
BIN
source/tools/pmdexp/pmdexp.smp
Executable file
BIN
source/tools/pmdexp/pmdexp.smp
Executable file
Binary file not shown.
54
source/tools/pmdexp/resource.h
Executable file
54
source/tools/pmdexp/resource.h
Executable file
@ -0,0 +1,54 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by PMDExp.rc
|
||||
//
|
||||
#define IDS_LIBDESCRIPTION 1
|
||||
#define IDS_CATEGORY 2
|
||||
#define IDS_CLASS_NAME 3
|
||||
#define IDS_PMD_CLASS_NAME 3
|
||||
#define IDS_PARAMS 4
|
||||
#define IDS_SPIN 5
|
||||
#define IDS_PSA_CLASS_NAME 6
|
||||
#define IDD_PANEL 101
|
||||
#define IDC_CLOSEBUTTON 1000
|
||||
#define IDC_DOSTUFF 1000
|
||||
#define IDC_COLOR 1456
|
||||
#define IDC_EDIT 1490
|
||||
#define IDC_SPIN 1496
|
||||
|
||||
#define IDD_NEW_POINTPARAM 118
|
||||
|
||||
#define IDC_POINT_SIZE 1059
|
||||
#define IDC_POINT_SIZESPIN 1060
|
||||
#define IDC_POINT_AXIS 1061
|
||||
#define IDC_POINT_MARKER 1062
|
||||
#define IDC_POINT_CROSS 1063
|
||||
#define IDC_POINT_BOX 1064
|
||||
#define IDC_POINT_SCREENSIZE 1065
|
||||
#define IDC_POINT_DRAWONTOP 1066
|
||||
|
||||
#define IDS_DB_POINT 8
|
||||
#define IDS_DB_POINTHELPER 9
|
||||
#define IDS_DB_POINTHELPER_CLASS 10
|
||||
#define IDS_DB_POINT_CLASS 11
|
||||
|
||||
#define IDS_POINT_PARAMS 18
|
||||
#define IDS_POINT_SIZE 19
|
||||
#define IDS_POINT_CENTERMARKER 20
|
||||
#define IDS_POINT_AXISTRIPOD 21
|
||||
#define IDS_POINT_CROSS 22
|
||||
#define IDS_POINT_BOX 23
|
||||
#define IDS_POINT_SCREENSIZE 24
|
||||
#define IDS_POINT_HELPER_NAME 25
|
||||
#define IDS_POINT_DRAWONTOP 26
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
BIN
source/tools/pmdexp/vssver.scc
Executable file
BIN
source/tools/pmdexp/vssver.scc
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user