Legacy GM [SOLVED-ISH] Normal Maps With Tiles?

Bastendorf

Member
First things first, I looked all over and found nothing on this topic anywhere, so if there was already a topic like this started, I apologize.

Now, I understand that what I'm asking is quite advanced, and could be out of the scope of GameMaker all together, so this is something I'm already expecting to be unable to pull off, or for there to be no one with any ideas to get this done. Just wanted to put that out there.

On to the question: Is it possible to use normal maps on/with tiles? I spent 4 to 5 days painstakingly making a bunch of normal map tiles for all the tiles in my first room, so I already have the normal maps. Right now, the plan was to use them by having a tileset background of the diffuse map tiles and a tileset background of the normal map tiles, with the corresponding tiles setup to share the exact same coordinates.

Uh... What I mean with my rambling there is, I have two tilesets, they're both identical, but one is normals the other is diffuse, so if you laid them over top of each other, the normals would line up perfectly with the diffuse. But I can't figure out how to use it that way. (It might not even be possible).

I'm already using the Sprite Lamp shaders, but in order for that to work, the sprite with the normal map has to be an object, which means either each tile has to be an object (the problems with this are self evident) or I would have to get an object to add all of the tiles, which means breaking my back to make the script draw the world, and the world isn't going to be a simple, repeating backdrop (think RPG). This sounds nightmare-ish, so I'd rather not have to do that.

What I'm hoping for is a way to do this:
shader_set(SpriteLampShader);

shader_set_uniform_f(u_SpriteAngle, image_angle * pi / 180.0);
shader_set_uniform_f(u_TextureRes, textureWidth, textureHeight);

var lightPosArray;
var lightColourArray;

if(numLights >= 1)
{
lightPosArray[0] = lightSource1.cameraSpacePosX;
lightPosArray[1] = lightSource1.cameraSpacePosY;
lightPosArray[2] = lightSource1.zPosition;
lightColourArray[0] = lightSource1.colourRed;
lightColourArray[1] = lightSource1.colourGreen;
lightColourArray[2] = lightSource1.colourBlue;
}
if(numLights >= 2)
{
lightPosArray[3] = lightSource2.cameraSpacePosX;
lightPosArray[4] = lightSource2.cameraSpacePosY;
lightPosArray[5] = lightSource2.zPosition;
lightColourArray[3] = lightSource2.colourRed;
lightColourArray[4] = lightSource2.colourGreen;
lightColourArray[5] = lightSource2.colourBlue;
}
if(numLights >= 3)
{
lightPosArray[6] = lightSource3.cameraSpacePosX;
lightPosArray[7] = lightSource3.cameraSpacePosY;
lightPosArray[8] = lightSource3.zPosition;
lightColourArray[6] = lightSource3.colourRed;
lightColourArray[7] = lightSource3.colourGreen;
lightColourArray[8] = lightSource3.colourBlue;
}
if(numLights >= 4)
{
lightPosArray[9] = lightSource4.cameraSpacePosX;
lightPosArray[10] = lightSource4.cameraSpacePosY;
lightPosArray[11] = lightSource4.zPosition;
lightColourArray[9] = lightSource4.colourRed;
lightColourArray[10] = lightSource4.colourGreen;
lightColourArray[11] = lightSource4.colourBlue;
}

shader_set_uniform_f_array(u_LightPosArray, lightPosArray);
shader_set_uniform_f_array(u_LightColourArray, lightColourArray);


texture_set_stage(u_NormalDepthMap, normalDepthTexture);
texture_set_stage(u_SpecGlossMap, specGlossMap);
texture_set_stage(u_AOMap, aoMap);
texture_set_stage(u_EmissiveMap, emissiveMap);

shader_set_uniform_f(u_CelLevel, celLevel);
shader_set_uniform_f(u_Shininess, shininess);
shader_set_uniform_f(u_WrapAroundLevel, wrapAroundLevel);
shader_set_uniform_f(u_AOStrength, aoStrength);
shader_set_uniform_f(u_EmissiveStrength, emissiveStrength);
shader_set_uniform_f(u_AmplifyDepth, amplifyDepth);

