Merrak's Isometric Adventures -- Dimetric Dilemna

merrak

  • *
  • Posts: 2507
That's good advice. Readability is the top priority. Getting tiles to line up properly in a graphics program is a hassle, but does reveal a few important things. Below is a crowded scene in 4:1 ratio.


In my level design style guide I have a rule prohibiting "floor over floor", such as in the illustration. This configuration wouldn't be possible because the player could be both on top of the bridge or below it. With a 2:1 w/h ratio, the floor graphics block much of the scene. With 4:1 w/h, actors are still visible under the bridge.

Unit positions relative to each other are still readable, which was my main concern with this ratio.

There is also one nice surprise: it's harder to create optical illustions by lining up the edges of floors of different heights. Below you can see the bridge extends slightly past the vertical line defining the doorway, making it easy to see the bridge is in front of it.


I'm thinking 4:1 may be the way to go.

The last question is if it's really worth changing the renderer code to support a new w/h ratio. It's been a while since I've looked at the derivation. Thankfully I've kept all of the notes (plus, math in this thread anyway). I'll need to study it again. I think I may have saved myself a lot of grief by using a linear algebra based solution rather than trigonometry.

Using trig, the angles of the coordinate vectors very much plays a role in the projection formulas. With a linear algebra approach, though, I should just need to change the bases and the rest of the changes resolve themselves.

merrak

  • *
  • Posts: 2507
Ratio Review. I took a couple of days to update the map editor to allow for arbitrary tile aspect ratios. It's made it a lot easier to experiment than using a graphics editor. Although I'll miss the look of the 2:1 (classic "isometric") ratio, 4:1 appears to be the best route. Here are some crude rooms. First--


This is a typical kind of room from Towers of Vallas. The 2:1 ratio would be a better fit for it, but 4:1 works. There is a good sized stage for melee combat. A technical concern is the size of the rectangles that would be produced by dividing the floor into rectangle partitions. I had a lot of issues with the renderer in TOV because of this--numerical precision problems that resulted in clipping errors. Fortunately, I think the larger resolution will help balance out the effect of the thinner rectangles.

The editor uses a different renderer than the main game's, so I won't see what's going on until I update the game's renderer.

Next:


This is closer to the kinds of structures I always imagined for Vallas. With the 2:1 ratio, overlapping floors creates a significant blind spot where the player can't see Marika. Early in the production of Towers, I wrote up a style guide that had a few rules for level design. The top rule was "no blind spots", although I made a few exceptions... such as for passages that lead to secret rooms.

Very early on I mentioned Prince of Persia as one of the inspirations for the Vallas games. I wanted the player to be able to scale floors, leap over chasms, and fall into unexplored depths.

Oddly enough, I always found Prince of Persia to be a bit dull after level 3 or so. It gets repetitive, and I'm not a big fan of timed mazes. I always thought something like an ARPG, with a deeper story, and a 3D world would add needed variety to the concept.

Here's a third structure:


This room would not be possible in Towers of Vallas or Temple of Idosra. Without lighting, the floors would "bleed" into each other, creating an Escher-like illusion. Lighting can help break the illusion, but there would still be significant blind spots. Not only could Marika be hidden by the floor above her, the wall in the back would also be hidden.

I think it's actually worse if the player can't see all of the room's geometry than the player character itself. Although I did use room geometry quirks to hide doors to secret rooms in Towers, secret rooms are a bonus. I think that's a different matter than hiding required information about the room.

I'm changing strategies for tiling, so that I can apply textures. This will make life a lot easier, since I won't have to take the time to adjust pixel offsets to line up images. I can still use hxPixels and apply a simple shear transformation.

With that upgrade in place, it would be more feasible to allow for multiple ratio options in one game. I couldn't apply textures to the actor sprites, though... so having multiple ratio options (in game) would still be a significant undertaking. Multiple ratios would be most useful in the map editor, so it might be worthwhile there.

« Last Edit: September 22, 2019, 01:59:11 pm by merrak »

merrak

  • *
  • Posts: 2507
Shaders Sunday. Here it is, all-shader supported lighting effects:


There is no image for the light itself, but it's up and to the right of the pillar.

Overall I'm mostly happy with the results. There are still a few issues left to resolve. The most significant--I was not able to pass an array to a shader. The original design called for one image shader per wall, which would then loop through all the lights and all of the shadows to render the final result.

I'm using the bitmap shader routines OpenFL provides, which is the same mechanism used in mdotedot's extension. In fact, I started off trying to use the extension itself, but it turned out to not mesh very well with the peculiar way "Vallas Engine" handles wall bitmaps. Instead, I took the approach that it seems the OpenFL designers intended: making classes that extend the base Shader class. At this time passing arbitrary arrays using this approach isn't supported... best I can tell from the documentation on ShaderParameter.

As an alternative, I created a separate shader for each light and each shadow and applied the array of shader filters to the wall bitmap. The problem here is if the same light casts multiple shadows against a wall, its effects are applied multiple times.

I think the best design would be to use a separate shader for each light, but each light loops through all of its shadows in the one shader. I can jury rig a solution using multiple if/then checks and hope OpenFL adds support for arbitrary arrays.

merrak

  • *
  • Posts: 2507
Lighting Models Extravaganza. After a few adjustments I now have the desired shader: One shader that accepts multiple shadows. This solves a lot of the problems I ran into, although I took the brute force approach to programming it: switch statements and multiple if/else if's. It's not an elegant solution, but it's easy to work with and debug.


