Skinny3DSources ƒ/3DAppSpecific.c

#include <Printing.h>
#include <AppleEvents.h>
#include <Windows.h>
#include <Fonts.h>
#include <ToolUtils.h>
#include <TextUtils.h>
#include <LowMem.h>
#include <SegLoad.h>
#include <Devices.h>
#include <Quickdraw.h>
 
 
#include    "SkinnyMain.h"
#include    "3DAppSpecific.h"
#include    "ValueControls.h"
 
#include    "BoxModel.h"
#include    "Cameras.h"
#include    "Lights.h"
 
/*
prototypes of QD3DFunctions called from the application (in "3DAppSpecific.h"):
 
OSErr   InitializeApplication(void);
void    Cleanup(void);
Boolean PreflightNew(void); // return TRUE if "New" will work
void    DoNew(void);
void    DoOpen(void);
void    DoClose(void); // FrontWindow() only
void    DoSave(void);
void    DoSaveAs(void);
void    DoPrint(void);
void    DoClickInContent(EventRecord *evt, WindowPtr theWindow);
void    ActivateWindow(WindowPtr newFrontWindow, Boolean active);
 
void    DoAppSpecificMenu(long menuNumber, long itemNumber);
void    UpdateAppMenus(void);
 
void    DrawAppSpecificContent(WindowPtr theWindow);
 
*/
enum { windowID = 128 }; // resource ID
 
#define kWidth      600
#define kHeight     400
#define kCntrlWdth  200
 
typedef enum DirectionType {
    none, left, right, up, down, reset
} DirectionType;
 
// data structure tied to a window (handle into wRefCon)
struct WindowInfo {
    TQ3ViewObject       view;
    TQ3GroupObject      model;
    TQ3TransformObject  rotation;
    TQ3StyleObject      interpolation;
    TQ3StyleObject      backfacing;
    TQ3StyleObject      fillstyle;
    TQ3ShaderObject         shader;
    VccPtr              vcc;
};
typedef struct WindowInfo WindowInfo, **WinfoH;
 
 
// local prototypes
void AddHierarchMenu(long parentMenuID, long item, long subMenuID);
 
static  WinfoH  Make3Dinfo(WindowPtr theWindow);
static  void    Dispose3Dinfo(WinfoH wInfoH);
 
static  void    Draw3DPart(WinfoH wInfo);
static  void    DrawDirections(void);
static  void    DrawTriangle(Point p, DirectionType e);
static  void    DrawControlPart(WinfoH wInfo);
static  Boolean         ControlClick(Point pt, WindowPtr theWindow);
static  DirectionType   RotationClick(Point pt, WindowPtr theWindow);
 
static  TQ3DrawContextObject    MakeDrawContext(WindowPtr theWindow);
 
static void SetRotation(WinfoH wInfo, DirectionType direction);
 
static  void    ChooseDebug(long item);
 
static  void    ChooseRenderer  (TQ3ViewObject view, long item);
 
static  void    ChooseBackfacing    (WinfoH wInfo, long item);
static  void    ChooseInterpolation (WinfoH wInfo, long item);
static  void    ChooseFillStyle     (WinfoH wInfo, long item);
 
static  void    MakeGeometry        (WinfoH wInfo, long selector);
static  void    UpdateGeometry      (WinfoH wInfo);
static  void    MakeDataControl     (WinfoH wInfo);
static  void    MakeNewCamera       (WinfoH wInfo, long selector);
static  void    MakeCameraControl   (WinfoH wInfo);
static  void    MakeLightControl    (WinfoH wInfo, long selector);
 
static  Boolean GoodFrontWindow(WindowPtr *theWindow, WinfoH *wInfo);
void    CheckMenuItem(short menuID, short checkItem);
 
static  Rect    gWindowRect;
static  Rect    g3DViewRect;
Rect    gContrlRect; // needed to make VCC
 
