I’ve extended the pixel man program – now includes color, full surface coverage and ability to work on a sequence of files.
Color in OBJ files
An obj file has a series of face lines, vertex lines, vertex normal lines, and vertex texture lines. A face line from a Poser OBJ file might look like this:
f 499/829/490 493/829/493 494/833/494 340/315/341
This is a set of 4 references to the corners of the face. In Poser almost all the faces are quadrilaterals. The line above reads: use the 499th vertex with the 829th vertex texture and 490th vertex normal. The 499th vertex line could look something like this:
v -0.0152238 0.626537 0.126882
which gives the x,y,z coordinates of the point in space. This fixes the vertex of this corner of the quad in space. The 829th vertex texture from a Poser OBJ file might look like this:
vt 0.117642 0.553414
These are x,y values ranging between 0.0 and 1.0 . Each corner will have a x,y coordinate – this quad (usually a parallelogram in poser) gives the section of texture to map onto the face. The texture is gotten from a usually square JPG file. The texture is considered to be on a unit square by the vt coordinates whatever size the actual JPG is. The texture maps from top left=(0.0,0.0) The way the faces know which texture map to use is that each section of face(f) lines is preceeded by a usemtl instruction like so:
This tells the program that is rendering the OBJ file to look up the MTL(material) file which always accompanies the OBJ file and see what to do with the “pants” texture. This could be a color or could in turn refer to a JPG to be used as a texture map.
In the top image I have just drawn the image (a 100*100 Bionic Man JPG) onto a single square. The obj file looks like this:
v 0 1 0
v 0 0 0
v 1 0 0
v 1 1 0
vt 0 1 0
vt 0 0 0
vt 1 0 0
vt 1 1 0
f 1/1 2/2 3/3 4/4
and the MTL file “bionicplane.mtl” looks simply like this:
For the Pixel man i’m not going to bother to map textures on the cubes, because a)this doesn’t go along with the abstract aesthetic and b)it’s harder. Rather i want to use the texture map information to get an single color for each cube. In the lower image above I have split the square into 20*20 smaller squares where each square picks up the color of the texture map at that particular region – specifically it picks the RGB values from the central pixel point for each square. It picks up the color as a RGB value in java image.getColor(x,y). This then needs to be converted to a color on our MTL file. I decided limit the model to use 1000 different colors. So a dummy MTL file is made where each color is defined by a red,blue,green triple in the range 0.0 to 1.0. here’s a section of the MTL file. Each usemtl is named “c” plus the rgb values from 0-9 (a very simple model) so red would be usemtl c900.
Kd 0.6 0.2 0.0
Kd 0.6 0.2 0.1
Kd 0.6 0.2 0.2
This MTL file is fixed and sits alongside the texture maps and OBJ files and java program. Now each cube can be drawn in one of 1000 colors depending on the value pulled from the texture map. Simply i used the central pointof the texture vertex (which as they all seem to be parallellograms is just the average of opposite coordinates).
Texture Maps in Poser
In Poser each OBJ is exported out with a MTL file. The MTL file lists the usemtl’s and tells you which JPG to use. I know that i’m not going to bother rendering the tongue,eyes,fingernails etc… so I can lose a lot of the texture maps. The program will ignore faces that are rendere in these materials. Poser stores the maps in sub folders eg: Poser6/Runtime/textures/P6ClothingTextures/James I copied the textures i needed to the folder in which i’m working making sure they are all the same size. For James Casual i need Head, Body, Shoes, Pants, Shirt. These textures will be wrapped around an OBJ so they look a bit warped.
Importantly you can draw whatever you want onto these texture maps – they are just JPGs after all. This was how i made the MADA logo by the way.
The first attempt at the color pixel man program resulted in this:
The texture maps haven’t been properly added to the figure. To try and work out what was going on i simplified the program to just look at the head and wrap a color bar around it. The image below shows the result:
From this i can see that the texture map is being added onto the OBJ upside down. On the left, the blue is at the bottom of the color bar but at the top of the head. After correcting this i got a successful mapping:
The yellow and red circle was crudely added to the Tshirt by me to help see that the texture was going ion the right place. This was almost right but just above the right hand you can see a section of hand material (pink) which sticks out of the grey(shirt) material. This is because in parsing the OBJ file i was dealing with cubes as i came to them. that is if a hand cube is after a shirt cube then it will over-ride the color of the shirt even though in reality it is slightly below it. This was fixed using a couple of tricks. 1) the faces are dealt with in a specific order so one material will always over-ride another. 2) i speeded up the code by allowing only the first cube from any given material to count. so further cubes in same location don’t bother recoloring the cubes. So the order of coloring is TShirt->Pants->Soles->Shoes->Head->Skin
Back-tracking, in the previous post i’d made a figure only using the vertices from the OBJ file. In the new version each face is fully filled in. This involved some 3d maths. The technique is as follows:
For each face to be filled we have 4 (x,y,z) coordinates.
1) create a box which covers all these coordinates by getting the minimum and maximum of each coord
2) for each cube in the box determine the distance from the central point to the plane of the 4 coords
3) this is done by ignoring each coordinate one at a time (effectively projecting the cube onto the ignored plane) and splitting the plane into 2 triangles (which always make a single plane) then we have a measurement of
4) the distance of a point to a triangle
5) this is done using vector maths. here’s the comments in my code which i adapted from online examples:
//we have a point px,py,pz
//and a triangle defined by 3d points ax,ay,az, etc...
//we need to use the three triangle points to define a plane of the form
//a*x + b*y + c*z + d = 0
//then the distance to the point px,py,pz is defined by the equation
//D = (a*px + b*py +c*pz + d )/Math.sqrt(a*a + b*b + c*c)
//to get the plane from three points
//we take a vector cross product from two of the triangle edges
//this gives a mormal nx,ny,nz
//then use the normal plane equation which is
//nx[x-ax] + ny[y-ay] + nz[z-az] = 0
//where x,y,z are the variables
This is reasonably fast – a single Poser figure takes about 15 seconds to convert to cubes.
The single figure is made up of about 80,000 cubes in an array – i wanted to make sure that the resulting OBJ file was as small as possible so wrote code to remove any faces which were duplicated. that is any faces which touch other faces (of the neighbouring cube) are culled. this resulted in an output face count of around 60,000 which Rhino can open fine. To check this was working OK i rendered out a sliced man to check that redundant faces weren’t being drawn:
In close up you can see that the output is effectively two rings of cube faces (an outer surface and an inner surface) – i may look to make these closer in the future, maybe even dumping the inner surface all together. I’m not sure how thick surfaces have to be for Rapid prototyping or Laser cutting but i’ll ask the technicians at the start of next term.
Finally i adapted the program so it could deal with a sequence of images. To minimize the size of the arrays being used i wrote a pre-parser program which scans all the OBJ files and simply outputs the maximum and minimum values for the x,y & z coordinates. these can then be hard coded into the real program so it isn’t using arrays bigger than it needs. For the 10 frames of running man below the ranges were (approx in cm) x=73 y=191 z=136. A nice trick is to render the images in reverse order so the last image (which is chronologically later) gets first chance to color a square (as this frame is bursting through the others).
This took about 2.5 minutes to create. So i now have a program which can quickly output a pixellated version of a movement sequence from Poser. I can use this to determine if the sequence is ‘interesting’ or not.
The last thing left to do with this program is to write interpolation code which takes any 2 OBJ files and fills in the gaps between them. This should be relatively straightforward in pixel land – in real 3d i can see a boolean nightmare in store. In pixel land you just add in a 4 extra faces between each of the respective faces from the 2 OBJ files in the same color.
There are artists who have created sculptures like this (and there’s a fair sized following of pixel art) . here’s one by Thomas Broome