It can only accept up to 10 shadows per wall, which ought to be plenty for most circumstances. I think I'm done with tests for now, and so should begin to work on the "real" lighting model for the game.

I weighed the pros and cons of software rendering vs hardware, and realized that the best fit may be a hybrid model.

Towers of Vallas uses all software rendering. Each wall is a bitmap. For most rooms, the bitmap is rendered when the level is loaded and never adjusted afterward. Dark rooms with the lamp are rendered every frame. Despite the fact that I significantly underestimated how much processing time memory management takes, I still easily achieve 60 FPS. But--Towers is only 160x144 pixel resolution. I'd be really suprised if I got 60 FPS in "Vallas 2", which is 960 x 540 pixel resolution.

Shader Pros... The obvious one is speed. The other significant advantage: memory. I don't need to store images in memory, and I don't need to bother with optimizing garbage collection if I'm not rendering walls in real time.

Shader Cons... Shaders are harder for me to debug. Getting environment variables into them is a pain.

Software Pros... It's easy to pull in all kinds of variables without bothering with some pipeline I'm still working on understanding. Debugging is easier. For static walls, it's fast enough.

Software Cons... Garbage collection isn't an issue if I'm only using software for static walls, but storing all of those images will consume a lot of RAM.

A rendering speed of 5 to 10 FPS is fine during the level loading process. I can render all the walls and use shaders to animate the lighting.

One last advantage to a hybrid model--if I drop support for dynamic wall rendering on the software side, I can take out all of the "math tricks" I threw at the problem to squeeze every bit of speed out of it. I store a lot of pre-computed values and this vastly complicates the level loading process. Every time I revisit the renderer, I have to re-learn what all I did. Taking these tricks out and reverting to simpler solutions will make the engine much easier to manage and upgrade over time.

Bombini

  • *
  • Posts: 1310
Very interesting and congrats on the progress!

merrak

  • *
  • Posts: 2507
Very interesting and congrats on the progress!

Thanks!

I now have something resembling the final lighting model. Test scene shown below, this time with the full screen showing.


I have plenty of room on stage for larger rooms, longer halls, and taller areas. The small size of rooms in Towers was the biggest constraint on map design.

The image was rendered using an updated version of the software renderer I used back in 2017. I always liked the look it produced, although it is incorrect, with regards to the physics of light, in some ways. Because the renderer makes permanent changes to the wall bitmaps, I can use shaders to add animated effects.

So now I have a dual renderer system: Software to load the map and draw the static lights, and shaders to add dynamic lights and effects on actors.

The biggest improvement is a quality-of-life one. Before I had to draw all of the walls in perspective and, in the case of the 2017 renderer, draw each wall in standard lighting (diffuse--probably a misnomer) and also in a fully lit (ambient--another misnomer) state. The renderer mixed the two images to create the final results seen in the older post I linked.

The new renderer can now apply textures, so I can draw walls flat. This will make it a lot easier to use Aseprite's Tiled Mode to draw seamless tiles.

I also only need to draw the diffuse wall image. Ambient is computed using a formula:

Code: [Select]
                // Red: 2.3204 t - 1.73792 t^2 + 0.405156 t^3
                a_r = 2.3204 * a_r - 1.73792 * Math.pow( a_r, 2 ) + 0.405156 * Math.pow( a_r, 3 );

                // Green: 1.24935 t + 0.591609 t^2 - 0.832233 t^3
                a_g = 1.24935 * a_g + 0.591609 * Math.pow( a_g, 2 ) - 0.832233 * Math.pow( a_g, 3 );

                // Blue: 0.960621 t - 0.0537841 t^2 + 0.0957061 t^3
                a_b = 0.960621 * a_b - 0.0537841 * Math.pow( a_b, 2 ) + 0.0957061 * Math.pow( a_b, 3 );

I hard-coded the coefficients to match the same effect as in the 2017 renderer, but I can also make these coefficients a property of the tile. This will let me adjust the lighting to get different material effects: shiny, glossy, matte, etc. I can still set different colored lights, too.

The next step will be to make some more tiles and some new materials. I also need to implement actor shaders. Here's a mock-up I created in GIMP using the curves tool with the same curve the renderer applies to the walls. It's very dark, but there's only one dim light in the room... not shown because it has no sprite.


merrak

  • *
  • Posts: 2507
Helper App Monday. One nice thing about developing with Stencyl is how quick it is to develop 'helper apps' to solve the little problems.

Here's one I put up with for far too long: Drawing tile images. Each tile is represented by a 3D coordinate. I then have to compute what screen coordinate the 3D point maps to. But I can't draw the image at that point, because the (x,y) coordinate of an image is its upper left corner. I need to subtract from the x and y position to shift the image so that it lines up with the tile correctly.


For simple shapes, like pillars that take up the entire tile box, it is easy to know how to shift the image. For other structures like staircases, windows, and the like, this has always been a lot of guesswork. Here's what happens when the shift is miscalculated:


One of the reasons these coordinates are complicated is because how each plane that makes up a tile is represented. The first coordinate in the plane's defining polygon is its origin, but the origin could be anywhere inside the tile's bounding box.

So today I finally decided enough is enough and made a small program that lets me adjust the position of an image and compute the offset coordinates.



I had a nice variety of tiles for Towers of Vallas: walls, stairs, windows, doors, water... 28 3D structures in total. I was dreading re-configuring all of them for the new 4:1 w/h pixel ratio. This app will make it a lot easier to port them over and make more 3D structures to put in the sequel.