SMF - 3D skeletal animation - Now with a custom Blender exporter!

TheSnidr

Heavy metal viking dentist
GMC Elder
You should use delta time for stuff like that! It may not run at exactly 1200 fps on other computers!

The play and loop constants only affect how the frames are interpolated. When using the _play constants, it will not interpolate between the final frame and the first frame, so it doesn't "loop around". Still, if you let the time argument go above 1, the animation will start over. When using the _loop constants, the animation will be entirely smooth and it will interpolate between the final and first frames.
The reason it goes all the way up to 19 is because of the final argument in smf_sample_create:
Code:
anim_final = smf_sample_create(anim, SMF_loop_quadratic, frac(anim_frame / 20));
Notice the frac(anim_frame / 20) part. If you want it to go from 0 to 1, replace that part with just frac(anim_frame);.
 
G

GproKaru

Guest
The reason it goes all the way up to 19 is because of the final argument in smf_sample_create:
Code:
anim_final = smf_sample_create(anim, SMF_loop_quadratic, frac(anim_frame / 20));
Notice the frac(anim_frame / 20) part. If you want it to go from 0 to 1, replace that part with just frac(anim_frame);.
For some reason I completely overlooked that... I don't know why I didn't realize that sooner~ xD

Also, is it possible to add a 'delete selected bone' or 'split bone' feature? I sometimes find myself having to rebuild entire rigs because there were areas where I didn't add enough bones between joints.
A join/connect selected bone to other bone feature would be a nice addition too.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
Excellent suggestion! I only had "delete last bone" since that was easier to keep track of. That was only a temporary solution, I've now added a better "delete selected bone" button, as well as an "insert bone" button, which splits a bone in halves.
A "connect bone to other bone" could be tricky. It would somehow have to avoid creating parenting loops. I'll look into it.
Here's the latest version
https://dl.dropboxusercontent.com/u/41252865/GamemakerExamples/SMF Model Tool.zip
It now also lets you resize the window, but the text will unfortunately get messed up in the process. I've filed a bug report on it:
http://bugs.yoyogames.com/view.php?id=26524

EDIT:
I'm also well underways making it possible to modify an animation in real time. I've added an extra step between interpolating between frames and creating a sample. I've called this stem "frame" for now. It only stores the local orientation of the bones, so that modifying one also affects the rest of the hierarchy. Getting it to work with absolute world coordinates is tricky though...
 
Last edited:
J

Joshua Allen

Guest
Not to be rude but why are you making your own editor? Would it not be easier to just make an exporter for Blender?
 

TheSnidr

Heavy metal viking dentist
GMC Elder
I've never used Blender! In fact I've never animated anything before at all. I wanted to learn how animation actually works, so I decided to create an animation system from scratch. It doesn't aim to compete with Blender, I just want to provide an easy to use, straight forward program that works seamlessly with GMS.

If it's easier to make an exporter from Blender, I'd love to see somebody create one. I wonder why nobody's done that before?
 
I

icuurd12b42

Guest
>I wonder why nobody's done that before?
Because Blender is the perfect example of software that evolved from a bloody mess to a cancerous mass of features no sane human can comprehend.

And I do believe you approaching the animation without a corrupted mind was a good thing.
 
G

GproKaru

Guest
I honestly prefer this setup than blenders. I use blender for all my 3D modelling but this animation tool made by Snidr completely thwarts blenders animation tools because of it's simplicity and the fact that it's a game maker program itself, there's no need to do a test render since it's already rendering your model with game maker's functions while you work on it.

EDIT:
I'm also well underways making it possible to modify an animation in real time. I've added an extra step between interpolating between frames and creating a sample. I've called this stem "frame" for now. It only stores the local orientation of the bones, so that modifying one also affects the rest of the hierarchy. Getting it to work with absolute world coordinates is tricky though...
Does it really need absolute world coordinates? Normally the model would be offset via a matrix setup. Then again, you would know the importance of these things more than I would. xD


[Edit]

One other thing, I decided to give your collision system a try but for some reason my version doesn't generate the collision area correctly as shown in your screenshots.
https://www.dropbox.com/s/1y6vp2yu4ncnugv/test.jpg?dl=0

I tried various settings but nothing seems to change, am I missing something?
 
Last edited by a moderator:

TheSnidr

Heavy metal viking dentist
GMC Elder
Does it really need absolute world coordinates?
Well, no, but it'd be nice for head tracking and the like. Right now you can only modify the local orientation of a bone within its parent bone.

I tried various settings but nothing seems to change, am I missing something?
When generating the buffer you get to set the bleedover argument. This affects how triangles bleed over from one region to the next so that the player will correctly collide with triangles in neighbouring regions when the periphery of the player's hit sphere touches the neighbour region. It also affects the minimum size of the regions, since regions smaller than some multiple of the bleedover argument would be unfeasible. Try setting bleedover to something ridiculously low, like 0.001, but not 0, and see what happens.

I just updated the inverse kinematics in the editor greatly by the way:
https://www.dropbox.com/s/6dx0g278n8w8jv8/SMF Model Tool.zip?dl=0
The bones are no longer bound to a single plane, but can move in all directions, and the parent bone will rotate in a believable way. When editing in the 3D view it's still bound by the plane defined by the child, parent and grandparent bones, but the child bone can move freely in the 2D views. Try it out :D
 

Lewa

Member
>I wonder why nobody's done that before?
Because Blender is the perfect example of software that evolved from a bloody mess to a cancerous mass of features no sane human can comprehend.

And I do believe you approaching the animation without a corrupted mind was a good thing.
I feel that a lot of people dismiss blender without good reason.
Yes, Blender had a fair share of problems (in terms of UI/usability) back then but this was only true for the pre 2.49 days. With blender 2.5 they overhauled the whole UI which made it not only look much nicer but also way more streamlined.
Heck, they even introduced a new renderer (called Cycles) which is an amazing high quality renderer.

