narrowPolyViewer.cpp

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

//  File Name: narrowPolyViewer.cpp
//
//  Description:
//      A simple test of the MPx3dModelView code.
//      A view that allows multiple cameras to be added is made.
//

#include "narrowPolyViewer.h"

#include <maya/MItDag.h>
#include <maya/MStringArray.h>
#include <maya/MGlobal.h>

#include <maya/MPoint.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>
#include <maya/MFnMesh.h>
#include <maya/MItMeshVertex.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MPointArray.h>
#include <maya/MIOStream.h>

#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif

#include <math.h>

narrowPolyViewer::narrowPolyViewer()
:   fDrawManips(true)
,   fLightTest(false)
{
    setMultipleDrawEnable(true);
    tol = 10;
}

narrowPolyViewer::~narrowPolyViewer()
{
}

MStatus narrowPolyViewer::setCameraList(const MDagPathArray &cameraList)
//
//  Description:
//      Replace the contents of fCameraList with the passed list of cameras.
//
{
    setMultipleDrawEnable(true);
    fCameraList.clear();

    unsigned ii = 0;
    unsigned nCameras = cameraList.length();
    for (ii = 0; ii < nCameras; ++ii) {
        fCameraList.append(cameraList[ii]);
    }

    refresh();

    return MS::kSuccess;
}

MStatus narrowPolyViewer::getCameraList(MDagPathArray &cameraList) const
//
//  Description:
//      Fills the passed camera list with the current camera list used by
//      this view. The default camera is not added to the list. Also, the list 
//      is cleared of any previous values.
//
{
    cameraList.clear();

    unsigned ii = 0;
    unsigned nCameras = fCameraList.length();
    for (ii = 0; ii < nCameras; ++ii) {
        cameraList.append(fCameraList[ii]);
    }

    return MS::kSuccess;
}

MStatus narrowPolyViewer::getCameraList(MStringArray &cameraList) const
//
//  Description:
//      Fills the passed camera list with the current camera list used by
//      this view. The default camera is not added to the list. Also, the list 
//      is cleared of any previous values.
//
{
    cameraList.clear();

    unsigned ii = 0;
    unsigned nCameras = fCameraList.length();
    for (ii = 0; ii < nCameras; ++ii) {
        cameraList.append(fCameraList[ii].partialPathName());
    }

    return MS::kSuccess;
}

MStatus narrowPolyViewer::appendCamera(const MDagPath &camera)
//
//  Description:
//      Append a camera to the list of cameras used by this view.
//
{
    MStatus ReturnStatus = fCameraList.append(camera);
    refresh(true);

    return ReturnStatus;
}

MStatus narrowPolyViewer::removeCamera(const MDagPath &camera)
//
//  Description:
//      Removes the specified camera from the list of cameras. If the
//      camera is not on the list, it is still considered removed.
//
{
    //  Loop backwards to avoid any array resize problems.
    //
    int ii = 0;
    bool cameraRemoved = false;
    int nCameras = (int)fCameraList.length();
    for (ii = nCameras - 1; ii >= 0; ii--) {
        if (camera == fCameraList[ii]) {
            fCameraList.remove(ii);
            cameraRemoved = true;
        }
    }

    if (cameraRemoved) {
        refresh();
    }
    return MS::kSuccess;
}

MStatus narrowPolyViewer::removeAllCameras()
//
//  Description:
//      Remove all cameras from the list.
//  
{
    MStatus ReturnStatus = fCameraList.clear();
    refresh();
    return ReturnStatus;
}

MString narrowPolyViewer::getCameraHUDName()
{
    MString hudName("narrowPolyViewer: ");

    //  Oops, well this method should be non-const, or there
    //  should be a const version of get camera. For now, we will
    //  live with a cast.
    //
    MDagPath cameraPath;
    getCamera(cameraPath);

    cameraPath.pop();
    hudName += cameraPath.partialPathName();
    return hudName;
}

MStatus narrowPolyViewer::setIsolateSelect(MSelectionList &list) 
{
    setViewSelected(true);
    return setObjectsToView(list);
}

MStatus narrowPolyViewer::setIsolateSelectOff()
{
    return setViewSelected(false);
}

MStatus narrowPolyViewer::setLightTest(MSelectionList &list)
{
    MStatus ReturnStatus = MS::kSuccess;
    MItSelectionList it(list, MFn::kLight, &ReturnStatus);
    if (MS::kSuccess != ReturnStatus) {
        return ReturnStatus;
    }

    for (; !it.isDone(); it.next()) {
        MDagPath lightPath;
        if (MS::kSuccess != it.getDagPath(lightPath)) {
            continue;
        }

        fLightList.append(lightPath);
    }

    if (fLightList.length() > 0) {
        fLightTest = true;
    }

    return ReturnStatus;
}