static long curGeometry;
static long curCamera;
static long curLight;
static long curRenderer;
static long curBackfacing;
static long curInterpol;
static long curFillStyle;
 
 
extern  THPrint     gPrintH;
extern  CursHandle  gWatchCursor;
 
 
//---------------------------------
OSErr   InitializeApplication(void)
{
    MenuHandle  mh;
    Handle      menuBar = GetNewMBar(128);
    TQ3Status   status;
    
    if (!menuBar) {
        return -30000;       
    }
    SetMenuBar(menuBar);
    AppendResMenu(GetMenuHandle(mApple),'DRVR');
    DrawMenuBar();
    DisposeHandle(menuBar);
    
    AddHierarchMenu(mControl, iCamera,      mCameras);
    AddHierarchMenu(mControl, iLight,       mLight);
    AddHierarchMenu(mControl, iRenderer,    mRenderer);
    
    AddHierarchMenu(mStyle,   iBackfacing,  mBackfacing);
    AddHierarchMenu(mStyle,   iInterpol,    mInterpolation);
    AddHierarchMenu(mStyle,   iFillStyle,   mFillStyle);
    
    gPrintH = (THPrint)NewHandle(sizeof(TPrint));
    if (gPrintH == nil)
        return -30001;       
    
    status = Q3Initialize();
    if ( status == kQ3Failure )
        return ( -30002 );
 
    SetRect(&gWindowRect, 0, 0, kWidth, kHeight);
    SetRect(&g3DViewRect, 0, 0, kWidth - kCntrlWdth, kHeight);
    SetRect(&gContrlRect, kWidth - kCntrlWdth, 0, kWidth, kHeight);
    InsetRect(&gContrlRect, 1, 1);
 
    return ( noErr );
}
 
//---------------------------------------------------------------
void AddHierarchMenu(long parentMenuID, long item, long subMenuID)
{
    MenuHandle  mh;
    
    mh = GetMenu(subMenuID);
    InsertMenu(mh, -1);
    mh = GetMenuHandle(parentMenuID);
    SetItemCmd(mh, item, 0x1B);
    SetItemMark(mh, item, (char)subMenuID);
}
 
 
//------------------------
void    Cleanup(void)
//------------------------
{
    TQ3Status status = Q3Exit();
    // don't worry - the application heap disappears anyway,
    // and we can't do anything about any failure of Q3Exit().
}
 
//------------------------------------
Boolean PreflightNew(void)
{
    long maxm, grow;
    
    maxm = MaxMem(&grow);
    return (maxm > 1000000); // that's what our new QD3D window roughly needs!
}
 
//------------------------------------
void DoNew(void)
{
    WindowPtr   myWindow;
    WinfoH      intoRefCon;
    
    myWindow = GetNewCWindow(windowID, nil, (WindowPtr)(-1));
    if ( myWindow == nil ) {
        ErrMsg("\pGetNewCWindow failed.");
        return;
    }
    SizeWindow(myWindow, kWidth, kHeight, false);
    ShowWindow(myWindow);
    SetPort(myWindow);
    intoRefCon = Make3Dinfo(myWindow);
    if (intoRefCon != nil) {
        SetWRefCon(myWindow, (long)intoRefCon );
        DoAppSpecificMenu(mGeometry, iBox);
    }
    else {
        ErrMsg("\pMake3Dinfo failed");
        DisposeWindow(myWindow);
    }
}
 
 
//------------------------------------
void DoOpen(void)
{
}
 
//------------------------------------
void DoClose(void)
{
    WindowPtr   theWindow;
    WinfoH      theWinfo;
    
    if (GoodFrontWindow(&theWindow, &theWinfo))
        Dispose3Dinfo(theWinfo);
    CloseWindow(theWindow);
}
 
//------------------------------------
void DoSave(void)
{
}
 
//------------------------------------
void DoSaveAs(void)
{
}
 
//------------------------------------
void DoPrint( void )
{
    SysBeep(50);
}
 
