Discussion [GUIDE] Getting Started with 3D in GMS2 (Project Download)

Discussion in 'GameMaker Studio 2 Community Tech Support' started by MaddeMichael, Nov 6, 2016.

  1. Lemth

    Lemth Member

    Oct 7, 2016
    Thank you, that was it! I actually need a "cull_noculling" later on and will only draw 54 triangles at maximum so I should be fine.

    One more question;

    I tried to modify your "third person camera" by using arrow keys to make it move around (0,0,0) in a spherical way like, but it only makes very weird movements. Any ideas?

    I'm trying to implement THIS
    By using the following formulas:
    This is what it looks like:
  2. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    Sorry about the delay! I was a little busy on another project.

    From looking at your code and doing a quick test, I only have about 3 things to suggest.
    1) Now that you're back in 3rd person, you don't need the 1 in y-up anymore, just the 1 in z-up, assuming you can avoid the lookat vector and the up vector from becoming parallel. In my tests, having both y-up and z-up set to 1 causes the camera to have a sort of "swinging" motion.
    The ideal 1-size fits all option is to actually calculate the up-vector every step (which can allow fully free orbital cameras), but this process varies depending on how you determine the lookat - I can't come up with an idea right of the top of my head as I haven't used this implementation before (at least, not knowingly) and I'm tired. Though the basic form of what I do is to determine the forward vector (normalised vector of lookfrom->lookto) and rotate it to point up.
    2) Just remember to add the target location to the look from position in order to keep the camera relative, otherwise the camera will hang about near the origin and watch as it's target runs away.
    3) I'm not 100% sure what calculation you are doing with "rho" here? From what I can tell, it doesn't actually do anything - I didn't need it in my test and I think the result of the calculation should be equal to what rho was before the calculation anyway - making no difference at all. Of course, I could be wrong - I'm very tired.

    So, all in all, here's what I think might work:
    -Remove "rho = blah" - it wasn't necessary in my test
    -Change the matrix_build bit to this:
    mLookat = matrix_build_lookat(
                                x + rho * sin(phi) * cos(theta),
                                y + rho * sin(phi) * sin(theta),
                                rho * sin(phi),
    Hopefully this helps!
  3. Lemth

    Lemth Member

    Oct 7, 2016
    @MaddeMichael Thank for the reply, no worry about delay! Very grateful that you are helping me!

    I upgraded the code as you suggested; can be tested here on GMLIVE: http://tinyurl.com/l9zznfn (SET TO GL AND PRESS RUN)
    The weird thing is that up-and down is no longer a rotate but a 'bounce' (press and hold up or down arrow to see what I mean.)

    Too bad the gmlive is only gms1, so I had to convert code backwards, perhaps I made a mistake there?

    EDIT: I found a helpful link that shows the EXACT movement that I want to create:


    Trying to convert the underlying Javascript to GML. But I find some of the mat4. functions difficult to understand how they work in Gamemaker
    Rukola likes this.
  4. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    Ah, very nice - I see what you mean!

    I've fixed the GMLive demo (dang, that tool is useful, thanks @YellowAfterlife!) - it was a simple typo where z = rho * sin(phi) instead of z = rho * cos(phi). So, yeah, probably just a little mistake porting :). I know for sure I've done this sort of thing too many times to count

    Here's the link to that: http://tinyurl.com/kwdauxm

    I've had a little look at that link, I don't think I want to recreate the whole thing, but I think I can at least give a few pointers on the mat4 stuff - this could be more info than you need, but I thought it could be helpful anyway:
    • "mat4.perspective(fov, aspect, clippingnear, clippingfar, targetmatrix)" is basically the same thing as "targetmatrix = matrix_build_perspective_fov(fov, aspect, clippingnear, clippingfar)";
    • "mat4.identity(mvMatrix)" is equivelant to "mvMatrix = matrix_build_identity()"
    • To acheive mat4.translate(mvMatrix, [0,0,-6]) after setting identity, you can just use "mvMatrix[14] = -6" (Matrices in GameMaker are just 16 index arrays where indices 12, 13 and 14 are the translation component)
    • I'm a little less sure about this one (I have no experience in direct WebGL with Javascript, but I think I get what's going on):
      • "mat4.multiply(mvMatrix, moonRotationMatrix)" should be the same as "mvMatrix = matrix_multiply(moonRotationMatrix, mvMatrix)", since the rotation should probably happen before the translation. If it acts a little wierd (i.e, the moon rotates around a point, but seemingly off-center), try swapping the arguments.
    • I see mat4.create() a few times, this isn't necessary in GM as matrix_build_* functions create the matrix anyway
    • The last mat4 bit I see is "mat4.rotate(target Matrix, angle, axis)" - this is a bit more complex - the equivelant function is d3d_transform_add_rotation_axis(), but that's a compatibility script - However, the demo only rotates about perfect x and y axes, which is a little easier.
      • Instead of creating a bunch of new matrices, the whole "newRotatinMatrix" stuff can probably be replaced with "moonRotationMatrix = matrix_multiply(moonRotationMatrix, matrix_build(0,0,0,deltaY / 10,deltaX / 10,0,1,1,1));" (Note that matrix build takes angle arguments in degrees, not radians) - this is assuming of course the camera aims from above (+Z), looking down (-Z) with the up-vector along the Y axis.
      • You could still convert the compatibility script into a useful matrix one for other uses though, even though it isn't necessary in this specific case.

    Hopefully this helps make some sense of the mat4 stuff!

    I'm considering updating the guide at some point and may include some more camera variations - perhaps including this orbital camera and a basic 3rd person and 1st person implementation. It might be in a little while though, when I've got some extra time.
    Lemth likes this.
  5. Lemth

    Lemth Member

    Oct 7, 2016
    I don't know what it was, but something about your explanation just made it click in my head!!

    Got this camera to work with a nicely colored cube example:

    http://tinyurl.com/l2cfgzb (set to GL and press RUN)

    Thanks for the help!
    Last edited: May 11, 2017
    Rukola likes this.
  6. Lemth

    Lemth Member

    Oct 7, 2016
    Any idea why the alpha is not working on all faces?

    http://tinyurl.com/ny9c5mn (set to GL and press RUN - arrow keys to move, space to change colors)
  7. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    That looks super nice! I'm glad you got it worked out.

    As for the alpha not working on all faces, that's actually because of something a little different - it's not a problem with your code, and not a problem with GameMaker either.
    If you draw a background behind all the faces, you'll see that all the faces are in fact transparent (GMLive version: http://tinyurl.com/lkd9x6q)
    The problem is really to do with alpha and the depth buffer.
    Basically, if something transparent that is closer to the camera is drawn before something further away, it will clip the further thing out of the drawing (in this case, some sides aren't drawn because they are drawn after the near side)
    This is an annoying issue with 3D development in general, and usually involves rendering anything transparent after all the solid stuff and using depth sorting to make sure the transparencies overlay nicely.

    A working method is to enable culling again and render the interior faces and exterior faces separetely, rendering the interiors first (GMLive verion: http://tinyurl.com/mn8xc8m) (this is probably easier in GMS2 as you can set the cull order to render the interior, then reverse the cull order and draw the exterior in the exact same way). You'd still need depth sorting if you render more of these, and I expect it may still end up looking wrong if meshes intersect, though I don't know what can be done about that yet.

    Another thing to rememeber is that even things with 0 alpha can result in clipping further objects out of the picture if they are drawn first - setting the alpha_ref_test or discarding pixels in a fragment/pixel shader can help.
    I hope that makes some level of sense? I know what I'm trying to say, but I sometimes have a hard time explaining it.
  8. Carsten

    Carsten Member

    Nov 25, 2016

    thanks for the tutorial, this is really a great help! :) I'm sorry to ask a question, but I don't know how to actually draw something at a position defined in the room with correct rotation. This is my code:

    //get the direction from the camera to the object and the pitch
    if (instance_exists(oCamera))
    var cdir = point_direction(oCamera.x, oCamera.y, x, y);
    var cdirtan = tan(oCamera.cz/point_distance(x, y, oCamera.x, oCamera.y)) + 90;
    var z = 100;
    var mat = matrix_build(x, y, z, 0, -90, direction + cdir, 1, 1, 1);
    matrix_set(matrix_world, mat);
    vertex_submit(vertex_buffer, pr_trianglelist, -1);
    matrix_set(matrix_world, matrix_build_identity());

    >The floor (tiles) are at z=100.
    >The vertex is the plane from the example above

    I've tried using the direction from the camera to the instance and then draw the instance with that data but that doesn't solve it because it then obviously draws the instance bound to the rotation of the camera. I'm a bit clueless now and would appreciate any help.

    This is a video demonstrating the problem:

    Thanks, Carsten
    Last edited: May 19, 2017
  9. Shut

    Shut Member

    Jun 21, 2016
    Thanks for the tutorial! I was just experimenting with 3D and got the basics, I'm trying to get the top down 3D view and having trouble with mouse positions. Any idea why mouse_x and mouse_y suddenly change to different values when using the following code?

    projMat = matrix_build_projection_perspective_fov(60, 1920/1080, 1, 32000);
    camera_set_proj_mat(camera, projMat);
  10. rwkay

    rwkay YoYo Games Staff YYG Staff

    Apr 12, 2016
    Shut likes this.
  11. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    **UPDATE 24/09/17**

    Finally, an update! First one in months. Here's the tweaks:
    • Removed normal information from the basic vertex buffer guide - it was kinda superfluous
    • Added info about d3d_set_shading being gone
    • Added more info about the projection matrix and it's weird handedness conversion
    • Updated the included project to reflect changes
    • Added links to my Blender model export/import tool
    • Added some basic info about vertex batching - I'm not super confident in this area, but I think what I've said is true - any corrections, let me know
    • Probably some other stuff, but I can't remember now.
    Shut likes this.
  12. slojanko

    slojanko Member

    Jun 20, 2016
    I've written the following code for updating the camera position through the step event:
    camera_x += movement_speed * (keyboard_check(ord("D")) -  keyboard_check(ord("A")));
    camera_y += movement_speed * (keyboard_check(ord("S")) - keyboard_check(ord("W")));
    camera_z -= movement_speed * (keyboard_check(ord("E")) - keyboard_check(ord("Q")));
    camera_rotation += window_get_width()/2 - window_mouse_get_x();
    camera_pitch += window_get_height()/2 - window_mouse_get_y();
    camera_pitch = clamp(camera_pitch, -88, 88);
    if (!keyboard_check(vk_space))
        window_mouse_set(640, 360);
    //Build a matrix that looks from the camera location above, to the room center. The up vector points to -z
    mLookat = matrix_build_lookat(camera_x, camera_y, camera_z, camera_x + dcos(camera_rotation), camera_y - dsin(camera_rotation), camera_z - dtan(camera_pitch), 0, 0, -1);
    //Assign the matrix to the camera. This updates were the camera is looking from, without having to unnecessarily update the projection.
    camera_set_view_mat(view_camera[0], mLookat);
    It takes into account the Y axis increasing down (2D) and Z depth being inverted (-Z points up). My only question is why projectionMatrix requires Fov_y -60
    projectionMatrix = matrix_build_projection_perspective_fov(-60, 1280/720, 1.0, 32000.0);
  13. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    Okay, basically this is a thing that has bugged me since the release of the GMS2 open beta. But I can sort of explain why.

    Long story short, pre-GMS2 used a left-handed coord system in 3D. GMS2's proj matrix building function effectively changes it to a Right-Handed coordinate system, to fall inline with 2D mode (I think it's to make the transition to 3D from 2D a bit easier, which is odd since 3D gets so little love in GM). An unfortunate side-effect is that it seems to invert the up-vector used in the lookat matrix (which can cause some issues down the line, particularly in stuff like flight sims where the camera up-vector must point up relative to the player/cockpit, not the world)
    In order to make the matrix function like 1.X and prior, making the y-fov and aspect negative returns everything to left-handed space and makes the up-vector act correctly.
    If you're happy with the coordinates being right handed and up vector being inverted, you can just ditch the negative values. I just like my up-vector to function as expected.

    I found this out by investigating the compatibilty script for d3d_set_projection_ext, which does function as I expected:
    /// @description d3d - set projection
    /// @param xFrom    x of from position
    /// @param yFrom    y of from position
    /// @param zFrom    z of from position
    /// @param xTo        x of to position
    /// @param yTo        y of to position
    /// @param zTo        z of to position
    /// @param xUp        x of up vector
    /// @param yUp        y of up vector
    /// @param zUp        z of up vector
    /// @param fov        field of view angle
    /// @param aspect    aspect ration
    /// @param zmin        z buffer min
    /// @param zmax        z buffer max
    var mV = matrix_build_lookat( argument0, argument1, argument2,
                                argument3, argument4, argument5,
                                argument6, argument7, argument8 );
    //This is the important line!! - notice that it uses negative values for the FOV and aspect
    var mP = matrix_build_projection_perspective_fov( -argument9, -argument10, argument11, argument12 );
    camera_set_view_mat( camera_get_active(), mV );
    camera_set_proj_mat( camera_get_active(), mP );
    camera_apply( camera_get_active() );
    Hopefully this kinda answers your question? I know what I'm trying to say, but I think I'm having trouble actually explaining it.

    I do wonder though if the inverted up-vector is a bug and should be reported. I may give it a submit. Worst case, they say it's intended and functioning properly.
    slojanko and Rukola like this.
  14. Rukola

    Rukola Member

    Jun 20, 2016
    Cool tutorial!

    When I found out that turning around the z axis flips the screen I went searching through 3d tutorials and found one where the math also rotates the x,y, and z-up. This prevents the camera from flipping.

    Check it out!

    I'm trying to create the same rotation Lemth does with awesome looking cube, but instead rotate the camera and not the cube. Any suggestions?

    Last edited: Dec 4, 2017
  15. NeZvers

    NeZvers Member

    Mar 24, 2018
    Am I only one who has a problem with inverted controls? I have 2d car object that rotated around its origin point, but with this camera, it's turning to opposite side. It looks like view is inverted vertically.
    Also whole picture is waving like water.
    mLookat = matrix_build_lookat(xx,yy,zz, xx+0,yy,0, 0, 1, 0);

    Attached Files:

    Last edited: May 16, 2018
  16. slojanko

    slojanko Member

    Jun 20, 2016
    I've got a bit of a problem with transparency. I've talked to MishMash he suggested writing my own shader for dealing with these stuff but I would like to ask if there's a direct way of doing it, maybe I'm not using some gpu_set functions properly?

    There's currently 2 issues: overlapping triangles in the torus not stacking properly and the terrain behind the cube not rendering at all but just showing the transparent water behind it.
    I use the following 3 setters:
    while the models have the alpha attribute set to 0.5 in the vertex buffer.
    Last edited: Jun 7, 2018
  17. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    I'm gonna preface this rather bluntly - transparency in 3D is a pain in the butt. There's not really one fits-all solution, but I can suggest something to help out.

    Using your own shader *might* help, but it's really not necessary and is likely rather complicated (I imagine you'd have to essentially implement Order-Independent-Transparency, which is unlikely to be a simple process - especially in GameMaker).

    Basically, this all comes down to render order and z-write.

    In general, you want to render opaque objects before anything else - and preferably things closer to the camera before things further away (so hidden pixels are skipped - this is not super important when your starting out or if you just target desktop platforms, but if you have a particularly complex fragment shader, it can be a good optimisation)

    Then, you want to render transparent stuff after. This will stop the transparent stuff from hiding the terrain.

    In this situation, for example:
    Draw The Terrain
    Draw The Water
    Draw The Cube
    Draw the knot
    For best results, you want to render transparent stuff far from the camera first and stuff closer last. It's probably also a good idea to keep the number of transparent things as low as possible, with fairly simple geometry and keep them fairly spread out to reduce weird artifacts.

    The next problem is the knot drawing over itself

    The best thing to do to fix this is to disable z-write when drawing transparent objects, while leaving z-test enabled. This will keep things that are behind other things hidden, while still giving a nice colour blend.

    Do this with gpu_set_zwriteenable(false);

    For this situation:
    Enable z-write
    Draw The Terrain
    Disable z-write
    Draw The Water
    Draw The Cube
    Draw the knot
    From the looks of it, this is kinda how Unity sorts stuff as well - it has 3 main passes, it seems: Solid Geometry then AlphaTest and finally Transparent, based on a little shader test I did recently.
    This can have weird results when using meshes that mix opaque and transparent colours, so it's probably a good idea to break stuff up as necessary.
    If you draw opaque stuff after the transparent stuff in the same place, it can look weird, given the transparent stuff doesn't write it's depth, so it compares with the depth of the opaque stuff drawn before, when z-write was active.

    Proof of Implementation:

    For a demo (All in GameMaker, so I can be sure it works, but the process is fundementally the same for any engine), here's a Mario with 30% transparency and no culling drawn before his environment:
    As you can see, he blocks out the terrain (much like you see in your scene), blending only with the background colour. This is rather undesirable

    Here he is drawn after the environment:
    That's better as he's actually blending with the world he's in, but he's still "erasing" bits of himself and looking weird. Let's disable z-write with gpu_set_zwriteenable(false):
    Ah, much better. Now he looks like a real ghost!

    Whether you want backface culling or not is purely situational - it depends on the look you are trying to achieve. Here's that same Mario but with backface culling on (Generally less visible, and you can't see the dungarees straps on his back among other things):

    Hopefully this helps out a bit. I think I've covered the important stuff, but if I've been unclear about anything or you need me to elaborate on something, do let me know and I'll try to do a better job!

    PS: As another little tip, I've been using gpu_push/pop_state() a lot recently - it's incredibly useful for switching back to a standard state after some specific drawing.
    Last edited: Jun 6, 2018
    DukeSoft, slojanko and xygthop3 like this.
  18. slojanko

    slojanko Member

    Jun 20, 2016
    There's still a lot about 3D in GMS2 that I apparently haven't touched yet, Thank you @MaddeMichael !
  19. The-any-Key

    The-any-Key Member

    Feb 2, 2017
    Speaking like a true programmer. :thumbsup:
  20. Zhanghua

    Zhanghua Member

    Aug 3, 2017
    Could you teach me what kind of 3D file format you export from the Blender?
  21. MaddeMichael

    MaddeMichael Member

    Jun 20, 2016
    I can't speak for HammerOn, but I did write this nifty export script: https://marketplace.yoyogames.com/assets/5839/export-3d-blendertogm
    It also includes a couple of GML scripts to make loading a bit simpler too.

    It might help out.
    Zhanghua likes this.
  22. FocusedBit

    FocusedBit Member

    Oct 15, 2016
  23. Gabriel

    Gabriel Member

    Jun 20, 2016
    I followed this tutorial once ago to build a 3D Dungeon Crawler (just for fun) in GMS 1.4:

    Of course with GMS2 it looks a mess if I import it.
    But I've been trying to adapt all the obsolete functions so it works in the new version of the engine as it did in the old one. Thanks to this topic I managed to set the camera projection properly (I used my old settings, though - the same ones referred in the video tutorial).

    The one thing I'm having trouble with is drawing the walls (as blocks).
    If I keep the old d3d_draw_block function (which GMS2 automatically adapted into a compatibility script on import) the walls are built, but as the player (aka the camera) walks around, the view gets through the walls.

    I'm not sure how to fix this. Any idea?

    I don't want to make this into a profissional game. As I said before, I'm just messing around for fun, but I would like to make it work as nice as it did before.
  24. Posho

    Posho Member

    Jul 23, 2016
    Bumping for this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice