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:
especially
For more information on OpenGL:
especially
For more information on GHOST:
especially
and see Angela for access to vendor documentation.
An introductory tutorial on VRML and OpenGL is at:
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)
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;
}
|