I started learning blender at the time where they just released the 2.5 version (which had the overhauled UI) so i never really had to touch the (horrible) UI of the older versions.
(In general, it is well known in the Blender community that the older versions had a really bad UI. That's the reason why it was overhauled in the first place.)

And i love it.

Here is a screenshot from the default scene in blender (made the screenshot myself):


To give you a comparison: that's how Autodesk Maya (2015) looks like: (screenshot taken from google search)



You can't tell me with a straight face that blender looks horrible :p

From personal experience (i've used blender and maya) i prefer blender. We were using Maya at school and i didn't really have a smooth experience with it.
It was using a ton of ressources on my laptop (blender is really lighweight compared to that), it had really weird bugs which sometimes required restarting maya or clearing the "undo" history manually (don't know how it's called anymore) and sometimes shortcut keys didn't work. (requiring a restart.) And don't get me started on low poly editing. (Maya was missing some basic tools for that. -.- )
Maybe others had a better experience with it.

What i want to say is that if you dismiss blender simply because you read old/outdated posts on some forums which said that blender is horrible, you miss out on an amazing piece of software. (And if you are a developer who works with 3D models, you would only shoot yourself in the foot by not using blender or equivalent software.)


In Blender you get things like blend weight manipulation, skeletal animation, inverse kinematics, bone constraints, etc... for free combined with all the functionality (shortcuts like G for grabbing selected object, S for scaling objects, F for creating polygons between selected vertices, Shift+A for opening mesh creation menu, etc...) and tools (like modifiers, curves , sculpting, etc...) which enhance the productivity and workflow immensely.


Go into blender with an open mind and you will thank me later. ;)
 
Last edited:
G

GproKaru

Guest
Well, no, but it'd be nice for head tracking and the like. Right now you can only modify the local orientation of a bone within its parent bone.
I'm sure it'd be possible to set just the angles with point_direction and translate those into the appropriate function/setting to create a tracking motion. I've had to do this before with game maker for head tracking, although I was using a separate mesh for the head since I couldn't use bone animations until now.

When generating the buffer you get to set the bleedover argument. This affects how triangles bleed over from one region to the next so that the player will correctly collide with triangles in neighbouring regions when the periphery of the player's hit sphere touches the neighbour region. It also affects the minimum size of the regions, since regions smaller than some multiple of the bleedover argument would be unfeasible. Try setting bleedover to something ridiculously low, like 0.001, but not 0, and see what happens.
That worked perfectly, I wasn't aware it would go as low as a decimal so I was only going as low as 1, might want to point that out in the tooltip when setting it.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
That worked perfectly, I wasn't aware it would go as low as a decimal so I was only going as low as 1, might want to point that out in the tooltip when setting it.
Well, the collision buffer is actually made for level models, not character models. I should probably update the first post to reflect that. Level models usually are larger, and in such cases, the bleedover should be the same or larger than the player's collision radius.
 
I

icuurd12b42

Guest
You can't tell me with a straight face that blender looks horrible :p
...
Go into blender with an open mind and you will thank me later. ;)
I did not say it looks horrible. it's just a terrible tool to use for simple things for someone without proper training. I learned Google sketch up in 5 minutes. 10 hours later I had 50 model.

It took 3 days for a friend of mine familiar with blender make one model...

And my point looking at maya and blender still stands
>And I do believe you approaching the animation without a corrupted mind was a good thing.

All these tools basically have the same approach, put everything in there at the expense of the learning curve. They are made for people with a specific mind set who have the desire and dedication to become specialists in the field
 
G

GproKaru

Guest
Just thought I'd ask since the thought just crossed my mind~ Is it possible to create a simple raycast collision check between two 3d points with you .COL model system?

[edit]
I'm also noticing one other issue I'm having. I'm using blender to export my models into .obj files but when I load them into you tool, the UV's get offset and shrunk regardless of the settings I use to export. Just wondering what tools you are using to export your models because it might be a blender obj export issue.
https://www.dropbox.com/s/11ed7vedi0y67w1/animbug.jpg?dl=0
 
Last edited by a moderator:

TheSnidr

Heavy metal viking dentist
GMC Elder
I've been thinking about adding a raycasting collision check script actually! I'll give it a whirl.

The texture problems look to be related to your texture resolution. Blender accepts textures of any size, but GMS only accepts textures with a size the power of 2, ie. 256x256, 512x512, 1024x512 etc. Textures that aren't this size have their canvas resized, leaving the actual texture in the upper left corner.
 
A

Ashmor

Guest
I've got some experience in dealing with Maya, so I can say that Maya is perfect for making 3d character animations.
 
G

GproKaru

Guest
Well, I did some more messing around with blender UV mapping for this tool. the issues usually come from some of the types of UV unwrapping within blender itself. Doing a "Reset" unwrap to default all the models uv maps to their basic shapes causes the issue I stated above. But manual UV mapping still seems fine.
Just seems like a minor issue that doesn't really need fixing, for a while there I thought it was actually affecting all UV maps and not just "Reset" maps.

[edit] Changing the texture resolution also helped close the gaps in the texture, so thanks for that advice.

[Edit 2] Welp, another question has come up. I've been trying to use GMS:2's fog, lighting or even simple features like draw_set_color with SMF models but they seem to be completely unaffected. Just wondering if that's because of the shader re-aligning the texture maps or am I just missing something?

In the mean time, I've had to use a surface to tint the sprite's texture which isn't exactly optimal (especially if I'm going to have to do this to all models) but it's all I can get working for now.
 
Last edited by a moderator:

TheSnidr

Heavy metal viking dentist
GMC Elder
Fog, lighting and draw colour use the default shader. Once you enable a custom shader, all those effects stop working. You'll have to program it into the shader manually.
There are a bunch of shaders here on the forums and on the marketplace. If you don't have any experience with shaders, I'd really recommend reading up on them a little, as they're incredibly handy! You can't use two shaders at once by the way, but you can combine them into one.
 
G

GproKaru

Guest
Hmm, makes sense. That makes things a bit difficult. I've tried to learn the working behind shaders for a long while but they still seem far too different of a language for me to get the hang of it, which is why I'm kind of dependent on pre-made shaders. I wasn't aware that you could combine shaders though, I might have to look into that.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
Okay, I've managed to make a raycasting script, although it's not as efficient as I'd like it to be. It'll work for now. It depends on recursion when looping through the regions the line segment intersects. There are a bunch of scripts that are necessary for this to work. Also, I've updated the .col format slightly, files made with the old format will not work properly now. Simply convert them over again with the new SMF Model Tool:
https://dl.dropboxusercontent.com/u/41252865/GamemakerExamples/SMF Model Tool.zip
Here's the latest demo file containing all smf scripts:
https://dl.dropboxusercontent.com/u/41252865/GamemakerExamples/SMF import scripts.yyz
And in case you'd like to import the scripts manually, here are the scripts necessary for raycasting:
smf_collision_cast_ray
Code:
/// @description smf_collision_cast_ray(collisionBuffer, x1, y1, z1, x2, y2, z2)
/// @param collisionBuffer
/// @param x1
/// @param y1
/// @param z1
/// @param x2
/// @param y2
/// @param z2
/*
Casts a ray from one point to another and returns the position of the first collision with geometry

Script made by TheSnidr
www.TheSnidr.com
*/
colBuffer = argument0;
buffer_seek(colBuffer, buffer_seek_start, 4);
modelX = buffer_read(colBuffer, buffer_f32);
modelY = buffer_read(colBuffer, buffer_f32);
modelZ = buffer_read(colBuffer, buffer_f32);
modelSize = buffer_read(colBuffer, buffer_f32);

transformScale = 65535 / modelSize;
lineStart[0] = transformScale * (argument1 - modelX);
lineStart[1] = transformScale * (argument2 - modelY);
lineStart[2] = transformScale * (argument3 - modelZ);
lineEnd[0] = transformScale * (argument4 - modelX);
lineEnd[1] = transformScale * (argument5 - modelY);
lineEnd[2] = transformScale * (argument6 - modelZ);

regionPos[2] = 0;

buffer_seek(argument0, buffer_seek_start, 0);
newPos = smf_collision_cast_ray_recursive(colBuffer, lineStart, lineEnd, 65535, regionPos);

returnX = modelX + newPos[0] / transformScale;
returnY = modelY + newPos[1] / transformScale;
returnZ = modelZ + newPos[2] / transformScale;
smf_collision_cast_ray_recursive
Code:
/// @description smf_collision_cast_ray_recursive(collisionBuffer, lineStart, lineEnd, regionSize, regionPos)
/// @param collisionBuffer
/// @param lineStart
/// @param lineEnd
/// @param regionSize
/// @param regionPos
var i, j, k, l, colBuffer, rayStart, rayEnd, regionSize, regionPos, checkPos, d, index, r, n, t, intersect, newPos, region, tris, ret;
colBuffer = argument0;
rayStart = argument1;
rayEnd = argument2;
regionSize = argument3 / 2;
regionPos = argument4;

r = buffer_read(colBuffer, buffer_s32);

//Exit condition
if !r
{
    buffer_seek(colBuffer, buffer_seek_start, -r);
    n = buffer_read(argument0, buffer_u16);
    for (l = 0; l < n; l ++){tris[l] = buffer_read(argument0, buffer_u16);}
    for (l = 0; l < n; l ++)
    {
        intersect = smf_line_triangle_intersection(rayStart, rayEnd, smf_get_triangle(colBuffer, tris[l]))
        if is_array(intersect)
        {
            rayEnd = intersect;
            rayEnd[0] += (rayStart[0] - rayEnd[0]) * 0.001;
            rayEnd[1] += (rayStart[1] - rayEnd[1]) * 0.001;
            rayEnd[2] += (rayStart[2] - rayEnd[2]) * 0.001;
        }
    }
    return rayEnd;
}

//Find starting region of the line segment
region[0] = (rayStart[0] > regionPos[0] + regionSize);
region[1] = (rayStart[1] > regionPos[1] + regionSize);
region[2] = (rayStart[2] > regionPos[2] + regionSize);
for (i = 0; i < 3; i ++)
{
    if rayStart[i] < regionPos[i] or rayStart[i] > regionPos[i] + regionSize * 2
    {
        checkPos = regionPos;
        checkPos[i] += region[i] * regionSize * 2;
        n[0] = (i == 0); n[1] = (i == 1); n[2] = (i == 2);
        intersect = smf_line_plane_intersection(rayStart, rayEnd, checkPos, n)
        if !is_array(intersect){continue;}
        j = (i + 1) mod 3;
        k = (i + 2) mod 3;
        if  intersect[j] < regionPos[j] or intersect[j] > regionPos[j] + regionSize * 2 or intersect[k] < regionPos[k] or intersect[k] > regionPos[k] + regionSize * 2{continue;}
        region[j] = (intersect[j] > regionPos[j] + regionSize);
        region[k] = (intersect[k] > regionPos[k] + regionSize);
        break;
    }
}

//Check for collisions in the starting region of the line segment
buffer_seek(colBuffer, buffer_seek_start, r + region[0] * 4 + region[1] * 8 + region[2] * 16);
newPos[0] = regionPos[0] + regionSize * region[0];
newPos[1] = regionPos[1] + regionSize * region[1];
newPos[2] = regionPos[2] + regionSize * region[2];
rayEnd = smf_collision_cast_ray_recursive(colBuffer, rayStart, rayEnd, regionSize, newPos);

//Check for intersections along the middle axis in all three dimensions
for (i = 0; i < 3; i ++)
{
    d[0] = rayEnd[0] - rayStart[0];
    d[1] = rayEnd[1] - rayStart[1];
    d[2] = rayEnd[2] - rayStart[2];
    if d[i] == 0{continue;}
    j = (i + 1) mod 3;
    k = (i + 2) mod 3;
    t = (regionPos[i] + regionSize - rayStart[i]) / d[i];
    if t < 0 or t > 1{continue;}
    intersect[i] = regionSize;
    intersect[j] = rayStart[j] + t * d[j] - regionPos[j];
    intersect[k] = rayStart[k] + t * d[k] - regionPos[k];
    if intersect[j] < 0 or intersect[j] > regionSize * 2 or intersect[k] < 0 or intersect[k] > regionSize * 2{continue;}
    region[i] = (rayStart[i] < regionPos[i] + regionSize);
    region[j] = (intersect[j] >= regionSize);
    region[k] = (intersect[k] >= regionSize);
    newPos[0] = regionPos[0] + regionSize * region[0];
    newPos[1] = regionPos[1] + regionSize * region[1];
    newPos[2] = regionPos[2] + regionSize * region[2];
    buffer_seek(colBuffer, buffer_seek_start, r + region[0] * 4 + region[1] * 8 + region[2] * 16);
    rayEnd = smf_collision_cast_ray_recursive(colBuffer, rayStart, rayEnd, regionSize, newPos);
}
return rayEnd;
smf_line_plane_intersection (this script has been updated and needs to be REPLACED)
Code:
/// @description line_plane_intersection(lineStart, lineEnd, planePos, planeN)
/// @param lineStart
/// @param lineEnd
/// @param planePos
/// @param planeN
/*
Script made by TheSnidr

www.thesnidr.com
*/
var lineStart, lineEnd, planePos, planeN, d, t, ret;
lineStart = argument0;
lineEnd = argument1;
planePos = argument2;
planeN = argument3;
d[0] = (lineEnd[0] - lineStart[0]);
d[1] = (lineEnd[1] - lineStart[1]);
d[2] = (lineEnd[2] - lineStart[2]);
t = d[0] * planeN[0] + d[1] * planeN[1] + d[2] * planeN[2];
if t == 0{return false;}
t = ((planePos[0] - lineStart[0]) * planeN[0] + (planePos[1] - lineStart[1]) * planeN[1] + (planePos[2] - lineStart[2]) * planeN[2]) / t;
if t < 0 or t > 1{return false;}
ret[0] = lineStart[0] + d[0] * t;
ret[1] = lineStart[1] + d[1] * t;
ret[2] = lineStart[2] + d[2] * t;
return ret;
smf_line_triangle_intersection
Code:
/// @description line_triangle_intersection(lineStart, lineEnd, triangle)
/// @param lineStart
/// @param lineEnd
/// @param triangle
/*
Script made by TheSnidr

www.thesnidr.com
*/
var lineStart, lineEnd, tri, dx, dy, dz, j, k, t, tx, ty, tz, vx, vy, vz, ret;
lineStart = argument0;
lineEnd = argument1;
tri = argument2;

dx = lineEnd[0] - lineStart[0];
dy = lineEnd[1] - lineStart[1];
dz = lineEnd[2] - lineStart[2];

t = dot_product_3d(tri[9], tri[10], tri[11], dx, dy, dz);
if t == 0 {return false;}
t = ((tri[2] - lineStart[2]) * tri[11] + (tri[1] - lineStart[1]) * tri[10] + (tri[0] - lineStart[0]) * tri[9]) / t;
if t <= 0 or t >= 1{return false;}
ret[0] = lineStart[0] + dx * t;
ret[1] = lineStart[1] + dy * t;
ret[2] = lineStart[2] + dz * t;

for (j = 0; j < 9; j += 3)
{
    k = (j + 3) mod 9;
    tx = ret[0] - tri[j];
    ty = ret[1] - tri[j+1];
    tz = ret[2] - tri[j+2];
 
    vx = tri[k] - tri[j];
    vy = tri[k+1] - tri[j+1];
    vz = tri[k+2] - tri[j+2];
 
    if dot_product_3d(tz * vy - ty * vz, tx * vz - tz * vx, ty * vx - tx * vy, tri[9], tri[10], tri[11]) < 0
    {
        return false;
    }
}
return ret;

EDIT:
I incorporated the line_plane and line_triangle intersection scripts into the recursive script, so now you only need these two scripts:
Code:
/// @description smf_collision_cast_ray(collisionBuffer, x1, y1, z1, x2, y2, z2)
/// @param collisionBuffer
/// @param x1
/// @param y1
/// @param z1
/// @param x2
/// @param y2
/// @param z2
/*
Casts a ray from one point to another and returns the position of the first collision with geometry

Script made by TheSnidr
www.TheSnidr.com
*/
var colBuffer, modelX, modelY, modelZ, modelSize, transformScale, lineStart, lineEnd, regionPos, newPos;
colBuffer = argument0;
buffer_seek(colBuffer, buffer_seek_start, 4);
modelX = buffer_read(colBuffer, buffer_f32);
modelY = buffer_read(colBuffer, buffer_f32);
modelZ = buffer_read(colBuffer, buffer_f32);
modelSize = buffer_read(colBuffer, buffer_f32);
transformScale = 65535 / modelSize;
lineStart[0] = transformScale * (argument1 - modelX);
lineStart[1] = transformScale * (argument2 - modelY);
lineStart[2] = transformScale * (argument3 - modelZ);
lineEnd[0] = transformScale * (argument4 - modelX);
lineEnd[1] = transformScale * (argument5 - modelY);
lineEnd[2] = transformScale * (argument6 - modelZ);
regionPos[2] = 0;
buffer_seek(colBuffer, buffer_seek_start, 0);
newPos = smf_collision_cast_ray_recursive(colBuffer, lineStart, lineEnd, 65535, regionPos);
returnX = modelX + newPos[0] / transformScale;
returnY = modelY + newPos[1] / transformScale;
returnZ = modelZ + newPos[2] / transformScale;
Code:
/// @description smf_collision_cast_ray_recursive(collisionBuffer, lineStart, lineEnd, regionSize, regionPos)
/// @param collisionBuffer
/// @param lineStart
/// @param lineEnd
/// @param regionSize
/// @param regionPos
var i, j, k, r, t, n, a, b, colBuffer, rayStart, rayEnd, regionSize, halfRegionSize, regionPos, checkPos, intersect, newPos, region, tris, tri, ret;
colBuffer = argument0;
rayStart = argument1;
rayEnd = argument2;
regionSize = argument3;
halfRegionSize = regionSize / 2;
regionPos = argument4;

r = buffer_read(colBuffer, buffer_s32);

//Exit condition
if (!r)
{
    buffer_seek(colBuffer, buffer_seek_start, -r);
    n = buffer_read(colBuffer, buffer_u16);
    for (i = 0; i < n; i ++){tris[i] = buffer_read(colBuffer, buffer_u16);}
    for (i = 0; i < n; i ++)
    {
        //Find intersection with triangle plane
        tri = smf_get_triangle(colBuffer, tris[i]);
        t = dot_product_3d(tri[9], tri[10], tri[11], rayEnd[0] - rayStart[0], rayEnd[1] - rayStart[1], rayEnd[2] - rayStart[2]);
        if (t == 0){continue;}
        t = dot_product_3d(tri[9], tri[10], tri[11], tri[0] - rayStart[0], tri[1] - rayStart[1], tri[2] - rayStart[2]) / t;
        if ((t <= 0) or (t >= 1)){continue;}
        ret[0] = lerp(rayStart[0], rayEnd[0], t);
        ret[1] = lerp(rayStart[1], rayEnd[1], t);
        ret[2] = lerp(rayStart[2], rayEnd[2], t);
       
        //Check if the intersection is inside the triangle. If not, discard and continue.
        a[0] = ret[0] - tri[0]; a[1] = ret[1] - tri[1]; a[2] = ret[2] - tri[2];
        b[0] = tri[3] - tri[0]; b[1] = tri[4] - tri[1]; b[2] = tri[5] - tri[2];
        if (dot_product_3d(tri[9], tri[10], tri[11], a[2] * b[1] - a[1] * b[2], a[0] * b[2] - a[2] * b[0], a[1] * b[0] - a[0] * b[1]) < 0){continue;}
        a[0] = ret[0] - tri[3]; a[1] = ret[1] - tri[4]; a[2] = ret[2] - tri[5];
        b[0] = tri[6] - tri[3]; b[1] = tri[7] - tri[4]; b[2] = tri[8] - tri[5];
        if (dot_product_3d(tri[9], tri[10], tri[11], a[2] * b[1] - a[1] * b[2], a[0] * b[2] - a[2] * b[0], a[1] * b[0] - a[0] * b[1]) < 0){continue;}
        a[0] = ret[0] - tri[6]; a[1] = ret[1] - tri[7]; a[2] = ret[2] - tri[8];
        b[0] = tri[0] - tri[6]; b[1] = tri[1] - tri[7]; b[2] = tri[2] - tri[8];
        if (dot_product_3d(tri[9], tri[10], tri[11], a[2] * b[1] - a[1] * b[2], a[0] * b[2] - a[2] * b[0], a[1] * b[0] - a[0] * b[1]) < 0){continue;}
       
        //The line intersects the triangle. The ray is shortened by a tiny amount to make sure the returned value is on the correct side of the triangle
        t += 1 / dot_product_3d(tri[9], tri[10], tri[11], ret[0] - rayStart[0], ret[1] - rayStart[1], ret[2] - rayStart[2]);
        rayEnd[0] = lerp(rayStart[0], rayEnd[0], t);
        rayEnd[1] = lerp(rayStart[1], rayEnd[1], t);
        rayEnd[2] = lerp(rayStart[2], rayEnd[2], t);
    }
    return rayEnd;
}

