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

mMcFab

Member
I will leave a (dd/mm/yy) formatted date here, so you know when it was last updated: 16/04/18 - as of IDE: 2.1.4.288, RUNTIME: 2.1.4.203 - Added a tiny tidbit about layer_force_draw_depth() ti setup section

I'll also update the guide if there a better methods that I haven't thought of/found out about yet.

I AM NO LONGER UPDATING THIS PAGE. FURTHER UPDATES OF THIS GUIDE CAN BE FOUND ON MY WEBSITE

Updated Guide Here (Includes a bit of info about transparency and render order): https://maddestudiosgames.com/guide-getting-started-with-3d-in-gms2-project-download/

The website features full syntax highlighting and documentation links too, to make guide-writing much easier.

I have received/seen a few questions regarding getting 3D working and I want to get involved in the forum now that GMS2 is coming, so here's a kickstarting guide.
I have posted this guide in the new GMS2 discussion board, as it would not really be appropriate to post it in another forum yet.
When a dedicated forum for GMS2 programming+guides is created, I will request this guide be moved there.

I recommend that you get @YellowAfterlife's plugin for forum code syntax highlighting. It makes reading code here way easier: https://yellowafterlife.itch.io/syntax-highlighting-for-gamemaker-forum

GM Version: IDE: 2.1.4.288, RUNTIME: 2.1.4.203
Target Platform
: ALL
Download
: Built Project (A zip, so free users can have a look!)
Links: N/A

Summary:

So, you want to get started in 3D with GMS2? Well, here's a few things you should know first:
  • I will be referring to "GameMaker Studio 2" as "GMS2" throughout the guide, because I am too lazy to keep writing it in full.
  • This guide is written with beginners to 3D in mind. If I'm explaining something and you think you already know all about it, just skip it. Alternatively, if something I've written is confusing, don't be afraid to ask and I'll try to be more clear!
  • I will be referencing matrix/matrices a lot. They are necessary to do almost anything in 3D, but you don't necessarily have to know how they work. A lot of matrices are autogenerated and assigned in the code without ever needing to know how they work. However, knowing how they work can help a lot.
  • GMS2, much like GM:S 1.X, is NOT a 3D engine. It is primarily designed for 2D, so 3D games require a LOT of manual work to set up.
  • You will need to know how to use GML. I don't know if any D&D functions have been added for 3D (I don't believe so anyway), but this guide will be GML based anyway.
  • This guide may be easier if you ever used the "d3d_*" functions, however, they no longer exist. This guide is written with beginners of 3D in mind anyway.
  • You can no longer import models using "d3d_model_load" (well, you CAN, but that uses compatibility scripts and is not recommended), so it's a good idea to write your own model importer/exporter. I̶ ̶m̶a̶y̶ ̶w̶r̶i̶t̶e̶ ̶a̶n̶ ̶i̶m̶p̶o̶r̶t̶e̶r̶ ̶i̶n̶ ̶f̶u̶t̶u̶r̶e̶ ̶s̶o̶ ̶p̶e̶o̶p̶l̶e̶ ̶c̶a̶n̶ ̶c̶o̶n̶t̶i̶n̶u̶e̶ ̶u̶s̶i̶n̶g̶ ̶"̶.̶d̶3̶d̶"̶/̶"̶.̶g̶m̶m̶o̶d̶"̶ ̶f̶i̶l̶e̶s̶ ̶i̶n̶ ̶f̶u̶t̶u̶r̶e̶.̶ UPDATE: I've written an exporter for Blender that creates a buffer file that can be loaded with buffer_load - check the extension here: https://marketplace.yoyogames.com/assets/5839/export-3d-blendertogm
  • The new tiles do work in a 3D environment, and their z-coordinate is directly taken from layer depth.
  • I'll not be covering HUD set up, as that works the same as it has since 1.X (draw GUI), and there are already guides and help available.
  • Remember to use the manual! It answers a LOT of the questions you may have, and can give you more information about a function.
  • If the manual doesn't help you, you can ask a question here, and I will try to help!

Now that that's out of the way, here's a list of what I'll be covering:
  1. Setting up 3D requirements and a 3D projection with the new camera
  2. Using a vertex buffer to build a model and render it
  3. Transforming models with the new matrix functionality
  4. Other useful d3d_* functions that have been replaced (texture repeat, texture interpolation, backface culling, etc.)
  5. Other odds-n-ends I think I should consider (batching, layer depth)

I will also cover some basic shader use in future (Both GLSL ES and HLSL), but only when the full release comes out, as we currently have no access to shaders. I will also cover using lighting, but they are more useful when combined with shaders so I may leave lighting until the full release too.
UPDATE ON THIS^^: I don't think I'll actually cover any shader stuff in this specific tutorial - it's a large topic that applies to both 2D and 3D, with loads of guides out already. I may make a more 3D-focused guide, or I may just release some extensions with 3D shaders, with a bunch of commented code to help you get the idea of what's going on. Lighting is an intersing one though. Hmm.

If you go through the guide from start to finish, this is pretty much what you'll see:

Not super impressive, but it covers the basics.

Relevant Documentation Links:

So, lets get started then!

Tutorial:

1) Setting up 3D requirements and a 3D projection with the new camera
To begin with, the following code will all go in the create event of some kind of control object, unless I specify otherwise.