//----------------------------------------------------------------------
void DoClickInContent(EventRecord *evt, WindowPtr theWindow)
{
    WinfoH          wInfo;
    ControlHandle   ctlh;
    Point           pt;
    Rect            frame;
    DirectionType   direction;
    int             part;
    
    wInfo = (WinfoH)GetWRefCon(theWindow);
    if (wInfo == nil) return;
 
    pt = evt->where;
    GlobalToLocal(&pt);
    if ((part = FindControl(pt, theWindow, &ctlh)) != kControlNoPart) {
        /* TrackControl Goes Here */
    }
 
    if (ControlClick(pt, theWindow)) { // our special ValueControl panel
        InvalRect(&g3DViewRect);
        return;
    }
    
    if ((direction = RotationClick(pt, theWindow)) != none) {
        SetRotation(wInfo, direction);
        InvalRect(&g3DViewRect);
    }
}
 
 
//------------------------------------
static void SetRotation(WinfoH wInfo, DirectionType direction)
{
    TQ3Status           status;
    TQ3BoundingBox      bBox;
    TQ3Point3D          center, tmpPt;
    TQ3Axis             axis;
    float               angle;
    TQ3Matrix4x4            previousMatrix, matrix, newMatrix, *tmp;
    TQ3TransformObject  transform;
    TQ3RotateAboutPointTransformData    tData;
 
    TQ3GroupObject      group;
    TQ3GroupPosition        position;
    TQ3ObjectType       type;
    TQ3Object           theObj;
    TQ3BoxData          boxData;
    
    if (direction == reset) {
        Q3Matrix4x4_SetIdentity(&matrix);
        status = Q3MatrixTransform_Set((**wInfo).rotation, &matrix);
        return;
    }
    
//  Q3View_StartBounds((**wInfo).view);
//  status = Q3DisplayGroup_BoundingBox((**wInfo).model, 
//                                      &bBox, 
//                                      kQ3ComputeBoundsApproximate,
//                                      (**wInfo).view);
//  Q3View_EndBounds((**wInfo).view);
    
    // Cannot link with the above, for some temporary reason.
    // Temporary workaround:
    group = (**wInfo).model;
    type = kQ3ShapeTypeGeometry;
    status = Q3Group_GetFirstPositionOfType(group, type, &position);
    if (status == kQ3Failure)
        return;
        
    status = Q3Group_GetPositionObject ( group, position, &theObj);
    type = Q3Geometry_GetType ( theObj );
    switch (type) {
        case kQ3GeometryTypeBox: 
                status = Q3Box_GetData( theObj, &boxData );
                bBox.min = boxData.origin;
                Q3Point3D_Vector3D_Add(&bBox.min, &boxData.orientation, &tmpPt);
                Q3Point3D_Vector3D_Add(&tmpPt,    &boxData.majorAxis, &tmpPt);
                Q3Point3D_Vector3D_Add(&tmpPt,    &boxData.minorAxis, &bBox.max);
            break;
            // add here the cases of other geometries, if required
        default:
            Q3Point3D_Set(&bBox.min, -1.0, -1.0, -1.0);
            Q3Point3D_Set(&bBox.max, 1.0, 1.0, 1.0);
            return;
    }
 
    Q3Point3D_RRatio(&bBox.min, &bBox.max, 1.0, 1.0, &center); // mid-point
    
    status = Q3MatrixTransform_Get((**wInfo).rotation, &previousMatrix);
    
    if ((direction == up) || (direction == down))
        axis = kQ3AxisX;
    else
        axis = kQ3AxisY;
    if ((direction == right) || (direction == down))
        angle = 0.1;
    else
        angle = -0.1;
        
    tData.axis = axis;
    tData.radians = angle;
    tData.about = center;
    transform = Q3RotateAboutPointTransform_New(&tData);
    if (transform == NULL)
        return;
    tmp = Q3Transform_GetMatrix(transform, &matrix);
    
    Q3Matrix4x4_Multiply(&previousMatrix, &matrix, &newMatrix);
    status = Q3MatrixTransform_Set((**wInfo).rotation, &newMatrix);
} 
 
 
//------------------------------------
void    ActivateWindow(WindowPtr newFrontWindow, Boolean active)
{
    if (active)
        ;// This window is now active.  Controls should be enabled, etc.
    else
        ;
    //  do anyting necessary to deactivate your windows here.
    //  controls should be dimmed, etc.
}
 
