GameMaker Need help with 3D Texture Repeat

Ubu

Member
So I've been struggling with this for a few weeks now without getting any closer and I'm hoping that someone with experience in 3d can help me out.

I have created a cube primitive 64x64x64 in size with texture applied. I know how to get the texture to repeat when creating the primitive using gpu_set_texrepeat(true) and then specify a value larger than 1 in the UV coordinates. (Changing the texture coordinates during runtime also works by "recreating" the primitive with new UV coordinates.)

But here is the problem, how to make the texture repeat seperately for each object made from the same primitive?

To clarify, I have a 64x64x64 sized cube-primitive with texture and no texture repeat. I then fill the scene with several of these cubes made from the same primitive, all of which are scaled to a random size (with increments of 64). So far it works fine. Now I would like to change the texture repeat independently for each of these cubes. So if a cube is scaled to 128x128x128 the texture should repeat twice on that cube and if a different cube is scaled to 256x256x256 it should repeat 4 times.

Is this even possible or do I have to make a separate primitive for each cube?

Any help would be greatly appreciated.
 
M

Misu

Guest
Well the only thing i can think of is shaders. Its possible with shaders by changing the texcoord base upon the scale factor. Other than that, i cant think of any other way since youre asking for a different result for each primitive under the same model index.
 
  • Like
Reactions: Ubu

Ubu

Member
Thanks for the quick reply.

I figured I had to delve into the dark alchemy of shaders. Unfortunately, I'm not too experienced in that field. :(

So do I pass a scale parameter to the vertex shader and then pass it onto the fragment shader to calculate the repeat values there? Or will it work just to pass a larger than 1 value to the texcoord in the vertex shader?

I'll fiddle around and see if I can get something to work.
 

Binsk

Member
You should be able to pass your scale value into the shader as a uniform and then have the textcoord multiplied by it. That's about all you have to do.

Note, if you use a shader then you will be bypassing GM's shaders. This means things like lighting, fog, etc. will need to be implemented manually in your shader if you want them.
 
  • Like
Reactions: Ubu

Ubu

Member
Got it working by passing a uniform with the scale value to the shader.

So the next goal will be to make a different repeat for each of the xyz directions. I think I'll be able to figure it out by some tiral and error so I'll consider this solved for now.

Thanks to both of you for pointing me in the right direction. :)
 

Ubu

Member
This turned out to be slightly more complicated than first anticipated.

I can think of 2 solutions to this problem. The first, and perhaps easiest, would be to define each pair of parallell faces as a seperate primitive. I could then pass these primitives to the shader (east/west as one, north/south as one and top/bottom as one), and store them in an array or something to hold the cube object. The downside to this is that it will make keeping track of each cube (movement, rotation, scaling etc.) slightly more cumbersome. And each scene will consist of many cubes, so would tripling the number of primitives be noticeably slower?

The other solution would be to "rip the cube apart" inside the shader. And this is where I'm completely out of my depth. Is there an (easy?) way to get each side from a 3d cube inside the shader? And if so, will this actually be a faster/better solution to the problem?

Any insights or opinions would be greatly appreciated.
 
are your cubes rotated and positioned arbitrarily? Or are their sides aligned to grid cells? The latter case would be much simpler.

With rotated cuboids, if you can figure out a way for each vertex to identify which side it belongs to, you can extract the scales out of the world matrix... the x scale is the length of the first column, the y is the length of the second column, and the z scale is the length of the third column. Of course, somehow a vertex needs to know which side it belongs to. Might be able to do it with the normal vector, but it would probably be easier to use a custom vertex attribute.
 
  • Like
Reactions: Ubu

Ubu

Member
Each cube is alligned to grid cells when placed in the world but will then be manipulated with scaling, quaternion rotation and tweening movement.

if you can figure out a way for each vertex to identify which side it belongs to
See, this is where I'm struggling...
 
Last edited:
Hey, I actually have something that is working for me using the normal vector. works with regular d3d_draw_block:

proof:

https://imgur.com/nSaLTqP

here's the vertex shader:

Code:
void main() {
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
 
    //scales of x y and z axis
    vec3 scales = vec3(
        length(gm_Matrices[MATRIX_WORLD][0].xyz),
        length(gm_Matrices[MATRIX_WORLD][1].xyz),
        length(gm_Matrices[MATRIX_WORLD][2].xyz)
    );
 
    //which axis to use depends on how texture coordinates are layed out
    vec2 UV = vec2(
        mix(scales.x,scales.y,abs(in_Normal.x)),
        mix(scales.z,scales.y,abs(in_Normal.z))
    );

    v_vTexcoord = in_TextureCoord * UV;
}
what is going on here is first, the scale of each axis is extracted from the world matrix.

then you choose which one of those axis scales to use depending on the normal vector. This will also depend on how your texture coordinates are set up. The computation used here should work with the regular d3d_draw_block call, on GMS1.4 1772,

Then the texture coordinate is just multiplied by the UV scale.

