## Oysi's 3D Rendering & Physics Engine

Germanunkol
Party member
Posts: 706
Joined: Fri Jun 22, 2012 4:54 pm
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

Oysi wrote:My things have leaked, become mega popular, and people have went as far as to hate and harass me, thinking I was the one who stole the stuff, not the one who got stolen from. =/ As you can understand, dealing with that is not very pleasant.
Hum, when I read this I thought that the best way to go open source is to use a public git server, like github.
There, people can easily see when you've made the repository, when you've pushed it and can go all the way back and check if you were there before that other guy. I don't see why anyone would ever want to do that, but then again I don't understand people who harass other people for writing cool code.

Of course, it's still your choice - and I respect if you want to keep it close. Just bare in mind that not wanting source to ever leak means you can never really release - any project out there can be reverse engineered, which I believe is one of the reasons why Löve Developers have not bothered to add a way to encrypt the sources when distributing.

Great work, keep it up. I'm not on the whole 3D-in-a-2D-Engine hype, but this is impressive!
trAInsported - Write AI to control your trains
Bandana (Dev blog) - Platformer featuring an awesome little ninja by Micha and me
GridCars - Our jam entry for LD31
Germanunkol.de

slime
Solid Snayke
Posts: 2858
Joined: Mon Aug 23, 2010 6:45 am
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

Germanunkol wrote:Löve Developers have not bothered to add a way to encrypt the sources when distributing.
Just to clarify, there isn't any LÖVE-specific way, but there are many Lua-specific ways to at least obfuscate your source code which work fine with LÖVE. However, it tends to be the case that the people asking for that kind of thing don't actually need it, even if they think they do.

Jasoco
Inner party member
Posts: 3650
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

If I could ask one thing, I'd be really interested in seeing how you handle texturing.

When I brought it up a while ago you said you hadn't implemented it, but then came back shortly after saying you whipped it up quickly. How?

I have a texturing library kindly created by a user named xXxMoNkEyMaNxXx (Typing that name is so awkward. Also I think he left the forum.) that does a pretty damn good job. It's a shader. But I need to call it for every polygonal quad I need to draw to the screen so if there's a lot of them it does use up a bit of processor. Though it does still go pretty fast and is more than enough textures for a Star Fox clone like what I hope to create.