//------------------------------------
void    DoAppSpecificMenu(long menuNumber, long item)
{
    WindowPtr   theWindow;
    WinfoH      wInfo;
    
    if (menuNumber == mDebug) {
        ChooseDebug(item); return;
    }
    
    if (!GoodFrontWindow(&theWindow, &wInfo)) {
//      ErrMsg("\pBad FrontWindow");
        return;
    }
    
    switch (menuNumber) {
        case mGeometry: MakeGeometry(wInfo, item);
                        MakeDataControl(wInfo);
                        curGeometry = item;
                        break;
        case mControl:  if (item == iData)
                        MakeDataControl(wInfo);
                        break;
                
        case mCameras:  if (item != curCamera) {
                            MakeNewCamera(wInfo, item);
                            curCamera = item;
                        }
                        MakeCameraControl(wInfo);
                        break;
        case mLight:    MakeLightControl(wInfo, item);
                        curLight = item;
                        break;
        case mRenderer: ChooseRenderer((**wInfo).view, item);
                        break;
 
        case mBackfacing:
                        ChooseBackfacing(wInfo, item);
                        curBackfacing = item;
                        break;
        case mInterpolation:
                        ChooseInterpolation(wInfo, item);
                        curInterpol = item;
                        break;
        case mFillStyle:
                        ChooseFillStyle(wInfo, item);
                        curFillStyle = item;
                        break;
        
        default:
            break;
    }
    InvalRect(&theWindow->portRect);
}
 
 
//------------------------------------
void UpdateAppMenus(void)
{
    long free, maxm, grow;
    Str255 freeItem = "\pFreeMem = ";
    Str255 maxmItem = "\pMaxMem  = ";
    Str255 s;
    WindowPtr   theWindow;
    WinfoH      wInfo;
    MenuHandle  menu = GetMenuHandle(mDebug);
    
    free = FreeMem();
    NumToString(free, s);
    pcat(freeItem, s);
    SetMenuItemText(menu, iFreeMem, freeItem);
 
    maxm = MaxMem(&grow);
    NumToString(maxm, s);
    pcat(maxmItem, s);
    SetMenuItemText(menu, iMaxMem, maxmItem);
    
    CheckMenuItem(mGeometry, curGeometry);
    CheckMenuItem(mCameras, curCamera);
    CheckMenuItem(mLight, curLight);
    CheckMenuItem(mRenderer, curRenderer);
    CheckMenuItem(mBackfacing, curBackfacing);
    CheckMenuItem(mInterpolation, curInterpol);
    CheckMenuItem(mFillStyle, curFillStyle);
    
    if (!GoodFrontWindow(&theWindow, &wInfo)) {
        DisableItem(GetMenuHandle(mGeometry), 0);
        DisableItem(GetMenuHandle(mControl), 0);
        DisableItem(GetMenuHandle(mStyle), 0);
    }
    else {
        EnableItem(GetMenuHandle(mGeometry), 0);
        EnableItem(GetMenuHandle(mControl), 0);
        EnableItem(GetMenuHandle(mStyle), 0);
    }
    DrawMenuBar();
}
 
 
void CheckMenuItem(short menuID, short checkItem)
{
    MenuHandle  menu = GetMenuHandle(menuID);
    short       n = CountMItems(menu);
    short       i;
    
    for (i = 1; i <= n; i++)
        CheckItem(menu, i, (i == checkItem));
}
 
 
//------------------------------------
static  void    ChooseRenderer(TQ3ViewObject view, long item)
{
    TQ3Status           status;
    TQ3ObjectType       rType;
    TQ3RendererObject   renderer;
    
    switch (item) {
        case iWireFrame: rType = kQ3RendererTypeWireFrame;  break;
        case iZBuffer:   rType = kQ3RendererTypeInteractive;    break;
    }
    
//  status = Q3View_SetRendererByType(view, rType);
//  alternatively:
    renderer = Q3Renderer_NewFromType(rType);
    if (renderer == nil)
        return;
    status = Q3View_SetRenderer(view, renderer);
    if (status == kQ3Failure )
        return;
        
    Q3Object_Dispose(renderer) ;
 
    curRenderer = item;
}
 
//------------------------------------
static  void    ChooseBackfacing(WinfoH wInfo, long item)
{
    TQ3Status           status;
    TQ3BackfacingStyle  bfStyle;
    
    switch (item) {
        case iBoth:     bfStyle = kQ3BackfacingStyleBoth;           break;
        case iRemove:   bfStyle = kQ3BackfacingStyleRemove;     break;
        case iFlip:     bfStyle = kQ3BackfacingStyleFlip;       break;
    }
    if ((**wInfo).backfacing != nil)
        status = Q3Object_Dispose((**wInfo).backfacing);
    (**wInfo).backfacing = Q3BackfacingStyle_New(bfStyle);
}
 
