Windows Warp3D

Samuel Venable

Time Killer
Hi everyone, I've got a quick update, I've made a few improvements to the engine while porting it to gms2.3:

Improved render batching system (slightly faster than before)

Batches are handled separately now,
(a "batch" struct is returned from "batch_create(list of instances)"
instead of only one global batch being possible,
so this allows say security cameras rendering a room the other side of the level in full detail.

Implemented a simple type of visibility culling using "zones"
You create rectangular zones for each say room or small area of the level,
and other zones nearby are flagged as "visible from that zone",
which, in the level init process a whole set of lists are created for instances
visible from each zone in the level, which cuts down draw calls a ton.

Also combined the zones into the collision system for dynamic instances (like the player, enemies, projectiles and anything else that actively checks for collision with things) in which instances only check collision in the zone they're currently in.
While testing collision with static instances(like items, ammo pickups etc.) and the precise triangles of the level model are still handled in the grid like before, as it's the fastest way.

Converted the entire gui to structs instead of instances. Before every button or thing you could click on was a separate instance of obj_gui, as it was way too complicated to do with arrays before, but I was able to convert them to structs in about 2 hours, just had to change a few instance_exists calls and other things where they had to be instances.

Made a few improvements to the model editor and the way "tools" are handled, which has made it easier to create new tools, so the list of tools should keep growing as time goes on.


I think that's it for now, I've mainly been refining all the code and thinking about it being a rock solid system, that's also easy to add things to. I'm very happy with how everything's going anyway.

Also, the new version is almost ready for beta testing. I'm going to invite a handful of people to beta test it, people that I trust and have spoke to before and some that've given constant support throughout:

@Amon
@Micah_DS
@TheSnidr
@JaimitoEs
@RujiK
@Yal
@Apapappa
@FoxyOfJungle
@Samuel Venable
@Xor
@flyingsaucerinvasion

If any of you are interested in testing the beta version, it'd be a HUGEE help, it will help with all sorts of angles to do with other people besides myself using it. Also in return for your help I'll give you lifetime access to the engine for free(via buying it on the marketplace and I then send you the money back on paypal 😆)

In the meantime, I made a cool sky dome shader, it basically uses a flat texture(photoshop clouds in this case) and projects it onto a sphere, created by a single cube, where the position coords are passed to the pixel shader, then normalized to create a sphere out of pixels. Then all that's left is to do a calculation that uses the normal and create a uv, plus some uniforms to affect the curve and scale. It's similar to how the sky worked on Quake 1, but I added a couple of other things to stop it looking so warped around the horizon

I just now noticed you tagged me in this. Is there still a spot open for beta testing? Thanks for thinking of me!

Edit:
fails to read exe demo out now lol
 
Last edited:

Joe Ellis

Member
I've got the ball rolling again! (literally)
This is the first time I've got any gameplay working since I moved to gms2.
I've been having more fun with ball physics and improved them quite a bit, and made a skate park to test it in:


I just love playing with those balls and watching them bash into each other, it brings me great pleasure seeing my dreams come true.

I've improved the collision system to be simpler to work with,
every collision function that uses precise collision (sphere - triangle) or (capsule - triangle)
uses this one function: normal_to_triangle(tri, x, y, z, sphere_radius)
Which returns a vec4, the first 3 being the normal to the intersection point on the triangle,
and the 4th being the distance from the instance position\centre of the sphere to the point on the triangle.
Then different collision functions can use this info in different ways.
So it's cut down a lot of collision code, now the ball's collision function looks like this:

GML:
function collision_sphere_triangle(l)//list of triangles (got from current grid cell)
{ 
    var i = 0, t, n, d;
    repeat l[0]
    {
    t = l[++i]
    n = normal_to_triangle(t, x, y, z, mask_radius)
    if n != 0
    && n[3] < mask_radius
    && dot_product_3d(xspeed, yspeed, zspeed, n[0], n[1], n[2]) <= 0 //check moving towards triangle
    {
    d = mask_radius - n[3] //get the distance from the edge of the sphere to the intersect point on the triangle
    x += n[0] * d //Push the instance back in the direction from the intersect point, at that distance so that the edge of the sphere becomes level with the triangle surface (or edge)
    y += n[1] * d
    z += n[2] * d
    if n[2] <= ground_nz //sort most upwards pointing triangle to be the "ground triangle" which can then be used to make the character rotate based on the angle of the triangle
    {
    ground = true
    ground_nz = n[2]
    ground_tri = t
    }
    instance_get_bbox_sphere() //update the instance's bbox
    }
    }
}
Instead of this: (before)

GML:
///collision_precise_ball(list of triangles)

var
l = argument0, i = 0, t, vx, vy, vz, d, nx, ny, nz;

repeat l[0]
{
t = l[++i]
if rectangle_in_rectangle(bbox_x1, bbox_y1, bbox_x2, bbox_y2, t[_tri.bbox_x1], t[_tri.bbox_y1], t[_tri.bbox_x2], t[_tri.bbox_y2])
&& dot_product_3d(xspeed, yspeed, zspeed, t[_tri.nx], t[_tri.ny], t[_tri.nz]) <= 0
{
vx = x - t[_tri.x1]
vy = y - t[_tri.y1]
vz = z - t[_tri.z1]
d = dot_product_3d(vx, vy, vz, t[_tri.nx], t[_tri.ny], t[_tri.nz])
if abs(d) < mask_radius
{
if dot_product_3d_normalised(vx, vy, vz, t[_tri.t1x], t[_tri.t1y], t[_tri.t1z]) <= 0
{
d = median(0, t[_tri.e1l], dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], t[_tri.e1x], t[_tri.e1y], t[_tri.e1z]))
nx = x - (t[_tri.x1] + (t[_tri.e1x] * d))
ny = y - (t[_tri.y1] + (t[_tri.e1y] * d))
nz = z - (t[_tri.z1] + (t[_tri.e1z] * d))
d = point_distance_3d(0, 0, 0, nx, ny, nz)
nx /= d
ny /= d
nz /= d
}
else
{
if dot_product_3d_normalised(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.t2x], t[_tri.t2y], t[_tri.t2z]) <= 0
{
d = median(0, t[_tri.e2l], dot_product_3d(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.e2x], t[_tri.e2y], t[_tri.e2z]))
nx = x - (t[_tri.x2] + (t[_tri.e2x] * d))
ny = y - (t[_tri.y2] + (t[_tri.e2y] * d))
nz = z - (t[_tri.z2] + (t[_tri.e2z] * d))
d = point_distance_3d(0, 0, 0, nx, ny, nz)
nx /= d
ny /= d
nz /= d
}
else
{
if dot_product_3d_normalised(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.t3x], t[_tri.t3y], t[_tri.t3z]) <= 0
{
d = median(0, t[_tri.e3l], dot_product_3d(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.e3x], t[_tri.e3y], t[_tri.e3z]))
nx = x - (t[_tri.x3] + (t[_tri.e3x] * d))
ny = y - (t[_tri.y3] + (t[_tri.e3y] * d))
nz = z - (t[_tri.z3] + (t[_tri.e3z] * d))
d = point_distance_3d(0, 0, 0, nx, ny, nz)
nx /= d
ny /= d
nz /= d
}
else
{
nx = t[_tri.nx]
ny = t[_tri.ny]
nz = t[_tri.nz]
}
}
}
if d < mask_radius
{
d = mask_radius - d
if nz <= slope_limit
{ground = true}
x += nx * d
y += ny * d
z += nz * d
instance_get_bbox_sphere()
}
}
}
}
So you can see it's a lot easier to see what the code does and how it handles it, and the complex calculations are out of sight and out of mind.

