Cramer Model Curriculum

This is the initial, MST teacher edition of the Cramer Model BuzzCamp module discussed during the June 2023 MST Buzzcamp. For the subsequent, simplified version of this delivered to students, go to http://www.cs.nmt.edu/~jeffery/cramerhall/buzzcamp.html

  1. Introduction
  2. Cramer Hall
  3. JSON
  4. A JSON 3D Model File Format
  5. Textures
  6. Measurement and Texture Collection
  7. Collating Models
  8. Playthrough

1.0 Introduction

In this camp module we will build a playable videogame level by brute force. Our videogame level will consist of a virtual 3D model of Cramer Hall at NMT that we can walk through. A similar activity could be performed for a selected building at most any institution such as a K-12 school, or junior or technical college. For each item, we will ask This is not a programming activity, it is a digital data activity. The data we produce can be used from programs written in any language. But, people are always going on about how important it is to integrate research into our teaching, so yeah, we are going to visit my research. The programs that will use our model in are written in the Unicon programming language, from unicon.org. Now, let's see whether the demo will work for us.

1.1 Cra1 Demo

This demo shows you why I/we are working on a model of Cramer Hall by running a little Unicon program that you can find at http://www.cs.nmt.edu/~jeffery/cramerhall/cra1.zip. You might have it in a subdirectory named cra1 under your home directory, or you might download it and unzip it into such a directory. From this little demo program, we could make a maze game, a puzzle/adventure game, a first-person shooter, or a networked multi-user roleplaying game. But our mission in this module is not to go do that, our mission is to represent Cramer Hall well enough that a user would recognize it when they see it in-game, and vice-versa. So let's talk 3D models.

1.2 Models

A videogame level is a virtual model of some real or imaginary place. Do we all know what a model is? There are a zillion different types of digital models used on computers. This is not vocational technical school. We are not teaching you a commercial modeling tool this week (sorry). We are trying to teach concepts by creating our own 3D model from scratch. But before that, let's talk a bit more about what we are modeling: Cramer Hall.

2.0 Cramer Hall

Cramer Hall was built around 1970. As of 2023 it houses the computer science and engineering department, the cybersecurity center, and the office of innovation and commercialization at NMT. So, how do we represent complex structured data on a computer? My first bruce force way of doing this was to just make it up as I went along. Later, I learned a standard data format that would do the job and make it easier for others to understand and use my data. That format is called JSON.

3.0 JSON

We will use the JavaScript Object Notation (JSON) to build our model. You can learn all there is to know about JSON in about 5 minutes; no wonder it is so popular. JSON is described at json.org which uses railroad diagrams and a simple grammar to give a precise definition.

3.1 Self-test (or maybe groups of two) activity

3.2 Python Cross-Check

(Extra for experts; save this for later) This week you will learn some Python, if you don't already know some. JSON is easy to use from Python and might be useful to you in future. If you store your answer to 3.1 in a file named ourcities.json, you can read it from Python like this:
import json
with open("ourcities.json", "r") as read_file:
   data = json.load(read_file)
and then use data in your program to access what you stored in your JSON file. See if you can write Python code to print out what was in your JSON file by walking through variable named data.

At this point you might want to ask yourself:

Unlike proprietary 3D Model formats that only do polygons and pixel colors, we can put anything we want in our JSON models...we could put text adventure descriptions in if we wanted.

Now, it is time to practice JSON by using it to represent data about Cramer Hall.

4.0 A JSON 3D Model File Format

We will build a 3D Model as a human-readable JSON file. Its contents will capture the information that we want to depict in our video game level (in our case, Cramer Hall), including spatial location and general appearance characteristics.

4.1 Coordinate System

We will use a standard/common coordinate system: 1.0 units = 1 meter, with an origin (0.0,0.0,0.0) in the northwest corner of Cramer Hall at ground level.


The origin of Cramer Hall's coordinate system