//Find starting region of the line segment
region[0] = (rayStart[0] > regionPos[0] + halfRegionSize);
region[1] = (rayStart[1] > regionPos[1] + halfRegionSize);
region[2] = (rayStart[2] > regionPos[2] + halfRegionSize);
for (i = 0; i < 3; i ++)
{
    if (rayEnd[i] == rayStart[i]){continue;}
    if ((rayStart[i] < regionPos[i]) or (rayStart[i] > regionPos[i] + regionSize))
    {
        checkPos = regionPos;
        checkPos[i] += region[i] * regionSize;
        t = (checkPos[i] - rayStart[i]) / (rayEnd[i] - rayStart[i]);
        if ((t <= 0) or (t >= 1)){continue;}
        j = (i + 1) mod 3;
        k = (i + 2) mod 3;
        ret[j] = lerp(rayStart[j], rayEnd[j], t);
        ret[k] = lerp(rayStart[k], rayEnd[k], t);
        if  ((ret[j] < regionPos[j]) or (ret[j] > regionPos[j] + regionSize) or (ret[k] < regionPos[k]) or (ret[k] > regionPos[k] + regionSize)){continue;}
        region[j] = (ret[j] > regionPos[j] + halfRegionSize);
        region[k] = (ret[k] > regionPos[k] + halfRegionSize);
        break;
    }
}

//Check for collisions in the starting region of the line segment
buffer_seek(colBuffer, buffer_seek_start, r + region[0] * 4 + region[1] * 8 + region[2] * 16);
newPos[0] = regionPos[0] + halfRegionSize * region[0];
newPos[1] = regionPos[1] + halfRegionSize * region[1];
newPos[2] = regionPos[2] + halfRegionSize * region[2];
rayEnd = smf_collision_cast_ray_recursive(colBuffer, rayStart, rayEnd, halfRegionSize, newPos);

//Check for intersections along the middle axis in all three dimensions
for (i = 0; i < 3; i ++)
{
    if (rayEnd[i] == rayStart[i]){continue;}
    t = (regionPos[i] + halfRegionSize - rayStart[i]) / (rayEnd[i] - rayStart[i]);
    if ((t <= 0) or (t >= 1)){continue;}
    j = (i + 1) mod 3;
    k = (i + 2) mod 3;
    intersect[j] = lerp(rayStart[j], rayEnd[j], t) - regionPos[j];
    intersect[k] = lerp(rayStart[k], rayEnd[k], t) - regionPos[k];
    if ((intersect[j] < 0) or (intersect[j] > regionSize) or (intersect[k] < 0) or (intersect[k] > regionSize)){continue;}
    region[i] = (rayStart[i] < regionPos[i] + halfRegionSize);
    region[j] = (intersect[j] >= halfRegionSize);
    region[k] = (intersect[k] >= halfRegionSize);
    newPos[0] = regionPos[0] + halfRegionSize * region[0];
    newPos[1] = regionPos[1] + halfRegionSize * region[1];
    newPos[2] = regionPos[2] + halfRegionSize * region[2];
    buffer_seek(colBuffer, buffer_seek_start, r + region[0] * 4 + region[1] * 8 + region[2] * 16);
    rayEnd = smf_collision_cast_ray_recursive(colBuffer, rayStart, rayEnd, halfRegionSize, newPos);
}
return rayEnd;
 
Last edited:
T

TBK_Jo$hu@

Guest
Wow, this look like it will be very useful for a game i'm currently working on! :)
 

TheSnidr

Heavy metal viking dentist
GMC Elder
Wow, this look like it will be very useful for a game i'm currently working on! :)
Awesome :D