void narrowPolyViewer::preMultipleDraw()
{
    fCurrentPass = 0;
    fDrawManips = false;

    MDagPath oldCamera;
    MStatus status = getCamera(oldCamera);

    if (MS::kSuccess != status) {
        status.perror("M3dView::getCamera");
    }

    fOldCamera = oldCamera;

    displayHUD();

    //  Clear the test camera list
    //
    fTestCameraList.clear();


    // get the first item in the selection list
    MStatus stat;
    MSelectionList sList;
    MGlobal::getActiveSelectionList(sList);
    MDagPath dagPath;
    stat = sList.getDagPath(0, dagPath);
    if (stat != MS::kSuccess) return;
    MItMeshPolygon itMeshPolygon(dagPath, MObject::kNullObj, &stat);
    if (stat != MS::kSuccess) return;

    beginGL();

    // Iterate over all faces on the mesh
    for (; !itMeshPolygon.isDone(); itMeshPolygon.next())
    {
        MPointArray points;
        itMeshPolygon.getPoints(points, MSpace::kWorld, &stat);
        int length = points.length();

        // Only care about triangles
        if (length == 3)
        {
            // Iterate over all points on the triangle face and if the angle
            // between any 2 sides are less than the tolerance, then draw the 
            // triangle white
            for (int i=0; i<length; i++)
            {
                // get the points of the triangle
                MPoint p = points[i];
                MPoint p1 = points[(i+1)%length];
                MPoint p2 = points[(i+2)%length];

                // create the 2 adjacent vectors to the current point
                MVector v1(p1 - p);
                MVector v2(p2 - p);

                // get the angle between the two vectors in degrees
                double angle = v1.angle(v2) * 180 / 3.14159;
                
                // Dump some data
                #if 0
                MString astr = "The angle is : ";
                astr += angle;
                astr += " tolerance : ";
                astr += tol;
                MGlobal::displayInfo( astr );
                #endif

                // if angle less than tolerance then draw the triangle white
                if ( (fabs( angle - tol ) < .0001) || angle < tol )
                {
                    glBegin(GL_POLYGON);
                        glVertex3f((float)points[0].x, (float)points[0].y, (float)points[0].z);
                        glVertex3f((float)points[1].x, (float)points[1].y, (float)points[1].z);
                        glVertex3f((float)points[2].x, (float)points[2].y, (float)points[2].z);
                    glEnd();
                    break;
                }
            }
        }
    }

    endGL();
}

void narrowPolyViewer::postMultipleDraw()
{
    MStatus status = setCamera(fOldCamera);
    fDrawManips = true;

    updateViewingParameters();
    if (MS::kSuccess != status) {
        status.perror("M3dView::setCamera");
    }
}