Okay, before we even get started, we need to enable some things.
In 3D, we have something called the "z-buffer" - this basically enables the sorting of draw order, so things that are further away appear to be behind things that are closer (Note that this behaviour CAN be changed in GMS2, . If we don't enable the z-buffer, then rendering will look very odd, as things far away may draw on top of things that are closer (even within a single model!)
To enable the z-buffer, we just use this code:
Code:
gpu_set_zwriteenable(true);//Enables writing to the z-buffer
gpu_set_ztestenable(true);//Enables the depth testing, so far away things are drawn beind closer things
You may also find it useful to force layers to draw at a depth of 0 (this will stop layers translating the z-coordinate of what you draw on any layer - though you may find the default behaviour preferable in some cases)
Code:
layer_force_draw_depth(true, 0);
(documentation for layer_force_draw_depth)

Believe it or not, that's all the required set up needed to get rendering in 3D!

Now we can move onto the next step - setting up and using a camera in a 3D environment.
Since the GMS2 update, views have been totally replaced with cameras, but so has the d3d projection functions - they are now the same thing, which is far more convenient when you get used to them.
The first thing we'll want to do is create a camera, assign it to a view, as well as enable views and make yours visible. You can still create views in the room editor, but sometimes it's nice to have all the code in front of you.
Technically speaking, you don't even need a view to set up a 3D projection, however, it makes managing the camera much easier.
Note that, as previously, the game window size will default to the view port/room size of the first room. For this reason, you should have a "set-up room", to get things ready and set the size to be appropriate. Otherwise, you could use the window_* functions combined with view_set_Xport to get the size you want.

Anyway, the code!
First, getting our view to actually appear (you don't need to do this if you have set up a view in the room editor)
Code:
//First, we need to enable views and make our view visible (in this case, view 0)
view_enabled = true;//Enable the use of views
view_set_visible(0, true);//Make this view visible
Next, we need to create a camera, and make it fully functional. In order to do this, we must create the camera, assign a projection matrix to it and bind it to the camera. We will keep the camera variable, though we will be using "view_camera[0]" to reference it later. We keep the camera variable in case we want to bind the camera to another view later, perhaps in another room, without re-creating it.
Code:
//First, create the camera. We could use camera_create_view, but that is more useful in a 2D environment
camera = camera_create();

//Then, we need to build a projection matrix. I keep this in instance scope in case I need to reassign it later. (Though you can retrieve matrices from a camera with camera_get functions
//I use matrix_build_projection_perspective_fov, as it gives the most control over how your projections looks.
//Here's how I use the arguments: I give a 60 degree vertical field of view, with a ratio of view_wport/view_hport, with a 32 unit near clipping plane, and a 32000 far clipping plane. Some of these values may need tweaking to your liking.
projMat = matrix_build_projection_perspective_fov(-60, -view_get_wport(0)/view_get_hport(0), 32, 32000);

//Now we assign the projection matrix to the camera
camera_set_proj_mat(camera, projMat);

//Finally, we bind the camera to the view
view_set_camera(0, camera);
NOTE: In GMS2, setting the projection differs from 1.4 - by default, it now does a weird conversion to right-handed coordinate space to stay consistent with 2D, but this makes the up-vector act weird. To make the projection act like 1.4 and before and use left-handed space and make the up-vector act as expected, you must make the fov and ratio values negative, hence the code above.
You may notice that we have assigned a projection matrix, but didn't actually point a camera at anything. This happens later, and separately - this is one of the benefits of the new camera system - we only have to build the projection once, hence saving a lot of time compared to calling d3d_set_projection_ext every draw frame, as half the math is already done.
If you want your camera to have an orthographic projection (instead of perspective), replace the matrix build with this function:
Code:
matrix_build_projection_ortho(view width, view height, znear, zfar);
There is one more thing you need to do to get your camera ready! Note that we still haven't assigned a lookat matrix to the camera! This is used to actually point a camera from a location, to a location.
Now, you could update this matrix in step or draw, but cameras come with a new, cool function - "camera_set_update_script()" (there are begin_script and end_script functions too! Check the manual to see more about them!).
Basically, this script is called every draw update per view, assuming the camera is bound to the view. This means you can enable frame skipping, or disable a view, and a camera won't waste time updating what it doesn't need to!
So, to use a camera update script, you must create a script. Ideally, the contents of these scripts should be able to be self contained, and the only external references should be globals or instance variables that you know exist, otherwise the game will probably crash.
For this demo, I just created a camera that would spin around the center of the room, based on time. The script is named "camera_update_script", and contains the code:
Code:
//Set up camera location
var zz = -640;
var xx = lengthdir_x(720,-current_time/10) + (room_width*0.5);//Rotation is negative now to match with the old gif and spin clockwise
var yy = lengthdir_y(720,-current_time/10) + (room_height*0.5);

//Build a matrix that looks from the camera location above, to the room center. The up vector points to -z
mLookat = matrix_build_lookat(xx,yy,zz, (room_width*0.5),(room_height*0.5),0, 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);
Note that we now reference view_camera[0] now. I have done this because it is the only proper way to guarantee that we are targeting the camera that belongs to view0.
As another point of interest, you could set up a test so that the camera only updates the lookat matrix if it has moved or changed orientation, which can save even more processing time.

Okay, the last thing you need to do is assign the update script to the camera. Back in the create event (where your other code should be), just add this:
Code:
//Assigns the update script named "camera_update_script" to the camera belonging to view0
camera_set_update_script(view_camera[0], camera_update_script);
An that is it! If you set up this camera in a simple, tiled room, it should look something like this:

Aiming and moving your camera around via user input is a little more work and more project-specific (e.g. 3rd person will have different controls to first person, and a flight sim would be different again), but as long as you have a sound understanding of algebra and trigonometry, it should be pretty straightforward.

2) Using a vertex buffer to build a model and render it
Okay, compared to the camera, this is probably harder to understand. However! Don't be put off! I kept putting off learning buffer related things for years, simply because of the word "buffer" - it sounded hard and scary, but they're pretty simply - especially in the context of vertex buffers, where a lot of work is done for you when it comes to reading the buffer.
Again, we'll define most of this stuff in a create event, unless otherwise specified.

So, the vertex buffer is used to build models - or maybe more appropriately - "vertex layouts" - they work in both 2D and 3D, and allow you to draw things with stuff like custom attributes, or with the bare minimum data needed to render, to minimise overhead. When used in conjunction with shaders, they are super powerful.

A vertex buffer is effectively comprised of 2 parts:
- the format, which defines what information the buffer contains
- the buffer, which actually contains the information passed to a shader for rendering

The first thing you'll want to do is build a vertex format. The format contains information about what every single vertex in the buffer will contain. The one we will create will contain a 3D position, a color and a texture coordinate.
It is important to remember the order that you define information in a vertex format, as data added to the buffer must be in the same order. Each defined vertex must also contain all the information, even if you feel a specific vertex does not need part of it.
NOTE: The format described below represents what we lovingly call the "full-fat format" - it has all the elements required to use GameMakers default shader - if you miss any of these elements, drawing will error with an "invalid input layout". If you wish to use fewer components, you need to write a shader. This can have a few benefits such as slightly lower memory usage and lower overhead when the buffer is sent to the shader.
The code:
Code:
//Begin defining a format
vertex_format_begin();

vertex_format_add_position_3d();//Add 3D position info
vertex_format_add_color();//Add color info
vertex_format_add_textcoord();//Texture coordinate info

//End building the format, and assign the format to the variable "format"
format = vertex_format_end();
Now we have a format, we can create and build a vertex buffer. In this case, I'll be building a simple, white plane (the flat type, not the flying one), built with triangle list usage in mind (to see more information on this, check the manual for "draw_primitive_begin". You can build far more complex models, but that would be a little time consuming for this tutorial.
To build the plane, we effectively need 2 triangles. They are defined as follows:
Code:
//Create the vertex buffer. Another function, vetex_create_buffer_ext can be used to create the buffer with its size predefined and fixed.
//With the standard vertex_create_buffer, the buffer will just grow automatically as needed.
vb_plane = vertex_create_buffer();

//Begin building the buffer using the format defined previously
vertex_begin(vb_plane, format);

//Using size to keep it square if we decide to change how bug it is.
var size = 32;

//Add the six vertices needed to draw a simple square plane.

//The first triangle
vertex_position_3d(vb_plane, -size, -size, 0);
vertex_color(vb_plane, c_white, 1);
vertex_texcoord(vb_plane, 0, 0);

vertex_position_3d(vb_plane, size, -size, 0);
vertex_color(vb_plane, c_white, 1);
vertex_texcoord(vb_plane, 1, 0);

vertex_position_3d(vb_plane, -size, size, 0);
vertex_color(vb_plane, c_white, 1);
vertex_texcoord(vb_plane, 0, 1);

//The second triangle. The winding order has been maintained so drawing is consistent if culling is enabled.
vertex_position_3d(vb_plane, -size, size, 0);
vertex_color(vb_plane, c_white, 1);
vertex_texcoord(vb_plane, 0, 1);

vertex_position_3d(vb_plane, size, -size, 0);
vertex_color(vb_plane, c_white, 1);
vertex_texcoord(vb_plane, 1, 0);

vertex_position_3d(vb_plane, size, size, 0);
vertex_color(vb_plane, c_white, 1);
vertex_texcoord(vb_plane, 1, 1);


//Finish building the buffer.
vertex_end(vb_plane);
All that's left is to render the vertex buffer - really that is it! I even set up the plane to take texture coordinates properly, so it can have a texture, which is good for floors or ceilings.
So, let's see the monster code that is necessary to render this buffer. This goes in the draw event:
Code:
//Arguments are just vertex_buffer, primitive type and texture.
vertex_submit(vb_plane, pr_trianglelist, -1);
And that's how to build a basic vertex buffer and render it. Hopefully, you'll be able to use this info to build buffers that are a little more complicated than a flat plane, which has little use anyway since the new tiles work in 3D.

One more thing to note is that you'll want to delete your vertex buffers and formats when you're done with them to free up memory.
Vertex buffers are deleted with "vertex_delete_buffer(buffer)", and formats are deleted with "vertex_format_delete(format)". Note that you can only delete a format when all buffers that use it are deleted, otherwise the runner will crash. There is also the function "vertex_freeze(buffer)" - this will disable editing/updating the buffer, but provides a performance increase.

So, now you know how to create a vertex buffer, but it can be a painfully boring process. It's good to understand what they are and how they work (and really useful for creating dynamic meshes), but there's a much easier way to create static meshes - Blender! And I made an export for Blender that creates models that can be loaded with buffer_load or my extension for GameMaker!
The extension includes a simple model_load("") script and vertex format building script - the models can then be rendered with vertex_submit.
(I've also got a Blender export/import working for rigged/animated models, but it needs so much documentation still... But it's coming!)

If you want to move the model around, check out the next part: "Transforming models with the new matrix functionality"

3) Transforming models with the new matrix functionality
Ok, at this point, you have a little permission to shout. If you'd just done the guide on vertex buffers, you could be shouting "Yeah, okay, but how do I move the model with building it over and over again at different positions! This is DUMB!!!"
Luckily for you, there is a solution!
Using matrices, you can transform where is drawn. In a most basic sense, you can transform the translation, rotation and scaling of how something is drawn. If you understand slightly more complex matrix usage, you can even do things like shearing a drawing. Don't let this put you off though! You don't *need* to understand matrices to use matrix_build, but it does help!
So, in order to transform your drawing, you use the function "matrix_build". This takes in rotation, translation and scaling as arguments, and builds a matrix. The order of transforms is YXZ, built to render in the order rotate->scale->translate. Note that this can have unwanted results when scaling each axis non-uniformly, as the scaling happens after rotation, unlike when drawing sprites which scales and then rotates. To work around this, you must build at least 2 matrices and multiply them (or use the new matrix stack). NOTE: I did request that order be configurable/changed, but it isn't planned.
This gif shows how the result may be unexpected (matrix on the left, sprite_ext on the right, same transform input)
Anyway, onto building the matrix. For this example, I will use a matrix to move the drawing to the center of the room, and rotate it by 45 degrees about the Z-axis. We must then assign this matrix to the world matrix. This is best done in the draw event, right before you need it.
Code:
var mat = matrix_build(room_width * 0.5, room_height * 0.5, 0, 0, 0, 45, 1, 1, 1);
//The world matrix is what is used to transform drawing within "world" or "object" space.
matrix_set(matrix_world, mat);
You can then render what you want, or submit your vertex buffers. It works on both.

