Files
firestorm/Gameleap/code/mw4/Tools/3DSPlugins/AnimationSuite/cvobject.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

837 lines
18 KiB
C++

/**********************************************************************
*<
FILE: CvObject.cpp
DESCRIPTION: 3dStudio Collision object implementation
CREATED BY: jkyle
HISTORY:
*> Copyright (c) 1997, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include "CvObject.h"
#include "polyshp.h"
#include "mw4.hpp"
#include <Stuff\StuffHeaders.hpp>
#include <adept\collisionvolume.hpp>
#pragma pack(push,8)
Stuff::OBB g_obbdeleteme;
#define MIN_ZOOM 0.0001f
#define RU_UPDATE WM_USER + 666
#define NUMVERTS 7
static Point3 arrow_side[7]=
{
Point3( 0.000000, -1.000000 , 0.00000),
Point3( 0.000000, -0.500000 , 0.40000),
Point3( 0.000000, -0.500000, 0.20000),
Point3( 0.000000, -0.000000, 0.20000),
Point3( 0.000000, -0.000000, 0.00000),
Point3( 0.000000, -0.500000, 0.00000),
Point3( 0.000000, -0.500000, 0.00000)
};
static Point3 arrow_flat[7]=
{
Point3( -0.00000, -1.000000, 0.0),
Point3( 0.400000, -0.50000, 0.0),
Point3( 0.200000, -0.50000, 0.0),
Point3( 0.200000, -0.0, 0.0),
Point3( -0.200000, -0.0, 0.0),
Point3( -0.200000, -0.50000, 0.0),
Point3( -0.400000, -0.50000, 0.0)
};
#define PARAMDESC_LENGTH 1
static ParamUIDesc descParam[] =
{
// Index 0 is the material list
// Spinner
// 3 points (x,y,z_
ParamUIDesc(
CV_SIZE,EDITTYPE_FLOAT,
IDC_EDIT_X,IDC_SPIN_X,
IDC_EDIT_Y,IDC_SPIN_Y,
IDC_EDIT_Z,IDC_SPIN_Z,
0.0,2000.0,1.0,
defaultDim)
};
/*
ParamUIDesc(
2,EDITTYPE_FLOAT,
IDC_OBB_EDIT_LOCALX2,IDC_OBB_SPIN_LOCALX2,
IDC_OBB_EDIT_LOCALY2,IDC_OBB_SPIN_LOCALY2,
IDC_OBB_EDIT_LOCALZ2,IDC_OBB_SPIN_LOCALZ2,
-200.0,200.0,1.0,
defaultDim),
ParamUIDesc(
3,EDITTYPE_FLOAT,
IDC_OBB_EDIT_LOCALRADIUS,IDC_OBB_SPIN_LOCALRADIUS,
0.0,200.0,1.0,defaultDim)
ParamUIDesc(
3,EDITTYPE_FLOAT,
IDC_OBB_EDIT_WORLDX,IDC_OBB_SPIN_WORLDX,
IDC_OBB_EDIT_WORLDY,IDC_OBB_SPIN_WORLDY,
IDC_OBB_EDIT_WORLDZ,IDC_OBB_SPIN_WORLDZ,
-200.0,200.0,1.0,
defaultDim),
ParamUIDesc(
4,EDITTYPE_FLOAT,
IDC_OBB_EDIT_WORLDX2,IDC_OBB_SPIN_WORLDX2,
IDC_OBB_EDIT_WORLDY2,IDC_OBB_SPIN_WORLDY2,
IDC_OBB_EDIT_WORLDZ2,IDC_OBB_SPIN_WORLDZ2,
-200.0,200.0,1.0,
defaultDim),
ParamUIDesc(
5,EDITTYPE_FLOAT,
IDC_OBB_EDIT_WORLDRADIUS,IDC_OBB_SPIN_WORLDRADIUS,
0.0,200.0,1.0,defaultDim),
/*ParamUIDesc(
6,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK1),
ParamUIDesc(
7,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK2),
ParamUIDesc(
8,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK3),
ParamUIDesc(
9,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK4),
ParamUIDesc(
10,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK5),
ParamUIDesc(
11,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK6),
ParamUIDesc(
12,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK7),
ParamUIDesc(
13,TYPE_SINGLECHEKBOX,IDC_OBB_CHECK8),
*/
/* ParamUIDesc(
6,TYPE_SINGLECHEKBOX,(int *)&descCheckID,8)
};
*/
#define PARAMBLOCK_LENGTH 2
//TODO: Add ParamBlockDescID's for the various parameters
static ParamBlockDescID descVer1[] = {
{ TYPE_INT, NULL, FALSE, 0}, // Material flags
{ TYPE_POINT3, NULL, FALSE, 1}
};
// #define CURRENT_DESCRIPTOR descVer1 //Not used
#define CURRENT_VERSION 1
// class variable for point class.
HWND CvObject::m_hParams = NULL;
IObjParam *CvObject::m_iObjParams;
IParamMap *CvObject::m_pmapParam = NULL;
class MyParamMapDlgProc : public ParamMapUserDlgProc
{
virtual BOOL DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
virtual void DeleteThis();
virtual void Update(TimeValue t) {};
};
static MyParamMapDlgProc g_myParamMapDlgProc;
MyParamMapDlgProc::DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
{
//Handle Panel's controls
HWND hwndCtrl = (HWND)lParam;
switch (LOWORD(wParam))
{
case IDC_LIST_MATERIAL:
{
switch (HIWORD(wParam))
{
case CBN_SELCHANGE:
{
int iSel = SendMessage(hwndCtrl,LB_GETCURSEL,0,0);
int data = SendMessage(hwndCtrl,LB_GETITEMDATA,iSel,0);
int iVal;
map->GetParamBlock()->GetValue(CV_MATERIAL,0,iVal,FOREVER);
map->GetParamBlock()->SetValue(CV_MATERIAL,t,data | (iVal & Adept::CollisionVolume::e_GoodEnough) );
break;
}
}
break;
}
case IDC_CHECK_GOODENUF:
{
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
int check = SendMessage(hwndCtrl,BM_GETCHECK,0,0);
int iVal;
map->GetParamBlock()->GetValue(CV_MATERIAL,0,iVal,FOREVER);
iVal &= ~Adept::CollisionVolume::e_GoodEnough;
map->GetParamBlock()->SetValue(CV_MATERIAL,t,iVal | (check?Adept::CollisionVolume::e_GoodEnough:0) );
break;
}
}
break;
}
}
}
}
return TRUE;
}
void MyParamMapDlgProc::DeleteThis()
{
}
/*
BOOL CALLBACK PointParamProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
CvObject *po = (CvObject*)GetWindowLong(hWnd,GWL_USERDATA);
if (!po && msg!=WM_INITDIALOG) return FALSE;
return TRUE;
}
*/
Animatable* CvObject::SubAnim(int i)
{
switch (i) {
case 0: return m_pblock; // Parameter block
default: return NULL;
}
}
TSTR CvObject::SubAnimName(int i)
{
switch (i) {
case 0: return TSTR(_T("Parameters"));
default: return TSTR(_T(""));
}
}
void CvObject::BeginEditParams(
IObjParam *ip, ULONG flags,Animatable *prev)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HINSTANCE loc_hInstance = AfxGetResourceHandle ();
m_iObjParams = ip;
if (m_pmapParam)
{
// Left over
m_pmapParam->SetParamBlock(m_pblock);
} else
{
// Gotta make a new one.
m_pmapParam = CreateCPParamMap(
descParam,PARAMDESC_LENGTH,
m_pblock,
ip,
loc_hInstance,
MAKEINTRESOURCE(IDD_CV_PANEL),
GetString(IDS_PARAMS),
0);
}
m_pmapParam->SetUserDlgProc(&g_myParamMapDlgProc);
HWND hList = GetDlgItem(m_pmapParam->GetHWnd(),IDC_LIST_MATERIAL);
if (hList)
{
int iVal;
m_pblock->GetValue(CV_MATERIAL,m_iObjParams->GetTime(),iVal,FOREVER);
int iCheck = iVal & (Adept::CollisionVolume::e_GoodEnough);
iVal &= ~Adept::CollisionVolume::e_GoodEnough;
for (int x=0;x<Adept::MaterialCount;x++)
{
int index = SendMessage(hList,LB_ADDSTRING,NULL,(LPARAM)Adept::MaterialTypeAsciiToText(x));
SendMessage(hList,LB_SETITEMDATA,index,x);
if (x==iVal)
SendMessage(hList,LB_SETCURSEL,index,0);
}
if (iCheck)
{
HWND hCheck = GetDlgItem(m_pmapParam->GetHWnd(),IDC_CHECK_GOODENUF);
SendMessage(hCheck,BM_SETCHECK,BST_CHECKED,0);
}
}
}
void CvObject::EndEditParams(IObjParam *ip, ULONG flags,Animatable *next)
{
if (flags&END_EDIT_REMOVEUI )
{
DestroyCPParamMap(m_pmapParam);
m_pmapParam = NULL;
}
m_iObjParams = NULL;
}
ObjectState CvObject::Eval(TimeValue time)
{
return ObjectState(this);
}
#define BOOL_CONTROL_CLASS_ID Class_ID(0x984b8d27,0x938f3e43)
CvObject::CvObject()
{
m_suspendSnap = FALSE;
m_pblock = NULL;
// m_pParent = NULL;
//m_pNode = NULL;
MakeRefByID( FOREVER, 0, CreateParameterBlock( descVer1, PARAMBLOCK_LENGTH, CURRENT_VERSION ) );
// MakeRefByID( FOREVER, 0, CreateParameterBlock( NULL, 0, CURRENT_VERSION ) );
}
RefTargetHandle CvObject::GetReference(int i)
{
if (i==0) return m_pblock;
return NULL;
}
// This methods sets the ith reference - there are two.
void CvObject::SetReference(int i, RefTargetHandle rtarg)
{
if (i==0)
{
m_pblock = (IParamBlock *)rtarg;
}
}
int CvObjectCreateCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat )
{
switch (msg)
{
case MOUSE_POINT:
case MOUSE_MOVE:
{
switch(point)
{
case 0:
m_ob->m_suspendSnap = TRUE;
m_p0 = vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE);
mat.SetTrans(m_p0);
break;
case 1:
// mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
m_p1 = vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE);
// m_ob->m_pblock->SetValue(PB_SITE_SCALE,0,Length(m_p1-m_p0));
if (msg==MOUSE_POINT)
{
m_ob->m_suspendSnap = FALSE;
return 0;
}
break;
}
break;
}
case MOUSE_ABORT:
return CREATE_ABORT;
case WM_COMMAND:
{
// int x=1;
break;
}
default:
{
// int x;
// x++;
}
}
return 1;
}
static CvObjectCreateCallBack cvObjectCreateCB;
CreateMouseCallBack* CvObject::GetCreateMouseCallBack()
{
cvObjectCreateCB.SetObj(this);
return(&cvObjectCreateCB);
}
RefTargetHandle CvObject::Clone(RemapDir& remap)
{
CvObject* newob = new CvObject();
// newob->triggerCount = triggerCount;
// newob->siteScale = siteScale;
newob->ReplaceReference(0, m_pblock->Clone(remap));
return(newob);
}
RefResult CvObject::NotifyRefChanged(
Interval changeInt,
RefTargetHandle hTarget,
PartID& partID,
RefMessage message)
{
switch (message)
{
/* case REFMSG_TARGET_DELETED:
{
DeleteReference(FindRef(hTarget));
break;
}
case REFMSG_NODE_LINK:
{
if (m_pNode)
{
m_pParent = m_pNode->GetParentNode();
DeleteReference(FindRef(hTarget));
}
break;
}*/
case REFMSG_CHANGE:
{
if (m_pblock)
{
if (m_iObjParams)
{
// m_pblock->GetValue(PB_Cv_SCALE,m_iObjParams->GetTime(),siteScale, FOREVER);
}
else
{
// m_pblock->GetValue(PB_SITE_SCALE, 0,siteScale, FOREVER);
}
}
if (m_pmapParam) m_pmapParam->Invalidate();
NotifyDependents(FOREVER,PART_OBJ,REFMSG_CHANGE);
if (m_iObjParams) m_iObjParams->RedrawViews(m_iObjParams->GetTime());
break;
}
case REFMSG_GET_PARAM_DIM:
{
GetParamDim *gpd = (GetParamDim*)partID;
switch (gpd->index)
{
case 0:
default: gpd->dim = defaultDim; break;
}
return REF_STOP;
}
case REFMSG_GET_PARAM_NAME:
{
GetParamName *gpn = (GetParamName*)partID;
return REF_STOP;
}
}
return REF_SUCCEED;
}
//#define OBB_MATERIALCHUNK 0x0130
//#define SITE_SCALE_CHUNK 0x0110
//#define TRIGGER_CHUNK 0x0120
// Don't need to save anything except for TM because it is a unit box
IOResult CvObject::Load(ILoad *iload)
{
// CvStruct cv;
/* ULONG nb;
IOResult res = IO_OK;
while (IO_OK==(res=iload->OpenChunk()))
{
switch (iload->CurChunkID())
{
case OBB_CHUNK:
{
res = iload->Read(&m_cvBox,sizeof(m_cvBox),&nb);
}
}
res = iload->CloseChunk();
if (res!=IO_OK)
{
return res;
}
}
*/
return IO_OK;
}
IOResult CvObject::Save(ISave *isave)
{
// CvStruct cv;
/*ULONG nb;
isave->BeginChunk(OBB_CHUNK);
isave->Write(&m_cvBox,sizeof(m_cvBox),&nb);
isave->EndChunk();*/
return IO_OK;
}
void CvObject::SetExtendedDisplay(int flags)
{
m_extDispFlags = flags;
}
void CvObject::GetLocalBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box )
{
Matrix3 tm(1);
tm.SetTrans(inode->GetObjectTM(t).GetTrans());
Point3 vec;
if (GetParamBlock()->GetValue(CV_SIZE,t,vec,FOREVER))
{
box = GetAxisBox(vpt,tm,vec, TRUE);
}
}
void CvObject::GetWorldBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box )
{
Matrix3 tm;
tm = inode->GetObjectTM(t);
if(!(m_extDispFlags & EXT_DISP_ZOOM_EXT))
{
Point3 vec;
if (GetParamBlock()->GetValue(CV_SIZE,t,vec,FOREVER))
{
box = GetAxisBox(vpt,tm,vec, FALSE);
}
}
else
{
box = Box3(tm.GetTrans(), tm.GetTrans());
}
}
// From BaseObject
int CvObject::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);
GraphicsWindow *gw = vpt->getGW();
tm = inode->GetObjectTM(t);
MakeHitRegion(hitRegion, type, crossing, 4, p);
gw->setRndLimits(((savedLimits = gw->getRndLimits())|GW_PICK)&~GW_ILLUM);
gw->setHitRegion(&hitRegion);
gw->clearHitCode();
DrawAxis(vpt,tm,1.0,inode->Selected(),inode->IsFrozen());
Point3 vec;
if (GetParamBlock()->GetValue(CV_SIZE,t,vec,FOREVER))
{
if (LengthSquared(vec)<0.00001)
{
vec = Point3(1.0,1.0,1.0);
}
vec = vec * 0.5f;
Box3 b(vec*-1,vec);
DrawBox(vpt->getGW(),b);
}
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();
}
void CvObject::Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt)
{
// if(m_suspendSnap)
// {
// return;
// }
Matrix3 tm = inode->GetObjectTM(t);
GraphicsWindow *gw = vpt->getGW();
gw->setTransform(tm);
Matrix3 invPlane = Inverse(snap->plane);
// Check for upside-down coordinate system in viewport
float ybias = (float)gw->getWinSizeY() - 1.0f;
// 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);
Point3 screen3;
Point2 screen2;
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->transPointFWin(&thePoint,&screen3); //this is for MAX 1.2
gw->transPoint(&thePoint, &screen3);
screen3.y = ybias - screen3.y;
screen2.x = screen3.x;
screen2.y = screen3.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 CvObject::Display(TimeValue t, INode* inode, ViewExp *vpt, int flags)
{
Matrix3 tm = inode->GetObjectTM(t);
Point3 pt(0,0,0);
vpt->getGW()->setTransform(tm);
Point3 vec;
DrawAxis(vpt,tm,1.0,inode->Selected(),inode->IsFrozen());
if (GetParamBlock()->GetValue(CV_SIZE,t,vec,FOREVER))
{
if (LengthSquared(vec)<0.00001)
{
vec = Point3(1.0,1.0,1.0);
}
vec = vec * 0.5f;
Box3 b(vec*-1,vec);
DrawBox(vpt->getGW(),b);
}
if(!inode->IsFrozen())
{
vpt->getGW()->setColor(LINE_COLOR,1.0f,0.9f,0.0f);
}
vpt->getGW()->marker(&pt,X_MRKR);
return(0);
}
/*--------------------------------------------------------------------*/
//
// Stole this from scene.cpp
// Probably couldn't hurt to make an API...
//
//
#define ZFACT (float).005;
#define AXIS_COLOR 0.4f,0.4f,0.4f
#define SELAXIS_COLOR 1.0f,1.0f,1.0f
void CvObject::DrawAxis( ViewExp *vpt, const Matrix3 &tm, float length, BOOL sel, BOOL frozen )
{
Matrix3 tmn = tm;
int limits;
vpt->getGW()->setTransform( tmn );
limits = vpt->getGW()->getRndLimits();
if (sel)
{
vpt->getGW()->setColor( TEXT_COLOR, SELAXIS_COLOR );
vpt->getGW()->setColor( LINE_COLOR, SELAXIS_COLOR );
}
else if (!frozen)
{
vpt->getGW()->setColor( TEXT_COLOR, AXIS_COLOR );
vpt->getGW()->setColor( LINE_COLOR, AXIS_COLOR );
}
vpt->getGW()->setRndLimits( limits );
}
void CvObject::DrawBox(GraphicsWindow *gw, Box3 &box) {
Point3 pt[5];
pt[0] = box[0];
pt[1] = box[1];
pt[2] = box[3];
pt[3] = box[2];
gw->polyline(4, pt, NULL, NULL, 1, NULL);
pt[0] = box[4];
pt[1] = box[5];
pt[2] = box[7];
pt[3] = box[6];
gw->polyline(4, pt, NULL, NULL, 1, NULL);
pt[0] = box[0];
pt[1] = box[4];
gw->polyline(2, pt, NULL, NULL, 0, NULL);
pt[0] = box[1];
pt[1] = box[5];
gw->polyline(2, pt, NULL, NULL, 0, NULL);
pt[0] = box[2];
pt[1] = box[6];
gw->polyline(2, pt, NULL, NULL, 0, NULL);
pt[0] = box[3];
pt[1] = box[7];
gw->polyline(2, pt, NULL, NULL, 0, NULL);
}
Box3 CvObject::GetAxisBox(ViewExp *vpt, const Matrix3 &tm,Point3 vec,int resetTM)
{
Matrix3 tmn = tm;
float zoom;
// Get width of viewport in world units: --DS
zoom = vpt->GetScreenScaleFactor(tmn.GetTrans())*ZFACT;
if (zoom < MIN_ZOOM) zoom = 1.0f;
if(resetTM)
{
tmn.IdentityMatrix();
}
Box3 box;
box += Point3(vec.x*0.5f, 0.0f, 0.0f) *tm;
box += Point3(0.0f, vec.y*0.5f, 0.0f) *tm;
box += Point3(0.0f, 0.0f, vec.z*0.5f) *tm;
box += Point3(-vec.x*0.5f, 0.0f, 0.0f) *tm;
box += Point3(0.0f, -vec.y*0.5f, 0.0f) *tm;
box += Point3(0.0f, 0.0f, -vec.z*0.5f) *tm;
box.EnlargeBy(10.0f/zoom);
return box;
}
/*void CvObject::CalcLocalBoundBox(int nSamples,ViewExp *vpt,Box3& box)
// nSamples must be at least 1
{
// Calculate the bounding box of the parent
if (m_pParent)
{
Object *pObject = m_pParent->GetObjectRef();
if (pObject)
{
//Interval iv = m_iObjParams->GetAnimRange();
TimeValue tStart,tEnd,tInc;
tStart = GetAnimStart();
tEnd = GetAnimEnd();
if (nSamples<1)
nSamples = 1;
tInc = (tEnd - tStart) / nSamples;
pObject->GetLocalBoundBox(tStart,m_pParent,vpt,box);
for (TimeValue t = (tStart+tInc);t<=tEnd;t+=tInc)
{
Box3 boxnew;
pObject->GetLocalBoundBox(t,m_pParent,vpt,boxnew);
box+=boxnew;
}
}
}
}
void CvObject::CalcOBBChildren(INode *pNode)
{
/* if (pNode)
{
char *pName = pNode->GetName();
if (0 == strncmp(pName,"cv_",3))
{
Class_ID c1 = CVOBJECT_CLASS_ID;
Class_ID c2 = pNode->GetObjectRef()->ClassID();
if (pNode != m_pNode)
{
MakeRefByID( FOREVER, 2, pNode);
Box3 box;
pNode->GetObjectRef()->GetLocalBoundBox(0,pNode,NULL,box);
m_cvBox+=box;
}
}
else
{
for (int x = 0;x<pNode->NumberOfChildren();x++)
{
INode *pChild = pNode->GetChildNode(x);
CalcOBBChildren(pChild);
}
}
}*/
//}
#pragma pack(pop)