shader_set_uniform_f(u_UpperAmbientColour,
upperAmbient_Red, upperAmbient_Green, upperAmbient_Blue);
shader_set_uniform_f(u_LowerAmbientColour,
lowerAmbient_Red, lowerAmbient_Green, lowerAmbient_Blue);

shader_set_uniform_f(u_numLights, numLights);

// must check each frame
spritelamp_updatelights();

draw_self();

shader_reset();
Which comes from the Sprite Lamp tutorial, but do it with tiles instead. Which I am unable to do because it requires me to run it from a draw event, and I'm not sure how to do that with tiles placed down in the room editor. I'll be perfectly honest, I'm not much of a coder. I can quite competently work on my own until I run into something advanced, like this. I usually use tutorials to rescue me, but there are very few shader-based lighting tutorials, and fewer that use normal maps.

I'm sure there are other ways, I just spent 2 months setting up my game to rely on normal map shaders only to realize, after doing a bunch of work, that using it with tiles isn't as straight forward, and would hate to have to scrap all that mind-numbing work I did.
But I wouldn't mind having to set the normal maps up a different way if the method I thought I was going to be able to use won't work.

And lastly, yes, normal maps are a must. I don't want to use the fake-looking alpha lighting engine, and although GlareEngine is cool, it doesn't give the same atmosphere that a normal map would have, and that normal map atmosphere is what I've been banking on since the start of the project.
 

Bastendorf

Member
Update: I had an idea, for anyone who was interested. It might be possible to ramp up the size of texture pages to get around GameMaker's large image compression problem, then do the tiles as a solid background, rather than a tiled background, in something like Photoshop or Gimp, and import them into GameMaker as pre-assembled backgrounds, and then have an object in the room handle drawing the backgrounds. That object could, in theory, be used to give the background images their normal map, but that would require two large backgrounds for each room. One diffuse, the other normals. With larger games, that might make the game's file size rather bulky.
It would also mean that the player character and other objects could never be layered under tiles, because all tiles would be 'baked' into a single image. Unless of course those were separate objects that drew a separate foreground tile layer. But that might require a lot of objects to handle, and most of us know what happens when there are too many objects in a room at one time. Doing a background composed of tiles also makes it impossible to use tile_add, tile_delete, tile_exists, etc. Unless a space was left in the background ahead of time to allow for an object to add a tile in during the game, but that would also add more objects for the game to run, and a sheet of tiles for the game to draw from.

However, the object "weight" in any given room could easily be reduced by simply having the objects deactivate when the player is a certain distance away from them, with them off screen, and then reactivate them long before they're ever meant to appear on screen again using instance_deactivate and instance_activate.

I haven't tried this out, yet, so it's all just theory. There are probably better ways of doing it, but I haven't found or come up with any, yet.
 

Bastendorf

Member
Update 2: Yeah, nevermind. That idea was terrible... I was going through the motions of trying it out when the size of the files I would be needing finally dawned on me. I didn't realize just how bulky that method was going to be until I was staring down a 2048x2048 Photoshop document, thinking to myself "This will never be big enough." I'd have to double and even triple up to make the levels large enough, or make the city fairly claustrophobic. And each 2048x2048 background image would have to have an equally large normal map. We're talking probably hundreds of huge images.
I posted over on Reddit to see if I could get more people on board the help train. (Actually got responses over there. =P I tease, I tease.) Unfortunately, no one has been able to help, yet. But while replying to someone, I came up with another potential idea. I might be able to use surfaces to get the tiles into an object like Sprite Lamp's original method of running the code called for. The only two problems I can foresee is 1. I don't know that I have the ability to line up the normals with the corresponding tiles drawn to the surface. 2. I've never used surfaces before, so I'm not sure if having a surface 4096x4096 and larger would cause performance issues or not.