From this origin Y grows "up", X grows east, and Z grows south. The entire building (except anything below ground level as viewed from the northwest corner) will have fairly small positive real numbers in the model. This coordinate system is referred to as TNN world coordinates (TNN=Tom Nartker Normal).

For each (x,y,z) or (l,w,h) that we need in our model, we have to figure out their value in meters using the TNN coordinates. Our options are to either

If the building floor plan or the blueprints include a legend with the scale used, all your (x,y) and/or (l,w) measures can be gleaned from them. Two of your dimensions are obtained cheap/easy that way. For the (y) or (height), you will need to measure, but probably for an entire floor of a building the ceiling height will be the same, except maybe for a special case or two.

Example: I went outside and measured the distance from the NW corner of the building (0,0,0) to the outside entry that sticks out a bit north of that. According to the tape measure, it was 23'4" (23 1/3 feet == 7.1 meters). The entryway that sticks out north of the building was 58" (1.5 meters). How do these compare with the numbers I would get from the blueprints or floor plans?

Another example: I measured the distance between the Cramer Hall 1st floor and the 2nd floor and it came out to 13'7" or 4.1 meters. How might we double check this number? I estimated the wall thickness outside room 228 to be ~18", or 0.5m. If those two numbers were accurate, the inside of Cramer 228 origin would begin at (0.5,4.1,0.5). Awesome, we have our first coordinate for this room.

4.2 The 3D Model is a Graph

A graph is possibly the most fundamental building block of mathematics for computer science. Every computer scientist learns about graphs. It turns out: graphs are easy and fun. For many years, New Mexico was home to one of the most prominent mathematician/computer scientists in the world, Frank Harary. He was quite a comedian. But here is a short definition of graphs.
graph
a graph is a set of nodes and a set of edges
node
a node, or vertex, is a place or a state of being. Nodes can have names or numbers to identify them. There can be different kinds of nodes. They can have any information (what I would call a "data payload") that you may want to use in conjunction with that place or state of being.
edge
an edge is a connection or function that allows movement or expresses relationship between two nodes. They also can have names or labels, different kinds, and any data payload you want.
Computer Scientists usually draw graphs that look something like this. Some graphs have edges that go both directions, other graphs' edges will only go in one way.

If you wanted to draw your own graphs you could:

What does all this have to do with our 3D Model?

Now it is time to dive into details of representing our 3D Models in JSON.

4.3 Rooms

The fundamental unit in our model is a box-shaped 3D volume of space, called a "Room". By default, a Room will have no connections to other rooms, and will be bounded by six solid walls. In practice, almost all Rooms are connected to one or more adjacent rooms, so that people can get around. Connections between rooms are discussed in the next section. Every room has a name, a local origin (x,y,z) in the northwest corner, given in TNN coordinates, and (w,h,l) width, height, and length showing the size of the room, again given in units of 1.0 = 1 meter. Rooms can optionally include wall, floor, and ceiling texture information, along with zero or more decorations, and zero or more obstacles. Here's an example with just the minimum (no decorations or obstacles).
{ "class": "Room",
  "name": "corridor 230",
  "x": 28.8,
  "y": 12.3,
  "z": 15.23,
  "w": 1.8,
  "h": 3.5,
  "l": 3
}
Most rooms have more data than just a boring stretch of corridor that uses default textures to draw its walls, floor, and ceiling. By the way, this example is not the corridor outside my office in Cramer Hall, it is from in the Janssen Engineering Building at the University of Idaho. We have to measure new corridor coordinates for Cramer Hall.

4.4 Doors and Openings

There are two ways to connect two adjacent Rooms together in our model: Doors and Openings. Both of these require that the Rooms are touching along part or all of one of their sides: the coordinates have to place them right next to each other.
Opening
An opening connects two logical rooms into one larger space.
Door
A door is an opening with an attached object that can prevent movement.
JSON Examples:
{"class":"Door",
	"x": 10.2 ,
	"y": 12.3,
	"z": 20.07,
	"w": 1.04,
	"height": 2.21,
	"collide_in": 1.2,
	"plane": 1,
	"rooms": ["csac" ,"lab"],
}