It basically accepts 4 sets of 2D coordinates and warps the texture properly without need for any 3D data. When Löve gained meshes I had hoped they could replace it, but alas they are not what I was hoping for. (And doing some speed tests, it actually takes the same amount of time roughly to draw a mesh vs. drawing a textured polygon using Monkey's library.) So I stick with this library.

But I need to dump it or replace it somehow or fix it possibly because it has a few limitations that hinder my progress...

First, I can only require the library once. And if I change the window settings via setMode, it resets and removes itself from memory. So if I want to do any window manipulation like setting Fullscreen or resizing the window to a set size, I need to do it once and on restart. Which totally sucks.

Secondly it requires me to be drawing onto an area the size of the window area. So if I wanted to say render my 3D stuff to a smaller canvas, I cannot use textures. Because for some reason the library depends on the window width and height. I've tried changing it so I can pass my own values to the drawing code but only the height can be changed easily. The width will not change without some strange math. It's just annoying.

Thirdly, in the past it has shown itself to work differently on different platforms and video cards which screws it up even more.

Lastly it also doesn't work with image quads. Only images themselves. Which is a slight annoyance.

I'd love to know if you have some sort of magical replacement texture library that might work differently and doesn't have the above limitations I'd love to see it and use it and give you credit. All I need is something that will take a set of 4 sets of x and y points at most and warp them properly. I'm still sad Meshes don't work the way I had hoped. When I asked they said it's because it has no 3D frame of reference to warp properly, but neither does the library and it only accepts 2D points with no 3rd dimension to reference and it warps it properly just like PhotoShop which also doesn't require a 3rd dimension.

I want to learn from you!

For reference, this is the library: (Slightly modified by me)

Code: Select all

--By xXxMoNkEyMaNxXx
--[[Complete API----  This was made to be a module, PLEASE USE IT AS A MODULE

.cw=false
-Counter clockwise vertex order starting at top left.

.cw=true
-Clockwise vertex order starting at top left.

-loadup==true sets pixel effect to polygon texturer,
*NOTE: preload(false) must be done to draw blank polygons after textured ones.

.setRepeat(origin,size):
-Makes the image repeat every 'size' starting at 'origin'.
-The default is origin={0,0},size={1,1}.

-if 'image' is nil, the function will prepare to make it blank.

.quad(v1,v2,v3,v4) - draws a polygon with no image.

.fast(image,v1,v2,v3,v4) - draws a polygon with 'image' on it.
-Must include an image.

Info:
Vertices are in the form {x,y}.
Vertices go clockwise from the top left.

v1---v2
| img |
v4---v3
--]]

--Modified by RogueCarrot

extern Image img;
extern vec2 v1;
extern vec2 v2;
extern vec2 v3;
extern vec2 v4;

extern vec2 p0;
extern vec2 rep;

extern number SIZEY;//So annoying
extern number SIZEX;//So annoying

vec2 one=vec2(1.0,1.0);

number c(vec2 v1,vec2 v2)
{
return v1.x*v2.y-v2.x*v1.y;
}
number intersect(vec2 v1,vec2 d1,vec2 v2,vec2 d2)
{
//v1+d1*
return c(v2-v1,d2)/c(d1,d2);
}
{
return vec4(over.rgb*over.a+base.rgb*(1-over.a),over.a+base.a*(1-over.a));
}
vec4 effect(vec4 colour,Image UNUSED1,vec2 UNUSED2,vec2 inverted)
{
vec2 p=vec2(inverted.x*SIZEX,SIZEY-inverted.y);//SO ANNOYING

vec2 A1=normalize(v2-v1);
vec2 A2=normalize(v3-v4);

vec2 B1=normalize(v2-v3);
vec2 B2=normalize(v1-v4);

number Bdiv=c(B1,B2);

vec2 uv;

if(eq0.x && eq0.y){
//Both edges are parallel, therefore the shape is a parallelogram (Isometric)
number dis=dot(p-v1,A1);

//cos theta
number ct=dot(A1,B1);

//Closest point on v1->A1 to p
vec2 pA=v1+A1*dis;

//uv
number r=length(p-pA)/sqrt(1-ct*ct);
uv=vec2(1-r/length(v2-v3),(dis+r*ct)/length(v2-v1));
}else if(eq0.x){
//One Vanishing point occurs in numerically set scenarios in 3D, and is a feature of 2.5D

//Horizon is A1 (=A2) from B
vec2 Vp=v3+B1*c(v4-v3,B2)/Bdiv;

//Some point in the distance that diagonals go to
vec2 D=Vp+A1*intersect(Vp,A1,v4,normalize(v2-v4));

//uv
number u=intersect(v1,A1,Vp,normalize(p-Vp));
number v=intersect(v1,A1,D,normalize(p-D))-u;

number len=length(v2-v1);
uv=vec2(len-v,u)/len;//Reversed components to match up with other one
}else if(eq0.y){
//If the other edge is the parallel one
vec2 D=Vp+B1*intersect(Vp,B1,v4,normalize(v2-v4));
number u=intersect(v3,B1,Vp,normalize(p-Vp));
number len=length(v2-v3);
uv=vec2(u,len-intersect(v3,B1,D,normalize(p-D))+u)/len;
}else{
//Else, two vanishing points

//*intersect(v1,A1,v4,A2)
//*intersect(v3,B1,v4,B2)

//Vanishing points
vec2 B=v3+B1*c(v4-v3,B2)/Bdiv;

//Horizon
vec2 H=normalize(A-B);

//Pixel
uv=vec2(intersect(v4,-H,A,normalize(p-A))/intersect(v4,-H,v2,-A1),intersect(v4,H,B,normalize(p-B))/intersect(v4,H,v2,-B1));
}
}
]]
local gl_send=glsl.send
local q=love.graphics.polygon
local win = love.window
-- local lgr = love.graphics
gl_send(glsl,"SIZEX",1)--So annoying
gl_send(glsl,"SIZEY",love.graphics.getHeight())--So annoying
gl_send(glsl,"p0",{0,0})
gl_send(glsl,"rep",{1,1})

module(...)
cw=true--clockwise
setEffect(glsl)
else
setEffect()
end
end
function setRepeat(origin,size)
gl_send(glsl,"p0",origin)
gl_send(glsl,"rep",size)
end
function fast(img,v1,v2,v3,v4)
gl_send(glsl,"img",img)
gl_send(glsl,"v1",v2)
gl_send(glsl,"v2",v3)
gl_send(glsl,"v3",v4)
gl_send(glsl,"v4",v1)
q("fill",v1[1],v1[2],v2[1],v2[2],v3[1],v3[2],v4[1],v4[2])
end
if h then gl_send(glsl,"SIZEY",h) end
if w then gl_send(glsl,"SIZEX",w/win.getWidth()) end
if img and v4 then
setEffect(glsl)
gl_send(glsl,"img",img)
if cw then
gl_send(glsl,"v1",v2)
gl_send(glsl,"v2",v3)
gl_send(glsl,"v3",v4)
gl_send(glsl,"v4",v1)
else
gl_send(glsl,"v1",v2)
gl_send(glsl,"v2",v1)
gl_send(glsl,"v3",v4)
gl_send(glsl,"v4",v3)
end
else
setEffect()
end
if v4 then
q("fill",v1[1],v1[2],v2[1],v2[2],v3[1],v3[2],v4[1],v4[2])
else--img acts as a vertex
q("fill",img[1],img[2],v1[1],v1[2],v2[1],v2[2],v3[1],v3[2])
end
setEffect()
end

