/********************************************************************** *< FILE: SiteObject.cpp DESCRIPTION: Appwizard generated plugin CREATED BY: HISTORY: *> Copyright (c) 1997, All Rights Reserved. **********************************************************************/ #include "stdafx.h" #include "SiteObject.h" #pragma pack(push,8) static void DrawAxis(ViewExp *vpt, const Matrix3 &tm, float length, BOOL sel=FALSE, BOOL frozen=FALSE); static Box3 GetAxisBox(ViewExp *vpt, const Matrix3 &tm,float length,int resetTM); #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) }; //TODO: Add Parameter block indices for various parameters #define PB_TRIGGER_COUNT 0 #define PB_SITE_SCALE 1 #define PB_TRIGGER_0 2 #define PB_TRIGGER_1 3 #define PB_TRIGGER_2 4 #define PB_TRIGGER_3 5 #define PB_TRIGGER_4 6 #define PB_TRIGGER_5 7 #define PB_TRIGGER_6 8 #define PB_TRIGGER_7 9 #define PB_TRIGGER_8 10 #define PB_TRIGGER_9 11 #define PB_TRIGGER_10 12 #define PB_TRIGGER_11 13 #define PB_TRIGGER_12 14 #define PB_TRIGGER_13 15 #define PB_TRIGGER_14 16 #define PB_TRIGGER_15 17 #define PB_TRIGGER_DEF(x) \ ParamUIDesc( \ PB_TRIGGER_##x, \ TYPE_SINGLECHEKBOX,IDC_SPIN_TRIGGER_##x) //TODO: Add ParamUIDesc's for the various parameters static ParamUIDesc descParam[] = { // Spinner ParamUIDesc( PB_TRIGGER_COUNT, EDITTYPE_INT, IDC_EDIT_NUM_TRIGGER,IDC_SPIN_NUM_TRIGGER, 0,32, 32), ParamUIDesc( PB_SITE_SCALE, EDITTYPE_FLOAT, IDC_EDIT_SIZE,IDC_SPIN_SIZE, 0.0f,1000.0f, 10.0f), PB_TRIGGER_DEF(0), PB_TRIGGER_DEF(1), PB_TRIGGER_DEF(2), PB_TRIGGER_DEF(3), PB_TRIGGER_DEF(4), PB_TRIGGER_DEF(5), PB_TRIGGER_DEF(6), PB_TRIGGER_DEF(7), PB_TRIGGER_DEF(8), PB_TRIGGER_DEF(9), PB_TRIGGER_DEF(10), PB_TRIGGER_DEF(11), PB_TRIGGER_DEF(12), PB_TRIGGER_DEF(13), PB_TRIGGER_DEF(14), PB_TRIGGER_DEF(15) }; #define PARAMDESC_LENGTH 18 #define PBLOCK_LENGTH 18 //TODO: Add ParamBlockDescID's for the various parameters static ParamBlockDescID descVer1[] = { { TYPE_INT, NULL, FALSE, 0 }, { TYPE_FLOAT, NULL, FALSE, 1 }, { TYPE_BOOL, NULL, TRUE, 2 }, { TYPE_BOOL, NULL, TRUE, 3 }, { TYPE_BOOL, NULL, TRUE, 4 }, { TYPE_BOOL, NULL, TRUE, 5 }, { TYPE_BOOL, NULL, TRUE, 6 }, { TYPE_BOOL, NULL, TRUE, 7 }, { TYPE_BOOL, NULL, TRUE, 8 }, { TYPE_BOOL, NULL, TRUE, 9 }, { TYPE_BOOL, NULL, TRUE, 10 }, { TYPE_BOOL, NULL, TRUE, 11 }, { TYPE_BOOL, NULL, TRUE, 12 }, { TYPE_BOOL, NULL, TRUE, 13 }, { TYPE_BOOL, NULL, TRUE, 14 }, { TYPE_BOOL, NULL, TRUE, 15 }, { TYPE_BOOL, NULL, TRUE, 16 }, { TYPE_BOOL, NULL, TRUE, 17 } }; #define CURRENT_DESCRIPTOR descVer1 #define CURRENT_VERSION 1 // class variable for point class. HWND SiteObject::m_hParams = NULL; IObjParam *SiteObject::m_iObjParams; IParamMap *SiteObject::m_pmapParam = NULL; BOOL CALLBACK PointParamProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) { SiteObject *po = (SiteObject*)GetWindowLong(hWnd,GWL_USERDATA); if (!po && msg!=WM_INITDIALOG) return FALSE; return TRUE; } ParamDimension *SiteObject::GetParameterDim(int pbIndex) { switch (pbIndex) { case PB_TRIGGER_COUNT: return defaultDim; case PB_SITE_SCALE: return defaultDim; default: return stdNormalizedDim; } } Animatable* SiteObject::SubAnim(int i) { switch (i) { case 0: return m_pblock; // Parameter block default: return NULL; } } TSTR SiteObject::SubAnimName(int i) { switch (i) { case 0: return TSTR(_T("Parameters")); default: return TSTR(_T("")); } } void SiteObject::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_SITE_PANEL), GetString(IDS_PARAMS), 0); } } void SiteObject::EndEditParams(IObjParam *ip, ULONG flags,Animatable *next) { if (flags&END_EDIT_REMOVEUI ) { DestroyCPParamMap(m_pmapParam); m_pmapParam = NULL; } m_iObjParams = NULL; } ObjectState SiteObject::Eval(TimeValue time) { return ObjectState(this); } #define BOOL_CONTROL_CLASS_ID Class_ID(0x984b8d27,0x938f3e43) SiteObject::SiteObject() { m_suspendSnap = FALSE; m_triggerCount = 0; m_siteScale = 0.0f; MakeRefByID( FOREVER, 0, CreateParameterBlock( descVer1, PARAMDESC_LENGTH, CURRENT_VERSION ) ); #if 0 Control *c; c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_0, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_1, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_2, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_3, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_4, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_5, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_6, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_7, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_8, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_9, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_10, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_11, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_12, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_13, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_14, c, TRUE); c = (Control *) CreateInstance(CTRL_FLOAT_CLASS_ID,BOOL_CONTROL_CLASS_ID); m_pblock->SetController(PB_TRIGGER_15, c, TRUE); #endif m_pblock->SetValue(PB_TRIGGER_0, 0, 0); m_pblock->SetValue(PB_TRIGGER_1, 0, 0); m_pblock->SetValue(PB_TRIGGER_2, 0, 0); m_pblock->SetValue(PB_TRIGGER_3, 0, 0); m_pblock->SetValue(PB_TRIGGER_4, 0, 0); m_pblock->SetValue(PB_TRIGGER_5, 0, 0); m_pblock->SetValue(PB_TRIGGER_6, 0, 0); m_pblock->SetValue(PB_TRIGGER_7, 0, 0); m_pblock->SetValue(PB_TRIGGER_8, 0, 0); m_pblock->SetValue(PB_TRIGGER_9, 0, 0); m_pblock->SetValue(PB_TRIGGER_10, 0, 0); m_pblock->SetValue(PB_TRIGGER_11, 0, 0); m_pblock->SetValue(PB_TRIGGER_12, 0, 0); m_pblock->SetValue(PB_TRIGGER_13, 0, 0); m_pblock->SetValue(PB_TRIGGER_14, 0, 0); m_pblock->SetValue(PB_TRIGGER_15, 0, 0); m_pblock->SetValue(PB_TRIGGER_COUNT,0,m_triggerCount); m_pblock->SetValue(PB_SITE_SCALE,0,m_siteScale); } RefTargetHandle SiteObject::GetReference(int i) { if (i==0) return m_pblock; return NULL; } // This methods sets the ith reference - there are two. void SiteObject::SetReference(int i, RefTargetHandle rtarg) { if (i==0) { m_pblock = (IParamBlock *)rtarg; } } int SiteObjectCreateCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ) { if (msg==MOUSE_POINT||msg==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; } } else { if (msg == MOUSE_ABORT) { return CREATE_ABORT; } } return 1; } static SiteObjectCreateCallBack siteObjectCreateCB; CreateMouseCallBack* SiteObject::GetCreateMouseCallBack() { siteObjectCreateCB.SetObj(this); return(&siteObjectCreateCB); } RefTargetHandle SiteObject::Clone(RemapDir& remap) { SiteObject* newob = new SiteObject(); newob->m_triggerCount = m_triggerCount; newob->m_siteScale = m_siteScale; newob->ReplaceReference(0, m_pblock->Clone(remap)); return(newob); } RefResult SiteObject::NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) { switch (message) { case REFMSG_CHANGE: { if (m_pblock) { if (m_iObjParams) { m_pblock->GetValue(PB_SITE_SCALE,m_iObjParams->GetTime(),m_siteScale, FOREVER); } else { m_pblock->GetValue(PB_SITE_SCALE, 0,m_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; switch (gpn->index) { case PB_TRIGGER_COUNT: gpn->name = TSTR(_T("Trigger Count")); break; case PB_SITE_SCALE: gpn->name = TSTR(_T("Site Scale")); break; case PB_TRIGGER_0: gpn->name = TSTR(_T("Trigger 0")); break; case PB_TRIGGER_1: gpn->name = TSTR(_T("Trigger 1")); break; case PB_TRIGGER_2: gpn->name = TSTR(_T("Trigger 2")); break; case PB_TRIGGER_3: gpn->name = TSTR(_T("Trigger 3")); break; case PB_TRIGGER_4: gpn->name = TSTR(_T("Trigger 4")); break; case PB_TRIGGER_5: gpn->name = TSTR(_T("Trigger 5")); break; case PB_TRIGGER_6: gpn->name = TSTR(_T("Trigger 6")); break; case PB_TRIGGER_7: gpn->name = TSTR(_T("Trigger 7")); break; case PB_TRIGGER_8: gpn->name = TSTR(_T("Trigger 8")); break; case PB_TRIGGER_9: gpn->name = TSTR(_T("Trigger 9")); break; case PB_TRIGGER_10: gpn->name = TSTR(_T("Trigger 10")); break; case PB_TRIGGER_11: gpn->name = TSTR(_T("Trigger 11")); break; case PB_TRIGGER_12: gpn->name = TSTR(_T("Trigger 12")); break; case PB_TRIGGER_13: gpn->name = TSTR(_T("Trigger 13")); break; case PB_TRIGGER_14: gpn->name = TSTR(_T("Trigger 14")); break; case PB_TRIGGER_15: gpn->name = TSTR(_T("Trigger 15")); break; default: gpn->name = TSTR(_T("")); break; } return REF_STOP; } } return REF_SUCCEED; } #define TRIGGER_COUNT_CHUNK 0x0100 #define SITE_SCALE_CHUNK 0x0110 #define TRIGGER_CHUNK 0x0120 IOResult SiteObject::Load(ILoad *iload) { ULONG nb; IOResult res = IO_OK; while (IO_OK==(res=iload->OpenChunk())) { switch (iload->CurChunkID()) { case TRIGGER_COUNT_CHUNK: res = iload->Read(&m_triggerCount,sizeof(m_triggerCount),&nb); break; case SITE_SCALE_CHUNK: res = iload->Read(&m_siteScale,sizeof(m_siteScale),&nb); break; //case TRIGGER_CHUNK: // res = iload->Read(&trigger,sizeof(int)*16,&nb); // break; } res = iload->CloseChunk(); if (res!=IO_OK) { return res; } } return IO_OK; } IOResult SiteObject::Save(ISave *isave) { ULONG nb; isave->BeginChunk(TRIGGER_COUNT_CHUNK); isave->Write(&m_triggerCount,sizeof(m_triggerCount),&nb); isave->EndChunk(); isave->BeginChunk(SITE_SCALE_CHUNK); isave->Write(&m_siteScale,sizeof(m_siteScale),&nb); isave->EndChunk(); //isave->BeginChunk(TRIGGER_CHUNK); //isave->Write(&trigger,sizeof(int)*16,&nb); //isave->EndChunk(); return IO_OK; } void SiteObject::SetExtendedDisplay(int flags) { m_extDispFlags = flags; } void SiteObject::GetLocalBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box ) { Matrix3 tm(1); tm.SetTrans(inode->GetObjectTM(t).GetTrans()); box = GetAxisBox(vpt,tm,m_siteScale, TRUE); } void SiteObject::GetWorldBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box ) { Matrix3 tm; tm = inode->GetObjectTM(t); if(!(m_extDispFlags & EXT_DISP_ZOOM_EXT)) { box = GetAxisBox(vpt,tm,m_siteScale, FALSE); } else { box = Box3(tm.GetTrans(), tm.GetTrans()); } } // From BaseObject int SiteObject::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,m_siteScale); 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 SiteObject::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 SiteObject::Display(TimeValue t, INode* inode, ViewExp *vpt, int flags) { Matrix3 tm = inode->GetObjectTM(t); Point3 pt(0,0,0); vpt->getGW()->setTransform(tm); DrawAxis(vpt,tm,m_siteScale,inode->Selected(),inode->IsFrozen()); 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... // // static void Text( ViewExp *vpt, TCHAR *str, Point3 &pt ) { vpt->getGW()->text( &pt, str ); } static void DrawAStar( ViewExp *vpt, float len ) { Point3 v[NUMVERTS+1]; //we'll scale the points to fill out the dlg size, almost float mult = len; /* for(int i= 0; i < NUMVERTS-1;i++){ v[0] = mult * roseverts[i]; v[1] = mult * roseverts[i+1]; vpt->getGW()->polyline( 2, v, NULL, NULL, FALSE, NULL ); } //close up the polyline v[0] = mult * roseverts[0]; v[1] = mult * roseverts[NUMVERTS-1]; vpt->getGW()->polyline( 2, v, NULL, NULL, FALSE, NULL ); */ for (int k= 0; kgetGW()->polyline( NUMVERTS, v, NULL, NULL, TRUE, NULL ); for (k= 0; kgetGW()->polyline( NUMVERTS, v, NULL, NULL, TRUE, NULL ); //draw an extra line in the north driection // v[0] = Point3(0.0f,0.0f,0.0f); // v[1] = mult * roseverts[3]; // vpt->getGW()->polyline( 2, v, NULL, NULL, FALSE, NULL ); } #define ZFACT (float).005; #define AXIS_COLOR 0.4f,0.4f,0.4f #define SELAXIS_COLOR 1.0f,1.0f,1.0f static void DrawAxis( ViewExp *vpt, const Matrix3 &tm, float length, BOOL sel, BOOL frozen ) { Matrix3 tmn = tm; //float zoom; int limits; // Get width of viewport in world units: --DS //zoom = vpt->GetScreenScaleFactor(tmn.GetTrans())*ZFACT; //tmn.Scale( Point3(zoom,zoom,zoom) ); vpt->getGW()->setTransform( tmn ); limits = vpt->getGW()->getRndLimits(); // vpt->getGW()->setRndLimits( limits & ~GW_Z_BUFFER ); JH 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 ); } #if 0 float corners = .9f * length; Text( vpt, GetString(IDS_EAST_MARK), Point3(corners,0.0f,0.0f) ); Text( vpt, GetString(IDS_WEST_MARK), Point3(-corners,0.0f,0.0f) ); Text( vpt, GetString(IDS_NORTH_MARK), Point3(0.0f,corners,0.0f) ); Text( vpt, GetString(IDS_SOUTH_MARK), Point3(0.0f,-corners,0.0f) ); #endif DrawAStar( vpt, length); vpt->getGW()->setRndLimits( limits ); } static Box3 GetAxisBox(ViewExp *vpt, const Matrix3 &tm,float length,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; // tmn.Scale(Point3(zoom,zoom,zoom)); length *= zoom; if(resetTM) { tmn.IdentityMatrix(); } Box3 box; box += Point3(0.0f, 0.0f, 0.0f) * tmn; box += Point3(4.0f*length, 0.0f, 0.0f) * tmn; box += Point3(0.0f, -9.0f*length, 0.0f) * tmn; box += Point3(0.0f, 0.0f, 4.0f*length) * tmn; box += Point3(-4.0f*length, 0.0f, 0.0f) * tmn; box += Point3(0.0f, 0.0f, -4.0f*length) * tmn; box.EnlargeBy(10.0f/zoom); return box; } #pragma pack(pop)