After drawing your transformed output, you should reset the transform matrix to the identity, so drawing returns to normal.
Code:
//Resetting transforms can be done like this:
matrix_set(matrix_world, matrix_build_identity());
If we use the vertex format from the previous tutorial, we'll have code like this:
Code:
var mat = matrix_build(room_width * 0.5, room_height * 0.5, 0, 0, 0, 45, 1, 1, 1);
//The world matrix is what is used to transform drawing within "world" or "object" space.
matrix_set(matrix_world, mat);

//Draw the buffer
vertex_submit(vb_plane, pr_trianglelist, -1);

//Resetting transforms can be done like this:
matrix_set(matrix_world, matrix_build_identity());
And that concludes the basics of transforming in 3D. These methods also work in 2D, as this is an exact replacement of the d3d_transform functions.
The above performs the same way as d3d_transform_set_*
To replace d3d_transform_add_*, you can multiply matrices together, using matrix_multiply. This is used like this:
Code:
newMatrix = matrix_multiply(currentTransformMatrix, addedTransformMatrix);
In future, I will add info about the matrix_stack. From what I can tell, it works roughly the same way as the d3d_transform stack, but I am not yet confident enough to write a guide about it. It seems to be a handy way of sotring matrices globally without the use of vars.

4) Other useful d3d_* functions that have been replaced (texture repeat, texture interpolation etc.)
Well, this is more like a list, but I'll go through them anyway. This is more useful to those used to d3d_* functions, but I'll have a breif explanation on each on wwhat they do. It's not a definitive list, but it contains the functions I used the most.

One of the most useful functions is culling, which boosts performance by not rendering both sides of a "triangle". This used to be "d3d_set_culling()". The new equivalent is "gpu_set_cullmode()".
The direct equivalent is using "gpu_set_cullmode(cull_counterclockwise);", however we can now reverse the cull order by using "gpu_set_cullmode(cull_clockwise)" instead - this is good for shader makers in particular.
To disable culling, just use "gpu_set_cullmode(cull_noculling)"
Note that depending on how you build your projection matrix, you may need to reverse the cull order

"gpu_set_texfilter" is used for enabling linear interpolation. An _ext version exists for setting specific texture stages.
"gpu_set_texrepeat" is used to enable texture repeating. An _ext version exists for setting specific texture stages.