//------------------------------------
static  void    ChooseInterpolation(WinfoH  wInfo, long item)
{
    TQ3Status               status;
    TQ3InterpolationStyle   ipStyle;
    
    switch (item) {
        case iNone:     ipStyle = kQ3InterpolationStyleNone;    break;
        case iVertex:   ipStyle = kQ3InterpolationStyleVertex;  break;
        case iPixel:    ipStyle = kQ3InterpolationStylePixel;   break;
    }
    if ((**wInfo).interpolation != nil)
        status = Q3Object_Dispose((**wInfo).interpolation);
    (**wInfo).interpolation = Q3InterpolationStyle_New(ipStyle);
}
 
 
//------------------------------------
static  void    ChooseFillStyle(WinfoH  wInfo, long item)
{
    TQ3Status           status;
    TQ3FillStyle            flStyle;
    
    switch (item) {
        case iFilled:   flStyle = kQ3FillStyleFilled;   break;
        case iEdges:    flStyle = kQ3FillStyleEdges;    break;
        case iPoints:   flStyle = kQ3FillStylePoints;   break;
    }
    if ((**wInfo).fillstyle != nil)
        status = Q3Object_Dispose((**wInfo).fillstyle);
    (**wInfo).fillstyle = Q3FillStyle_New(flStyle);
}
 
 
//------------------------------------
static void ChooseDebug(long item)
{   
}
 
//////////////////////////////////////////////////////////
//===========================================
WinfoH Make3Dinfo(WindowPtr theWindow)
{
    WinfoH              wInfo;
    TQ3DrawContextObject drawContext;
    TQ3RendererObject   renderer;
    TQ3GroupObject      lightGroup;
    TQ3Matrix4x4            matrix;
    TQ3Status           status;
    
    wInfo = (WinfoH) NewHandleClear(sizeof(WindowInfo));
    if (wInfo == nil) {
        ErrMsg("\pNewHandle(WindowInfo) failed");
        return nil;
    }
    
    (**wInfo).view = Q3View_New();  // Escher View
    if ( (**wInfo).view == nil ) goto fail;
 
    // Set DrawContext, Camera, lightGroup and Renderer
    drawContext = MakeDrawContext(theWindow);
    if ( drawContext == nil ) goto fail;
    status = Q3View_SetDrawContext((**wInfo).view, drawContext);
    if ( status == kQ3Failure ) goto fail;
    status = Q3Object_Dispose(drawContext); 
 
    MakeNewCamera(wInfo, iAspectRatio);
 
    lightGroup = MakeLights();
    if ( lightGroup == nil ) goto fail;
    status = Q3View_SetLightGroup((**wInfo).view, lightGroup);
    if ( status == kQ3Failure ) goto fail;
    // decrease reference count (was increased by Q3View_SetLightGroup
    Q3Object_Dispose(lightGroup);
    
    curLight = 0; // selector for valuecontrol initially undefined
    
    ChooseRenderer((**wInfo).view, iWireFrame);
 
    (**wInfo).shader = Q3LambertIllumination_New() ;
 
    //  Create model to display.
    (**wInfo).model = Q3OrderedDisplayGroup_New();
    if ( (**wInfo).model == nil ) goto fail;
 
    // Set identity transform
    Q3Matrix4x4_SetIdentity(&matrix);
    (**wInfo).rotation = Q3MatrixTransform_New(&matrix);
 
    //  Configure the drawing styles.
    ChooseInterpolation (wInfo, iNone);     curInterpol     = iNone;
    ChooseBackfacing    (wInfo, iBoth);     curBackfacing   = iBoth;
    ChooseFillStyle     (wInfo, iFilled);   curFillStyle    = iFilled;
 
    return wInfo;
    
fail:
    Dispose3Dinfo(wInfo);
    return nil;
}
 
// ---------------------------------------------------------------------
void    Dispose3Dinfo(WinfoH wInfoH)
{
    TQ3Status   status;
    
    if (wInfoH == nil) return;
    if ((**wInfoH).view != nil)
        status = Q3Object_Dispose((**wInfoH).view);
    if ((**wInfoH).model != nil) {
        // need to dispose properly of everything ...
        status = Q3Object_Dispose((**wInfoH).model);
    }
    if ((**wInfoH).interpolation != nil)
        status = Q3Object_Dispose((**wInfoH).interpolation);
    if ((**wInfoH).backfacing != nil)
        status = Q3Object_Dispose((**wInfoH).backfacing);
    if ((**wInfoH).fillstyle != nil)
        status = Q3Object_Dispose((**wInfoH).fillstyle);
    DisposeHandle((Handle) wInfoH);
}
 
 
// --------------------------------------------------------------------
TQ3DrawContextObject MakeDrawContext(WindowPtr theWindow)
{
    TQ3DrawContextData      drawContextData;
    TQ3MacDrawContextData   macDrawContextData;
    TQ3ColorARGB            ClearColor;
    
    //  Set the background color.
    ClearColor.a = 1.0; /* Solid alpha */
    ClearColor.r = 1.0;
    ClearColor.g = 1.0;
    ClearColor.b = 1.0;
    
    //  Fill in draw context data.
    drawContextData.clearImageMethod = kQ3ClearMethodWithColor;
    drawContextData.clearImageColor = ClearColor;
    drawContextData.pane.min.x = (theWindow->portRect).left;
    drawContextData.pane.max.x = (theWindow->portRect).right;
    drawContextData.pane.min.y = (theWindow->portRect).top;
    drawContextData.pane.max.y = (theWindow->portRect).bottom;
    drawContextData.paneState = kQ3True;
    drawContextData.maskState = kQ3False;
    drawContextData.doubleBufferState = kQ3True;
 
    macDrawContextData.drawContextData = drawContextData;
    macDrawContextData.window = (CWindowPtr) theWindow;
    macDrawContextData.library = kQ3Mac2DLibraryQuickDraw;
    macDrawContextData.viewPort = nil;
    macDrawContextData.grafPort = (CGrafPtr) theWindow;
 
    //  Create draw context.
    return  Q3MacDrawContext_New(&macDrawContextData);
    
}
 