Here's a pastebin of all the relevant scripts just to help make idea-forming easier. http://pastebin.com/xi1KtDCj
 

kburkhart84

Firehammer Games
Hmm, the idea of using "background" images could work fine actually. The trick is to just put a few images together. If one 2048x2048 isn't enough, then make another. You would want to a couple objects for this though, so you could draw it with the spritelamp shader. I'm kind of thinking though, if you are really interested in this higher end graphics style, maybe you are over-worrying about the amount of space the graphics will take. I could be wrong on that though, I don't know your target audience.

I honestly would like to be able to draw the tiles with a shader, and have it be automatic. Another workaround though if they never implement this features could be to access the tile array via code, and store all of those tiles in your own array. Then, you delete all the tiles, and draw them manually based off the arrays you created, with the needed shaders. This offers the advantage that you can use the tile/room editor to lay your level design down, and still allows you to draw things however you want to.
 

Bastendorf

Member
Hmm, the idea of using "background" images could work fine actually. The trick is to just put a few images together. If one 2048x2048 isn't enough, then make another. You would want to a couple objects for this though, so you could draw it with the spritelamp shader. I'm kind of thinking though, if you are really interested in this higher end graphics style, maybe you are over-worrying about the amount of space the graphics will take. I could be wrong on that though, I don't know your target audience.

