CS 236 Term Project

(April 6, 2000 version)

Overview:

Half of the class will build a translator that accepts as input a 3-d model expressed in VRML and produces as output a C program with calls to the OpenGL libraries that builds an equivalent model.

Half of the class will build a translator that accepts as input a 3-d model expressed in VRML and produces as output a C program with calls to the GHOST library that builds an equivalent model which can be explored hapticly by use of the PHANToM.

General Background

For more information on VRML:

http://www.vrml.org

especially

http://www.vrml.org/resources/learn.htm

For more information on OpenGL:

http://www.opengl.org/

especially

http://www.opengl.org/Documentation/Specs.html

For more information on GHOST:

http://www.sensable.com/

especially

http://www.sensable.com/products/ghost.htm

and see Angela for access to vendor documentation.

An introductory tutorial on VRML and OpenGL is at:

http://www.csee.wvu.edu/~vanscoy/idesk.html

Test Data

Your goal should be to translate as many as possible of the VRML examples found at:

http://www.csee.wvu.edu/~kandepet/vrml/vrmlTutor/examples.html

Submission of Components

You should submit the components of the term project on the indicated days on a diskette. Your diskette should contain a single subdirectory or folder with your name on it. Inside the subdirectory or folder should be all of the pieces of the project today: lex and yacc files, test data, executable module, and so forth.)

component

due date

weight

scanner

February 17

10%

parser (syntax checking only)

March 16

10%

symbol table manager and intermediate code generator

April 6

10%

entire project with documentation

April 20

20%

The Scanner