//----------------------------------------------------------------------------------
void    DrawAppSpecificContent(WindowPtr theWindow)
{
    WinfoH wInfo;
    
    wInfo = (WinfoH)GetWRefCon(theWindow);
    if (wInfo == nil)
        return;
    Draw3DPart(wInfo);
    DrawControlPart(wInfo);
}
 
//----------------------------------------------------------------------------------
static void Draw3DPart(WinfoH wInfo)
{
    TQ3Status               status;
    //TQ3DrawContextObject      myDrawContext;
    TQ3ViewStatus           myViewStatus;
    WindowInfo              wI = **wInfo;
    TQ3RendererObject       myRenderer ;
    
    status = Q3View_StartRendering(wI.view);
    if ( status == kQ3Failure ) goto bail;
    
    if (curInterpol == iPixel)
        SetCursor(*gWatchCursor);
 
    do {
        if (curFillStyle == iFilled) {
            status = Q3Shader_Submit(wI.shader, wI.view);
            if ( status == kQ3Failure ) goto bail;
        }
        status = Q3Style_Submit(wI.interpolation, wI.view);
        if ( status == kQ3Failure ) goto bail;
        status = Q3Style_Submit(wI.backfacing, wI.view);
        if ( status == kQ3Failure ) goto bail;
        status = Q3Style_Submit(wI.fillstyle, wI.view);
        if ( status == kQ3Failure ) goto bail;
        status = Q3Transform_Submit( wI.rotation, wI.view);
        if ( status == kQ3Failure ) goto bail;
        status = Q3DisplayGroup_Submit(wI.model, wI.view);
        if ( status == kQ3Failure ) goto bail;
        myViewStatus = Q3View_EndRendering(wI.view);
    } while ( myViewStatus == kQ3ViewStatusRetraverse );
    
    // get the renderer from the view
    status = Q3View_GetRenderer( wI.view, &myRenderer ) ;
    if ( status == kQ3Failure ) goto bail;
            
    // call sync 'cos we are going to draw over it.  This is needed since if we are running with
    // hardware (which is asynchronous) then we need to ensure that the HW has finished drawing
    // before drawing the controls.
    Q3View_Sync( wI.view ) ;
    
    status = Q3Object_Dispose(myRenderer); 
    
    DrawDirections();
    
    InitCursor();
    return;
 
bail:
    SysBeep(50);
}
 
//------------------------------------
static  void    DrawDirections(void)
{
    Point   p;
    short   d = 4; // distance from border
    
    p.h = (g3DViewRect.right - g3DViewRect.left) / 2;
    p.v = g3DViewRect.top + d;
    DrawTriangle(p, up);
    p.v = g3DViewRect.bottom - d;
    DrawTriangle(p, down);
    p.h = g3DViewRect.left + d; 
    p.v = (g3DViewRect.bottom - g3DViewRect.top) / 2;
    DrawTriangle(p, left);
    p.h = g3DViewRect.right - d;
    DrawTriangle(p, right);
    MoveTo(g3DViewRect.right - 20, g3DViewRect.top + 14);
    TextFont(geneva); TextSize(12); TextFace(0);
    DrawString("\p(R)");
}
 