The way the UV computation was determined was with this table:

Code:
normal UV
1 0 0  yz
0 1 0  xz
0 0 1  xy
SIDE NOTE: and heads up. I just discovered matrix_build is bugged in gms1.4 1772. So if you happen to be using that, be aware, it will not produce correct matrices at all times. It sometimes introduces a skew by mistake.

EDIT: missed the part where you are rotating using quaternions. how are you scaling the cubes?
 
Last edited:
  • Like
Reactions: Ubu

Ubu

Member
Ah, thank you very much, I really appreciate your help. I'll study your code and try to implement it in my project and report back when I (hopefully) get it working.

works with regular d3d_draw_block
Btw, I'm using GMS2 so I'm using vertex buffers to build my models. I assume that won't cause any problems.

EDIT: missed the part where you are rotating using quaternions. how are you scaling the cubes?
For now, I'm just using the scale arguments in the matrix_build function.
 
Last edited:
it should totally work as long as the texture coordinates are aligned in the same way. But I dunno exactly what you are doing with quaternions, i know for sure it will work with a matrix world transformation though.

Are you putting a single block in 1 vertex buffer?
 
  • Like
Reactions: Ubu

Ubu

Member
Each cube is its own instance made from the same primitive.

(Don't know if you saw my edit; "For now, I'm just using the scale arguments in the matrix_build function.")
 

Ubu

Member
This is how I do it (for each instance):
Code:
//Place the model at its origin
matrix_set(matrix_world, matrix_build(-x_offset + world_offset_x, -y_offset + world_offset_y, -z_offset + world_offset_z, 0,0,0, x_scale,y_scale,z_scale));
//Rotate the model
matrix_set( matrix_world, matrix_multiply( matrix_get( matrix_world ), quaternion_matrix( quaternion ) ) );
//Position the model in the world
matrix_set( matrix_world, matrix_multiply( matrix_get( matrix_world ), matrix_build(x + x_offset - world_offset_x, y + y_offset - world_offset_y, z + z_offset - world_offset_z, 0,0,0, 1,1,1)));
 
seems like way more work than necessary.

so you have an initial translation and scale

then you are rotating all that by some amount

and then you add another translation.

is that correct?

although it almost looks like you are trying to cancel out the two translations somehow. not sure what's going on there to be honest. I feel like you could do it all at once with one matrix_build
 
  • Like
Reactions: Ubu

Ubu

Member
The reason why I do it this way is because I'm using both local roataion (where each cube is rotated around their own axis - for this I would only need one call) and world rotation (where a selection of several cubes are rotated relative to the world - this is why I use the offsets as they differ for each cube in world space). So for world rotation I need to bring the selection of the cubes I want to rotate to the origin so the rotation axis is at 0,0,0. Then I rotate all cubes around this axis and then I move the cubes back to their position in world space. (So I guess in some way the two translations cancel each other out).
 
Last edited:
If i understand you, you should be able to do that all with:

a = transformations local to each block
b = transformations local to the chunk

ultimate matrix is b * a

by the way, how many cubes are you trying to draw? Are you setting a matrix at draw time for every single cube? If there are lots of cubes, it is giong to slow way down doing it this way. Not a problem if there aren't lots of cubes though.
 
  • Like
Reactions: Ubu

Ubu

Member
If i understand you, you should be able to do that all with:

a = transformations local to each block
b = transformations local to the chunk

ultimate matrix is b * a
That would probably work. I will have to go through my code to try to fix that bit.

by the way, how many cubes are you trying to draw? Are you setting a matrix at draw time for every single cube? If there are lots of cubes, it is giong to slow way down doing it this way. Not a problem if there aren't lots of cubes though.
The more the better... Yes, I am setting a matrix at draw time for every cube. So what are my options?
 
some guy was making a minecraft clone, so they would be a better person to ask. But pretty sure you will want to combine blocks into buffers. For example, one chunk could be a buffer, containing all of the blocks in that chunk. This will require the chunk to be rewritten whenever it changes. If you can somehow get away with setting a matrix for each block without tanking performance, I guess you can keep using that if you'd like. It should start to noticably affect performance somewhere in the hundreds or low thousands of blocks.
 
  • Like
Reactions: Ubu

Ubu

Member
Thats why I wanted to use scaled blocks to begin with, combining several blocks into one large scaled one with texture repeat. Sort of rendering the scene to reduce the number of blocks. My aim is to use this for different projects, some with relatively few blocks where speed hopefully won't be a problem (I get reasonable good performance with several hundred blocks and lots of lights as well). But I would like to expand on this and make larger projects where I probably will end up using thousands of blocks. The chunk idea sounds like a good one I will have to look further into. Maybe I could place all static blocks in a buffer and keep the moving/animated blocks as instances.

You have provided me with lots of excellent information to further my project and I can't thank you enough for taking your time and for your kind help. I will report back when I've tried implementing your ideas and whether I get it to work or not. For now, I have some work to do...
 
Top