I honestly would like to be able to draw the tiles with a shader, and have it be automatic. Another workaround though if they never implement this features could be to access the tile array via code, and store all of those tiles in your own array. Then, you delete all the tiles, and draw them manually based off the arrays you created, with the needed shaders. This offers the advantage that you can use the tile/room editor to lay your level design down, and still allows you to draw things however you want to.
An interesting concept for sure.
Mostly, with my first idea, the background idea, I'm worried about the game's file size being too large, due to having hundreds of 2048x2048 images. Let's say a single level is a 3x3 grid of 2048x2048 pixel backgrounds, that's 9 whole backgrounds, diffuse. I'd have to make 9 more out of normal tiles, which means a total of 18 full 2048x2048 for one room. I'm hoping to break the "long GameMaker game" taboo by having this be a long-ish game. (It's an isometric game which is why things need to be so large.) All those large backgrounds are going to start adding up. I'd rather not have my game file size end up being around 5GB.

Back to your theoretical idea. As cool as it sounds, I think GM:Studio is dead. With GM:Studio 2 up, Studio 1 will go dormant and fade just like GM 7 and just like GM 8. There won't be any updates to Studio 1 from this point forward, so if they did go with that, it would be something in GM:S2, something I still won't have access to. (Which annoys me to no end, because I got GM:S Pro literally only two moths before they announced GM:S2.) I'd love nothing more to be proven wrong, but the cynic in me doubts there's any incentive to continue paying any more attention to GM:S.
 

DukeSoft

Member
GM:S 1 will still be supported and updated. From what I've heard "for te forseeable future" so I'm not sure how long this support will remain. As far as I can tell, 80% of all resources are now going into GM:S 2...

As for your problem, making backgrounds for every room which are basically tiled is quite useless, isn't it? I think you could very well use a tileset.

I'd use 1 small, low res tileset for in the room editor, and then afterward in the game replace then with a high-detailed tileset which you render with 1 extra texture (gm_BaseTexture being the high-detailed one, and putting the normal mapped one in).

You'll still need to convert UV points from one to another..

Another solution is render the tileset on 2 different surfaces - once with a diffuse texture, second with a normal texture. Then put those 2 into the shader and boom - you dont have to worry about UV mapping.

Please tell me if this is not what you ment - I globally read your post, might have skipped some key points.
 

Bastendorf

Member
GM:S 1 will still be supported and updated. From what I've heard "for te forseeable future" so I'm not sure how long this support will remain. As far as I can tell, 80% of all resources are now going into GM:S 2...

As for your problem, making backgrounds for every room which are basically tiled is quite useless, isn't it? I think you could very well use a tileset.

I'd use 1 small, low res tileset for in the room editor, and then afterward in the game replace then with a high-detailed tileset which you render with 1 extra texture (gm_BaseTexture being the high-detailed one, and putting the normal mapped one in).

You'll still need to convert UV points from one to another..

Another solution is render the tileset on 2 different surfaces - once with a diffuse texture, second with a normal texture. Then put those 2 into the shader and boom - you dont have to worry about UV mapping.

Please tell me if this is not what you ment - I globally read your post, might have skipped some key points.
Basically what I'm trying to do is this but with tiles. The light doesn't have to orbit like in the gif. That's just an example I made. All I want to do is normal map the tiles without having to program in "ok, 0,0 is tile 1. 0,32 is tile 1 also. 0,64 is tile 56." and so on. I have all that I need to get that to work, I just ran into trouble figuring out how to apply it to tiles without days of work per room.
I was thinking two surfaces might be a solution, so it sounds like your idea might actually work perfectly. Unfortunately, I haven't a clue how to do any of what you just suggested. When it comes to game development, I rely on tutorials like quadriplegics rely on mobility chairs. I'm not really a game developer, lol. I'm just a moron who is just competent enough to program on his own, but frequently gets stuck and has to find a tutorial to get unstuck. How exactly would I put surfaces into a shader?
 

icuurd12b42

TMC Founder
GMC Elder
if you were using LUX, I would say, set the tile layer to 1000000. add an object at depth 1000000 plus 1 (1 unit deeper) to turn on the shader (in the draw) with the tile background and the tile normal texture data.. and add another object at depth 1000000 minus 1 (1 unit above) to shader_reset() the shader...
 

DukeSoft

Member
What the thing basically is that you want;

- Still use tiles (draw your diffuse on there)
- Draw the same tiles, but the normal map as well
- Merge them with a shader, and output that to the screen

Could you try? See where you're getting stuck? Or don't you know where to start? I'd love to help but its best if you do the thinking :) Thats how you learn
 

Bastendorf

Member
if you were using LUX, I would say, set the tile layer to 1000000. add an object at depth 1000000 plus 1 (1 unit deeper) to turn on the shader (in the draw) with the tile background and the tile normal texture data.. and add another object at depth 1000000 minus 1 (1 unit above) to shader_reset() the shader...
Ah, PixelatedPope's shaders-on-tiles solution. I learned about that just this morning. I didn't think that would work, though. It seems to my I've-been-thinking-too-hard-and-it's-1-am brain that when the background and normal tiles are drawn, it would just draw the diffuse and normals images as solid overlay. Does that actually work? Would GameMaker spread the tiles out over their correct positions automatically, or would I have to tell it where to put them? Because I'm not manually telling GameMaker were to put potentially 16,384 tiles via code. That's some 9th Circle of Hell level stuff, right there. Wait, what's a LUX?


What the thing basically is that you want;

- Still use tiles (draw your diffuse on there)
- Draw the same tiles, but the normal map as well
- Merge them with a shader, and output that to the screen

Could you try? See where you're getting stuck? Or don't you know where to start? I'd love to help but its best if you do the thinking :) Thats how you learn
Actually, that's not how I learn. I learn by being hands on. I taught myself how to program at the level I can program at today, by taking apart other people's codes and figuring out how they work. (Mind you, I don't need to do that as much any more, but that's how I learned.)
Like I said above, though, to my tired brain, it looks like I'd have to tell GameMaker where to put the tiles in your example, too. I'm not a fan of having to code in the coordinates and IDs of 16,384 tiles. I could be completely mistaken on how your idea, works. I need rest before I can analyze for your ideas. So I guess I'll be back in the morning to take a second look at them.
 

Bastendorf

Member
Ah, I see. I already bought Sprite Lamp which is probably vastly inferior to LUX.

Yes, I'm aware of the tile_ functions. It's 2AM. I'd like to see you do any better at 2 in the morning with no coffee. :p Sounds like your idea might work. Also sounds like it's quite similar Icuurd's idea. But I'll find out after some rest.
 

DukeSoft

Member
Its basically the same - allthough my idea was a more abstract version of Icuurd's version :)

Funfact: LUX is made by icuurd.

Yeah, I know the feeling man. Go get some sleep and look at it again tomorrow, you'll probably grasp the idea much quicker and be able to put it into code.
 

kburkhart84

Firehammer Games
if you were using LUX, I would say, set the tile layer to 1000000. add an object at depth 1000000 plus 1 (1 unit deeper) to turn on the shader (in the draw) with the tile background and the tile normal texture data.. and add another object at depth 1000000 minus 1 (1 unit above) to shader_reset() the shader...
Wait...your saying you can change the shader and it will draw tiles with the chosen shader? I'm gonna have to investigate that one because I didn't know GMS would use the shaders you set when it does its automatic tile drawing.
 

Bastendorf

Member
Wait...your saying you can change the shader and it will draw tiles with the chosen shader? I'm gonna have to investigate that one because I didn't know GMS would use the shaders you set when it does its automatic tile drawing.
Apparently it can. PixelizedPope suggested the exact same technique a while back, over of Reddit. Haven't tried it to confirm, but if they both claim the same technique, then it must work.
 

icuurd12b42

TMC Founder
GMC Elder
yep pretty sure the engine only sets it's core shader on draw begin then it uses that shader for the entire draw. which you can override by setting your own... if you dont call shader reset it keeps using your shader... the same is true for setting blend mode and so on. I think you can alse override the background drawing to use your own shader by setting your own shader in the begin draw event.

the shader resets before the draw GUI I think
 

kburkhart84

Firehammer Games
Apparently it can. PixelizedPope suggested the exact same technique a while back, over of Reddit. Haven't tried it to confirm, but if they both claim the same technique, then it must work.
yep pretty sure the engine only sets it's core shader on draw begin then it uses that shader for the entire draw. which you can override by setting your own... if you dont call shader reset it keeps using your shader... the same is true for setting blend mode and so on. I think you can alse override the background drawing to use your own shader by setting your own shader in the begin draw event.

the shader resets before the draw GUI I think
I haven't tested it, but it makes sense. You just have to know what the normal map is and set that as the sampler. I'm betting I could get it to work with LUX too.

@Bastendorf I should also say that I highly recommend TMC LUX. It would replace the spritelamp shader you are using. You should be able to use the same maps you get from the spritelamp program with LUX, and LUX has some other nifty things with it, like a light management system.

I've personally never used spritelamp, though I've heard of it. Since I tend to get better results using pre-rendered sprites, I just render out normals using a "normal" material in Blender along with my shadeless sprite. Once I finish my kbinput update, I plan on making a demo of a simple asteroids clone using LUX and my rendering techniques. I actually kind of promised this to icuurd a ways back and never got around to it.
 

Bastendorf

Member
yep pretty sure the engine only sets it's core shader on draw begin then it uses that shader for the entire draw. which you can override by setting your own... if you dont call shader reset it keeps using your shader... the same is true for setting blend mode and so on. I think you can alse override the background drawing to use your own shader by setting your own shader in the begin draw event.

the shader resets before the draw GUI I think
Very cool. I'm going to have to remember that. May be handy for future games.



I haven't tested it, but it makes sense. You just have to know what the normal map is and set that as the sampler. I'm betting I could get it to work with LUX too.

@Bastendorf I should also say that I highly recommend TMC LUX. It would replace the spritelamp shader you are using. You should be able to use the same maps you get from the spritelamp program with LUX, and LUX has some other nifty things with it, like a light management system.

I've personally never used spritelamp, though I've heard of it. Since I tend to get better results using pre-rendered sprites, I just render out normals using a "normal" material in Blender along with my shadeless sprite. Once I finish my kbinput update, I plan on making a demo of a simple asteroids clone using LUX and my rendering techniques. I actually kind of promised this to icuurd a ways back and never got around to it.
No offense to you or Iccurd, but I'm not exactly made of money. LUX costs over half of what I have left for the remainder of this month. As cool as it sounds, and as neglected as Sprite Lamp is, I simply don't think LUX is worth enough to be a very bright idea right now. (Get it? Because it's a program that makes light? I'm horrible. =p) The lighting engine is the only thing I can use out of that set. The platforming engine, ramps included, I could get for free from Shaun Spalding. Plus, I already have my own platforming engine built off Shauns that's much smoother and less floaty than Icuurd's. So there's not a lot of use that would be to me.
 

Bastendorf

Member
Hey, I understand your situation. I've been there and been without money plenty of times over my life.
Not fun is it? X3 "Oh god, what if something comes up before the end of the month? Will I have the money?" Hate that feeling...

Anyways, I do like the look of LUX. I played around with the demo a little. (Well duh, how else would I know what the platforming engine was like, otherwise?) The thing that bothered me about the Sprite Lamp engine is that it feels like it was created with lights in mind rather than with the uses it would have, in mind. There doesn't seem to be a whole lot that can be done with the lights. I found no way to shape them made apparent in the code. What if I want strips of light? What if I want flood-fill lighting to simulate the florescent bulbs of the inside of a building? What if I want a light that's brighter or dimmer than the others? What if I want spotlights? What I got from Sprite Lamp leaves me with little way of doing anything other than making a simple, circular light, which I'm disappointed by. I definitely do want to get LUX, but I just can't, right now.
 

icuurd12b42

TMC Founder
GMC Elder
Good pun. I'm not pushing that on you. but if/when you start to have issues with that shader, like when changing the xscale/yscale or it starts to lag like hell or you cant leverage a tons of lights in the room, and your game has potential , dont waste much time and get it then. I guarantee you there is 500 hours of work in lux, I spent months on this, time that you could bypass in one go ;), and I'm not giving that away for peanuts, obviously ;) it's one of the most expensive assets on the store right now