I just managed to convert the recursive ray casting script into an iterative one! :D I've spent way too much time on getting this to work...
It's now only necessary with a single script:
Code:
/// @description smf_collision_cast_ray(collisionBuffer, x1, y1, z1, x2, y2, z2)
/// @param collisionBuffer
/// @param x1
/// @param y1
/// @param z1
/// @param x2
/// @param y2
/// @param z2
/*
Casts a ray from one point to another and returns the position of the first collision with geometry

Script made by TheSnidr
www.TheSnidr.com
*/
var colBuffer = argument0, regionSize = 65535, progress = 0, stack = ds_stack_create();
var a, b, n, i, j, k, l, t, tri, tris, modelX, modelY, modelZ, modelSize, transformScale, lineStart, lineEnd, regionPos, bufferPos, halfRegionSize, region, intersect;
buffer_seek(colBuffer, buffer_seek_start, 0);
bufferPos = buffer_read(colBuffer, buffer_s32);
modelX = buffer_read(colBuffer, buffer_f32);
modelY = buffer_read(colBuffer, buffer_f32);
modelZ = buffer_read(colBuffer, buffer_f32);
modelSize = buffer_read(colBuffer, buffer_f32);
transformScale = 65535 / modelSize;
lineStart[0] = transformScale * (argument1 - modelX);
lineStart[1] = transformScale * (argument2 - modelY);
lineStart[2] = transformScale * (argument3 - modelZ);
lineEnd[0] = transformScale * (argument4 - modelX);
lineEnd[1] = transformScale * (argument5 - modelY);
lineEnd[2] = transformScale * (argument6 - modelZ);
regionPos[2] = 0;
returnNormal = -1;
returnNormal[2] = 1;
while true
{
    if (bufferPos > 0)
    {   //Iterate through the octree
        halfRegionSize = regionSize / 2;
        for (l = progress; l < 4; l ++)
        {
            if l == 0
            {   //Check either the starting region of the ray or the first region it intersects
                region[0] = (lineStart[0] > regionPos[0] + halfRegionSize);
                region[1] = (lineStart[1] > regionPos[1] + halfRegionSize);
                region[2] = (lineStart[2] > regionPos[2] + halfRegionSize);
                for (i = 0; i < 3; i ++)
                {
                    if (lineEnd[i] == lineStart[i]) continue;
                    if (lineStart[i] >= regionPos[i] and lineStart[i] <= regionPos[i] + regionSize) continue;
                    t = (regionPos[i] + region[i] * regionSize - lineStart[i]) / (lineEnd[i] - lineStart[i]);
                    if (t < 0 or t > 1) continue;
                    j = (i + 1) mod 3; k = (i + 2) mod 3;
                    intersect[j] = lerp(lineStart[j], lineEnd[j], t) - regionPos[j];
                    intersect[k] = lerp(lineStart[k], lineEnd[k], t) - regionPos[k];
                    if (intersect[j] < 0 or intersect[j] > regionSize or intersect[k] < 0 or intersect[k] > regionSize) continue;
                    region[j] = (intersect[j] > halfRegionSize);
                    region[k] = (intersect[k] > halfRegionSize);
                    break;
                }
            }
            else
            {   //Check for intersections with the middle plane of each dimension
                i = l - 1;
                if (lineEnd[i] == lineStart[i]) continue;
                t = (regionPos[i] + halfRegionSize - lineStart[i]) / (lineEnd[i] - lineStart[i]);
                if (t < 0 or t > 1) continue;
                j = (i + 1) mod 3; k = (i + 2) mod 3;
                intersect[j] = lerp(lineStart[j], lineEnd[j], t) - regionPos[j];
                intersect[k] = lerp(lineStart[k], lineEnd[k], t) - regionPos[k];
                if (intersect[j] < 0 or intersect[j] > regionSize or intersect[k] < 0 or intersect[k] > regionSize) continue;
                region[i] = lineStart[i] < regionPos[i] + halfRegionSize;
                region[j] = intersect[j] >= halfRegionSize;
                region[k] = intersect[k] >= halfRegionSize;
            }
       
            //Push this region to stack
            ds_stack_push(stack, bufferPos);
            ds_stack_push(stack, regionPos[0]);
            ds_stack_push(stack, regionPos[1]);
            ds_stack_push(stack, regionPos[2]);
            ds_stack_push(stack, l);
       
            //Go to intersected child region
            if (region[0]){regionPos[0] += halfRegionSize; bufferPos += 4;}
            if (region[1]){regionPos[1] += halfRegionSize; bufferPos += 8;}
            if (region[2]){regionPos[2] += halfRegionSize; bufferPos += 16;}
            buffer_seek(colBuffer, buffer_seek_start, bufferPos);
            bufferPos = buffer_read(colBuffer, buffer_s32);
            regionSize /= 2;
            progress = 0;
            break;
        }
        if (l < 4) continue; //If we ended the for-loop prematurely, we should also restart the while-loop
    }
    else
    {   //If this is a leaf region, check for intersections with the triangles in this leaf
        l = 0;
        buffer_seek(colBuffer, buffer_seek_start, -bufferPos);
        repeat buffer_read(colBuffer, buffer_u16) 
            tris[l++] = buffer_read(colBuffer, buffer_u16);
        repeat l
        {   //Find intersection with triangle plane
            tri = smf_get_triangle(colBuffer, tris[--l]);
            t = dot_product_3d(tri[9], tri[10], tri[11], lineEnd[0] - lineStart[0], lineEnd[1] - lineStart[1], lineEnd[2] - lineStart[2]);
            if (t == 0) continue;
            t = dot_product_3d(tri[9], tri[10], tri[11], tri[0] - lineStart[0], tri[1] - lineStart[1], tri[2] - lineStart[2]) / t;
            if (t <= 0 or t >= 1) continue;
            intersect[0] = lerp(lineStart[0], lineEnd[0], t);
            intersect[1] = lerp(lineStart[1], lineEnd[1], t);
            intersect[2] = lerp(lineStart[2], lineEnd[2], t);
           
            //Check if the intersection is inside the triangle. If not, discard and continue.
            a[0] = intersect[0] - tri[0]; a[1] = intersect[1] - tri[1]; a[2] = intersect[2] - tri[2];
            b[0] = tri[3] - tri[0]; b[1] = tri[4] - tri[1]; b[2] = tri[5] - tri[2];
            if (dot_product_3d(tri[9], tri[10], tri[11], a[2] * b[1] - a[1] * b[2], a[0] * b[2] - a[2] * b[0], a[1] * b[0] - a[0] * b[1]) < 0) continue;
            a[0] = intersect[0] - tri[3]; a[1] = intersect[1] - tri[4]; a[2] = intersect[2] - tri[5];
            b[0] = tri[6] - tri[3]; b[1] = tri[7] - tri[4]; b[2] = tri[8] - tri[5];
            if (dot_product_3d(tri[9], tri[10], tri[11], a[2] * b[1] - a[1] * b[2], a[0] * b[2] - a[2] * b[0], a[1] * b[0] - a[0] * b[1]) < 0) continue;
            a[0] = intersect[0] - tri[6]; a[1] = intersect[1] - tri[7]; a[2] = intersect[2] - tri[8];
            b[0] = tri[0] - tri[6]; b[1] = tri[1] - tri[7]; b[2] = tri[2] - tri[8];
            if (dot_product_3d(tri[9], tri[10], tri[11], a[2] * b[1] - a[1] * b[2], a[0] * b[2] - a[2] * b[0], a[1] * b[0] - a[0] * b[1]) < 0) continue;
       
            //The line intersects the triangle. Save the triangle normal and intersection.
            returnNormal[0] = tri[9];
            returnNormal[1] = tri[10];
            returnNormal[2] = tri[11];
            lineEnd = intersect;
            intersect = -1;
        }
    }
    if !ds_stack_size(stack) break; //If the stack is empty, break the loop
   
    //Pop the previous region from stack
    progress = ds_stack_pop(stack) + 1;
    regionPos[2] = ds_stack_pop(stack);
    regionPos[1] = ds_stack_pop(stack);
    regionPos[0] = ds_stack_pop(stack);
    bufferPos = ds_stack_pop(stack);
    regionSize *= 2;
}
ds_stack_destroy(stack);
returnX = modelX + lineEnd[0] / transformScale;
returnY = modelY + lineEnd[1] / transformScale;
returnZ = modelZ + lineEnd[2] / transformScale;
 
Last edited:
G

GproKaru

Guest
I've just discovered another issue that I can't find a resolve to. Since this method of rendering 3D depends on shaders, things like gpu_set_alphatest will no longer work. I've been trying for the past day now to replicate the effects of that through the shader but I've had no luck. I'm aware that changing the depth between certain objects/models will make alpha textures work correctly but I used to use gpu_set_alphatest to remove completely transparent segments of textures like fences and windows within the same object/model.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
I do believe the gpu functions will still work as they should? Either way, it should work with something along the lines of "if (gl_FragColor.a < 0.5) discard;" at the end of the fragment shader.
 
G

GproKaru

Guest
You never cease to amaze me... I'm still new to shaders but I didn't know about the 'discard' function until you pointed it out... I was pretty much doing everything to reduce the alpha to stop it from rendering the alpha areas, but I could never find anything on how to prevent rendering of a pixel all together. Thank you!
 

Dacker

Member
Wow this is cool as heck! Well done sir!

It's probably no trivial task, but in your octree level example do you have any plans for doing hidden surface removal?

As in, employing the octree on the drawn model as well and testing which nodes are present within a viewing frustum?
Or perhaps for indoor scenes with lots of smallish rooms, precomputing which other nodes can possibly be visible from within a particular node?

That's probably oversimplified thinking, I've never built an octree and occlusion culling makes my head hurt :D

Anyhow, awesome work you have here! :cool:
 

TheSnidr

Heavy metal viking dentist
GMC Elder
J

Jasnsathome

Guest
Thank you for the GMS 1.4 version. It's amazing how easy(relatively) it is to understand and use. Very well made. Thanks again for making this available to everyone!
 
This is an amazing work !
The best thing about 3d whith GM i haver never seen : really good job, you rock ! :)

I'm trying to use it for my project, thanx a lot for the compatible GM1.4 support.

A little feedback after a little hour of use :
1 : In the skinning , when i select points in the rectangle, some of them are not selected (look at the image)

2 : Here is a little error i had in the skinning :
Code:
##########
ERROR in
action number 1
of Draw Event
for object oRigSystem:

