Home My Page Projects Code Snippets Project Openings 3D graphics for Standard ML
Summary Activity SCM

SCM Repository

[sml3d] View of /trunk/sml3d/examples/md3-viewer/md3-viewer.sml
ViewVC logotype

View of /trunk/sml3d/examples/md3-viewer/md3-viewer.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 391 - (download) (annotate)
Tue Jan 13 23:01:40 2009 UTC (10 years, 6 months ago) by jhr
File size: 11359 byte(s)
  Fixed type errors
(* md3-viewer.sml
 *
 * COPYRIGHT (c) 2008 John Reppy (http://cs.uchicago.edu/~jhr)
 * All rights reserved.
 *
 * This is a MD3 model viewer for testing the MD3 loader.
 *)

structure MD3Viewer =
  struct

    open SML3dTypes

    datatype view = V of {
	isVis : bool ref,
	cam : Camera.camera ref,
	frustum : Frustum.view_frustum ref,
	wireFrame : bool ref,
	bbox : bool ref,
	model : MD3.model,
	curFrame : int ref,
      (* viewer control state *)
	lastUpdate : Time.time ref,
	fXDiff : float ref,
	fYDiff : float ref,
	fZDiff : float ref,
	xLastIncr : int ref,
	yLastIncr : int ref,
	fXInertia : float ref,
	fYInertia : float ref,
	fXInertiaOld : float ref,
	fYInertiaOld : float ref,
	fScale : float ref,
	rotate : bool ref,
	xLast : int ref,
	yLast : int ref,
	rotL : float ref,
	activeCtrl : bool ref		(* true when ctrl is active *)
      }

  (* Rotation defines *)
    val inertiaThreshold : float = 1.0
    val inertiaFactor : float = 0.5
    val scaleFactor : float = 0.01
    val scaleIncrement : float = 0.5
    val timerFrequency = Time.fromMilliseconds 20

    fun init model = V{
	    isVis = ref false,
	    cam = ref(Camera.newd{
		eye = {x = 0.0, y = 0.0, z = ~10.0},
		center = {x = 0.0, y = 0.0, z = 0.0},
		up = {x = 0.0, y = 1.0, z = 0.0}
	      }),
	    frustum = ref(Frustum.newd {
		wid = 800, ht = 800,
		fovy = 120.0,
		zNear = 1.0, zFar = 50.0
	      }),
	    wireFrame = ref true,
	    bbox = ref true,
	    model = model,
	    curFrame = ref 0,
	    lastUpdate = ref(Time.now()),
	    fXDiff = ref 206.0,
	    fYDiff = ref 16.0,
	    fZDiff = ref 10.0,
	    xLastIncr = ref 0,
	    yLastIncr = ref 0,
	    fXInertia = ref ~0.5,
	    fYInertia = ref 0.0,
	    fXInertiaOld = ref 0.0,
	    fYInertiaOld = ref 0.0,
	    fScale = ref 1.0,
	    rotate = ref true,
	    xLast = ref ~1,
	    yLast = ref ~1,
	    rotL = ref(Float.M_PI /180.0),
	    activeCtrl = ref false
	  }

    fun numFrames (MD3.MODEL{frames, ...}) = List.length frames

  (* initialize the model-view matrix *)
    fun initModelViewMatrix (V{cam, ...}) = Camera.initModelView (!cam)

  (* initialize the projection matrix *)
    fun initProjection (V{frustum, ...}) = (
	  Frustum.initViewport (!frustum);
	  Frustum.initProjection (!frustum))

(*
  (* setup the various array pointers to render the i'th frame of the surface *)
    fun makeFrameCurrent (MD3.SURF{texCoords, frames, ...}, i) = let
	  val MD3.MESH{verts, norms, ...} = List.nth(frames, i)
	  in
	    GL.texCoordArray2f texCoords;
	    GL.vertexArray3f verts;
	    GL.normalArray3f norms
	  end

    fun renderSurf (MD3.SURF{tris, ...}) = let
	  val state = [GL.NORMAL_ARRAY, GL.VERTEX_ARRAY]
	  in
	    GL.color3f (1.0, 1.0, 1.0);
	    List.app GL.enableClientState state;
	      GL.drawElementsui (GL.TRIANGLES, tris);
	    List.app GL.disableClientState state
	  end
*)
(*DEBUG*)val curFrame = ref 0
  (* setup the various array pointers to render the i'th frame of the surface *)
    fun makeFrameCurrent (_, i) =
	  curFrame := i

    fun renderSurf (MD3.SURF{frames, tris, ...}) = let
	  val MD3.MESH{verts, norms} = List.nth(frames, !curFrame)
	  fun drawVertex i = let
		val i = Word.toIntX i
		in
		  GL.normal3f (DataBuffer.get3f(norms, i));
		  GL.vertex3f (DataBuffer.get3f(verts, i))
		end
	  in
	    GL.color3f (1.0, 1.0, 1.0);
	    GL.beginPrim GL.TRIANGLES;
	      DataBuffer.app drawVertex tris;
	    GL.endPrim ()
	  end

    fun drawAxes len = (
	  GL.beginPrim GL.LINES;
	    GL.color3f (1.0, 0.0, 0.0);
	    GL.vertex3f (0.0, 0.0, 0.0);
	    GL.vertex3f (len, 0.0, 0.0);
	    GL.color3f (0.0, 1.0, 0.0);
	    GL.vertex3f (0.0, 0.0, 0.0);
	    GL.vertex3f (0.0, len, 0.0);
	    GL.color3f (0.0, 0.0, 1.0);
	    GL.vertex3f (0.0, 0.0, 0.0);
	    GL.vertex3f (0.0, 0.0, len);
	  GL.endPrim ())

    fun drawBBox (minBounds, maxBounds) = let
	  val {x=x1, y=y1, z=z1} = minBounds
	  val {x=x2, y=y2, z=z2} = maxBounds
	  in
	    GL.beginPrim GL.LINES;
	      GL.color3f (1.0, 1.0, 0.0);
	      GL.vertex3f (x1, y1, y1); GL.vertex3f (x2, y1, y1);
	      GL.vertex3f (x1, y1, y1); GL.vertex3f (x1, y2, y1);
	      GL.vertex3f (x1, y1, y1); GL.vertex3f (x1, y1, y2);
	      GL.vertex3f (x2, y1, y1); GL.vertex3f (x2, y2, y1);
	      GL.vertex3f (x2, y1, y1); GL.vertex3f (x2, y1, y2);
	      GL.vertex3f (x1, y2, y1); GL.vertex3f (x2, y2, y1);
	      GL.vertex3f (x1, y2, y1); GL.vertex3f (x1, y2, y2);
	      GL.vertex3f (x1, y1, y2); GL.vertex3f (x2, y1, y2);
	      GL.vertex3f (x1, y1, y2); GL.vertex3f (x1, y2, y2);
	      GL.vertex3f (x1, y2, y2); GL.vertex3f (x2, y2, y2);
	      GL.vertex3f (x2, y1, y2); GL.vertex3f (x2, y2, y2);
	      GL.vertex3f (x2, y2, y1); GL.vertex3f (x2, y2, y2);
	    GL.endPrim ()
	  end

    fun displayCB view () = let
	  val V{
		  model=MD3.MODEL{frames, surfs, ...}, curFrame, wireFrame, bbox,
		  fXDiff, fYDiff, fZDiff, xLastIncr, yLastIncr, fXInertia, fYInertia, fScale, ...
		} = view
	  val MD3.FRAME{minBounds, maxBounds, ...} = List.nth(frames, !curFrame)
	  fun drawSurf surf = (
		makeFrameCurrent (surf, !curFrame);
		renderSurf surf)
	  in
	    GL.matrixMode GL.MODELVIEW_MATRIX;
	    GL.pushMatrix();
	    GL.rotatef (!fYDiff, 1.0, 0.0, 0.0);
	    GL.rotatef (!fXDiff, 0.0, 1.0, 0.0);
	    GL.rotatef (!fZDiff, 0.0, 0.0, 1.0);
	    GL.scalef(!fScale, !fScale, !fScale);
	    GL.smoothShading true;
	    GL.enable GL.LIGHTING;
	    GL.ambientLight {r = 1.0, g = 1.0, b = 1.0, a = 0.0};
	    GL.enable GL.COLOR_MATERIAL;
	    GL.enable GL.DEPTH_TEST;
	    GL.depthFunc GL.LESS;
	    GL.depthMask true;
	    if !wireFrame
	      then (
		GL.polygonMode (GL.BOTH_FACES, GL.LINE);
		GL.disable GL.CULL_FACE)
	      else (
		GL.polygonMode (GL.FRONT_FACE, GL.FILL);
		GL.enable GL.CULL_FACE;
		GL.cullFace GL.BACK_FACE);
	  (* clear the screen *)
	    GL.clearColor {r=0.2, g=0.2, b=0.2, a=1.0};
	    GL.clear [GL.COLOR_BUFFER_BIT, GL.DEPTH_BUFFER_BIT];
	    drawAxes 20.0;
	    if !bbox then drawBBox(minBounds, maxBounds) else ();
	    List.app drawSurf surfs;
	    GL.popMatrix ();
	    GLUT.swapBuffers()
	  end

    fun reshapeCB (view as V{isVis, frustum, ...}) vp = (
	  frustum := Frustum.updateViewport (!frustum, vp);
	  initProjection (view);
	  if (! isVis)
	    then GLUT.postRedisplay()
	    else ())

    fun visibleCB (V{isVis, ...}) vis = (
	  isVis := vis;
	  if vis then GLUT.postRedisplay() else ())

    fun keyboardCB (V{
	    model, wireFrame, bbox, curFrame,
	    fScale, rotate, fXInertia, fYInertia, fXInertiaOld, fYInertiaOld, ...
	  }) (c, _) = (
	  case c
	   of #"q" => OS.Process.exit OS.Process.success
	    | #"w" => wireFrame := not(!wireFrame)
	    | #"b" => bbox := not(!bbox)
	    | #"." => curFrame := (!curFrame + 1) mod (numFrames model)
	    | #" " => (
		rotate := not(!rotate);
		if !rotate
		  then (
		    fXInertia := !fXInertiaOld;
		    fYInertia := !fYInertiaOld;
		  (* to prevent confusion, force some rotation *)
		    if (Real32.==(!fXInertia, 0.0) andalso Real32.==(!fYInertia, 0.0))
		      then fXInertia := ~0.5
		      else ())
		  else (
		    fXInertiaOld := !fXInertia;
		    fYInertiaOld := !fYInertia))
	    | #"+" => fScale := !fScale + scaleIncrement
	    | #"-" => fScale := !fScale - scaleIncrement
	    | _ => TextIO.output(TextIO.stdErr,
		"\nKeyboard commands:\n\n\
        	\q, <esc> - Quit\n\
        	\w - Toggle wireframe mode\n\
        	\b - Toggle bounding box\n\
		\. - advance to next animation frame\n\
		\+/- - zoom in/out\n\
        	\? - Help\n\
        	\<home>     - reset zoom and rotation\n\
        	\<space> or <click>        - stop rotation\n\
        	\<+>, <-> or <ctrl + drag> - zoom model\n\
        	\<arrow keys> or <drag>    - rotate model\n\
		\")
	  (* end case *);
	  GLUT.postRedisplay())

    fun specialCB (V{
	    fXDiff, fYDiff, fZDiff, xLastIncr, yLastIncr, fXInertia, fYInertia, fScale, ...
	  }) (key, _) = (case key
	   of GLUT.KEY_HOME => (
		fXDiff := 0.0; fYDiff := 0.0;  fZDiff := 0.0;
		xLastIncr := 0; yLastIncr := 0;
		fXInertia := ~0.5; fYInertia := 0.0;
		fScale := 1.0)
	    | GLUT.KEY_LEFT => fXDiff := !fXDiff + 1.0
	    | GLUT.KEY_RIGHT => fXDiff := !fXDiff - 1.0
	    | GLUT.KEY_UP => fYDiff := !fYDiff - 1.0
	    | GLUT.KEY_DOWN => fYDiff := !fYDiff + 1.0
	    | _ => ()
	  (* end case *))

    fun timerCB (V{rotate, fXDiff, fYDiff, fXInertia, fYInertia, ...}) = let
	  fun timer' () = (
		if !rotate
		  then (
		    fXDiff := !fXDiff + !fXInertia;
		    fYDiff := !fYDiff + !fYInertia)
		  else ();
		GLUT.timerFunc (timerFrequency, timer'))
	  in
	    timer'
	  end

    fun mouseCB (V{xLast, yLast, xLastIncr, yLastIncr, activeCtrl, fXInertia, fYInertia, ...})
	  (but, state, {x, y})
	= (
	  activeCtrl := #ctrl(GLUT.getModifiers());
	  case (but, state)
	   of (GLUT.LEFT_BUTTON, false) => let
		val xLastIncr = Float.fromInt(!xLastIncr)
		val yLastIncr = Float.fromInt(!yLastIncr)
		in
		  xLast := ~1;
		  yLast := ~1;
		  if (xLastIncr > inertiaThreshold)
		    then fXInertia := (xLastIncr - inertiaThreshold) * inertiaFactor
		    else ();
		  if (~xLastIncr > inertiaThreshold)
		    then fXInertia := (xLastIncr + inertiaThreshold) * inertiaFactor
		    else ();
		  if (yLastIncr > inertiaThreshold)
		    then fYInertia := (yLastIncr - inertiaThreshold) * inertiaFactor
		    else ();
		  if (~yLastIncr > inertiaThreshold)
		    then fYInertia := (yLastIncr + inertiaThreshold) * inertiaFactor
		    else ()
		end
	    | (GLUT.LEFT_BUTTON, true) => (
		fXInertia := 0.0;
		fYInertia := 0.0)
	    | _ => ()
	  (* end case *);
	  xLastIncr := 0;
	  yLastIncr := 0)

    fun motionCB
	  (V{fScale, fXDiff, fYDiff, fZDiff, xLast, yLast, xLastIncr, yLastIncr, activeCtrl, ...})
	  {x, y}
	= (
	  if (!xLast <> ~1) orelse (!yLast <> ~1)
	    then (
	      xLastIncr := x - !xLast;
	      yLastIncr := y - !yLast;
	      if (!xLast <> ~1)
		then if (!activeCtrl)
		  then (
		    fZDiff := !fZDiff + Float.fromInt(!xLastIncr);
		    fScale := !fScale + Float.fromInt(!yLastIncr)*scaleFactor)
		  else (
		    fXDiff := !fXDiff + Float.fromInt(!xLastIncr);
		    fYDiff := !fYDiff + Float.fromInt(!yLastIncr))
		else())
	    else ();
	  xLast := x;
	  yLast := y)

    fun idleCB (V{lastUpdate, rotL, ...}) () = let
	  val thisTime = Time.now()
	  in
	    rotL := !rotL - FP.dtof(Time.toReal(Time.-(thisTime, !lastUpdate)));
	    lastUpdate := thisTime;
	    GLUT.postRedisplay ()
	  end

    fun runViewer file = let
	  val model = LoadMD3.loadFile file
	  val view as V{frustum, ...} = init model
	  in
	    GLUT.init();
	    GLUT.initDisplayMode [GLUT.RGBA, GLUT.DOUBLE, GLUT.DEPTH];
	    GLUT.initWindowSize (Frustum.viewport(!frustum));
	    GLUT.createWindow "MD3 Model Viewer";
	  (* inialize matrices *)
	    initModelViewMatrix view;
	    initProjection view;
	  (* setup callbacks *)
	    GLUT.displayFunc (SOME(displayCB view));
	    GLUT.reshapeFunc (SOME(reshapeCB view));
	    GLUT.keyboardFunc (SOME(keyboardCB view));
	    GLUT.visibilityFunc (SOME (visibleCB view));
	    GLUT.motionFunc (SOME(motionCB view));
	    GLUT.mouseFunc (SOME(mouseCB view));
	    GLUT.specialFunc (SOME(specialCB view));
	    GLUT.timerFunc (timerFrequency, timerCB view);
	    GLUT.idleFunc (SOME(idleCB view));
	    GLUT.mainLoop ()
	  end

    fun main (_, [file]) = runViewer file
      | main _ = OS.Process.exit OS.Process.failure

    val _ = main (CommandLine.name(), CommandLine.arguments())

  end

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0