All the blendmode functions have been replaced with "gpu_get/set_blendmode" functions. Look em' up! They are far more flexible now. You can also enable/disable blending with "gpu_set_blendenable".
Fog and alpha tests are now "gpu_*" functions.

d3d_lights still exist! They are just draw_lights instead.

d3d_set_shading is gone! So you no longer have a choice between automatic flat or gouraud shading - the alternative is to write a shader and make sure your normals are correct for the type of shading you want. Luckily, my Blender export handles both flat and smooth normals on export.

"d3d_set_perspective()" is gone too (the one that flips the y axis in 3D mode), but it can be replicated. Noting the above cameras, we used negative fov and aspect values in order to use left-handed space.
e.g, go from this:
Code:
matrix_build_projection_perspective_fov(-60, -view_get_wport(0)/view_get_hport(0), 32, 32000);
to this:
Code:
matrix_build_projection_perspective_fov(60, view_get_wport(0)/view_get_hport(0), 32, 32000);

5) Other odds-n-ends I think I should consider
Layer depth - this is a thing that can and will affect any 3D rendering! Layer depth of the instance that is drawing is added on relative to the current world matrix! Keep this in mind if something seems to have its Z offset weirdly.

Vertex batches - the more of these, the slower your game can run as more info has to be sent to the graphics pipeline. This is more of a concern on mobile targets as most modern PCs can handle a lot, but optimisation is still important and good.
When using normal rendering (e.g., draw_sprite), GameMaker automatically handles batches for you, but there are a few things that can break a batch (into more parts - don't worry, your game won't be broken):
  • Texture Swaps - this makes it important to organise texture pages to minimise the amount of swaps (e.g, keep GUI on one texture page and render it in one group)
  • Shader changes - whether you are changing the shader target or just updating a uniform, the batch will be broken
  • matrix_set() - when on of the main render matrices is set, internal matrices need updating and shader uniforms for gm_Matrices are updated. Hence, the batch is broken.
  • There are probably more, but I'm not an expert at what makes GameMaker batches break, so I've just listed the ones I'm confident about - if you have a correction or know of something else that breaks the batch, I'll list it here with a " - thanks, YOURNAME!".
Okay, so that stuff breaks GameMakers automatic batching, but here's a separate issue - when using vertex buffers, you're all on your own - no automatic batching - each buffer is a batch, so you need to batch models by yourself.

This is pretty challenging for a couple of reasons.
  • Sprites are on texture pages. Texture Page swaps break a batch, and there's no way to tell what texture page a sprite is on in GameMaker. That really sucks. The only current workaround I know of is to make your own texture atlas and manage sprite rendering yourself.
  • Packing info into one buffer is annoying - vertex_begin clears the vertex buffer first, and there is no "vertex_append" equivalent. In order to do this, you've got to manage all vertex information in a regular buffer (which is fairly easy if you know how vertex buffers are structured) and then convert it to a vbuffer.
  • In order to not break a batch and still be able to transform vertices, you've got to change them CPU side with matrix_transform_vertex(). This can get pretty slow with a lot of vertices, particularly in GML with a lot of array access calls to get the new location, then lots of buffer writes. This can be partially solved with an extension that directly accesses the buffer, but that's far from ideal. If you're only transforming the location of vertices or doing basic scaling, it can be faster though, as you don't need matrices.
If you keep things simple, it can be doable, but it's rather tricky to get right at the moment.
Currently, I've been just using separate buffers anyway. As long as you freeze them, they're usually still really fast.
I've rendered 2000 separate texured cubes, 25 terrain tiles each consisting of 32,768 faces, and 3 rigged, animated and textured characters each with about 9 meshes while staying above 90 fps_real on my low-mid range PC. Performance could definitely be better (and it will be when I've got basic batching in), but that's just a rough baseline for concern. In my experience, the limit is much harsher on mobile platforms in general.

Okay, I think that covers the basics! I will be adding to this, especially when shaders are public. If you have any questions or corrections, let me know!

I'm also considering doing more tutorials like this - perhaps including "Getting to Know the New Camera" - but I'm not sure how I should do it. I'd like to just post them in the forum, but it takes a lot of my time. Do any of you think a Patreon or a Udemy course would be a good idea? Let me know please.
 
Last edited:
X

Xaby

Guest
Would be nice to see also Vulkan SPIR-V Shader. My hopings for GMS-2 are, that Vulkan is available for export as well.
 

rwkay

GameMaker Staff
GameMaker Dev.
We are looking at supporting Vulkan in the future (for certain platforms), it is unlikely to be an option on the Windows desktop platform - we currently have an experimental renderer running internally but have no plans to release to the public in the near future.

Russell
 
B

BJ_Vynz

Guest
Thank you for creating this! I'm comfortable now changing the code. Still have no idea how I'd get any models rendered, I'll wait and see how that goes :)
 
H

HammerOn

Guest
Thank you. This made it easy to get started with the new camera system.

I tested it with a sculpt I did in Blender.
1.338.288 vertices. No problems.
The artifacts are from the texture itself (baking and uv stretch).

 

kamiyasi

Member
Nice, comprehensive guide. I'm currently working in 3D and am worried that it would be hard to port to GMS2's new systems, but if I can get into the beta I may give it a try.
 

mMcFab

Member
Nice, comprehensive guide. I'm currently working in 3D and am worried that it would be hard to port to GMS2's new systems, but if I can get into the beta I may give it a try.
Trickiness will simply vary with how you've set up your project before. If you're already using vertex buffers, then most of the hard work is already done. Changing how the camera works and updating the other functions is fairly simple in comparison (when you know what function replaces the old one and how)
When GMS2 comes out of beta, it'll generate compatibility scripts for the old functions when a GM:S project is imported, so that's one less worry at least.
 

mMcFab

Member
now we just need a guide to import blender models :D
Ack, I tried that once - as far as I'm aware, you'd need to use a Python script which I don't think GameMaker handles as an extension. I know very little about python, so I don't even know if you can wrap it in a DLL.
I'll also warn you now - don't bother trying to write a COLLADA importer in native GML - it's too slow to be practical.
What I ended up doing to load was writing a DLL wrapper for Assimp, and using that to import models - the format it seems to work best with is FBX (which you can export from Blender), though it does scale them to be 1000x too big in the version I use, so you have to counter that with a matrix. The problem with Assimp is it's compatibilites vary from format to format (it drops bones in a lot of them, loses names in some).
I am considering re-writing the DLL to just use the Maya FBX SDK, but it's methods of accessing bones (or even vertex data in general) are even worse than Assimps, which I already think a pretty bad - e.g. Inverse Bind Matrices are per model, rather than per armature - this can sometimes lead to some messy situations. They're probably okay if you're building a game in c++ and can give the environment a nodal heirarchy and mix matrices whenever you feel like it, but making it play nice (and fast) with GameMaker is a bit of an ass.
 

