0 Favourites

3D graphics using arrays?

  • I was playing around (experimenting) with arrays a bit to see if i could use arrays to render an object in 3D a 64x64 array holding rgb info in each cell.

    Kind of like this example here done in excel.

    I can get the stuff to render, but maybe someone has some good idea to get started creating the motion? How would you go about doing something similar in C2?

    And would it be possible to read data from an 3D file. Like an obj, or any other format to populate the array?

    Any thoughts?

    This link Explains how it's done, but for me it's incomprehensible.

  • I did a simple vector 3D grid floor, red lines on a black background (very 80s style!) when I was just messing around a few months ago.

    .

    I used the simple version formula right at the bottom of the wiki 3D projection page (under diagram).

    https://en.wikipedia.org/wiki/3D_projection

    It worked very well.

    To do what you want to do you would have to implement the orthographic (or even the perspective) projection maths with all the rotation stuff on the same wiki page. Im not sure if it would be done with just points or would be better with points and lines .

    Also this covers the basic 3D but does not cover occlusion so will be just see-through wireframe models.

    but yes definitely you could do it directly in the construct 2 editor probably even without any custom js.

    I would love to try but I just don't have the time these days....barely have 2 hours on a weekend to work on Construct 2 stuff so I have to be disciplined with what I get into.

    actually importing models though that is a whole different ball game!

  • You can find more info about that sort of thing by an Internet search of "software rasterizer." The aticle you linked only gives a general overview from what I see.

    You said you came up with a way to display the array as pixels so I'll leave that subject for now.

    The simplest thing to draw is points. Basically you just have a list of 3d points that project to 2d like the link in the second post and just set the nearest pixel to the color.

    Lines can be done by stepping from one projected point to another with something like "bresenham's line algorithm."

    Animation is done by clearing the array and doing something like a 3d rotation on the points before projecting them to 2d. So just clear, move points, project to 2d, and repeat. It's hard to make it fast though.

    The obj file format is a text format so it'll be easier to parse than something else. Tokenat() can be used to do this. Basically look at one line at a time and see what the line starts with. If it starts with "v" it's followed by 3 numbers seperated by spaces that is a 3D point. If it starts with "f" it's a face and it's followed by indexes of the points that make up the points. Lines can be found from pairs of points of the face. That's the basics of it. There is other info you can parse if you need it.

    Here's a tutorial that goes in depth on how to make a software rasterizer that may be useful:

    ’s-Line-Drawing-Algorithm

  • tunepunk

    I had some fun trying my hand at implementing some of that stuff. My events are mostly clean and as readable as possible, although it's at the point where a lot of it needs to be refactored and cleaned up. It is mostly organized so maybe you'll find it useful.

    What it does is fairly straight forward.

    1

    The object file is loaded so we have a list of 3d points and a list of faces which list indices of the points.

    2

    The points are moved around, or in the capx rotated around in 3d.

    Rotation

    The rotation math to rotate on the xy plane is this:

    newx = x*cos(ang)-y*sin(ang)

    newy = y*cos(ang)+x*sin(ang)

    It's basically the same for xz or yz rotations. Just change the x,y variables.

    Changing the amount of rotation every frame can be used for animation.

    Translation

    Next after rotating the points around we need to move the points away from 0,0,0 which is the viewer position. So for example we want the object center to be five units in front of the viewer you could do this:

    newz = z+5

    This is important to do before the perspective transform because things get distorted near 0 and we shouldn't be able to see points with z<0 or behind the camera.

    Perspective

    Perspective is really simple, it's just multiplying x,y by a scale number and dividing by z:

    perspectiveX= x*scale/z

    perspectiveY= y*scale/z

    final transformation to screen position

    This is basically changing the center from 0,0 to the center of the screen.

    screenx = x+320

    screeny = y+240

    3

    Now that the points are transformed we next want to draw points and lines and perhaps polygons. There are a few ways that come to mind.

    Using sprites

    Sprites could be created at the transformed xy position and lines could be done by stretching sprites from one point to another. Polygons could be done with some madness with triangle sprites but that's not straightforward.

    Using the canvas plugin

    You can plot points, draw lines and draw polygons fairly easy. The antialiased lines may not be what you want. Also the paster plugin can be used for textured polygons but that's a different topic.

    Using a tilemap

    The tilemap can be thought of as an array and it removes the step needed to convert the array to something visual. One color is simple but if you look at the capx it shows a way to do a spectrum of colors, well 4096 to be exact. It takes red,green and blue components in the range of 0 to 15 and converts it to a tile number.

    Line drawing

    This is done by lerping from the one point to the other and drawing the inbetween points. Look the "plot line" function in the capx, it's fairly simple.

    polygon drawing

    This took me a bit to wrap my head around. I tried two methods to draw triangles.

    The first one is fairly simple to understand. First you find the bounding box that contains the all three points of the triangle, then you loop over every pixel in that bounding box and see if that pixel is inside the polygon. Unfortunately the math looks a bit hairy.

    The second method is a scanline method which is faster. It involves sorting the three points by y and then lerping along the edges to get the x positions at every y. It has less hairy math but isn't the simplest to understand either.

    Performance on the monkey model is about 4-6 fps and I don't think a whole lot of extra performance for this can be squeezed out of the events. This sort of thing is always pretty slow which is why the rasterization is usually all done by the gpu. It can be made faster if most of it was done in javascript, and the events were only used to issue commands. 60fps should be possible with the events ported directly over, but js isn't pleasant to work with imo.

  • That's pretty awesome R0J0hound ...u got a lot further than me on that. It's quite amazing it worked that well.

    I was just going to try a new approach as well trying out rendering point cloud data just for fun, but currently stuck since I didn't find any good (free) software to convert some model to point cloud data yet, as in this case I thing it would be faster than drawing polygon data. Not a very dense point cloud but a bit bloated points to fill the gaps between them.

  • Look here:

    Load your 3d model in obj format and search for "Poisson Disk Sampling". You could then export it back out to a obj file. The only catch is color/texture isn't used. I think an orthogonal render of the model could be used to sample the colors.

    Another idea could be to convert the mesh to voxels. There's a command line utility poly2vox that looks like it supports a few formats. The voxel formats would need to be decoded though.

  • R0J0hound Yeah voxel data would do the trick.

    Just theorizing here of a possible way, maybe you have some input.

    But wonder if there's any raw format or a way to convert it to JSON or something, easily readable by construct. Basically what you would need is only XYZ position for each voxel (point), and maybe color information for each point. (Rgb)

    For each voxel, you then draw or place a 1x1 pixel sprite on screen.

    I got the idea from here, which I'm sort of trying to back-engineer now.

    Just to start simple. Imagine you have a screen resolution of 50x50 or pixels, for each pixel you want to cast a ray forward. If they ray hit something in 3D space (a point in the point cloud or the average color of a group of points) you render that pixel, in the correct color.

    I'm just curious to try out the voxel/point cloud approach also, to see if it would render faster, since you don't have to draw triangles. Just getting the RGBA info from a point in 3D.

  • You may be able to do that with meshlab or cloudcompare, but there's probably a more direct way to do it.

    I found this:

    So with cloudcompare you can load a mesh and get a point cloud of the surface. I think a file format like ply would help here since it can be in text.

    Here's the test. I took the model in blender and created a uv for it and baked a texture to it, then after a little fanagling I managed to get the exported obj file to include the image with it. Then I followed the guide in the link above to sample points on the 3d model. I used 2000 points since that seemed reasonable, then I exported with the ply file format since that stores vertex colors.

    It works and is slightly faster but the object has to be drawn smaller to hide the gaps. I also disabled the perspective transform since that caused even more gaps.

  • Impressive R0J0hound u basically made both a mesh and voxel renderer in 50 events.

    Took a look at your example and tried to identify what was so heavy on the cpu. Draw calls seemed very small, so it must be updating the rotation every tick. None the less it's a very cool example of what can be done with a bit of brute force and Vaseline.

    My main goal with the experiment was just to see if it worked and clearly you have proven that it does. Very nice example indeed. I really liked the use of Tile map object to draw the pixels. Clever.

  • It's more of a point cloud renderer than a voxel one. I'm pretty sure something more efficient is done with voxel renderer's than drawing everything.

    Loops in the event sheet are probably the biggest culprit of why it's slow. I've compared equivelent loops in just JavaScript and they're over 20x faster. I guess in general the slowdown is events are interpreted and there is some overhead involved with it.

  • It's more of a point cloud renderer than a voxel one. I'm pretty sure something more efficient is done with voxel renderer's than drawing everything.

    Loops in the event sheet are probably the biggest culprit of why it's slow. I've compared equivelent loops in just JavaScript and they're over 20x faster. I guess in general the slowdown is events are interpreted and there is some overhead involved with it.

    R0J0hound, I just had a few ideas on how to speed it up. Since the loops seem to be the problem, and not the actual rendering I was thinking of different ways of how to optimize it.

    Event based for each loops are not very good. There's the internal higher level ones that's a lot faster, (like when you pick something by a variable/boolean/family there's an internal for each) so here's my proposal approach.

    On start of layout. Create Hidden sprites on a hidden layer and set XY and (and maybe a variable for Z). You could even pin them to a dummy object then Rotate dummy object, or rotate the entire layer, to prevent any picking which also a bit of a performance hog. I'm imagining these sprites would represent a top view in this hidden layer, rotating the dummy object would be a rotation along X axis. (horizontal in a front view). Then have the values from these hidden sprites drive the tile map rendering? Basically no loops would be needed to calculate new point cloud positions when rotating the object. Do you think something like that would work?

    If that works Maybe more if these tricks can be used to limit the amount of loops, and increasing the performance without using js.

  • Construct 3

    Buy Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Buy Now Construct 3 users don't see these ads
  • It is faster, although less readable, even when doubling the number of points. It's actually a bit faster just using sprites instead of plotting to a tilemap, although coloring is a bit trickier. You can switch between the two with the use_tilemap variable.

    Loading a model file takes a few seconds right now. Tokenat is inefficient with large amounts of text. There are some topics on loading a dictionary that show faster ways which could be used here.

  • That is pretty awesome.

  • Nice. I played around with it a little bit and managed to get a constant 60 fps but that was only when rotatating in one axis. I'm going to continue my tests tomorrow to see if I can push it even further.

    Using the tile map is very easy on draw calls but heavy on cpu, using sprites is heavier on draw calls but a lighter on the cpu. I'm going to see if I can figure out a way to make it even less cpu intensive. I have some ideas I'm going to try out tomorrow.

    I'll get back with results once I've tried it.

  • I was also thinking maybe it's possible to store x, y, z in one value using rgb() format. If so.... A one dimensional array could hold all the position values... Maybe you would lose some accuracy with using rgb value, but maybe not noticeable with tile map rendering. Haha ...here's a wacky idea. Theoretically you could pretty much control point translation using color. 0,0,0 (black) would be top left corner deep in z, while 1,1,1 would be bottom right near in z.

    If we normalize the hidden point cloud positions based on the window width and height, we could also store these points in a 2d array the same size as the tile map, and maybe use that as an intermediate before rendering with the tile map.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)