and the animation was created with sprite lamp btw

I dont have light strips (light lines (like for a laser in the engine though) sounds like a chalenge
 

kburkhart84

Firehammer Games
I dont have light strips (light lines (like for a laser in the engine though) sounds like a chalenge
Maybe you should do some upgrades on it. I think you were missing shadows and stuff, and you may have to do something to make it work for GMS2. Now is a good time....no pressure!
 

Bastendorf

Member
Good pun. I'm not pushing that on you. but if/when you start to have issues with that shader, like when changing the xscale/yscale or it starts to lag like hell or you cant leverage a tons of lights in the room, and your game has potential , dont waste much time and get it then. I guarantee you there is 500 hours of work in lux, I spent months on this, time that you could bypass in one go ;), and I'm not giving that away for peanuts, obviously ;) it's one of the most expensive assets on the store right now

and the animation was created with sprite lamp btw

I dont have light strips (light lines (like for a laser in the engine though) sounds like a chalenge
Eh, don't grind your knuckles down over the price. It's actually a whole $6 (or if you were to look at the Pro edition) $70 cheaper than Sprite Lamp with many times more work and functionality... I'm totally getting it when I can afford it. In fact, I think I'm going to go ahead and call this solved and put off programming with Sprite Lamp's engine until I can get LUX. My game is a shooter, I'll need muzzle flash lighting, so engine speed is a must.
And since we're talking about shadows now, would it be possible for LUX to do what the GlareEngine does? I really like the wall-shadow thing.
 

icuurd12b42

TMC Founder
GMC Elder
wall casting penumbra on the ground is a no. Right now I only have top down offset shadow... but I'm hoping this colab I mentioned will pan out
 
wall casting penumbra on the ground is a no. Right now I only have top down offset shadow... but I'm hoping this colab I mentioned will pan out
I'm thrilled. I already asked Tizzio if he plans to add normal mapping to his glare engine because I want en engine with normal mapping and shadows. Hope either LUX or glare manages to do that in the future :) Compliment on the LUX engine btw. It's an awesome piece of work.
 
Top