If you're wondering, this is the code for normal_to_triangle:

GML:
function normal_to_triangle(t, x, y, z, max_dist)
{
  
    var
    nx = t[_tri.nx],
    ny = t[_tri.ny],
    nz = t[_tri.nz],
    d = dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], nx, ny, nz);
    if abs(d) >= max_dist
    {return 0}

    if dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], t[_tri.t1x], t[_tri.t1y], t[_tri.t1z]) <= 0
    {
    if t[_tri.e1_concave]
    {return 0}
    d = median(0, t[_tri.e1l], dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], t[_tri.e1x], t[_tri.e1y], t[_tri.e1z]))
    nx = x - (t[_tri.x1] + (t[_tri.e1x] * d))
    ny = y - (t[_tri.y1] + (t[_tri.e1y] * d))
    nz = z - (t[_tri.z1] + (t[_tri.e1z] * d))
    d = point_distance_3d(0, 0, 0, nx, ny, nz)
    var r = 1 / d;
    return [nx * r, ny * r, nz * r, d]
    }

    if dot_product_3d(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.t2x], t[_tri.t2y], t[_tri.t2z]) <= 0
    {
    if t[_tri.e2_concave]
    {return 0}
    d = median(0, t[_tri.e2l], dot_product_3d(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.e2x], t[_tri.e2y], t[_tri.e2z]))
    nx = x - (t[_tri.x2] + (t[_tri.e2x] * d))
    ny = y - (t[_tri.y2] + (t[_tri.e2y] * d))
    nz = z - (t[_tri.z2] + (t[_tri.e2z] * d))
    d = point_distance_3d(0, 0, 0, nx, ny, nz)
    var r = 1 / d;
    return [nx * r, ny * r, nz * r, d]
    }

    if dot_product_3d(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.t3x], t[_tri.t3y], t[_tri.t3z]) <= 0
    {
    if t[_tri.e3_concave]
    {return 0}
    d = median(0, t[_tri.e3l], dot_product_3d(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.e3x], t[_tri.e3y], t[_tri.e3z]))
    nx = x - (t[_tri.x3] + (t[_tri.e3x] * d))
    ny = y - (t[_tri.y3] + (t[_tri.e3y] * d))
    nz = z - (t[_tri.z3] + (t[_tri.e3z] * d))
    d = point_distance_3d(0, 0, 0, nx, ny, nz)
    var r = 1 / d;
    return [nx * r, ny * r, nz * r, d]
    }

    return [nx, ny, nz, abs(d)]
}
It's so far the fastest method I've found, using "edge tangents", I actually came up with this concept completely by myself which I'm quite proud of, but obviously don't wanna seem like I'm bragging either. If you're interested in this method just message me, I'm happy to share any knowledge or stuff I use.