mMcFab

Member
That one is pretty good, the trouble is is it limits you to static models and is now outdated as it exports in d3d_ functions, which are obselete in GMS2. Ideally, that plugin will need updating for GMS2 before it can be practical.
If you want bones + animation, you have to write your own importer (or ask someone who's already made one if they can share it with the public ;))
 
H

HammerOn

Guest
Ack, I tried that once - as far as I'm aware, you'd need to use a Python script which I don't think GameMaker handles as an extension. I know very little about python, so I don't even know if you can wrap it in a DLL.
I'll also warn you now - don't bother trying to write a COLLADA importer in native GML - it's too slow to be practical.
What I ended up doing to load was writing a DLL wrapper for Assimp, and using that to import models - the format it seems to work best with is FBX (which you can export from Blender), though it does scale them to be 1000x too big in the version I use, so you have to counter that with a matrix. The problem with Assimp is it's compatibilites vary from format to format (it drops bones in a lot of them, loses names in some).
I am considering re-writing the DLL to just use the Maya FBX SDK, but it's methods of accessing bones (or even vertex data in general) are even worse than Assimps, which I already think a pretty bad - e.g. Inverse Bind Matrices are per model, rather than per armature - this can sometimes lead to some messy situations. They're probably okay if you're building a game in c++ and can give the environment a nodal heirarchy and mix matrices whenever you feel like it, but making it play nice (and fast) with GameMaker is a bit of an ass.
You shouldn't be using converters within the game. Formats like COLLADA and FBX are intermediate files. 3D game engines automatic convert them for an custom and trimmed format optimized for the engine. This process is not part of the game itself but the game editor's toolset.
The sculpt I loaded in GMS2, for example, I coded something to create a simple buffer with the vertex data (just a stream of vertices). And then I send to the game project using sockets (secret to workaround included files in the beta ;)). Because the data is trimmed and optimized for my use-case, it takes less than 1 seconds to build a vertex buffer with 1.338.288 vertices (each one has position and uv).
 

mMcFab

Member
You shouldn't be using converters within the game. Formats like COLLADA and FBX are intermediate files. 3D game engines automatic convert them for an custom and trimmed format optimized for the engine. This process is not part of the game itself but the game editor's toolset.
The sculpt I loaded in GMS2, for example, I coded something to create a simple buffer with the vertex data (just a stream of vertices). And then I send to the game project using sockets (secret to workaround included files in the beta ;)). Because the data is trimmed and optimized for my use-case, it takes less than 1 seconds to build a vertex buffer with 1.338.288 vertices (each one has position and uv).
Woah, don't worry about me using converters for a finished game, I'm not that crazy! :p.
I used a wrapper so I could convert+test models (in a separate program) way faster - I do export the buffer when I'm happy with the result so I can load it into a proper game significantly faster without using a DLL (which makes it cross platform, which is a nice bonus)
I trim out unnecessary info during convert, so I don't even have to worry about that
 

makas

Member
I knew that method described in the old forum and it works well with gms1 but I was talking about GMS2, it would be nice we are able to achieve the same for GMS2...

and by the way how hard is to combine a 2d game with 3d elements? an example of this:

Imagine a top down game where most of the moving objects are just simple 2d sprites, but some of the static objects are actually 3d, like buildings and other stuff (basic shapes, like rectangles with textures), just like in the first gta games... it would be hard to achieve this with the new functions? I messed a bit with the code of this project and I could see how the proyections are made, and how the layers actual work in GM, it was interesting, but there were some artifacts with the sprites animations, note that Im a total noob about 3d, I just have made some basic 3d tutorials... that's why Im asking, and Im really interesting in mixing both thing for my current 2d project, any advice and warning would be greatly appreciated...
 

mMcFab

Member
I knew that method described in the old forum and it works well with gms1 but I was talking about GMS2, it would be nice we are able to achieve the same for GMS2...

and by the way how hard is to combine a 2d game with 3d elements? an example of this:

Imagine a top down game where most of the moving objects are just simple 2d sprites, but some of the static objects are actually 3d, like buildings and other stuff (basic shapes, like rectangles with textures), just like in the first gta games... it would be hard to achieve this with the new functions? I messed a bit with the code of this project and I could see how the proyections are made, and how the layers actual work in GM, it was interesting, but there were some artifacts with the sprites animations, note that Im a total noob about 3d, I just have made some basic 3d tutorials... that's why Im asking, and Im really interesting in mixing both thing for my current 2d project, any advice and warning would be greatly appreciated...
Mixing is fine!

As long as you're not using full 3D environments, it's actually easier to mix it - kinda why GameMaker has these functions, even though it's a 2D engine - allows for 2.5D stuff.

You'll need to be aware of depth sorting (pretty much vital for 3D particles) and alpha testing (close transparent things drawn before and on top of opaque things that are further away will "cut out" the opaque thing)

This isn't really definitive, and I'm by no means an expert at general 3D stuff, so there's probably much more stuff to know about. All I can really suggest is to try stuff out!

Regarding the blender stuff, there's a plugin for blender to export to d3d (I know because I have it), as well as a compatibility script in "C:\ProgramData\GameMakerStudio2\Cache\runtimes\runtime-2.0.1.16\lib\" which deals with this (though I think it relies on other compatibilty scripts). I could modify this script to work directly with buffers and that'd pretty much fill the void

EDIT: I've been looking at the compatibility scripts to deal with importing d3d files, and I don't feel like it's worth me a vbuff alternative - too many things rely on other things + mixed primitive types (lists, strips) - it's something that would be a lot of work to do, and may not work as well with models exported from earler GM versions. It would probably work with blender exports, but it just doesn't seem to be worth my time right now.
 
Last edited:

xygthop3

Member
Is there a specific reason your drawing the camera upside down?

Code:
var zz = -640;
mLookat = matrix_build_lookat(xx,yy,zz, (room_width*0.5),(room_height*0.5),0, 0,0,1);
Looking from -640 to 0 tells me that you're looking at your floor vertex buffer from underneath rather then the camera being above the drawn buffer, this is confusing compared to what we're used to with GM 1.x
 

xygthop3

Member
(note: all primitive/model drawing is done via vertex buffers in both GMS 1.x and GMS 2, no d3d_model_* or primitive functions are used at all)