The scanner should be implemented using lex (although I'm open to discussion with individuals about alternative approaches).

A key design decision you must make in constructing the scanner is the structure of the "token." A token should be a C struct with at least two fields: one indicating the class of token (ex. keyword, numeric literal, punctuation, and one giving specific information about this particular token).

For the project component due on February 17 you must be able to produce an ascii file (which is to be included on the diskette you submit) which displays the contents of the list of tokens. However in the remainder of the project you will pass token values to the parser (so your scanner may well be a function which returns an object of type token (or a pointer to an object of type token) when called by the parser).;

For this part of the project, submit:

your lex file

the names of 3 VRML files you tested your lex file with

3 ascii files containing the results of processing these test files with your scanner (They should be named based on the name of the VRML file. For example, if you test your scanner using logcabin.wrl then you should name the output file logcabin.txt on the diskette.)

the executable version of your scanner

The Parser

The parser should be implemented using yacc or bison or ayacc (although I'm open to discussion with individuals about alternative approaches).

With one exception , note that for this part of the project you do NOT need to add semantic actions to the yacc file. That is part of the assignment for April 6. However you DO need to add semantic actions to a few key grammar components that will write a message saying that the parser has successfully found an instance of ---.

For the project component due on March 16 you must produce for each VRML file tested an ascii file (which is to be included on the diskette you submit) which displays the "successful parsing" methods mentioned above. However in the remainder of the project you will pass token values to the parser (so your scanner may well be a function which returns an object of type token (or a pointer to an object of type token) when called by the parser).

As for the previoius assignment, your work is to be submitted on a diskette. At the top level your diskette should contain one folder or subdirectory with your name as its name. All files submitted as part of the project should be in that folder or subdirectory. For this part of the project, submit:

your lex file (the scanner)

your yacc file (the parser)

3 ascii files containing the results of processing these test files with your scanner (They should be named based on the name of the VRML file. For example, if you test your scanner using logcabin.wrl then you should name the output file logcabin.txt on the diskette.)

the executable version of your parser

a file name "README" which contains:

your name

your e-mail address

the names of the VRML files you used for testing your parser

directions for how to create the executable version of your parser (all command lines) including: hardware, operating system, compiler name and version

the data structure you use to represent a token (and a listing of the relevant

C types used to implement a token)


Semantics

The ultimate goal of this project is to construct lex and yacc files which will generate a C program which ill take as input a VRML file and produce as output a valid C program with calls to either the OpenGL library or the GHOST library

Here are two examples of equivalent VRML, OpenGL, and GHOST models. Notice that the OpenGL and GHOST programs were written by humans, not software, and some things were done differently that your translator will do. (For example, the color of the top of the white cyliner representing the cake was modified.) The OpenGL program in this example contains several variables; your "compiler" might generate their declarations based on information stored in the "symbol table" generated by your software.

VRML - cake.wrl

OpenGL - cake.c

#VRML 2.0 utf8
WorldInfo {
  title "Simple Birthday Cake"
  info "by Frances Van Scoy"
}

 

/* OpenGL includes */

#include <GL/gl.h>

#include <GL/glut.h>

 

/* C includes */

#include <math.h>

 

/* Function prototypes */

int main(int argc, char **argv) ;

void draw_world(void) ;

 

/* main */

int main(int argc, char **argv)

{

glutInit (&argc, argv);

glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);

glutInitWindowSize(640,480);

glutInitWindowPosition(50,50);

glutCreateWindow(argv[0]);

glutDisplayFunc(draw_world);

 

/* establish initial viewport */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho( -10, 10, -10, 10, -10, 10 );

glMatrixMode(GL_MODELVIEW);

 

glutMainLoop();

return 0;

}

 

/* draw_world */

void draw_world(void)

{

int i;

GLint cylinder_slices;

GLdouble axis_length;

GLUquadricObj *cake;

GLdouble cake_radius, cake_height;

GLUquadricObj *candle;

GLdouble candle_radius, candle_height;

GLUquadricObj *flame;

GLdouble flame_radius;

char* text;

char* text_index;

 

glShadeModel(GL_SMOOTH);

 

 

/**************************************/

/* clear the screen -- very important */

glClearColor(0.0, 0.0, 0.0, 0.0);

glClearDepth(1.0);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

 

 

Shape {
  appearance Appearance {
    material Material {
      diffuseColor 1.0 1.0 1.0
    }
  }
  geometry Cylinder {
    radius 3
    height 2
    side   TRUE
    top    TRUE
    bottom TRUE
  }
}

 

/* draw a white cylinder, the birthday cake */

glPushMatrix();

glColor3ub(255, 255, 255);

glTranslatef(0.0, 0.0, 0.0);

glRotatef(90.0, 1.0, 0.0, 0.0);

cake = gluNewQuadric();

cake_radius = 3.0;

cake_height = 2.0;

cylinder_slices = 1024;

gluCylinder(cake,cake_radius,cake_radius,cake_height,cylinder_slices,1);

 

 

/* draw a white circle, the bottom of the cake */

glColor3ub(170, 170, 170);

glBegin(GL_POLYGON);

for (i = 0; i < cylinder_slices; i++) {

glVertex2f(cake_radius * cos( (float) i * 360.0 / (float) cylinder_slices ),

cake_radius * sin( (float) i * 360.0 / (float) cylinder_slices ));

}

glEnd();

 

/* draw a white circle, the top of the cake */

/* we make the top of the cake a bit grey to show a distinction between top and sides */

glColor3ub(0,0,0);

glTranslatef(0.0, 0.0, cake_height);

glBegin(GL_POLYGON);

for (i = 0; i < cylinder_slices; i++) {

glVertex2f(cake_radius * cos( (float) i * 360.0 / (float) cylinder_slices ),

cake_radius * sin( (float) i * 360.0 / (float) cylinder_slices ));

}

glEnd();

glPopMatrix();

 

Transform {
  translation 0.0 3.0 0.0
  children [
    Shape {
      appearance Appearance {
        material Material {
          diffuseColor 0.0 0.8 0.0
        }
      }
     geometry Cylinder {
        radius 0.2
        height 4
        side   TRUE
        top    TRUE
        bottom TRUE
     }
  ]
}

    /* draw a green cylinder, the candle */
    glPushMatrix();
        glColor3ub(0, 204, 0);
        glTranslatef(0.0, 2 * cake_height, 0.0);
        glRotatef(90.0, 1.0, 0.0, 0.0);
        candle = gluNewQuadric();
        candle_radius = 0.2;
        candle_height = 4.0;
        gluCylinder(candle,candle_radius,candle_radius,candle_height,cylinder_slices,1);
    glPopMatrix();

 

Transform {
  translation 0.0 5.35 0
  children [
    Shape {
      appearance Appearance {
        material Material {
          emissiveColor 1.0 1.0 0.0
        }
      }
      geometry Sphere {
        radius 0.2
      }
    }
  ]
}

    /* draw a yellow sphere, the flame on the candle */
    glPushMatrix();
        glColor3ub(255, 255, 0);
        flame_radius = candle_radius;
        glTranslatef(0.0, candle_height + 3 * flame_radius, 0.0);
        flame = gluNewQuadric();
        flame_radius = candle_radius;
        gluSphere(flame, flame_radius, cylinder_slices, cylinder_slices);
    glPopMatrix();

 

Transform {
  translation 0 -3 0
  children [
    Shape {
      appearance Appearance {
        material Material {
          emissiveColor 1 0 0
        }
      }
      geometry Text {
        fontStyle FontStyle {
          family      "SERIF"
          style       "PLAIN"
          size        1.0
          spacing     1.0
          justify     "MIDDLE"
          horizontal  TRUE
          leftToRight TRUE
          topToBottom TRUE
        }
        string "Happy Birthday !"
      }
    }
  ]
}

    /* generate text that says "Happy Birthday!" */
    glPushMatrix();
        glColor3ub(255, 0, 0);
        glTranslatef(-8.0, 8.0, 0.0);
        glRotatef(85.0, 0.0, 0.0, 0.0);
        text = (char *) malloc(32);
        strcpy(text, "Happy Birthday!\0");
        glRasterPos2f(50.0, 0.0);
        for (text_index = text; *text_index; text_index++) {
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *text_index);
        }
    glPopMatrix();

 

}

To create the executable for this program, here is the Makefile:

CC = cc 
CFLAGS = 
LIBS = -lglut -lGL -lGLU -lXmu -lX11 -lm 
cake: cake.c 
$(CC) cake.c $(CFLAGS) $(LIBS) -o cake 
 
 

VRML - cake.wrl

GHOST - cake.c

#VRML 2.0 utf8
WorldInfo {
  title "Simple Birthday Cake"
  info "by Frances Van Scoy"
}

 

/*

*

* Birthday cake example

*

* All numeric constants in VRML file I multiplied times

10 to get decent real

* life measurements in mm (that is the scale of Phantom

models). I can't think of

* any generic way to map VRML scale to haptic scale

since there is no VRML scale I am

* aware of ... maybe it could be user defined, a command

line argument or something.

*

* The candle and sphere on top come out almost

imperceptibly tiny, but they *are*

* there.

*

* - Chaim (based on Dr. VanScoy's VRML cake model)

*/

 

 

// C Includes

#include <stdlib.h>

 

// GHOST stuff

#include <gstBasic.h>

#include <gstSphere.h>

#include <gstPHANToM.h>

#include <gstSeparator.h>

#include <gstScene.h>

#include <gstCylinder.h>

 

 

int main(int argc, char* argv[])

{

 

 

/*

* Set up the basic Phantom stuff

*/

gstScene *scene = new gstScene ;

gstSeparator *root = new gstSeparator ;

gstPHANToM *phantom = new

gstPHANToM("Default PHANToM") ;

 

root -> addChild(phantom) ;

scene -> setRoot(root) ;

 

 

 

Shape {
  appearance Appearance {
    material Material {
      diffuseColor 1.0 1.0 1.0
    }
  }
  geometry Cylinder {
    radius 3
    height 2
    side   TRUE
    top    TRUE
    bottom TRUE
  }
}

 

        gstCylinder *cake = new gstCylinder ;
        cake -> setHeight(40) ;
        cake -> setRadius(2) ;
        cake -> translate( 0, 30, 0 ) ;
        root -> addChild( cake ) 
 

Transform {
  translation 0.0 3.0 0.0
  children [
    Shape {
      appearance Appearance {
        material Material {
          diffuseColor 0.0 0.8 0.0
        }
      }
     geometry Cylinder {
        radius 0.2
        height 4
        side   TRUE
        top    TRUE
        bottom TRUE
     }
  ]
}

gstCylinder *candle = new gstCylinder ;

candle -> setHeight( 20 ) ;

candle -> setRadius( 30 ) ;

root -> addChild( candle ) ;

 

Transform {
  translation 0.0 5.35 0
  children [
    Shape {
      appearance Appearance {
        material Material {
          emissiveColor 1.0 1.0 0.0
        }
      }
      geometry Sphere {
        radius 0.2
      }
    }
  ]
}

gstSphere *sphere = new gstSphere ;

sphere -> setRadius( 2 ) ;

sphere -> translate( 0, 53.5, 0 ) ;

root -> addChild( sphere ) ;

 

Transform {
  translation 0 -3 0
  children [
    Shape {
      appearance Appearance {
        material Material {
          emissiveColor 1 0 0
        }
      }
      geometry Text {
        fontStyle FontStyle {
          family      "SERIF"
          style       "PLAIN"
          size        1.0
          spacing     1.0
          justify     "MIDDLE"
          horizontal  TRUE
          leftToRight TRUE
          topToBottom TRUE
        }
        string "Happy Birthday !"
      }
    }
  ]
}

    (not implemented/implementable)

/*

* Run the simulation

*/

scene -> startServoLoop() ;

 

while (!scene -> getDoneServoLoop()) {

}

 

return 0;

}

 
ÿ