I've also improved the collision system by making it detect and flag convex\concave edges, so now in the normal_to_triangle function, collisions with concave edges are ignored, as it's impossible for a sphere to collide with them, it'll collide with the flat surface of the triangle before. So this has cut down calculations quite a bit, it no longer has to get the nearest point along a triangle's edge if it's concave.

I've also made a big improvement to how dynamic lights are handled, before in 1.4, I had to render the lighting separately to the main materials using additive blending, because directx9c couldn't use a for loop in a shader with a variable length, ei. you couldn't pass in a uniform integer telling it how many active lights there are. It lagged terribly, even if only one light was used. So I had to make it go up in bands of 8, every lighting shader had to have 4 versions (_x8, _x16, _x24, _x32), and it had to render all the vertex buffers a second time using the lighting shaders that were configured for their main shaders. So it was really complicated and annoying.
But this problem with the for loop no longer happens in directx 11, so only one shader is needed, the lighting no longer has to be rendered separately to the main materials and now it can just be done in the main shaders. (Or not if certain shaders don't use lighting (another advantage))

Also there is now a special node type for a light (Before they worked with instances, and was a bit pointless and wasteful cus most lights are static most of the time) So they're a separate type of object\node now(structs), and for dynamic lights, you'd use an instance that controls it\edits it's values. They can also be created, deleted, activated and deactivated during gameplay with the functions (light_create, light_activate etc.)

@beta testers, I'm gonna upload a new version to test in the next couple of days, and you'll be able to test out these new things, and a few other gameplay things\objects (depending how many I get working in next couple of days) :)
 
Last edited:

Joe Ellis

Member

Got materials working again in the 2.3 version. These are basically structs that can use a shader, inherit certain uniforms from it that're marked as "material uniforms", and then anything using that material will get rendered with the material's specific uniform values.
So you can write shaders with more custom variables for certain effects and basically things that you want to adjust per material, rather than globally that affects everything, like the sun direction or color.

I made a pbr shader a while ago shortly before I released the demo of the 1.4 version, I used it on the weapons mainly to make them look shiny. But I've added dynamic lighting to it this week, and also made a mapped version like the materials you can design on Substance etc, 5 different maps for roughness, metallic, attentuation, ambient occlusion and normalmap. I'm also gonna make a combined map version of this, where you can combine the roughness, metallic, attentuation and ambient occlusion maps into one rgba texture.

I really love the plain pbr without the maps cus you can make every kind of shading you need in the game with just one shader, even for cartoony or retro games.

This is just one eggzample of what you can do with materials though, basically anything you can think of writing in a shader you can make all the variables fully adjustable by setting them as material uniforms.
shd_portal for instance has uniforms for the two colors it blends between, the speed, and the number of rings.



Code:
///shd_portal(render_portal, color)

attribute vec3 in_Position;
attribute vec4 in_Color;

varying vec3 v_pos;
varying float v_merge;
varying float v_alpha;

void main()
{
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
    v_pos = vec3(gm_Matrices[MATRIX_WORLD] * vec4(in_Position, 1.0));
    v_merge = in_Color.r;
    v_alpha = in_Color.a;
}


varying vec3 v_pos;
varying float v_merge;
varying float v_alpha;

//Global Uniforms
uniform vec3 fog_color;
uniform float fog_start;
uniform float fog_length;
uniform vec3 view_pos;
uniform float time;

