So, in my quest for Cool Stuff(tm) for ,

So, in my quest for Cool Stuff™ for #MatrixClock, I’ve decided to work out 3D object parsing. The spinning cube/octahedron code I have works, but it gets a little confusing when trying to lay out faces and vertices properly. I also need to figure out scaling, and then I can implement things like backface culling.

One goal is to be able to load models from SD card - so I’m looking at preprocessing models into a more streamlined format that’s easy for the teensy to load. I’m currently looking at parsing Wavefront OBJ files, as they’re a pretty simple format. I don’t plan on doing very complex models, mind you.

The video is the current state of the octahedron code - it looks okay, but it’s using a very rudimentary “Painter’s Algorithm” for drawing the edges. I need to work out the surface normals, so I can implement a better algorithm.

hmm. Binary STL might be the way to go.

Absolutely fantastic.

Sory @Noel_Bundy I didn’t have the courage of my convictions when I posted before (and deleted). STL is really good and there are a ton of apps out there that can turn primitives into simple mesh models.

So, looking at the file format of the binary version (easily exported from Sketchup), I’m not sure how to handle reading the “REAL32” entry. It’s supposed to be a 32-bit float- which means 4 bytes read. I know how to read it in as a UINT32:
(from my bitmap class:)

uint32_t Bitmap::read32(SdFile* f) {
uint32_t result;
((uint8_t *)&result)[0] = f->read(); // LSB
((uint8_t *)&result)[1] = f->read();
((uint8_t *)&result)[2] = f->read();
((uint8_t *)&result)[3] = f->read(); // MSB
return result;
}

I’d really prefer to read the binary STL, as it’s smaller, and faster to read.

You could try pointers or unions, here is a version with a union which I think is clearer, based on your original code:

union uRealUInt {
uint32_t IntVersion;
real32 RealVersion;
};

real32 Bitmap::readReal(SdFile* f) {
uRealUInt result;

((uint8_t *)&result.IntVersion)[0] = f->read(); // LSB
((uint8_t *)&result.IntVersion)[1] = f->read();
((uint8_t *)&result.IntVersion)[2] = f->read();
((uint8_t *)&result.IntVersion)[3] = f->read(); // MSB
return result.RealVersion;
}

You will need to make sure the byte orders match the processor and the file you’re using as the orders vary. Some will store the bytes in LSB to MSB, others MSB to LSB. Some even more complicated, like 1,0,3,2.

Should be enough to move you onward.

Hmm. I tried that, but I get an error: “real32 does not name a type”. I guess it’s not defined in stdint.h

Beyond that, I’ve found that the code I’m working from, the Spinning Cube, is really really janky. I’ve got to figure out how to apply transformation matrixes in a smarter way - I don’t really understand them yet. I know a 3x3 is sufficient for rotation, and a 4x3 will let me do scaling and translation, but I just don’t know how that actually works.

You could try a real as these are often 4 bytes. And use a sizeof to verify that it is 4 bytes long and then work out the byte order.

I used to have to some 3D routines somewhere to do fast animations and ray tracing, from years back. God knows where they went to.

“float” is four bytes on Arduino.
(So is “double”!)

[Side note: a four-byte “float” has many fewer bits of precision than a “long” (aka int32_t) ! A four-byte IEEE754 binary32 “float” has only 24(or 23) significant bits of mantissa, but a four-byte int has 31 significant bits (plus one bit for sign). That means that a signed int can be up to 100X more precise than a float!
Of course, if you do this sort of fixed-point math using ints, you have to track the position and meaning of the ‘decimal point’ yourself, which is potentially too much of a PITA to be worth it. We use fixed point math all over FastLED; it’s a perfect match for this kind of high-performance, fixed-range coding.]

Mark: I wouldn’t even know where to begin with that. :slight_smile: I changed the code to use float, and it seems to work.

Fixed point maths is well worth reading up on Noel. Even if you don’t use it, it is important to know about its existence and uses. It’s actually not as bad as it sounds.

The lib8tion.h file in FastLED has some notes on fixed point math and comments on how we’re using it in FastLED. Most commonly, we use a one-byte value 0-255 to represent a fraction from 0.00 - 0.99. The function “scale8” is used to scale one single-byte value by a ‘fraction’ from 0.00-0.99.

Oh, fair enough. It’s something I will learn eventually.

Current status: Managed to find some code with proper explanations for doing matrix transformations of points. I might actually have to switch to OBJ format, because STL files have so much duplication - each triangle is a set of points + normal, and if I have 5 faces surrounding a point (like an icosahedron), that point is duplicated 5 times. Which means a lot more work for doing the transforms.

I might have to make a preprocessor for the OBJ files, to make them easier to load.

And I got it working! I’ve managed to a) write a simple OBJ -> C file convertor with PHP, and backface culling is also a go. Basically, makes a nice C array. If I get fancy, I could probably make it spit out prototypes for drawing/transforming the mesh.

Video is here: https://www.youtube.com/watch?v=GzbEO13n4xE

The next challenge, I guess, is to get z-ordering for the faces, so I can draw them from back to front, and add depth cues.

From someone who only mildly understands this all— and for no practical value except to express myself— awesome!

With some help from the fine folks on the arduino irc, I now have a painter’s algorithm working beautifully.

I’m going to polish up the PHP script, and make it web-facing. The code is written to work with triangular faces, but with some changes to the drawing code, it can handle others. Can’t see a reliable way of handling files with varying face sides, though.