{ "class": "Opening",
  "x": "26.6",
  "y": "12.3",
  "z": "15.23",
  "w": "2.2",
  "height": "3.5",
  "collide_in": "1.2",
  "plane": "3",
  "rooms": ["JEB228", "JEB230"],
  }

4.5 Decorations

Every room can optionally contain a list of zero or more 2D or 3D objects that are there just for their appearance. The most basic decoration is a 2D (rectangle) object. The class "Wall" is actually just a 2D object that has four corner (x,y,z) vertices and an associated texture image. The fact that Rooms' walls happen to be solid is enforced by the Room class, not the Wall class. While the vast majority of decorations are simple rectangle (Wall class) objects, there are some classes for things like Windowblinds that might involve many repetitions of a texture instead of a simple picture of some window blinds.
{ "class": "Room",
  "name": "JEB228",
  "x": 26.6,
  "y": 12.3,
  "z": 15.23,
  "w": 2.2,
  "h": 3.5,
  "l": 3,
  "texture": "jeb230wall.gif",
  "floor": { "class": "Wall",
    "texture": "jeb230carpet.gif"
    },
  "decorations": [
   { "class": "Wall",
     "texture": "whiteboard.gif",
     "coords": [26.65,13.3,15.3, 26.65,14.8,15.3,
		  26.65,14.8,17.9, 26.65,13.3,17.9]
      },
      {"class": "Windowblinds",
       "coords": [29.4, 1.5, 29.1],
       "angle": 90,
       "crod": "blue",
       "cblinds": "very dark purplish brown",
       "height": 3.05,
       "width": 4.7
      }
   ]
  }
You could actually put arbitrary objects, including 3D objects, on the decorations list; as a decoration, they would be visible but would have no other gameplay mechanic such as interfering with the user's ability to walk around. One of the For objects that the user can "feel", you put the objects on another list: the obstacles list.

4.6 Obstacles

Every room can optionally contain a list of zero or more 3D objects that force users to walk around rather than walk through them.
 {
   "comment1": "sample rooms JSON file",
   "class": "Room",
   "name": "SH 167",
   "x": 29.2,
   "y": 0,
   "z": 0.2,
   "w": 6,
   "h": 3.05,
   "l": 3.7,
   "floor": {
      "class": "Wall",
      "texture": "floor.gif",
      "coords": [29.2,0,0.2, 29.2,0,3.9, 35.2,0,3.9, 35.2,0,0.2]
      },
   "obstacles": [
      {
      "comment": "column",
      "class": "Box",
      "walls": [
         { "class":"Wall",
           "coords": [34.3,0,0.2, 34.3,3.05,0.2, 34.3,3.05,0.6, 34.3,0,0.6] },
         { "class":"Wall",
           "coords": [34.3,0,0.6, 34.0,0,0.6, 34.0,3.05,0.6, 34.3,3.05,0.6]},
         { "class":"Wall",
           "coords": [34.0,0,0.6, 34.0,3.05,0.6, 34.0,3.05,0.2, 34.0,0,0.2]}
         ]
      },
      { "comment": "window sill",
      "class": "Box",
      "walls": [
         { "class":"Wall",
           "coords": [29.2,0,0.22, 29.2,1.0,0.22, 35.2,1.0,0.22, 35.2,0,0.22]},
         { "class":"Wall",
           "coords": [29.2,1,0.22, 29.2,1.0,0.2,  35.2,1.0,0.2, 35.2,1,0.22]}
         ]
      }
      ],
   "decorations": [
      { "comment": "please window",
        "class": "Wall",
        "texture": "wall2.gif",
        "coords": [29.2,1.0,0.22, 29.2,3.2,0.22, 35.2,3.2,0.22, 35.2,1.0,0.22]
      },
      { "comment" : "whiteboard",
        "class": "Wall",
	"texture": "whiteboard.gif",
	"coords": [29.3,1.0,3.7, 29.3,2.5,3.7, 29.3,2.5,0.4, 29.3,1.0,0.4]
      }
      ]
 }