//------------------------------------
static  void    DrawTriangle(Point p, DirectionType e)
{
#define     k   10 
 
    short   x1, y1, x2, y2, x3, y3;
    
    switch (e) {
    case up:    x1 = y1 = x3 = k; y3 = -k; x2 = - k - k; y2 = 0;    break;
    case down:  x1 = y1 = x3 = -k; y3 = k; x2 = k + k; y2 = 0;      break;
    case left:  x1 = y1 = y3 = k; x3 = -k; x2 = 0; y2 = - k - k;    break;
    case right: x1 = y1 = y3 = -k; x3 = k; x2 = 0; y2 = k + k;      break;
    }
        
    MoveTo(p.h, p.v);
    Line(x1, y1); Line(x2, y2); Line(x3, y3);
}
 
//------------------------------------
static  void    DrawControlPart(WinfoH wInfo)
{
    EraseRect(&gContrlRect);
    FrameRect(&gContrlRect);
    if ((**wInfo).vcc != nil)
        DrawVCluster((**wInfo).vcc);
}
 
 
//------------------------------------
static Boolean  ControlClick(Point pt, WindowPtr theWindow)
{
    WinfoH      wInfo;
    OSType      tag;
    TQ3Status   status;
    
    wInfo = (WinfoH)GetWRefCon(theWindow);
    if (wInfo == nil)
        return false;
 
    if ( TakeHit(pt, (**wInfo).vcc) ) {
        tag = GetIdTag((**wInfo).vcc);
        switch (tag) {
            case kQ3GeometryTypeBox:
                    UpdateGeometry(wInfo);
                break;
                
            case kQ3CameraTypeOrthographic:
            case kQ3CameraTypeViewPlane:
            case kQ3CameraTypeViewAngleAspect:
                {
                    TQ3CameraObject camera;
                
                    status = Q3View_GetCamera((**wInfo).view, &camera);
                    VCCtoCameraData((**wInfo).vcc, camera);
                    status = Q3View_SetCamera((**wInfo).view, camera);
                    status = Q3Object_Dispose(camera); 
                }
                break;
 
            case kQ3LightTypeAmbient:
            case kQ3LightTypeDirectional:
            case kQ3LightTypePoint:
            case kQ3LightTypeSpot:
                {
                    TQ3CameraObject camera;
                    TQ3GroupObject  lightGroup;
 
                    status = Q3View_GetLightGroup((**wInfo).view, &lightGroup);
                    VCCtoLight((**wInfo).vcc, lightGroup);              
                }
                break;
 
        // add here other types of valueControls
        }
        return true;
    }
    else
        return false;
}
 
 
//----------------------------------------------------------------------------------
static  DirectionType  RotationClick(Point pt, WindowPtr theWindow)
{
    Rect    leftRect, rightRect, upRect, downRect, resetRect;
    Rect    r = g3DViewRect;  // shorthand notation
    short   h3 = (g3DViewRect.right - g3DViewRect.left) / 3;
    short   v3 = (g3DViewRect.bottom - g3DViewRect.top) / 3;
    
    SetRect(&leftRect,  r.left,       r.top + v3,    r.left + h3,   r.bottom - v3);
    SetRect(&rightRect, r.right - h3, r.top + v3,    r.right,       r.bottom - v3);
    SetRect(&upRect,    r.left + h3,  r.top,         r.right - h3,  r.top + v3);
    SetRect(&downRect,  r.left + h3,  r.bottom - v3, r.right - h3,  r.bottom);
    SetRect(&resetRect, r.right - 32,  r.top, r.right,  r.top + 32);
    if (PtInRect(pt, &leftRect))    return left;
    if (PtInRect(pt, &rightRect))   return right;
    if (PtInRect(pt, &upRect))      return up;
    if (PtInRect(pt, &downRect))    return down;
    if (PtInRect(pt, &resetRect))   return reset;
    return none;
}
 
