Fender: A Simple Modeling Package

  1. What is Fender?
  2. Screenshots

What is Fender?

Fender is an end to end Test-Driven Development project. Since I'm the only one working on it. I don't see any problems with just hacking this webpage to record progress. How Test Driven Development works is that we identify a requirement, and break it down into Tasks. Tasks have 1 or more corresponding Tests. We then add just enough code to make the Tests past. And I do mean just enough. We watch out for code smells, and refactor the code when they occur.

What is a Code Smell?

Here is a list of code smells

I don't take credit for this list, it comes from David Astel's book Test-Driven Development: A Practical Guide which inspired me to do this project in this fashion. Although, I doubt that his list was totally original either, although my explainations are.

Comments

This may come as a shocker, but comments are a sign of bad code. By comments we mean comments in side procedures. Why? Because it either means 1 of two things. 1) It explains the obvious, like:

// now we add two numbers together result = a + b

Or it explains something non-obvious. In the first case, the comment takes up visual space, and is worthless to the reader. In the second, the code is overly complex, it should be broken up into smaller routines.

Data Classes

A data class is equivalent to a C structure. A corner stone of OOP is that you group data and operations that work on the data together. Classes that just provide data violate this principle and is a sign of bad design.

Duplicate Code

Code that is duplicated causes synchronization problems. Which is at the heart of almost all software engineering difficulties.

Inappropriate Intimacy

Usually associated with a data class. It is when class B grabs all the data from class A and does some calculation with it. This also violates the principles of associating data and operations that manipulate the data.

Large Classes

Large Classes are difficult to use. Unless they are machine generated, they should be delegating their work to other classes.

Lazy Classes

A lazy class is one that doesn't pull its own weight. And generally adds more mental strain on the programmers than it's worth.

Long Methods

Methods should be short. Longs methods are breeding grounds for code duplication and bugs.

Switch Statements

Polymorphism is the preferred way of implementing a switch. A method dispatch is extensible. A switch statement has to rethought anytime the possible values change.

Shotgun Surgery

If one change means you have to change code all over the place. The code smells.

Complex Conditionals

Code that repeatedly tests some complex condition is actually begging to be it's own class.

Rolled Together Operations

Operations like payTheBillsAndTakeOutTheTrash() are unrelated and need to be separated. Operatations like payTheBills(), and takeOutTheTrash() are more resuable because they contain orthogonal functionality. They should not be composed.

Magic Numbers

Where did this number come from? I don't know, I pulled it out of a hat once. If a number is important to the system, it should be a symbolic constant to tell you what it means. If it is a number just pulled out the hat, why is it in there?

Code Smells: When is the Right Time to Refactor?

Code is a environment. Refactoring everytime you see a smell is a little extreme, just like rigorously cleaning every dish the second you are done eating is unproductive. A good rule of thumb is the so-called rule of three. The third time you do the exact same thing, it is probably time to rethink how you are doing it. Code smells guide where we refactor, it doesn't say that you have to. But you probably should.

How are we going to model?

There are basically four ways to model a surface. One way is directly placing points, then defining line loops for polygons. This is also called point-pushing. Another way is to try and model whatever you are trying to do as one surface, usually nurbs. The third is to model by patches, that is make NURBS surfaces then stitch them together. The fourth and final way is subdivision modeling. That is you build a rough hull of what you want the surface to look like, then a procedure comes along and makes it look smooth.

Fender will implement the fourth style of modeling. Since I suck as an artist, but I'm pretty sure I can make a crude model of what I want, then the computer comes along and makes it look cooler.

We will be modeling with quads, I've decided quite arbitrarily that quads are easier to model with than triangles. The modeling algorithm is going to be centered around Catmull-Clark Subdivision Surfaces. Which has the added advantage that the limit surfaces of Catmull-Clark surfaces are... NURBS surfaces.

The general workflow for modeling with fender is the following. Most everything starts from a cube. You model by creating cubes, manipulating their vertices, welding or connecting them together and tagging particular edges in the mesh with sharp values.

At the end of the manipulation, you must end up with a two-manifold mesh, which defines your subdivision surface.

Fender Technologies

XML Based Mesh Format