4.7 Tools

A third category of thing besides obstacles and decorations that can be placed in a room are called tools. This category is for things that are not quite obstacles or decorations, but more like interactive objects that a user might manipulate by clicking on them, or pick up in order to use later. For example, the model might have virtual whiteboards, and whiteboard markers to draw on them with. I guess maybe we will skip tools in our model. We might change our mind later.
 {"class" : "Pen",
  "Id"    : 1,
  "coords": [60.5, 0.95, 20.55],
  "color" : "blue",
  "Angle" : 0
  }

4.8 Furnishings

Although you can make almost anything by a suitable arrangement of Box objects, common types of furniture will have game mechanics. They go in the obstacles list. Classes "Chair" and "Table" support at least two chair types "office" and "lab"; maybe there is a third for "classroom". Field "position" gives the direction the chair is facing, in degrees; maybe 0 is due east, I forget. It is an interesting question whether such objects should be modeled as a logical entity like this, or simply as a collection of triangles or something like that. A logical entity such as a "Chair" class tells the application what game mechanics it should support, whereas just describing a chair's appearance as a set of triangles would not.
	{
	"class": "Chair",
	"coords": [31.2,0,1.4],
	"position": 0,
	"color": "red",
	"type": "office":
	"movable": "true"
	},
	{
	"class": "Table",
	"coords": [31.4,0,2.4],
	"position": 180,
	"color": "very dark brown",
	"type": "office"
	},
        {"class": "Computer",
         "coords_mntr": [58.85, 1.02, 20.4],
         "coords_cpu": [59.2, 0.2, 20.25],
         "coords_kb": [58.85, 0.96, 20.05],
         "color": "very light gray",
         "angle": 180
        },
        {"class": "Printer",
         "coords" [42.25, 13, 27.4],
         "angle": 0,
         "color": white
         }

4.8 Specialty Objects

Ramps, and Stairs are examples of objects that can be placed in a room that interact with game mechanics in a fundamental way. Instead of preventing movement, the user's y value is calculated from the base of the room, plus some amount depending on their position on the ramp or stairs.

In class Ramp, there is a "type" field.

The number of steps (numsteps) field is used only for types 4-5.

 {
 "class": "Ramp",
 "coords": [70.76, 0, 32.5],
 "color":  "pink",
 "texture": "dat/nmsu/texsmall/blue_tile.gif",
 "type": 3,
 "width": 3.1,
 "height": 1,
 "length": 13.2,
 "numsteps": 5
 }
While we are talking about stairs, it might be worth discussing elevators. I've implemented elevators before by sleight of hand, providing two or more elevator rooms at different "y" elevations, and implementing a mechanic for changing the user's "y" value to the selected floor, perhaps via a button press. It would be possible to have a single elevator room object move from one floor to another when the door is closed, but might be harder to code...and the user might not see any difference.

5.0 Textures

The coordinates in the JSON model would be sufficient to show a wireframe model of Cramer Hall. In order for a 3D view to look recognizable, we need to obtain textures for the walls, floors, ceilings, and objects in our model.