Cannot apply sqrt to negative number.
at gml_Script_rig_draw_points
################
-------------------
stack frame is
gml_Script_rig_draw_points (line 0)
gml_Object_oRigSystem_Draw_0
3 : if i use the autoskin model function, but then want to remove 1 or 2 points to a bone, how can i do it plz ? :)

4 : I have quit the editor, and restart it. I want to finish skinning. How do i load my .smf file in the editor ?

5 : some suggestion :
- i have a complexe model : maybe a button to color points wich aren't assign to any bone could be nice :)
- maybe a sphere system to select points near a bone could be easier to use ?

This is really a great tool, hope you will continue to improve it :)
 

Attachments

Last edited:
G

GproKaru

Guest
It's been a while since I checked in here. I decided to try your new version of the model tool but your dropbox download link no longer works. Is it possible to get a new link to it?
 

TheSnidr

Heavy metal viking dentist
GMC Elder
@CyberTwister:
1: The selection box is 3D, and doesn't extend infinitely far in the 3rd dimension. When selecting in one view, notice the box' size in the other views. Points outside the box will not be selected.
2: Ooops, I'm gonna have to try to reproduce that. Did you do something special? :p
3: You can autoskin parts of the model, and you can modify the skinning manually afterwards! To modify it manually you have to select individual or groups of points, and assign them to a bone.
4: Skinning information should be loaded along with the .smf model. Make sure to export the skinned .smf model when you're done!
5: More complex skinning tools have been suggested, but they'll take so much work from my side, I don't think they're worth it. This only aims to be a simple, bare-bones animation system. For more advanced animation techniques you should use a different animation program.

Speaking of which, I'm working on COLLADA support, which lets you export a rigged, skinned and animated model from Blender straight into the model tool. So far I have the model, rig and skinning information down, only the animation keyframes to go:

COLLADA is a very clunky format...

I would love if somebody could test the COLLADA import function:
https://dl.dropboxusercontent.com/u/41252865/GamemakerExamples/SMF Model Tool - Collada test.zip
It works with the file I made, but I don't know if it's sturdy enough to import any .dae-file.
EDIT: Seems like it's far from stable. A lot of work remains!
 
Last edited:

Aragon

Member
I've donwload it and it runs perfectly fine (FPS wise), except that when going into the little "black entrance" and moving left. I got through the walls. Might be a geometry issue?

Used it with GMS 1.4
 
A

adrys88x

Guest
Hello! Thanks for awesome tools and scripts for import and management 3D Models. But got question. How Can I define in code 4 texture files(PNG) with 1 Model Object(OBJ)? I ask beacause sometimes on other website I downloaded 1 OBJ with 2-6 PNG texture files.
 
G

GproKaru

Guest
Hello! Thanks for awesome tools and scripts for import and management 3D Models. But got question. How Can I define in code 4 texture files(PNG) with 1 Model Object(OBJ)? I ask beacause sometimes on other website I downloaded 1 OBJ with 2-6 PNG texture files.
Usually when 3D models are made, they use different 'materials' to define multiple textures to a single model within other game engines. Unfortunately since GMS is primarilly a 2D engine, it doesn't support that feature naturally.
From what I can tell, you can do one of two things:

1: (1 object with 1 texture, but requires some effort and 3D knowledge) Basically get all your textures, make them into one big texture. Open the model in a 3D program of your choice and realign the UV maps to the new larger texture.

2: ('X' objects with 'X' textures, ( 'X' = number of textures ) less effort but more resource heavy) You could separate pieces of the model based on the textures/materials and export them individually. Then you can render 4 different textures onto 4 different models. But as I said, this option will be resource heavy and can hit your FPS pretty quickly with a lot of models being rendered at once. Not the best option, but still an option.
 
Last edited by a moderator:
M

Multimagyar

Guest
Well technically if you are fine with shaders you could just send multiple textures you store data of in an array or such and could sum it up in one buffer with multiple textures as a third option. How optimal that would be I would not be able to tell. Single texture-single buffer sounds like the cheapest for me in general.

Also I'm not too familiar with the collision script, but would I be able to add my triangle to a collision buffer or something like that? would be quite useful and actually would make me use 1 less dll in general.
 
G

GproKaru

Guest
Also I'm not too familiar with the collision script, but would I be able to add my triangle to a collision buffer or something like that? would be quite useful and actually would make me use 1 less dll in general.
From my experimentation with the collision system. You generate a static collision map with the external tool snidr provided and the collision function snidr made does a spherical radial check against an assigned collision map. There doesn't seem to be any other collision types outside of a sphere at the moment.

[edit] On a side note. Snidr's method on how you crate bones from branching nodes also aided my thought process on making a dynamic rail/ledge grinding system. Using a model to detect a player collision and then cycling through nodes to make a grind path.
GIF demonstration: https://www.dropbox.com/s/9meeeidg5j4k00s/Beat2.gif?dl=0
 
Last edited by a moderator:
Hey @TheSnidr - I have been playing with your tool and scripts to import some objects I created in another program. First off, let me say it is super easy to use - I have really no 3D modeling experience, so I am just sort of winging it at this point.

Anyways - my question - I am using d3d_set_fog in my game to limit the visual distance and add some ambiance. My problem is that the models I am importing using your tool and then scripts don't seem to take the fog into account. See the pic, the trees in the distance are your SMF models, the walls around, ground, etc... those are all just d3d_draw_blocks and other elements.

http://imgur.com/a/711cB

Is there a trick to allowing these objects to be affected by the fog you can recommend? My guess is that they are being rendered after the fog or something, but even if I have the fog line right before the smf_model_draw, it doesn't seem to affect it.

And I think the same is true for d3d_light_define_ambient, I don't think it is affecting the models (though I need to test further to confirm it).

Maybe I need to look into some shader options, though that is a whole separate thing I am just learning...

Thanks!

Oh - and I am on 1.4 (if that makes any difference).

The more I look through your code (as to gain a better understanding) it appears as though you are using shaders to render the models? I could be wrong on that...

But, basically, I am guessing that the built in d3d functions, like fog and lighting can't interact with the SMF models.

Do I want to work on a shader based fog system?

Fog, lighting and draw colour use the default shader. Once you enable a custom shader, all those effects stop working. You'll have to program it into the shader manually.
There are a bunch of shaders here on the forums and on the marketplace. If you don't have any experience with shaders, I'd really recommend reading up on them a little, as they're incredibly handy! You can't use two shaders at once by the way, but you can combine them into one.
Sorry one last post - I read up the thread and this is the answer to my question. Once we use a custom shader, all the other built in stop working.

Combining shaders... that seems to be what I need to do, any good guides on how to do that? I found xygthops 3D fog shader and I think it will do the trick, but combining these together seems daunting.
 
Last edited by a moderator:

TheSnidr