If you have a look at a projection that uses a mouse look in GMS 1.x for example:
Code:
    bearing -= (display_mouse_get_x() -display_get_width()/2)/10;
    pitch += (display_mouse_get_y() -display_get_height()/2)/10;
    pitch = max(min(pitch,88),-88);
 
    ss = sin(degtorad(bearing));
    cc = cos(degtorad(bearing));
   
    vector_x = cc*cos(degtorad(-pitch));
    vector_y = ss*-cos(degtorad(-pitch));
    vector_z = sin(degtorad(-pitch));

    d3d_set_projection_ext(x,y,z,x+vector_x,y+vector_y,z+vector_z,0,0,1,fov,aspect,znear,zfar);
    display_mouse_set(display_get_width()/2,display_get_height()/2);
Models are drawn as expected

and if you copy the same basic code setup for GMS 2 (changed for the new matrix functions for drawing the camera projection) you will see that the projection is inverse(?) compared to GMS 1.x

Code:
bearing -= (display_mouse_get_x() - display_get_width()/2)/10;
pitch += (display_mouse_get_y() - display_get_height()/2)/10;
pitch = max(min(pitch,88),-88);
 
ss = sin(degtorad(bearing));
cc = cos(degtorad(bearing));
    
vector_x = cc*cos(degtorad(-pitch));
vector_y = ss*-cos(degtorad(-pitch));
vector_z = sin(degtorad(-pitch));


viewmat = matrix_build_lookat(x,y,cam_z, x+vector_x,y+vector_y,cam_z+vector_z, 0,0,1);

camera_set_view_mat(_camera, viewmat);

display_mouse_set(display_get_width()/2,display_get_height()/2);
Models are not drawn as expected
 

GMWolf

aka fel666
I think GM users tend to use z for up and down SBD y for into-out of screen because x and y are built in variables.

For instance, if you build a racing game, you could use box 2D for physics and ignore the z axis physics.
 

rwkay

GameMaker Staff
GameMaker Dev.
How are you setting up your projection matrix for this as I think that is incorrect now (or the problem at least) - can you show us how you are doing that...

Russell
 

xygthop3

Member
Heres how:

Code:
gpu_set_zwriteenable(true);
gpu_set_ztestenable(true);
gpu_set_cullmode(cull_clockwise);

_camera = camera_create();
znear = 1;
zfar = 32000;
fov = 60;
aspect = 1366/768;

projmat = matrix_build_projection_perspective_fov(fov, aspect, znear, zfar);

camera_set_proj_mat(_camera, projmat);
view_set_camera(0, _camera);
 

mMcFab

Member
Is there a specific reason your drawing the camera upside down?

Code:
var zz = -640;
mLookat = matrix_build_lookat(xx,yy,zz, (room_width*0.5),(room_height*0.5),0, 0,0,1);
Looking from -640 to 0 tells me that you're looking at your floor vertex buffer from underneath rather then the camera being above the drawn buffer, this is confusing compared to what we're used to with GM 1.x
Woah, big bunch of stuff!
Okay, I set up my camera at -640 because as depth is less, things are drawn on top, which insinuates to me that negative is up - hence why camera is at -640.
However, I did notice that the z-up of the upvector seems to be backwards in GMS2 compared with 1.X, which is why the z-up vector is +1, even though I would expect it to be -1. Not sure why, though. Maybe it's a down vector now? that'd be odd. I don't think my code is otherwise wrong.
 

xygthop3

Member
Nothing wrong with your code, it was just confusing seeing the setup different to what I've seen and expected from using 3D in GMS for many years.
 

mMcFab

Member
Nothing wrong with your code, it was just confusing seeing the setup different to what I've seen and expected from using 3D in GMS for many years.
Yep, it definitely threw me too. In general, it seems to be just wrong - the up-vector most certainly should be negative in this situation. Could be a bug, I don't know how extensively lookat matrices were tested internally, but this certainly seems like a bug worth fixing before we leave beta. Heck, I can update the guide easily post-fix - just add a minus!
Eh, I'll submit a report it and see what happens
 
Y

Yazuka

Guest
When testing out the 3D Perspective FOV Camera this is what I have been using code wise:

Code:
view_enabled                           = true;
view_visible[0]                       = true;
view_xport[0]                           = 0;
view_yport[0]                           = 0;
view_wport[0]                           = 1920;
view_hport[0]                           = 1080;
view_camera[0]                       = camera_create();

cameraXPosition    = 0;
cameraYPosition    = 80;
cameraZDepth    = -75;
targetZDepth        = cameraZDepth + -700;
cameraFOV        = 80;

projectionPerspectiveFOVMatrix    = matrix_build_projection_perspective_fov(cameraFOV, (view_wport[0] / view_hport[0]), 1.0, 10000.0);

camera_set_proj_mat(view_camera[0], projectionPerspectiveFOVMatrix);    
camera_set_update_script(view_camera[0], UpdateCamera);
Then in my UpdateCamera script I do the following for my View Matrix:


Code:
OBJLoader.viewMatrix    = matrix_build_lookat(   OBJLoader.cameraXPosition,    OBJLoader.cameraYPosition,        OBJLoader.cameraZDepth,
                                                                                           OBJLoader.cameraXPosition,    OBJLoader.cameraYPosition,        OBJLoader.targetZDepth,
                                                                                            0,                                                1,                                                     0);

camera_set_view_mat(view_camera[0], OBJLoader.viewMatrix);
camera_apply(view_camera[0]);
Using this code I get the following when I load in a 3D Model I load from an .obj file (Ignore the fact that the textures are wrong)

 

xygthop3

Member
You have Y as the up vector?

Code:
OBJLoader.viewMatrix    = matrix_build_lookat(   OBJLoader.cameraXPosition,    OBJLoader.cameraYPosition,        OBJLoader.cameraZDepth,
                                                                                           OBJLoader.cameraXPosition,    OBJLoader.cameraYPosition,        OBJLoader.targetZDepth,
                                                                                            0,                                                1,                                                     0);
 

JaimitoEs

Member
You have Y as the up vector?

Code:
OBJLoader.viewMatrix    = matrix_build_lookat(   OBJLoader.cameraXPosition,    OBJLoader.cameraYPosition,        OBJLoader.cameraZDepth,
                                                                                           OBJLoader.cameraXPosition,    OBJLoader.cameraYPosition,        OBJLoader.targetZDepth,
                                                                                            0,                                                1,                                                     0);
Yes, i was to doing the same with my current project.
 

xygthop3

Member
When using 3D in GMS 1.x I've only ever used and only ever seen others use the 3D co-ordinate system with Z-up for thier 3D Projections, this made it simple to use the room editor for basic collisions.

Now it seem we will need to change our habits to use Y-up (or a negative Z-up) instead which would mean that pretty much every 3D game that is imported from GMS 1.x to GMS 2 would need to have a major overhaul and if anyone was using the room for sprite collisions checks would need to rewrite there system.....

Am I right or am I completely missing something here????
 

mMcFab