//----------------------------------------------------------------------------------
void    MakeGeometry(WinfoH wInfo, long selector)
{
    if ((**wInfo).model != NULL) {
    // ¥¥¥ need to release memory allocated in attributes!
        Q3Group_EmptyObjects((**wInfo).model);
        Q3Object_Dispose((**wInfo).model);
    }
    
    switch (selector) {
        case iBox:  (**wInfo).model = MakeBox();
            break;
        case iEllipsoid:// aGeometry = MakeEllipsoid();
                    SysBeep(10);
            break;
        case iCylinder: // aGeometry = MakeCylinder();
                    SysBeep(10);
            break;
        case iCone:     // aGeometry = MakeCone();
                    SysBeep(10);
            break;
        case iTorus:    // aGeometry = MakeTorus();
                    SysBeep(10);
            break;
 
        default:
            break;
    }
}
 
 
//----------------------------------------------------------------------------------
void    MakeDataControl(WinfoH wInfo)
{
    TQ3GroupObject      group;
    TQ3GroupPosition        position;
    TQ3ObjectType       type;
    TQ3Status           status;
    TQ3Object           theObj;
 
    if ((**wInfo).vcc != nil) {
        DisposeVCluster((**wInfo).vcc);
        (**wInfo).vcc = nil;
    }
    
    group = (**wInfo).model;
    type = kQ3ShapeTypeGeometry;
    status = Q3Group_GetFirstPositionOfType(group, type, &position);
    if (status == kQ3Failure)
        return;
        
    status = Q3Group_GetPositionObject ( group, position, &theObj);
    type = Q3Geometry_GetType ( theObj );
    switch (type) {
        case kQ3GeometryTypeBox: 
            (**wInfo).vcc = BoxDataToVCC(theObj);
            break;
            // add here data of other geometries
        default:
            return;
    }
}
 
 
//----------------------------------------------------------------------------------
static void MakeNewCamera (WinfoH wInfo, long selector)
{
    TQ3CameraObject     camera;
    TQ3Status           status;
    
    camera = MakeCamera(selector);
    status = Q3View_SetCamera((**wInfo).view, camera);
    if ( status != kQ3Success )
        ErrMsg("\pSetCamera failed");
    status = Q3Object_Dispose(camera); 
    curCamera = selector;
}
 
 
//----------------------------------------------------------------------------------
static void MakeCameraControl(WinfoH wInfo)
{
    TQ3CameraObject camera;
    TQ3Status       status;
 
    if ((**wInfo).vcc != nil) {
        DisposeVCluster((**wInfo).vcc);
        (**wInfo).vcc = nil;
    }
    status = Q3View_GetCamera((**wInfo).view, &camera);
    if (status != kQ3Success)
        return;
    (**wInfo).vcc = CameraDataToVCC(camera);
    status = Q3Object_Dispose(camera); 
}
 
 
//----------------------------------------------------------------------------------
static void MakeLightControl (WinfoH wInfo, long selector)
{
    TQ3Status       status;
    TQ3GroupObject  lightGroup;
        
    if ((**wInfo).vcc != nil) {
        DisposeVCluster((**wInfo).vcc);
        (**wInfo).vcc = nil;
    }
    status = Q3View_GetLightGroup((**wInfo).view, &lightGroup);
    (**wInfo).vcc = LightToVCC(lightGroup, selector);
}
 
 
//----------------------------------------------------------------------------------
static void UpdateGeometry(WinfoH wInfo)
{
    TQ3GroupObject      group;
    TQ3GroupPosition        position;
    TQ3ObjectType       type;
    TQ3Status           status;
    TQ3Object           theObj;
    
    group = (**wInfo).model;
    type = kQ3ShapeTypeGeometry;
    status = Q3Group_GetFirstPositionOfType(group, type, &position);
    if (status == kQ3Failure)
        return;
        
    status = Q3Group_GetPositionObject( group, position, &theObj);
    type = Q3Geometry_GetType( theObj );
    switch (type) {
        case kQ3GeometryTypeBox: {
                TQ3BoxData  boxData;
                status = Q3Box_GetData(theObj, &boxData);
                if (status == kQ3Failure)
                    return; 
                VCCtoBoxData((**wInfo).vcc, &boxData);
                status = Q3Box_SetData(theObj, &boxData);
            //  Q3Box_EmptyData(&boxData);
            }
            break;
            // add here data of other geometries
        default:
            return;
    }
    status = Q3Group_SetPositionObject( group, position, theObj);
    status = Q3Object_Dispose(theObj); 
}
 
 
 
//----------------------------------------------------------------------------------
static Boolean GoodFrontWindow(WindowPtr *theWindow, WinfoH *wInfo)
{
    WindowPtr   w;
    Boolean     isGood = true;
    
    w = FrontWindow();
    if (w == nil) // menu item should be disabled!
        isGood = false;
    if ( (*(WindowRecord *)w).windowKind != userKind )
        isGood = false;
    *theWindow = w;
    *wInfo = (WinfoH)GetWRefCon(w);
}