//Material Uniforms (Uniforms are marked as material uniforms by putting "//" followed by the default value)
uniform vec3 color1; //rgb(0, 255, 255)
uniform vec3 color2; //rgb(0, 0, 255)
uniform float speed; //1.0
uniform float num_rings; //3.0

void main()
{
    gl_FragColor = vec4(mix(color1, color2, mix(0.0, 1.0, abs((fract((v_merge * num_rings) + (time * speed)) - 0.5) * 2.0))), v_alpha);

    //Fog
    gl_FragColor.rgb = mix(gl_FragColor.rgb, fog_color,
    min(1.0, max(0.0, distance(view_pos, v_pos) - fog_start) * fog_length));
}
 
Last edited:

Joe Ellis

Member
I've got the spread function working with the mouse in the model editor. Basically where you point the mouse, it has a radius and all the vertices in that radius are affected by what you do, with a soft falloff based on how far they are away from the 3d position the mouse falls on the mesh. This has brought a whole new level to the model editor, sculpting is now possible, soft color painting and it works with several functions(from the function tab on the menubar) so if you repeat a certain function (ctrl F) it applies the function to all the vertices in the radius with soft falloff. So it's really good for things like smoothing the mesh, randomizing\erosion, deform functions, color & alpha functions. Here's a video showing some of the things, and I also threw in some skeletally animated terrain just for sh*ts and gigs (the weights of the bumps were painted with the weight tool using the new soft brush. I'm going to make a tutorial video about rigging\weighting a model soon)

A few other pics:

 

Joe Ellis

Member

I've got some more things working this week,