Member
When using 3D in GMS 1.x I've only ever used and only ever seen others use the 3D co-ordinate system with Z-up for thier 3D Projections, this made it simple to use the room editor for basic collisions.

Now it seem we will need to change our habits to use Y-up (or a negative Z-up) instead which would mean that pretty much every 3D game that is imported from GMS 1.x to GMS 2 would need to have a major overhaul and if anyone was using the room for sprite collisions checks would need to rewrite there system.....

Am I right or am I completely missing something here????
I think that anything using z-up is going to have to change, unless it is a bug that gets fixes. I assume some people use y-up for 2.5D sidescrollers? Either way, anyone updating 3D from 1.X is gonna have problems updating

Oh, and I just found something in the compatibility script for d3d_set_projection_ext that pretty much explains everything:
Code:
/// @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 );
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() );
Both perspective (argument9) and aspect ratio (argument10) have been flipped compared to 1.X. This is super evident in the compatibility script, and this is the reason the z-up is wrong (well, z-up isn't wrong, the other stuff is) - but why? Why are they flipped? This just feels bad. When writing my guides, I like to explain why something is the way it is. How can I explain to someone that their aspect and FOV must be negative "because reasons"? It is illogical and unintuative
 
Last edited:

rwkay

GameMaker Staff
GameMaker Dev.
I have to admit in every 3D game I have worked on -Y is always up and we have used an inverted right handed coord system as this mimics the 2D coord system for the screen as you look at it and makes it generally easier to swap between an orthogonal and perspective camera. Now this is all personal preference and generally down to what you are used to and all the maths multiplies out in the end (as long as you are using the camera matrix to get the forward, right and up vectors to generate any movement of that camera in the world) - if you start hard coding assumptions without using the camera matrices then you are going to be in a world of hurt.

I never understood 3DS Max using Z as up as it feels so foreign to me but most other packages (Maya, SoftImage etc all used, or were configured to use) Y as up, we generally used Maya though occasionally SoftImage.

As long as you are using the current camera matrix it to base any 3D movement it does not really matter what handedness the coordinate system is in or what is Up (the camera will tell you).

Russell

P.S. Before YoYo I worked on several high profile 3D games (Medal of Honor, Formula 1 (for Psygnosis), F1 (for EA), Quidditch World Cup and several other Harry Potter games) - so I am talking from real world experience.
 

xygthop3

Member
Okay thanks, I know other engines/CGS software uses Y up, I guess it's just that I'm (and I'm sure many others) are used to setting up the camera/models/buffers/etc all to work with Z as up with GMS (and GM8.x) and have been doing it this particular way for many years and now none of our projects are going to (visually) work correctly without major changes once imported to GMS 2 is a little disheartening.

Not to mention that if any 3D assets on the Marketplace will become instantly unusable (un-sellable even) for the GMS 2 user base without major rewrites.

I agree that the new 3D camera/matrix/etc setup in GMS2 is far superior to what was offered previously.

It's all good, I understand Yoyos stance on "GMS is 2D with 3D elements". It just that the progress made since the addition of shaders and vertex buffers with GMS 1.x and with the amount of advanced GMS users jumping into using 3D and showing significant jumps with what is actually possible with GMS in the past few years for them to go and change all their projects or even just give-up on using 3D in GMS because they either don't like the new setup or they can't be bothered anymore would be a sad thing to happen.

RIP GMS 3D users, I will miss you. :p
 

rwkay

GameMaker Staff
GameMaker Dev.
This is not our intention, in fact quite the opposite we want it to be seamless transition so please file bugs with us on what we have gotten wrong, we do not have many 3D projects to test with here so if we have got the camera setup incorrect then please tell us.

We want to get this right and we are not set in our ways, perhaps just a difference in what we are used to and not necessarily the way that we would have done it, as we go along we may nudge things into better practices, but we definitely don't want to alienate any part of the community.

Our goal with 1.x -> 2.x transition is for it to be as easy as possible so sorry for wrinkles and niggles but we will improve it and get it right.

Please file a proper bug with a project to show how it has changed between 1.x to 2.x and we will fix the 2.x compatibility layer. (this is what Beta is all about)

Russell
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
I think the important thing in this discussion is that with Zup you can easily use the built in GMS collision events and functions... that alone seems to me to be a good reason to maintain the old system (and I suspect it may have been the reason it was designed this way).
 

xygthop3

Member
This is not our intention, in fact quite the opposite we want it to be seamless transition so please file bugs with us on what we have gotten wrong, we do not have many 3D projects to test with here so if we have got the camera setup incorrect then please tell us.
Thanks Russell.

The code in GMS2 is not wrong at all, nor would I treat this as a bug per-say, it's just not what I would expect. The compatibility script has a nice (trick) way of correcting the projection for you when importing from GMS1.x and if you leave your project with the compatibility scripts in place then everything is happy and drawn as expected.

As soon as you step away from compatibility scripts that are forcing the Zup and start using the the new (which is great btw) camera/matrix system, Y becomes the up vector. Projects that were working in GMS2 with compatibility scripts all of a sudden don't have the same camera projection as you would except.

The easy fix is to just mimic the compatibility scripts when building the projection matrix with the -fov and -aspect eg: projmat = matrix_build_projection_perspective_fov(-fov, -aspect, znear, zfar); and everything is drawn as expected again (I may have over-reacted a bit in may last post with having to rebuild entire projects again) which will only require minor changes in existing projects to come into line with GMS2's new features and any new projects can be written to suit the Yup vector if that's expected to be the long term GMS2 standard.

As Nocture mentioned, the Zup has been used (extensively) by GMS 3D users mainly because of easy implementation with collision checks when using sprites in the GM room editor.
 

mMcFab

Member
I have to admit in every 3D game I have worked on -Y is always up and we have used an inverted right handed coord system as this mimics the 2D coord system for the screen as you look at it and makes it generally easier to swap between an orthogonal and perspective camera. Now this is all personal preference and generally down to what you are used to and all the maths multiplies out in the end (as long as you are using the camera matrix to get the forward, right and up vectors to generate any movement of that camera in the world) - if you start hard coding assumptions without using the camera matrices then you are going to be in a world of hurt.

I never understood 3DS Max using Z as up as it feels so foreign to me but most other packages (Maya, SoftImage etc all used, or were configured to use) Y as up, we generally used Maya though occasionally SoftImage.

As long as you are using the current camera matrix it to base any 3D movement it does not really matter what handedness the coordinate system is in or what is Up (the camera will tell you).

Russell

P.S. Before YoYo I worked on several high profile 3D games (Medal of Honor, Formula 1 (for Psygnosis), F1 (for EA), Quidditch World Cup and several other Harry Potter games) - so I am talking from real world experience.
Glad to see your open to stuff!
I tend to use z-up as it just feels more convenient to use with the collisions in a full 360 degree game. I'd probably use y-up in a sidescroller. I think I understand the change too - it flips perspective to use the same coordinate space as 2D - like d3d_set_perspective(false). I suppose the thing I just don't like about it is the fact that no matter what you do, some values will have to be negative compared to what you expect in setup - either the up direction or the perspective/aspect values. I think I'd rather have to set the perspective to negative to slip it, rather than have confusing results.
I'll file it anyway (now that I know what's up), so do with the suggestion as you wish

EDIT: Maybe I should clarify - my argument isn't really about what direction up is, but about the numbers making sense - negative up should point to negative up, not positive. My view perspective should be positive, not negative. I like numbers, especially when they make sense.
 
Last edited:

Micah_DS

Member
I've always gone with Zup because it made more sense to me in when developing in GameMaker.

In explaining, I always thought of it as maintaining whats already set in place (i.e. the strong 2D workflow of GameMaker and built in xy functionality) while just adding and additional 'z' dimension to it - not changing what's there, but instead just extending and expanding upon it. In this way, I feel that I'm still playing to GameMaker's strengths while also getting 3D functionality.

It's maybe an unusual way of doing 3D, but this is actually why I love doing 3D in GameMaker as opposed to using something like Unity or Unreal for 3D. It keeps me in the world I know while also allowing me to enter into the more complicated 3D realm. Besides, the kind of 3D games I make don't need super complicated 3D physics or collisions. Because of this, I'll probably continue to go with Zup. To me, it really works well because of GameMaker being so 2D-focused.
 
T

Toomuchbob

Guest
Can someone give a brief tutorial on applying a texture to a primitive? I've built a plane but I can't seem to get a texture to apply to it. I assume it has something to do with texcoord, but I have no idea how any of this works and there's very little documentation. :/
 

slojanko

Member
Can someone give a brief tutorial on applying a texture to a primitive? I've built a plane but I can't seem to get a texture to apply to it. I assume it has something to do with texcoord, but I have no idea how any of this works and there's very little documentation. :/
I got it working by enabling the Separate texture page on the sprite. If you use the code MaddeMichael used to draw a square, then just replace -1 with the sprite name.
vertex_submit(vb_plane, pr_trianglelist, -1;

Can someone please tell me why setting z to a model to 5 means its drawn below a model that has it set to 0?
 

rwkay

GameMaker Staff
GameMaker Dev.
The Z value is the distance from the viewer (camera) to the object so larger numbers are further away... that is the easy way to think about it

Russell
 
L

Lahssoo

Guest
I should have looked for this thread before trying to figure it out myself.

So, everything is working fine, except when I resize the window, or use display_reset() : the polygon are suddenly drawn backward. Since I'm a noob in both GameMaker and 3D, I can imagine I'm doing something wrong, but I can't figure out what. Any help please?

(if needed, I can produce an example file)
 

mMcFab

Member
I should have looked for this thread before trying to figure it out myself.

So, everything is working fine, except when I resize the window, or use display_reset() : the polygon are suddenly drawn backward. Since I'm a noob in both GameMaker and 3D, I can imagine I'm doing something wrong, but I can't figure out what. Any help please?

(if needed, I can produce an example file)
I can only assume that these events reset the cull order (Perhaps a bug, or maybe just a DX11 quirk - may still be worth reporting).
Only thing I can suggest right now is resetting the cullmode either every step or after either of these events with gpu_set_cullmode
I'd do a little test but my nice computer recently died on me so I'm using a laptop that doesn't actually work with GMS2 very well. I hope this can still help though!
 
L

Lahssoo

Guest
I can only assume that these events reset the cull order (Perhaps a bug, or maybe just a DX11 quirk - may still be worth reporting).
Only thing I can suggest right now is resetting the cullmode either every step or after either of these events with gpu_set_cullmode
I'd do a little test but my nice computer recently died on me so I'm using a laptop that doesn't actually work with GMS2 very well. I hope this can still help though!
Nap, sadly even with setting the cullmode at every step, it doesn't fix it :/ Thanks for trying though.
 
L

Lemth

Guest
Question; I'm trying to make a top-down projection. When I take your example and set:

mLookat = matrix_build_lookat(xx,yy,zz, xx+1,yy,0, 0,0,1);

I get a projection like so:


But if I set it to complete top-down:

mLookat = matrix_build_lookat(xx,yy,zz, xx+0,yy,0, 0,0,1);

Then everything turns black..:


Why does this happen and how can I fix it? now I do workaround by setting "xx+0.0001".
 

mMcFab

Member
Question; I'm trying to make a top-down projection. When I take your example and set:

mLookat = matrix_build_lookat(xx,yy,zz, xx+1,yy,0, 0,0,1);

I get a projection like so:


But if I set it to complete top-down:

mLookat = matrix_build_lookat(xx,yy,zz, xx+0,yy,0, 0,0,1);

Then everything turns black..:


Why does this happen and how can I fix it? now I do workaround by setting "xx+0.0001".
If I remember right, this is probably to do with the up-vector (when it and the projection are parallel, everything turns black - hence why adding a small value to xx fixes it)
Try replacing it with this:
Code:
mLookat = matrix_build_lookat(xx,yy,zz, xx+0,yy,0, 0,-1,0);
This should make the up-vector point towards negative y and let this work (unless GMS2 still does that thing where it "flips" the up-vector) . If it ends up upside down, try making the second-to-last argument positive 1 instead.
I hope this works!
 
L

Lemth

Guest
Cool, that works! already had it at 1, but -1 does the job.

Now I have your moving plane and I also added a cube (used compatibility d3d script), but it's perspective looks backwards. Any idea on how to fix this?
 

mMcFab

Member
Looks like a culling thing!
I believe you have two options when dealing with this:
1) If you are drawing the cube with d3d_draw_block, it usually corrects if you just swap any of the x, y or z coordinates - i.e swap x1 and x2 OR swap y1 and y2 OR swap z1 and z2. If this makes your texture end up the wrong way around, you can usually transforms to rotate the box to the correct angle etc.

2) Alternatively, you could reverse the cull order for drawing the cube (or disable culling altogether with gpu_set_cullmode(cull_noculling);, although this can have a negative impact in games with higher-poly models - depends on the computer though), so if the current cullmode is gpu_set_cullmode(cull_counterclockwise) (which I think is what I had for that guide), you can switch it to gpu_set_cullmode(cull_clockwise) and switch it back after the draw.
While this is certainly easier, I'm pretty sure this updates a GPU state which I believe can get a little slow if done too often - not certain, but pretty sure (I've only een getting into GPU and shader stuff fairly recently).

Hopefully one of those should fix it!
 
Top