Oysi
Prole
Posts: 48
Joined: Fri May 02, 2014 11:18 pm
Location: Earth

### Re: Oysi's 3D Rendering & Physics Engine

@Open sauce
meh =/ at least not for now

@Jasoco
Monkeyman! I know him. Smart fellow, that one. He always overcomplicates his code, though. =P Like, something that can be done in 5 lines, he does in 20, with all sorts of weird algorithms that don't fit very well with Lua. Maybe he's too used to other languages, I don't know.

Either way, as for texturing, I did indeed whip up some simple math. Although I used the 3D coordinates. It just seemed simpler, and I'm using the 3D coordinates for other things anyway, like shading and the zbuffer. So... First off, I do everything with triangles. They're just a lot easier to work with, and even if you do stuff with quads you will basically just end up doing the same thing twice anyway.

Now, I assume you know about UV coordinates... It's basically just the 2D coordinates for each pixel, but on the polygon. And the way I set up my shader is I have my triangle with 3D vertices a, b, c and its normal n. I also have the intersection (p) for the current pixel, which is basically just the 3D position of the pixel (which lies inside the triangle). So, for the sake of visualizing this, imagine a = down left, b = up, c = down right.

ab = b - a
ac = c - a

This means that a + ab = b, and a + ac = c. So what we assume with the UV coordinates, is this:

a + ac*u + ab*v = p

Both the lines ab and ac have normals of their own, perpendicular to the normal of the triangle. These are equal to:

ab_n = cross(n, ab)
ac_n = cross(n, ac)

The points (a + ac*u) and p both lie on the same plane, ab_n. Similarily, the points (a + ab*v) and p both line on the same plane, ac_n. We also know that for a plane where v is a point on the plane and n is its normal, the point p lies somewhere on the plane if and only if (p - v) dot n = 0. So we plug that in for u first:

dot((a + ac*u) - p, ab_n) = 0
dot(a + ac*u - p, ab_n) = 0
dot(a, ab_n) + u*dot(ac, ab_n) - dot(p, ab_n) = 0
u*dot(ac, ab_n) = dot(p, ab_n) - dot(a, ab_n)
u*dot(ac, ab_n) = dot(p - a, ab_n)
u = dot(p - a, ab_n) / dot(ac, ab_n)

Do the same for v, and we get:

v = dot(p - a, ac_n) / dot(ab, ac_n)

Then to apply that to a texture, you just do the same as I did earlier:

a + ac*u + ab*v

at + (ct - at)*u + (bt - at)*v

So... In actual GLSL code, that would be: (assuming we already have a,b,c, at,bt,ct, n, p, ab,ac, textureMap)

Code: Select all

vec3 ab_n = cross(n, ab);
vec3 ac_n = cross(n, ac);

float u = dot(pos - a, ab_n) / dot(ac, ab_n);
float v = dot(pos - a, ac_n) / dot(ab, ac_n);

vec2 texCoord = at + (ct - at)*u + (bt - at)*v;
vec4 texColor = texture2D(textureMap, texCoord);

This might not be the proper way to do it, but I didn't find any proper way to do it online, so I just came up with this. It does seem to work, and it's also rather fast, so I'm good with that. =) One thing to note is that this is not only for texture mapping. Once you have the UV coordinates, you can do stuff like normal mapping and color interpolation as well, as I showed earlier.

