These are some awesome textures. For reference, the texture in the file is: , and I think a 64x64 texture (those squares are 4x4 pixels each) is probably too large for the small hexagons there.
After all my freaking out about how terribly hard textures could be to implement, they were actually, uh.
pngInfo info;
gentext = pngBind("../64x64.png", PNG_NOMIPMAP, PNG_SOLID, &info, GL_CLAMP, GL_NEAREST, GL_NEAREST);
if(gentext == 0) {
printf("Cannot load generic texture file.\n");
exit(3);
}
glBindTexture(GL_TEXTURE_2D, gentext);
REALLY EASY. Yes, that is all the code I needed to add. There's a little more informational output & error checking elsewhere, but. seriously. easy as pie with the libglpng-dev library.
If you had told me, when I started this project, that I would find things like tile joins & sloping more complicated to implement than textures, I probably would have laughed at you.
Slightly better. () It looks like 32x32 pixel textures are the happy medium between the pointlessly-large 64x64 textures and the pixelated 16x16 textures. It'll be more obvious what to use once I turn on blending. and maybe make some textures that don't suck. this grass texture is from early 2005, and it was the only non-isometric texture I had on hand. It uses four colours. gg me.
Made a pretty basic stone texture (), but I don't want to go on a texturing spree before I get anything more useful working. I changed the texture wrapping a bit— previously (i.e, in the two screenshots above) there was no distortion when placing the texture, at the expense of having parts of the square texture be placed outside of the polygon and thus never be drawn. I decided to change the wrapping a bit, so the square texture would basically be pulled out at the middle to form a hexagonal tile. There are certain drawbacks to this, as any 3d modeler or mapmaker knows. For example: this texture (
) is divided up into six uneven slices. But when it's loaded ingame...
...it's stretched so each coloured slice has the same area. Likewise, shapes are deformed when you stretch them: is drawn as two concentric circles when it's placed ingame. Well. they're not perfect circles, but they're pretty close.
later: I reset the texture wrapping to not stretch. mostly because I already don't trust my texturing abilities & adding weird distortion into the mix just makes everything worse. is the new 'six slices' texture and it's kind of obvious why it'd be easier to draw textures for. because hey, no distortion. just the white edges getcut off, so they're wasted space.
Top of the to-do list:
I dunno which of those, if any, I'll end up working on next.
Turns out it was the "figure out a way to make a smooth map with sloping" option that I would do first. and. per usual, it was a dumb bug that only existed because I am dumb. Remember how I said in a previous post that the way I used to do sloping was backwards? Well no, it wasn't, and changing that made it backwards which caused all the dumb rips in the landscape. Note how there are still slight rips in the landscape in that screenshot. It's not really a big deal, though— I know how to get rid of them, and like I said previously, I do like how the rips look (of course, they'd look better if I wrote the join code) but it was just annoyed to not be able to get smooth paths.
The last two bits of slope code I should ever need to change ever: something to set the center part of a tile to the average height of all its edges, to prevent something like a tile being pulled up in all directions and thus have the center be pointed; and a small bit of alteration to the make_path() variable list, so you can specify how much to slope for each path, and so generate 'smooth' paths and 'ripped' maps whenever you want.
I did this mostly, uh, for the amusement value of it. I had written down buffer->height[CENTER] = fArrSum(buffer->height, 6);
and was just about to save and recompile, and then I was like. wait. what does that code do? that's not right.
And then I saved and recompiled anyway, because I wanted to see what it would look like. Most of the time it just freezes when it tries to display (presumably due to float overflow? it'd increase the center x6 each step, and with 256~ steps that's more than enough to overflow a float multiple times), but sometimes you get lucky and it just breaks terribly. Note the tiles rapidly falling out of the display area, something that doesn't tend to happen pretty much ever (well, sometimes. I'll take a screenshot of a normal map-falling-out-of-screen view later) normally.
But then I added the /6.0
part to the line and recompiled and everything was good.
you really can't tell where one tile ends and another begins.
next: I have no clue! probably joins!
not only are the joins being drawn totally wrong, but (more importantly!) this makes the maps totally look like those oatmeal cream cookies. I want a cookie now.
whoops. I finally wrote some code to respawn a map without restarting the game (yeah, yeah, I suck) and I forgot to take some important data into account. notably, the rotation & translation of the map are both stored in the map structure itself, but the state of the rotation (for this example, the "is this map tilted up or now" bool) is stored in the battle() function. this lead to trouble when regenerating the map-- control left battle and wiped out the temporary is_tilted variable, and if the map happened to be tilted, you could tilt it up again, eventually leading to the front-on view in this screenshot if you did that enough times.
set is_tilted and zoomedOut to be static chars. (yeah, I know I should be using a single bit for those, but I don't know bitwise arithmatic well enough to do so)
(incidently, I fixed the way inside joins are drawn. it was another dumb bug. outside joins remain overly complex)
John wanted to see a screenshot of the 'fixed' joins. Man, that's an ugly texture. Seriously. It's supposed to be sand. It's way too saturated, among other things.
So maybe I should try to avoid dropping this project for months while I go off and do other stuff.
I turned textures off because you would not believe how much they slow down the program. kind of sad, considering it's only one texture repeated a hundred or so times. My video card really sucks. Also, I fixed some recurring bugs regarding regenerating the map while it was rotating.
I'm considering taking, uh, pretty much every 'how to program a game!' online tutorial I've seen and actually make this thing playable, even if it's with blocks of colour instead of sprites and no animations and really terrible default fonts.
Basically I'm kind of afraid to implement a path-finding algorithm, because graph theory really scares me. Hopefully it'll be one of those "I thought it was really hard until I added it in 20 lines of code" problems.
So I think I'll be working on one of the following:
Last update of the year! All the code I've been writing has been boring structural stuff, so there isn't really any difference between this screenshot and the last except for its size (one of the things I changed was totally rewriting the stack implementation so I can make regions larger than 256 tiles— the region in the screenshot is 512 tiles) but I felt like I should update now, so.
But yeah, things accomplished: set up SVN archive for code; rewrote stack to use void **
instead of struct tile *
; wrote some simple unit tests; fixed a few misc. errors w/ other stuff. I started on graphs & pathfinding, but... did not get far. I'm thinking of just using an unsorted adjacency list and see how that performs, see where to go from there.
(The humor here is that once I get the map generator finished it'll work by creating many small regions and stitching them together, so I probably won't ever need to create a region larger than 256 tiles.)
My goal here is to have something shiny and impressive-looking by Jan 29th, so I can submit a second phantomnation screenshot to sheezyart one year after the first (which, by the way, was this screenshot).
My to-do list remains basically the same, with the potential addition of 'textures'.
I started trying to implement Dijkstra's algorithm for pathfinding, only to fail terribly and lose hope of ever accomplishing anything with my life. So I didn't spend a lot of time coding for the past two weeks. And then I looked at the screenshot gallery and realized I really needed to focus more on dumb errors and less on logging just what I spent the last week doing.
Then I started editing the code and made a series of dumb errors that make good exemplary screenshots.
Screenshot the first: I was fiddling around with the new sloping code and had an idea to try negative slopes. That is to say, instead of sloping the tiles closer together, it slopes them away from each other. This is what happens. Note to self: don't make the map generator use negative slopes. Moral of the story: always remember the assumptions you made when you first wrote the code.
Screenshot the second: I finally got fed up with having no outside joins, so I decided to write some outside join code again. It's not perfect, but it works. Except, as shown in this screenshot, it doesn't do well with overlapping tiles. I accidently wrote it so that it would only generate outside joins to overlapping tiles when they were implausibly close to each other (less than 20 units apart), which lead to... well, this.
Screenshot the third: I also got fed up of having such terrible texture support. One of the things I had to do was rewrite the way joins were drawn, so that I can properly texture the first quad and all those that follow. (the top quad of the join can have a different texture, if you so desire. Not yet sure what the use of that will be.) It's not actually that hard to code, but I forgot how OpenGL does quads and accidently gave it the vertices in the wrong order, causing this.
As a bonus, though, it showed another, more subtle problem-- you can kind of tell in this screenshot, but this made each join be drawn at least six units deep. Which meant lots of quads were drawn too large. And I might not have noticed until later if I hadn't made that dumb mistake! I love it when I screw up in such a way that alerts me to my other screwups.
(note also that the 'new' tile join code works exactly the same way as the old join code (it's actually worse) that I called ugly. I rock so much.)
And then I got really tired of the flat white hexagons and decided to get some form of decent texturing working. It... remains a work in progress. I don't have any clue why some of the textures aren't showing up-- each tile is assigned textures (and they all have unique texture numbers even though I'm using the same three textures on all the tiles) after everything else about it has been determined, in the make_path()
function. At first I thought "oh, of course, I must have written two different 'add this tile to the region' code blocks and only changed one!" but no, there's only one time at2region
is called, and the texture code is right before that. So then I was like "Maybe it has to do with the stack? Maybe if you end up pushing and popping you might end up adding textures to a tile that's already had its textures added?" so I tried if(!buffer->tile_texture) {
but that made all the textures vanish. And for that matter, you still got no textures if you removed the
type_tile(buffer);
}!
. So I have no clue. I'm sure I'll work it out later, though.
Solution: region generation works by storing each placed tile in a stack. If the path of tile placement is ever 'stuck' (by running over its own path and having tiles placed in all six directions, for example) it backtracks by popping copies of the placed tiles off the stack until it finds a way out. Also, you have to free the duplicate tiles before you pop another, to prevent memory leaks. Well, the free_tile()
function also frees the textures for that tile. Tiles had their textures initialized right before they were pushed on the stack. So, any tile duplicate that was popped off the stack used the same texture ID as the one already stored in the region, and freeing the duplicate freed the texture for the real tile, too. However, since the real tile still had references to the texture ID, checking buffer->tile_texture
wouldn't work-- the texture was set, it was just invalid.
As the code gets more complex, I am probably going to increasingly run in to these kinds of bugs-- complex systems interacting to create behaviour that is, at first glace, totally weird.
The solution to this is two-part: first, only add textures to tiles after the entire region has been generated. Second, create some texture-handling code to notify me when I have invalid textures on a tile (and also to reuse textures when the same texture is placed on multiple tiles).
Almost!
I wrote some graphing code that actually works, and it was... well, it's really complex, but thankfully not that complex. You can see the results in the screenshot. Orange lines connect each adjacent tile. Only some adjacent tiles are not connected. I'm betting this has to do with the initial run through of the function-- the first tile checked might not connect to all of its neighbors correctly? Something like that. But hey, progress!
(ha ha, no. it turned out to be I used GL_LINES instead of GL_LINE_STRIP when drawing the graph connections. They calculate fine in simple situations.)
Which brings me to the second test of the graph code: overlapping tiles! It doesn't work quite right yet, and it's really hard to even see if it does work, visually, because it's so hard to match the lines up to the tiles they're connecting to.
Here are a few screenshots that I made. For the upper two, I tried to make the graph lines curve in an i
-ward direction, forgetting that in this context i
was a pseudorandom number, as opposed to a direction. For the bottom one, I set min. overlap height to -1 (it's usually at 42 or 46 or 9999 or thereabouts) to see what would happen.
These look all... artistic. or something. I like them. BUT NOW: Since I have the graph generated and working (excepting overlapping tiles), I have to implement the Dijkstra's algorithm search so I can actually walk across it. wooo. Something I suspect might be a problem: characters walking through joins. Clearly I need to factor the depth of a tile's joins into the walkability calculations.
Once again I wander off adding misc. features. I got really tired of the black backdrop, 'k? Eventually it will be a map-specific picture. Right now, it's a generic test pattern. Problem: all OpenGL textures must be as tall as they are wide and a power of two pixels along each side. this image is really 640x480 pixels. Watch and be amazed as libglpng warps and folds the image to bring it to OpenGL's standards! So yeah, I need to fix that, also.
Graph Theory is still beating me up. ...actually, I have a few lengthy graph-related papers marked toread on my del.icio.us account; I should go and read them and see if it makes things easier. ANYWAY.
Since my video card sucks and I don't have any good background images to put up and I still have no clue how to fix that texture problem, I decided to just use a gradient for the background. woo. Except... it's clamped to the edges of the screen, only when you zoom out the edges get much wider. Hence, the screenshot.
Actually, my windowing setup really sucks. That's the primary cause of this-- I have some old & badly coded SDL window code that... well, is badly coded. I need to replace it with something less dumb and then I should no longer have to deal with problems like this. I hope.
That's not a phantomnation screenshot!
Graphs stuff was still making me want to kill things, so instead I did some work on Perlin Noise generation, which segued into trying to get glBitmap() to work wrt fonts. Good news: I know how it works; the code can be copied over to phantomnation. Bad news: I have to transcribe all of the font characters into the code by hand. D:. So much D:. At least I got to see how that font looks for real. On the one hand, kind of ugly. On the other hand, I don't want to redraw all 512~ characters. On the other other hand, I don't want to figure out the bitmasks for 512 characters, either. (just FYI, those two crazy characters interspersed into the regular alphabet are Ǖ and Ɣ.)
It's been a while. Mostly because I went on unexpected vacation, but...
Anyway. I got back and quickly solved the problem of rotation (quick quiz: what's the formula for integer rotation by 60° on a hexagonal coordinate plane?) and set to writing the code required to actually implement map symmetry.
I was confused regarding this screenshot (no other 'arm' sticking out from the origin point) until I turned on wireframe mode...
Clearly I did not write something correctly. (p.s. I was doing the translation from the origin wrong. I don't understand why the tiles stacked up like that, though.)
Okay, I know I said I'd avoid pretty screenshots with nothing wrong with them, but symmetry is so pretty. Also a major failing of the mirroring system: it doesn't check for tile collisions, so most of the time you end up with overlapping tiles. alas. That'll have to be fixed before the function can actually be used.