In this context, a texture is a 2D image, part or all of the contents used to draw the surface of an object in a 3D scene. (Aside: for what it's worth, textures a.k.a. "fill patterns" can also be used in 2D graphics).

5.1 Texture Basics

How much do your students already will know about digital images?
pixel
A pixel (from: picture element) is one dot in a digital image
RGB
The color of a pixel is typically given in (red,green,blue) values. (0,0,0) is black. white is often (255,255,255) or (1.0,1.0,1.0). Other colors are obtained by mixing R, G and B in different ratios.
resolution
the number of dots in an image. frequently given as an image size, sometimes given as a ratio such as "dots per inch".

5.2 Texture Sizes

5.3 Converting a photo into a texture

For this part, we should learn a little bit of a program called the GIMP. You can do some of this crudely with Windows Paint, or you can do it with Photoshop if you know that tool.
Crop the image
cut off around the edges, the part that is not part of your texture. GIMP has selection tools, and a Crop To Selection command.
Stretch out your bad perspective.
GIMP has a command that let's you straighten out crooked edges that result from taking a picture at an angle.
Rescale it to use width/height powers of two
Changing an image's size is easy.
Save it, probably as .jpg or .png
GIMP expects you to "export" to save in an image format. Its normal "save" commands saves in some weird proprietary GIMP format.

5.4 Texture Activity

In this exercise, let's practice making a usable texture of the Tom Nartker plaque from Cramer 1st floor, south hallway. You should apply this process, if you have time, to other textures that you encode in your room.

Step #0: if the camera shot is poor, your texture may come out better if you re-shoot it. In this case, from my poor camera, in the left shot (semi-broken regular phone camera) the plaque is readable but poor. The right shot is the re-shoot using the non-broken lower-resolution selfie camera. (In subsequent instructions where I show the filename nartkerplaque.jpg, you probably want to pick nartkerplaque2.jpg, since that is the better image on the right.)

Step #0.5: Download the image you want to turn into a texture ont your computer. You may well have to transfer the image from your camera, or in this case save it from the NMT CSE department website, in order to edit it. The URL for our sample image is http://www.cs.nmt.edu/~jeffery/cramerhall/nartkerplaque2.jpg. So, visit link and right click the image and click "Save image as..." and save a local copy under your home directory.

Step #1: Launch the GIMP. It takes awhile to startup. If working on your own computer, you might have to first download it from gimp.org and install it. I believe it is installed on the NMT lab machines. After you launch the GIMP successfully, Open the Image.

Step #2: Select a bounding rectangle subset of the Image. The bounding rectangle is the smallest rectangle that contains the entire object that you want to turn into a texture. It is awesome computer science jargon to help you fit in at computer science parties.

Step #3: Crop the Image. The Image menu has a Crop to Selection command that will cut off everything not in our bounding rectangle.

After you click Crop to Selection, your cropped image looks something like this.

Step #4: Fix the perspective

To use this texture, we need to straighten it out. Go into Tools menu, pick Transform Tools submenu, and pick the Perspective tool.

Drag the yellow/orange corners of the image over to the corners of the object in the picture. You can drag all four corners, although in this case cropping got the left two corners perfect and I only had to drag the two right corners.

After clicking Transform, your image should look a lot better.

Step #5: Rescale width and height to the nearest power of two, or a power of two that is smaller.

Pick the Scale Image function from the Image menu.

Unlock the lock that forces width and height to scale proportionally.

Change width and height to powers of 2, and click Scale. For the Nartker plaque, I took a 16xx pixel height down to 1024. I could have scaled the width also to 1024, forming a square that stretched the image sideways a lot. Instead I took width down to 512, reducing stretching and shrinking the image a bit.

If the texture will usually be viewed from a distance within your 3D scene, you may want a texture MUCH smaller than the original camera image.

Step #6: Save your texture

Pick the File menu Export to... or Export As. This time I am using Export As.

Export As let's you choose a filename, including what extension and what kind of image format to use. For textures JPEG (.jpg) or PNG (.png) or maybe even the venerable GIF (.gif) might be good.

The Export As for the JPEG format, and some others, will ask you what "quality" to use; this affects image file size and accuracy. The default (quality 90 in this case) is generally good.

Since it had to be a power of two, the finished texture image may look a bit funny in terms of its width, height and stretching. When we apply the texture in the 3D scene, the ratio of width to height will get corrected at that time.

The resulting texture image could still be enhanced further by some professional PhotoShip (or GIMP) nerd; maybe it has red-eye or reflections to remove or other photographic enhancements needed. In our case there are a couple reflections on the black side and bottom border, but I am going to call it good for now.

6.0 Measurement and Texture Collection Activity

This part of the module asks students to each build a part of the Cramer Hall Model. This will require tape measures, cameras, paper and writing utensils. Turn in your .json and images on Canvas. Zones for instructor week are shown here:

Cramer 230b Example

Jeffery decides to do his office. Tape measure says width = 3.35m, height = 3.6m (false ceiling hangs lower over most of it), and length = 6m.

Jeffery calculates x from 2nd floor floorplan, where 20' is ~90pixels and the x in the northwest corner of room is 308 pixels from the origin. 308/90*20 = 68.4' = 20.8m, so x is 20.8m. y was calculated Tuesday at 4.1m. z = 0.5m thanks to outside wall thickness.

{ "class": "Room",
  "name": "Cramer 230b",
  "x": 20.8,
  "y": 4.1,
  "z": 0.5,
  "w": 3.35,
  "h": 3.6,
  "l": 6
}
OK, so that's a "D" grade. Now to bump that grade up by adding textures, decorations and obstacles. Except woops, when I try to run
./cra1 cra230b.json
it dies with the message:
valid JSON, but not a model file: cra230b.json
What could possibly be wrong?

6.2 Testing Your JSON Model

After you write a JSON file for your room(s), and validate it as legal JSON using jsonlint.com, you can further test it in two stages.
  1. Validate it using the Unicon JSON checker, checkjson.icn. Save this to a directory/folder under your home directory, whereever you are storing your .json files
  2. Test it using the cra1 model viewer.
Suppose you write a file named cramer228.json. To run the checkjson program you might download checkjson.icn and then say at a Terminal window:
 unicon checkjson
(this compiles checkjson.icn down to an executable named checkjson) and then type
 ./checkjson cramer228.json
If checkjson can't load your JSON, then the cra1 model viewer will surely not be able to load and view it.

If checkjson is OK, then you can run the cra1 model viewer like this:

 ./cra1 cramer228.json

Cramer 230b Example, continued

In the case of my cra238b.json: When I add square brackets around my json code, it will display in cra1:
[{ "class": "Room",
  "name": "Cramer 230b",
  "x": 20.8,
  "y": 4.1,
  "z": 0.5,
  "w": 3.35,
  "h": 3.6,
  "l": 6
}]
Of course, this room is completely boring, and wrong on the walls, floor, and ceiling, before we even start to talk about decorations.
not used

Where to start with textures? The walls, floor, and ceiling. How's about I start with the floor. Resize it to power of two (in this case, by just cropping).

Stick it in the 230b Room:

[{ "class": "Room",
  "name": "Cramer 230b",
  "x": 20.8,
  "y": 4.1,
  "z": 0.5,
  "w": 3.35,
  "h": 3.6,
  "l": 6,
  "floor": "floor.jpg"
}
]
Note: to switch over the default walls for all rooms, convert your wall texture to a .GIF file and name it walltest.gif

Sticking in a decoration.

The false ceiling in Cramer 230b hangs down 30" (0.75m) and is offset 22" (0.56m) from the outside walls. Its coordinates start at

20.8,6.95,1.06
and extend as a box of size 3.35-0.56, 0.75, 6-.56 we might use this texture to place it:

7.0 Collating Models

The models produced in the preceding activity will need to be collated together into one big model file. It was previously conjectured in a MST camp planning meeting, and possibly true, that "this would best be performed by CJ". What follows this are various fancy next steps that are not part of this week's camp curriculum, but we would introduce next if we were continuing.

7.1 Tiling textures

Tiling is when a texture is repeated to cover a large area.

7.2 Minimizing Polygon Counts

Since our model is to be used in a videogame, we could discuss some of the simplifications, abstractions, and trade-offs that keep the model simple and the polygon count low, so that we can display it even on low-end computing devices.

8.0 Playthrough

This part of the module will allow students to run the CVE collaborative virtual environment on lab computers, and see all the models, stitched together.

8.1 What Dr. J Learned from teaching Cramer Module at MST Camp

Don't repeat these in your reflections, unless you have something more and different to say!

8.2 What teachers Learned from Cramer Module at MST Camp