I took the liberty of making a picture: (not sure if it's gonna be understandable, but oh well xD)

(this turned out quite fancy-looking, it looked simpler in my head)

One thing I would like to point out is that you should send all the stuff to the shader with one call. I've noticed that if there's anything that drastically reduces performance, it's shader:send. What I do is send all the vectors as an array. I even use 3D vectors for my 2D texture coordinates just so I don't have to send a separate vec2 array, because it really takes up a lot of time. So all you should be sending is the texture map + an array of all your vectors. Then you just unfold that array in the shader. By doing this you will probably see your fps double.

On a different note... I have been experimenting with multiple rigid bodies, and them colliding together. I found that it is a complete bitch, because there are so many things that go wrong, including: sliding, jittering, sinking, bouncing, so many things. Not to mention the actual collision checking. Anyway, I made this scene of 20 objects, 10 cubes, 10 tetrahedrons. It's running only 1 iteration per frame, and also uses some extra weird magic to make tetrahedron collisions work, which basically means I can't stack 'em, as they will just slide/sink/bounce off if I have like 3 of 'em stacked. But it makes for a nice little "pool" of objects, that I can just move around, and they all propagate the force, meaning if one hits another, it gets pushed, and it can continue to push others, in a chain like fashion, you know. Anyway, here's a pic, but I imagine I'll get a video up later on: (all of these objects are doing physics stuff with the static scene, as well as all the other objects)

Lastly, I would like to point out that the other video I made (https://www.youtube.com/watch?v=_1OhoPTLaIc), the one with the reflections, it does not use a shader at all. It doesn't even use canvases. =P

Jasoco
Inner party member
Posts: 3650
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

Well my project only uses one shader anyway. The texturing. If I could avoid it I would. (Darn meshes not working how I had hoped.)

I wonder if your texture code would work for me if I tried to use it. Does it accept the 3D points raw? I could probably modify my code to do it. I dunno. I just need something more flexible. I'd settle for just fixing the two major issues. Being the inability to reload the module/library (And it unloading itself when I change the window video mode) and the inability to pass a width and height for drawing to. I'm sure I could figure it out if I do some math. It seems to accept height as a pixel height and width as a decimal scale value.

Problem is right now I render at full window resolution and in order to do a shadow layer, I need to draw to a canvas. But of course, my display being HiDPI that means a canvas of up to 3840x2400. I'd much rather output to like 640x400 and have a retro pixel look to it. Though the FSAA looks really nice on my Retina display.

I could learn a lot from you. If I ever created a thread for my project, would you be willing to help when it's needed? (When I ever get around to cleaning up the code. I'd hate to post it as is. So much extra crap.)

Does your engine have a performance graph?
Screen Shot 2014-05-25 at 11.50.02 PM.png (149.26 KiB) Viewed 2023 times
(I do not know what that damn "heartbeat" is. It changes with every world loaded and is inconsistent and I can't tell if it's Löve or my engine. There's nothing happening on a timed basis that could cause it.)

bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

Jasoco wrote: (I do not know what that damn "heartbeat" is. It changes with every world loaded and is inconsistent and I can't tell if it's Löve or my engine. There's nothing happening on a timed basis that could cause it.)
Garbage collection, perhaps?

davisdude
Party member
Posts: 1154
Joined: Sun Apr 28, 2013 3:29 am
Location: North Carolina

### Re: Oysi's 3D Rendering & Physics Engine

Version 0.666?
GitHub | MLib - Math and shape intersections library | Walt - Animation library | Brady - Camera library with parallax scrolling | Vim-love-docs - Help files and syntax coloring for Vim

Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

davisdude wrote:Version 0.666?
Aye, Jasoco, wouldn't most of the bugs be solved if you change the version number ?

Jasoco
Inner party member
Posts: 3650
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

### Re: Oysi's 3D Rendering & Physics Engine

bartbes wrote:Garbage collection, perhaps?
Is there anything I can do to stop it happening like that? It causes noticeable framerate lag when it happens. And it happens at different intervals each time. Never the same timing for each world and changes between each app launch. I was planning on putting a section in my OT about it when I get around to making it. It's more of an annoyance than a problem. Because you notice that the rest of the time, the framerate is well below the 1/60th of a second needed for smooth framerate. But then that one section of frames that suddenly drop below 60FPS. I've been hunting this bug forever. It must be something I'm not deleting properly or something. My code is a mess. Which is the main reason I haven't done an OT yet as it is.
davisdude wrote:Version 0.666?
No. It's not 0.666. It's 0.0.666pre.

Oysi
Prole
Posts: 48
Joined: Fri May 02, 2014 11:18 pm
Location: Earth

### Re: Oysi's 3D Rendering & Physics Engine

@Jasoco
You could indeed use it for your thing. And the vertices are in world space. As in the pure, 3D, unaltered vertices. Also, the way my thing works is I have a xMid, yMid, pMul variables. These determine how the project function maps the stuff onto the screen (xMid - x/z*pMul, yMid + y/z*pMul), where xMid and yMid are essentially the middle of the drawing viewport, and pMul is a combination of the viewport and the field of view. What this allows me to do is set any viewport with any field of view, and have the shader know exactly what is going on. So if I wanted to only render to a small square in the bottom left corner, all I do is call my lovely glViewport(0, 500, 100, 100) and then it renders to that. =P So if I need to render to a different area, swap resolution, render to a special canvas, render with special shadow res, then all that is done very easily, with 3 variables. And the shader essentially does the mapping the other way around, maps the 2D coordinates of the pixel to a 3D direction from the camera (with the xMid, yMid, pMul variables). This direction is then matched against the plane of the polygon (just take a point from the triangle and the normal), which gives you the 3D position of the pixel.

But instead of posting code, I feel like doing more tutorials, where I slowly transform it into what I use. That way I can also explain every step of the process, so you wouldn't just be left there with your jaw on the ground. =P