Fender uses an XML format to store meshes. Why? First of all XML is text based. If Fender messes up, you can look at the XML file and find out what is giving Fender problems. Secondly, it gives Fender a certain amount of implementation flexibility since it doesn't hard code the sizes of any of the variables. Third, meshes have to be used by a number of different graphics tools, XML is great for promoting this type of interoperability. Fourth, there are already a large number of tools that help you handle XML based documents. Lastly, you don't have to invent your own, completely arbitrary syntax tree.

Thankfully, interpreters for the mesh file can be fairly trivial. This is because the DTD makes it hard (sometimes impossible) to express invalid meshes. Errors in the mesh file are caught by other tools within the XML tool-suite allowing the parser to be very simple.

Here is the mesh.dtd:

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT mesh (vertex+,polygon+)>
<!ELEMENT vertex (diffuse?,normal?,pointSize?,specular?,texCoord4*,texCoord3*,texCoord2*,texCoord1*)>
<!ATTLIST vertex x CDATA #REQUIRED
                 y CDATA #REQUIRED
                 z CDATA #REQUIRED
                 w CDATA #IMPLIED>
<!ELEMENT diffuse (EMPTY)>
<!ATTLIST diffuse r CDATA #REQUIRED
                  g CDATA #REQUIRED
                  b CDATA #REQUIRED
                  a CDATA "1.0">
<!ELEMENT normal (EMPTY)>
<!ATTLIST normal x CDATA #REQUIRED
                 y CDATA #REQUIRED
                 z CDATA #REQUIRED>
<!ELEMENT pointSize (EMPTY)>
<!ATTLIST pointSize size CDATA #REQUIRED>
<!ELEMENT specular (EMPTY)>
<!ATTLIST specular r CDATA #REQUIRED
                   g CDATA #REQUIRED
                   b CDATA #REQUIRED
                   a CDATA "1.0">
<!ELEMENT texCoord4 (EMPTY)>
<!ATTLIST texCoord4 set CDATA #REQUIRED 
                    r CDATA #REQUIRED
                    s CDATA #REQUIRED
                    t CDATA #REQUIRED
                    u CDATA #REQUIRED>
<!ELEMENT texCoord3 (EMPTY)>
<!ATTLIST texCoord3 set CDATA #REQUIRED 
                    r CDATA #REQUIRED
                    s CDATA #REQUIRED
                    t CDATA #REQUIRED>
<!ELEMENT texCoord2 (EMPTY)>
<!ATTLIST texCoord2 set CDATA #REQUIRED 
                    r CDATA #REQUIRED
                    s CDATA #REQUIRED>
<!ELEMENT texCoord1 (EMPTY)>
<!ATTLIST texCoord1 set CDATA #REQUIRED 
                    r CDATA #REQUIRED>
<!ELEMENT polygon (index+)>
<!ELEMENT index (EMPTY)>
<!ATTLIST index id CDATA #REQUIRED>

Cg and CgGL For SWT/Java bindings

A library that gives access to the runtime api of Cg, the real-time shading programing language by nVidia.

Here is a sample mesh that describes a cube.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mesh SYSTEM "mesh.dtd">
<mesh>
<vertex x="-1.0" y="-1.0" z="-1.0">
</vertex>
<vertex x="1.0" y="-1.0" z="-1.0">
</vertex>
<vertex x="-1.0" y="-1.0" z="1.0">
</vertex>
<vertex x="1.0" y="-1.0" z="1.0">
</vertex>
<vertex x="-1.0" y="1.0" z="-1.0">
</vertex>
<vertex x="1.0" y="1.0" z="-1.0">
</vertex>
<vertex x="-1.0" y="1.0" z="1.0">
</vertex>
<vertex x="1.0" y="1.0" z="1.0">
</vertex>
<polygon>
<index id="0"/>
<index id="1"/>
<index id="3"/>
<index id="2"/>
</polygon>
<polygon>
<index id="0"/>
<index id="2"/>
<index id="6"/>
<index id="4"/>
</polygon>
<polygon>
<index id="3"/>
<index id="1"/>
<index id="5"/>
<index id="7"/>
</polygon>
<polygon>
<index id="0"/>
<index id="4"/>
<index id="5"/>
<index id="1"/>
</polygon>
<polygon>
<index id="2"/>
<index id="3"/>
<index id="7"/>
<index id="6"/>
</polygon>
<polygon>
<index id="6"/>
<index id="7"/>
<index id="5"/>
<index id="4"/>
</polygon>
</mesh>

Valid HTML 4.01!