Free to a good home - my 3D engine!

Still hovering over these forums regularly in case anybody needs Qiso support etc, but otherwise much less active than I used to be.

Some of you might remember years ago I started tinkering with developing a 3D engine for Solar2D - well, still Corona SDK back then. I always intended to jump back onto this and turn it into a proper plugin with a variety of basic shapes and support to import .obj files but alas the years are flying by and I just never get time for it, so I’ve decided the best thing I can do at this point is to upload my work so far and see if anybody else wants to build from it. So here we are!

If you just launch main.lua in the simulator you should see spinning cubes and the code to generate this scene is really simple and self explanatory. The engine itself is in the plugin folder - again my intention was to eventually release this as a plugin. Following the code through for how everything works should be easy enough, it’s pretty decently commented I believe. There are also various TODO lines and obviously temporary bits so it might even be clear where I was planning to extend and replace functionality. Much of this is very crude at the moment to be honest. It only does cubes for example, but how the vertices are mapped out for those should be indicative of how they’d be mapped out for other shapes too.

Anyway, I just hope this is useful or at least motivating for anybody else wanting to do 3D in Solar2D. All I ask is that if anybody does build from this, I’d appreciate if you could just attribute QWeb Ltd in some way.

Q3d 3D Engine.zip (1.1 MB)

10 Likes

I chimed in on the earlier thread, about having done some stuff to manage the depth buffer and add z-coordinates to polygons. I also mentioned setting it aside for a while to tackle a Vulkan backend and tease out any portability concerns. (There were a few, but nothing terrible.)

The backend is basically at parity with the current OpenGL one. When it got there, I took a second sweep through the original project: the results are now posted as a “Custom objects” series of PRs: #1-4, 5 being something new. (Vulkan’s there as well. Also, I have a consolidated branch here.)

There have been some recent discussions about starting to integrate this, though I don’t know how soon that would extend to the 3D-relevant bits, PRs #3 and 4.


Also, for anybody “looking to adopt”: I’m NOT describing a competing engine. :smiley: Rather it’s a way to expose real hardware support through plugins; then and now, it’s relevant in that depth buffer access is one such possibility.

2 Likes

I do recall our depth buffer conversations and your work to add that in to the polygon code sounded really promising. I never got around to looking at your project properly but working that in to my engine would be a brilliant leap forwards for anybody who does want to take this on.

I did at one point experiment with stripping out the polygon rendering code and using the memorybitmap plugin with some crude raytracing approach to draw my own z buffered pixels and then have rhe renderer part just swap that bitmap out to the screen every 1 fps - simulated buffer swapping and software based depth buffering basically. I still have that code somewhere too I’m sure, but unsure how far I ever got with it. Presumably not far enough to have ever shown anything off.

This is really great work, pure science, mathematics, engineering, and art … reminds me of John Carmack somehow :slight_smile:

Great Job Truly

1 Like

Thanks. I’m not sure I’d go as far as saying it was art, but I’ll take the rest, ha. I just hope it’s useful to somebody!

Thanks for creating this. I am just starting to see what I can do with your work. My first plan is to create a full set of platonic solids to see how that works. This is an excellent starting point.

1 Like

Please do post back with anything you end up with. I’m still hovering over these forums, despite my lack of activity, and would love to see movement on this :grin:

I’ve been doing a spate of plugins lately and some of them are relevant, so I’ll chime in too.