First is the ability to matrix transform the skeleton and animations, so if you matrix transform a model (scale, center it around zero, or rotate it so it's pointing forwards) the transformation gets applied to the skeleton and all the animation frames aswell.
It was really hard to figure out, or at least, I spent countless hours trying different things and working out ways to do it, but in the end I realized a pretty simple method. (Which always happens)
So each animation frame is a set of dual quaternions for each bone, and they're relative and move the bones from the base pose of the skeleton, so if you just apply another dual quaternion to them from the matrix transform it makes parts that didn't rotate at all rotate.
So I needed to rotate the rotations... like the axis they rotate around, but keep the amount of rotation the same.
But in the end, you just need to invert the rotation\transformation, apply it to the base pose of the skeleton, then multiply that result frame\set of dual quats with each animation frame. So that's a huge weight off my mind.

The second thing is that I've merged the 1st person player and 3rd person player into one, mainly cus it was really bugging me having 2 player objects and having horrible long names, where I just wanted obj_player. So I've combined all the controls and movement so that all styles of controls can be toggled, and the 1st person view happens by default if there isn't a camera instance active at the time. So you can switch between 3rd and 1st person during the game, and toggle control style from either mouselook with wasd, or the old style controls of the arrow keys and a z to look up and down. I've also added usb controller inputs, so if a controller is connected it will use that instead. I still need to set up custom key & button config and the in game option menu for setting them though..

3rd, I've improved the item and inventory system a lot. Items are now basically "global structs" and are saved as files and loaded when the engine starts up. They have basic settings like max amount you can hold, the model & sprite they use and their collect and use functions. Plus they can have custom variables, like for weapons or special things you use that need other variables to control how they do things. These work by assigning an object to them, in the same way that instances placed in the level do, and they use any variables you've defined for that object and are all adjustable per item.
A good example of this is for weapons, so if you've equipped a shotgun, the item's object is obj_gun, which has variables for fire_rate, damage, accuracy, scatter etc. and every different gun item can have different config with these variables. There's obj_melee too, which has things like attack_speed, range, damage, pushback_force.. and you can completely make any object and variables you want for any item. So I'm really happy with how versatile this system is. Any saved games also save all the items currently in the inventory.
Now I just need to make the player be able to equip things again like in the 1.4 demo. That will hopefully be next week.

Finally, I've almost got the game finalizing process sorted out. Which basically saves all of the game files into a single rom. Then when the game is compiled into an exe it loads the files from the rom instead of the individual files. The only part with this that I'm still puzzling over is the textures, cus I could glue them all together into a rom, but then they'd be really easy to rip out, so I'm considering working out how to make an automatic function that edits the project file and adds all of the textures into the project as sprites.

So overall, these new things I've got working were things that I've been trying to work out for months and have really been stressing me out, so I'm really satisfied that I've finally managed to sort them out.
 

Joe Ellis

Member
Hi everyone,
I've been inactive for a while, I've been in a "transitional period" or as some would say "a period of depression", I prefer the term "transitional period" as it sounds more positive but both are accurate. I have found the light and have seemed to have come through the other side of it (Which always happens unless thou kills one's self) and have a fresh new energy vibe that I'm loving so far.
I've started my actual game that I've been wanting to make for years: Silkinstein
He is the cute lovable, lurky and likabley creepy character that's been my avatar for several years, and I've been waiting to get the game engine sorted to make the actual game he's in.
So here is a very early prototype of him and the game:


At the minute I've just got him walking about, but the vibe is right, that's the important thing.
The enemies and plot revolve around "The mice with long yellow hair" which are crossbreeding plants and animals,
like spunions(spider onions), sludders(slug udders), toad dogs(self explanitory),
and Slunion (A slug onion) He's one of the bosses, he has anger\rage, similar to a big gorilla, but in slug onion form.
It will probably visually take on a more industrial theme as you get into the MWLYH's bases and generally will have a dream-like theme throughout. (Including the floating rhabi with the two kids at the top of the hill watching him and commenting on his every move) (I love my dreams)
There are a lot of other things, but I'll leave them till I've made some of them, I'm pretty excited about this game though, it should be a kind of cult game in years to come, if I get everything right, which I will if I focus on the important things like the "vibe" and kind of implementing & translating all the ideas from thoughts\dreams to digital form.. :D

I've made several other improvements and progressions with Warp3D aswell, but my progress was so slow the last few months cus I've been depressed and found it really hard to get energy and motivation to do anything. Any tiny thing felt like such a huge task and required so much effort. But it's passed now and I should get loads of stuff done for at least until next winter..
So hopefully Warp3D will actually be released before then :D
 
Awesome. How is your progress? Do you use external rendering ? (External OpenGL / DX context), what version of OGL, DX? Or everything is done using GM Studio?
Thanks.
 

Joe Ellis

Member
Thanks, everything is done with gm. I used a dll in the gms1.4 version to load textures with mipmaps, but gms2 can do that itself now.
The dll could also pass texture pointers into the vertex shader, which still can't be done in gms2. So I might bring that dll back at some point.

I'm still making good progress, just been fixing bugs so far this week though 😴
 

Joe Ellis

Member
Here's some improvements I've made the last couple of months



Texture blending, for terrain mainly, the material lets you choose 4 textures,
then you paint with the soft brush the rgba values, each component is used for the blending,
so you'd mainly use full red, green, blue, the alpha is a bit confusing though, you need to use black, and 0% alpha..



Geodesic\hex grid terrain. Kind of a nicer look than a grid of quads, plus holes are smoother.
I like doing manual uv shifting aswell to make the edges of the holes look cool.



Expensive functions now do a gradual loading window, I'm going to add a cancel button aswell, cus sometimes subdivision ends up taking over 5 minutes or is gonna crash the game entirely, cus subdividing multiplies the amount of faces by 4, so it can quite easily go over the poly limit.



A screenshot of Silkinstein looking groovy, and the HUD might look abit like this.



Nice smooth lighting using dynamic lights, they can really add a lot of extra smoothness to ambient shading. The scene without them just had basic flat shading.



Concept art of Slunion, one of the bosses on Silkinstein, he's a slug onion. He has the temper of an angry gorilla, he slams and jumps about like one, but his arms and undercarriage are slug based, so he's pretty sticky aswell. He will be for sure interesting to make :)
He will be in similar style to Silkinstein, low poly ps1 character. (He has eyebrows even though he has no eyes)

I've also improved the general node handling system quite a lot, but it's not really much of a talking point.
I've basically just generalized it more, and made all nodes be handled in the same way with a few functions like node_create, node_delete, node_copy, node_paste.
Also I've made a kind of garbage collector, where anything that's deleted or instances destroyed get processed at the end of the step by the main engine object, which has made a lot of problems with instances or nodes not existing cus they've been destroyed\deleted while
code is asking about them. Everything has a variable "deleted" which can be checked before doing things if needed, and is better than "instance_exists" in most cases, plus some things might not be instances but checking ".deleted" works for all things.

Anyway, the engine since porting to 2.3 is pretty solid now, it's almost as solid as the 1.4 demo version.
Once it's at that stage I'm going to release it. Any extra things will be released as free "asset packs".
They'll be for specific things that not everyone will want like 1st person shooter ai, 3d platformer enemies, vehicle physics etc.
Then I haven't got to worry so much about everything being in the core version. People will be able to download the packs through the marketplace and just import them into their project.
 

mikix

Member
This is probably the most ambitious project on GMC. Welcome to the Raid Boss Team. Anyway, I have huge respect for Joe Ellis. If Joe Ellis was a raid boss, definately the hardest one to beat.
 
Top