Heavy metal viking dentist
GMC Elder
Hello! I'm still on vacation, don't have time to do much programming from here. But I'll try to reply to some of the questions asked in the topic!
I've donwload it and it runs perfectly fine (FPS wise), except that when going into the little "black entrance" and moving left. I got through the walls. Might be a geometry issue?
Hm, I just tried it and can confirm, you can go through the wall. I'm gonna have to look into this when I get home.
Hello! Thanks for awesome tools and scripts for import and management 3D Models. But got question. How Can I define in code 4 texture files(PNG) with 1 Model Object(OBJ)? I ask beacause sometimes on other website I downloaded 1 OBJ with 2-6 PNG texture files.
No automatic way to do this at the moment, sorry. I may modify the format to support automatic segmentation of a model based on material so that it draws a separate model for each texture. If course, the most efficient method is to manually combine the textures into one!
From my experimentation with the collision system. You generate a static collision map with the external tool snidr provided and the collision function snidr made does a spherical radial check against an assigned collision map. There doesn't seem to be any other collision types outside of a sphere at the moment.

[edit] On a side note. Snidr's method on how you crate bones from branching nodes also aided my thought process on making a dynamic rail/ledge grinding system. Using a model to detect a player collision and then cycling through nodes to make a grind path.
GIF demonstration: https://www.dropbox.com/s/9meeeidg5j4k00s/Beat2.gif?dl=0
Correct, the collision system is very basic at the moment, just sphere-shape collision detection and a simple collision response. Still, it allows you to make more advanced physics responses, like this:

Your game looks ridiculously promising, very fluid animations and a lovely artstyle! Are you gonna make a topic for it some time soon?
Sorry one last post - I read up the thread and this is the answer to my question. Once we use a custom shader, all the other built in stop working.

Combining shaders... that seems to be what I need to do, any good guides on how to do that? I found xygthops 3D fog shader and I think it will do the trick, but combining these together seems daunting.
Better learn the shader language if you want to use this engine! The provided animation shader is a bare bones shader that should be greatly modified by the user. It's not ready to use as-is.
 
Hello! I'm still on vacation, don't have time to do much programming from here. But I'll try to reply to some of the questions asked in the topic!

Hm, I just tried it and can confirm, you can go through the wall. I'm gonna have to look into this when I get home.

No automatic way to do this at the moment, sorry. I may modify the format to support automatic segmentation of a model based on material so that it draws a separate model for each texture. If course, the most efficient method is to manually combine the textures into one!

Correct, the collision system is very basic at the moment, just sphere-shape collision detection and a simple collision response. Still, it allows you to make more advanced physics responses, like this:

Your game looks ridiculously promising, very fluid animations and a lovely artstyle! Are you gonna make a topic for it some time soon?

Better learn the shader language if you want to use this engine! The provided animation shader is a bare bones shader that should be greatly modified by the user. It's not ready to use as-is.
Thanks for replying while on vacation. Sure, good to know - I just wanted to verify the path forward if I was going to use it. I will see what I can learn about shaders, I have used some - but mostly basic ones to change colors, etc...

If you have any good starting points for working with 3D shaders, I'd love if you shared. If not, no biggy. Thanks again! Have a good rest of your vacation.
 
G

GproKaru

Guest
Correct, the collision system is very basic at the moment, just sphere-shape collision detection and a simple collision response. Still, it allows you to make more advanced physics responses
Your game looks ridiculously promising, very fluid animations and a lovely artstyle! Are you gonna make a topic for it some time soon?
Yeah, I already started doing some advanced stuff myself.


One thing that did raise my concern though. I noticed that you can't have too many models with active animation processes going on at the same time, otherwise the framerate begins to tank drastically. I've had to incorporate a method to throttle the frequency of the animations update rate based on the distance between the instance and the camera. I was just wondering if you had any other suggestions on how to get the most performance out of your animation process without sacrificing too much quality.

The only other idea coming to mind is to have an object pre-render the animation and then have multiple objects use that exact same animation, but it would look odd having a 10-20+ person crowd walking in unison. Might be a better idea for at least models that are distant though.

Oh, and about this project? For now I'm just working on it as a hobby thing, not sure if I'll post a topic on it (not at least anytime soon) because I'd like to build it up some more before I feel like openly putting it out there.
It's basically just a fangame project based on the game Jet Set Radio Future. An Xbox OG game that hasn't seen the day of light in over 15 years, yet the prequel has~
 
Last edited by a moderator:

TheSnidr

Heavy metal viking dentist
GMC Elder
DividingByZero: There are many tutorials on shaders in the tutorials section! I learned mostly from reading the GLSL ES manual and experimenting on my own, so I do unfortunately not have any good sources for you. You can start by learning what shaders actually do in the Shaders Overview article line by YYG:
https://www.yoyogames.com/blog/14/shaders-overview-part-1

GproKaru: Awesome!
Generating lots of samples every step is very resource intensive! What you can do instead is to save a bunch of samples in a global array, and linearly interpolate between them using smf_sample_lerp. This way, each object can be at a different time in the animation without having to create new samples from scratch every step. Linear interpolation between samples is much more efficient than generating new samples, but it's also less accurate, especially for large movements.
You should also cull objects outside the view.
 
G

GproKaru

Guest
Generating lots of samples every step is very resource intensive! What you can do instead is to save a bunch of samples in a global array, and linearly interpolate between them using smf_sample_lerp. This way, each object can be at a different time in the animation without having to create new samples from scratch every step. Linear interpolation between samples is much more efficient than generating new samples, but it's also less accurate, especially for large movements.
You should also cull objects outside the view.
Yeah, I already have a culling method based on view angle and distance but considering how resource intensive generating new frames was, I had to seek other alternatives like throttling the frame creation rates and using a single frame shared among all distant models.
 
E

Esteban Devia

Guest
Hello, First of all I want to say that this is AMAZING! TheSnidr, you are Amazing hahahaha, Todo un mago! I've been reading a lot of how to create shaders like you told me and the more I learn, the more amazed I get when I see these type of things. I tested the last update that you did to see if it was possible to load a Collada .Dae file, but it crashed. It seems that the files that I export using Blender and Maya are not working.
 
G

GproKaru

Guest
Hello, First of all I want to say that this is AMAZING! TheSnidr, you are Amazing hahahaha, Todo un mago! I've been reading a lot of how to create shaders like you told me and the more I learn, the more amazed I get when I see these type of things. I tested the last update that you did to see if it was possible to load a Collada .Dae file, but it crashed. It seems that the files that I export using Blender and Maya are not working.
Instead of a .Dae, export your models into .Obj models. I use blender myself

[Edit]
On a side note. I have come across a bug with doing rotation animations involving the root bone. If you apply a global rotation (it could be any rotation but I usually use globals) in an animation to the root bone, sometimes the model thinks it rotated a full 360 degrees or more in a certain direction. I have to try and spin it back to normal, which it may spin again and again during the process which could take quite a fair bit of time to re-correct.
 
Last edited by a moderator:
Top