void narrowPolyViewer::preMultipleDrawPass(unsigned index)
{
    fCurrentPass = index;
    MStatus status;

    if (MS::kSuccess != (status = setDisplayAxis(false))) {
        status.perror("M3dView::setDisplayAxis");
    }
    if (MS::kSuccess != (status = setDisplayAxisAtOrigin(false))) {
        status.perror("M3dView::setDisplayAxisAtOrigin");
    }
    if (MS::kSuccess != (status = setDisplayCameraAnnotation(false))) {
        status.perror("M3dView::setDisplayCameraAnnotation");
    }

    MDagPath dagPath;

    //  Draw the main camera first.
    //
    if (fCurrentPass == 0) {
        getCamera(dagPath);
    } else {
        unsigned nCameras = fCameraList.length();
        if (fCurrentPass <= nCameras) {
            dagPath = fCameraList[fCurrentPass-1];
        } else {
            cerr << "Error ... too many passes specified: " 
                 << fCurrentPass << endl;
            return;
        }
    }

    if (MS::kSuccess != (status = setCameraInDraw(dagPath))) {
        status.perror("M3dView::setCamera");
        return;
    }

    //  Add the camera to the list of cameras used.
    //
    MString thisCameraPath = dagPath.partialPathName();
    fTestCameraList.append(thisCameraPath);

    //  Turn on the display of everything, then selectively disable
    //  things.
    //
    setObjectDisplay(M3dView::kDisplayEverything, true);

    //  Only draw manips and grids for the main cameras.
    //  Also do not draw lights, cameras, ikHandles, dimensions,
    //  and selection handles for non-main cameras.
    //
    if (dagPath == fOldCamera) {
        fDrawManips = true;
        setObjectDisplay(M3dView::kDisplayGrid, true);

        //  For testing purposes, verify that the display grid is
        //  properly set.
        //
        if (!objectDisplay(M3dView::kDisplayGrid)) {
            MGlobal::displayError("objectDisplay kDisplayGrid should be true!");
        }

        //  Add fog for the main camera
        //
        if (MS::kSuccess != (status = setFogEnabled(true))) {
            status.perror("M3dView::setFogEnabled");
        } 

        if (!isFogEnabled()) {
            MGlobal::displayError("setFogEnabled did not work!");
        }

        //  Turn off background fog. If not set, background fog would be
        //  used.
        //
        setBackgroundFogEnabled(false);

        setObjectDisplay(M3dView::kDisplayLights, true);
        setObjectDisplay(M3dView::kDisplayCameras, true);
        setObjectDisplay(M3dView::kDisplayIkHandles, true);
        setObjectDisplay(M3dView::kDisplayDimensions, true);
        setObjectDisplay(M3dView::kDisplaySelectHandles, true);

        //  Draw some text in the main view.
        //
        MPoint textPos(0, 0, 0);
        MString str("Main View");
        drawText(str, textPos, M3dView::kLeft);
    } else {
        fDrawManips = false;
        setObjectDisplay(M3dView::kDisplayGrid, false);

        //  For testing purposes, verify that the display grid is
        //  properly set.
        //
        if (objectDisplay(M3dView::kDisplayGrid)) {
            MGlobal::displayError(
                "objectDisplay kDisplayGrid should be false!");
        }

        if (MS::kSuccess != (status = setFogEnabled(false))) {
            status.perror("M3dView::setFogEnabled");
        }
        if (isFogEnabled()) {
            MGlobal::displayError("setFogEnabled did not work!");
        }

        setObjectDisplay(M3dView::kDisplayLights, false);
        setObjectDisplay(M3dView::kDisplayCameras, false);
        setObjectDisplay(M3dView::kDisplayIkHandles, false);
        setObjectDisplay(M3dView::kDisplayDimensions, false);
        setObjectDisplay(M3dView::kDisplaySelectHandles, false);
    }
    
    if (fLightTest) {
        unsigned step = 4;
        unsigned min = 0 + (fCurrentPass * step);
        unsigned max = (fCurrentPass * step);

        if (max > 0) {
            max--;
        }

        unsigned nLights = fLightList.length();
        if (nLights < min) {
            setLightingMode(kLightDefault);
        } else {
            if (nLights < max) {
                max = nLights;
            }

            MSelectionList list;
            unsigned ii = 0;
            for (ii = min; ii < max; ii++) {
            list.add(fLightList[ii].node());
            }

            MGlobal::setActiveSelectionList(list);
            setLightingMode(kLightSelected);
        }
    } else {
        setLightingMode(kLightDefault);
    }

    //  Make every other pass alternate between shaded and wireframe.
    //
    if (fCurrentPass % 2 == 0) {
        setObjectDisplay(M3dView::kDisplayNurbsSurfaces, true);
        setObjectDisplay(M3dView::kDisplayNurbsCurves, true);
    }

    updateViewingParameters();
}

void narrowPolyViewer::postMultipleDrawPass(unsigned index)
{
    //  Until better control over selection and picking is exposed,
    //  turn on the display of everything after drawing. The selection
    //  methods will not consider things that are not visible.
    //
    //  If picking masks are to be used for the view, set them
    //  here. 
    //
    setObjectDisplay(M3dView::kDisplayEverything, true);
}

bool narrowPolyViewer::okForMultipleDraw(const MDagPath &dagPath)
{
    // Don't draw manipulators unless the fDrawManips value is true.
    //
    if (!fDrawManips && dagPath.hasFn(MFn::kManipulator3D)) {
        return false;
    }

    return true;
}

unsigned narrowPolyViewer::multipleDrawPassCount()
{
    //  The camera list plus the main camera.
    //
    return fCameraList.length() + 1;
}

void narrowPolyViewer::removingCamera(MDagPath &cameraPath)
//
//  Description:
//      A method that is called when the passed camera path is
//      being deleted. Be sure to reemove any call to this 
//      camera.
//
{
    int ii = 0;
    int nCameras = (int)fCameraList.length();
    for (ii = nCameras - 1; ii >= 0; ii--) {
        if (cameraPath == fCameraList[ii]) {
            fCameraList.remove(ii);
        }
    }
}

void * narrowPolyViewer::creator()
{
    return new narrowPolyViewer();
}

MStatus narrowPolyViewer::copy(MPx3dModelView &src)
//
//  Description:
//      Copy the contents for the src and put them 
//      into this.
//
{
    return MS::kSuccess;
}

MStatus narrowPolyViewer::swap(MPx3dModelView &src)
//
//  Description:
//      Swap out the contents for the src and put them 
//      into this.
//      
{
    return MS::kSuccess;
}

MString narrowPolyViewer::viewType() const
//
//  Description:
//      Returns a string with the type of the view.
//
{
    return MString("narrowPolyViewer");
}