I posted this (in #plugins) on Discord last month:

"Years ago, when external textures were fairly new, I had a “render 3D object to texture” plugin more or less ready, built on top of Tiny Renderer. I was hoping to make it available, but there were mysterious crashes on exit, randomly but fairly frequent.

You’ll see that these same issues came up in Graphics Extensions for Solar2D, and I alluded to problems I’d seen in the past.

Well, the upshot is that I revisited this and with the related fix does indeed now seem stable. Actually, what I updated was something more general, allowing a scene with multiple objects (I don’t actually remember writing this :smile:).

While I’ve been at it, I also added SSGE (albeit rather heavily modified) as another backend.

I still need to kick the tires on a few things, and probably do a little bit more to make those two backends’ interfaces match, but it’s getting there.

I also have MoonAssimp mostly ready as a plugin now–the Lua binding was trivial; building Assimp, not so much–to replace the rather iffy script I’d been using to load models.

(This is unrelated to what I previously showed here, which would enable genuine hardware 3D, within the display hierarchy: Discord post)"

My tests for SSGE are here: SSGE.zip (146.4 KB) (Note that they depend upon the assets from the original repository, which are a hefty download.) It won’t win any awards for speed, but the results are pretty and it might be good for capturing still shots, e.g. for animation frames or backgrounds.

@richard11 I believe I mentioned an earlier version of the Tiny Renderer one to you ages ago in some DMs.

Anyhow, those listed in that post and a few others are now available, though I haven’t done any documentation and I’d consider them still in beta: plugins

I split up the “render 3D object to texture” backends into separate plugins, obviously. As for the “few others”, I’ve wrapped some shape generators to accompany this stuff:

octasphere
par shapes
generator
yocto shapes (This is kind of an odd fit for a Lua API so I’m not far along on testing this one.)

@baukburg You’ll see the solids there. Possibly a few times; I tried to be thorough but this meant some crossover. :slightly_smiling_face:

This has been my Tiny Renderer test lately, although since I had it open I’ve been adding the shape tests (and an unrelated memory thing; never mind that): tinyrenderer-and-shapes.zip (574.3 KB) Most of these actually had save-to-file stuff I’ve used for tests whereas with Yocto I’ve been sending geometry to the renderer. The shape generators are renderer-agnostic, however, e.g. they could be used to supply polygons to Q3d.

There are a few fixes I’ll need to upload, which I’ll try to do soon, but these two attachments should give some semblance of how all of this works.

1 Like

Bloody hell, how many projects can one person work on at once? :neutral_face::joy:. I honestly find myself struggling to juggle just two these days.

1 Like

One thing I would like to note actually, to @baukburg or anybody else picking up from where I left off - if I were to brush the dust off of this again myself, I think I’d actually strip the texture support out completely to be honest. It was cool to get that working and see textured polygons in motion, but it meant adding loads of vertices to even just basic cubes for decent enough UV mapping and without proper multithreading support keeping the vertex count down is going to become important.

Strip the texturing out, simplify the cubes back to just one vertex per corner, and go with just single colour faces. The lighting mechanic should be enough to keep flat colour objects looking 3D and the simpler the cubes are, the easier it’ll be to work other shapes in. For mobile games I don’t really think there’s much need for textured faces anyway.

@richard11 - thanks, I already followed that path. I am using a simple “paint” scheme and reduced the cube to 8 vertices. One thing I am looking to change is to add color assignment per face. I need to dissect your rendering.lua to determine the best way to add that.

Probably obvious but this is what the cube definition now looks like:
vertices = { – each vertice is an x,y,z relative to the object x,y,z
{x = 1, y = 1, z = 1, u = 0, v = 0},
{x = 1, y = 1, z = -1, u = 0, v = 0},
{x = 1, y = -1, z = -1, u = 0, v = 0},
{x = 1, y = -1, z = 1, u = 0, v = 0},
{x = -1, y = 1, z = 1, u = 0, v = 0},
{x = -1, y = 1, z = -1, u = 0, v = 0},
{x = -1, y = -1, z = -1, u = 0, v = 0},
{x = -1, y = -1, z = 1, u = 0, v = 0},

	},
	faces = { -- each face links 3 or more vertices. Fan mode needs an extra vertice at the beginning to fan from
		{1,2,3,1,3,4},
		{4,3,7,4,7,8},
		{8,7,6,8,6,5},
		{5,6,2,5,2,1},
		{1,4,5,4,8,5},
		{2,6,3,3,6,7},
	},
	mode = "triangles", -- tells the renderer how to iterate the defined face vertices and uvs
	fnormals = { -- each face has an x,y,z direction indicating the axis it's pointing towards
		{x = 1, y = 0, z = 0},
		{x = 0, y = -1, z = 0},
		{x = -1, y = 0, z = 0},
		{x = 0, y = 1, z = 0},
		{x = 0, y = 0, z = 1},
		{x = 0, y = 0, z = -1},
	}

Q3d 3D Engine.zip (1.2 MB)
I have only been playing with this code for a bit but, as @richard11 indicates, it’s very clear. I have added SimpleCube, Tetrahedron, Octahedron, and Dodecahedron and changed from texture mapping to simple phong-type shading.

I plan to add a “normal” function so that those are computed automatically from the first three points of any face.

I’m also interested in adding globalRotate functions which would perform rotations relative to the device instead of the current position of an object. Grand plans which may or may not fall through.

2 Likes

Oh that’s just beautiful. Kudos for getting those shapes in so quickly!

My only critique is that the mid-point of your tetrahedron seems to be out somehow. Watching your example code run, it’s as though that shape is sort of floating around a little instead of spinning on its exact centre… could be an illusion just because of its shape perhaps… I don’t see anything immediately wrong with your vertices. Regardless, it’s brilliant to watch.

Ref rotations and movements - from what I remember, my original thinking was to have rotate() and move() be relative adjustments, and rotation() and position() be absolute settings. I.e. a rotation() of 15 would set the object to a 15 degrees rotation within world space, where rotate() of 15 would turn the object 15 degrees relative to its own current rotation. So effectively you should already have a global rotation of sorts… remembering you can move the camera around too, so setting the camera rotation() to values fed by the device gyro sensors for example should effectively give you VR of sorts. Something I’d not even considered myself… oh wow that would be a cool use-case!

The biggest challenges still to face with this engine are an object import function, and dealing with intersecting faces.

@StarCrunch and I had many discussions about intersecting faces back in the day, and some of what he’s been working on might be massively helpful in being able to pass depth buffering on to the hardware to deal with itself, particularly now that Solar2D itself is open source and such functionality can technically be hacked in to the core graphics code - something that’s far outside of my own realm of expertise. You could still use this engine without dealing with intersectings though, by just designing the 3D elements in a way that means they never come into contact with each other.

For importing objects, my original intention was to only support .obj files. They’re plain text and exportable from most modelling software. If you open one up you’ll see the way they define their vertices aligns nicely with the way I define them in the engine, so iterating a .obj file and replicating its vertices shouldn’t be too complicated, but iterating a complex object and replicating all of its normals and UVs etc, particularly if it’s structured in groups of nested shapes - that’s going to be quite a challenge I expect. I think my advice here would be to keep things simple. Support the basics of an .obj format and require that people simplify their designs for export. Performance first - again textures and whatnot aren’t that important for mobile games anyway, and animation can just be a future consideration. The normals are important for face culling and lighting though, so getting those right on imported models is the key.

Whatever direction you take, have fun with it and absolutely don’t feel obliged to follow my way of thinking. I’m just glad to see it getting some traction again. This thing was sitting dormant for years.

With a display object, an anchor point of (.5, .5) means the middle of its bounding box, rather than the average position. With rects and circles the geometry is balanced on both axes, so this is an unimportant distinction, but with polygons and meshes you can get some puzzling rotation results if you don’t account for it.

A tetrahedron is just such a “problem” shape, only in 3D, so maybe something similar is at work?

As far as importing, take a look at those MoonAssimp docs above; that’s pretty much a kitchen sink importer. (The author is a Lua and 3D aficionado himself and it’s basically as described in his docs; I only modified the entry point needed by require() and linked the Assimp libs directly.) Everything is also divvied up so that you can ignore components you won’t need.

In Q3d, objects have an origin and their faces are constructed from vertices positioned relative to that origin, so the usual complexities of display object mid-points isn’t exactly applicable here. It has to be that way because a 3D objects origin point isn’t necessarily always in the middle… importing a character for example, you’d want the origin point to be the base of its feet so that you can move them around relative to the ground.

I have confirmed that the Tetrahedron is properly centered by changing camera position and doing some pure axial spins. It’s just a visual illusion due the nature of that shape.

1 Like

Fair enough. Bloody eyes and the tricks they play!

Regarding rotate() and rotation(), here is what I have found.

Rotate(x=5) will rotate the object 5 degrees around the x axis from its current position.
Rotation(x=5) will rotate the object to 5 degrees from zero around the x axis regardless of current position.
Those are both clear from your documentation and descriptions. All good so far.

However, in my example, the x axis is the CURRENT x axis of the object and not the absolute x axis of the global environment.
If I take an initial object and do rotation(x=90) and then do rotation(y=90), the second rotation will be around the global Z axis because the first rotation rotated the entire coordinate system. The same is true for rotate() in this particular case.

I am interested in being able to take an object, tilt it by some angle and then make it rotate around a global axis. For a single object, I can mimic the effect, as you mentioned, by changing the camera position and angle. However, if I want one object behaving this way amongst others that are still or have different rotations, no luck. So, I think I need to figure out and create absoluteRotate() and absoluteRotation() functions.

Also, please take all of this in the best way possible. I love what you built and am having fun trying to figure out how to do new stuff.

1 Like

Q3d 3D Engine.zip (1.1 MB)
And here is the latest will all five platonic solids, faces now support individual colors, and I have added a normal calculator.

1 Like