Here are some updates I've made the last few weeks
In short:
- Removed custom collision system and built everything to work with gm's built in collision system.
- Changed the way nodes are handled to be more general and versatile.
- Added some cool features to nodes like linking, emitting light and the ability to create custom structs.
- Improved file format, made error proof.
- Worked out a fast way of doing indirect lightmapping.
And now an essay about them:
Collision System
The custom 3d collision system it used has been removed. This was basically 2 grids of everything (instances and precise triangles) that spread over the area of the level. But it was quite awkward to set up different objects doing different functions upon collision and wasn't\couldn't be as fast as gm's built it collision system.
So I changed it so instances use one of 2 mask sprites: mask_central(2x2 pixels with origin of 1, 1) or mask_topleft (1x1 pixels with origin of 0, 0) This way the bbox_left, right, top and bottom get updated automatically and the instances just have to update their bbox_z1(top\up) & bbox_z2(bottom\down) each step.
So now everything works with the built in collision functions and best of all, the collision events.
Now you can create collision events between objects and all you have to do is make sure it's also colliding along the z axis with
"if rectangle_in_rectangle(0, bbox_z1, 1, bbox_z2, 0, other.bbox_z1, 1, other.bbox_z2)" before any of the code for the event runs.
This has made everything with collision a lot more organized and easier to manage.
Before you had to assign\define specific functions to run when there is a collision with another object.
eg. object_define_collision(obj_item, item_collect)
And it this was pretty annoying to work with cus you'd always have to make a special function for that collision event.
But now you can put code right in the actual collision event and they're all contained inside each object so much easier to browse through.
Nodes
I've changed the way nodes(objects in the level) are handled to be a bit more general, and preset files can be a preset of any node type instead of just for instances.
Linking
I've made it so you can link nodes together. (By pressing L when the mouse is over them) And the links are displayed as lines between them. (This can be toggled off in the View menu)
So this could be used with triggers and things, like linking a switch to a door, so that when it's pulled it opens the door.
Or an invisible trigger in a doorway that when the player touches it it turns a light on and activates all the instances in that room.
You can just write a function for the trigger that loops through it's list of links and does different things to them depending on what they are. Like if it's a light and the light is off, turn it on. If it's an item and it's invisible, set it to visible. If it's an enemy, alert it and tell it
where the player is.
And you could do things like when an item is collected or an enemy is killed trigger something to happen elsewhere in the level like open a door or spawn something.
The level editor on rayman 1 (Rayman Designer) worked like this and it was a really easy and hands on way of dealing with triggers. Like you could actually see lines for what everything was linked to instead of it being hidden in their properties.
Custom Structs
I've added the ability to create static structs instead of instances in the level, so for any instance that uses a blank object with no events it can be a struct instead. The structs can have all the same properties as instances including having an object assigned to them in which case it will contain a set of variables that were defined for that object.
I don't know if I made it clear before but the variables for each object are defined in the event user 0 of each object and they're all executed once while the engine is starting up.
To define a variable, you just write one of the variable create functions which are all named "var_" + the name of the type you want it to be.
eg. an object's event user 0 could look like this:
//obj_projectile
var_real("Move Speed", 10)
var_real("Grav", 0)
var_preset("Explosion", "explosion_normal")
var_integer("Max Bounces", 3)
var_ratio("Bounce Scatter", 0.3)
Custom Save & Load Functions per object
I've added the option to define a custom save & load function for an object, so any instances or structs using it will run the custom functions when saving and loading it. (after the regular variables are saved)
So this should make it a lot easier to make your own types of things with specific file data
eg. dynamic\heightmap terrain, 3d voxel maps, sector maps(doom style) or something like the player instance having a nested array structure going on that can't be saved with the standard variable save functions.
I've also come up with a nice and easy way to handle each instance being able to have it's own inventory.
So for example a container, you could add an inventory to it with 3 crystals, 4 gems and 2 diamonds.
Then in it's destroy event you could make it spawn the inventory as collectible items.
Light emitters
Any instance or struct can have "emit_light" enabled, in which case it adds the light variables to it and they get updated automatically as they move about.
The light variables are:
light_active
light_x
light_y
light_z
light_color
light_brightness
light_radius
(The xyz values are relative, so they work like an offset)
Summary
With these additions, it doesn't really need the separate node types (lights, points and any other type I'd make in
the future (voxel maps, terrain etc.)) Now nodes are just instances or structs.
Presets
Presets can also work as "global structs", which can be used in a lot of ways. Such as inventory items, like different guns having different variables for fire_rate, damage, accuracy etc. and they can be useful in other cases like different enemy types sharing the same variables. They can all refer to their type struct instead of having identical copies of a set of variables.
This would also make it a lot easier to tweak these variables while testing and it'll update all instances using them instead of only being able to edit 1 instance at a time.
Postface
I think these changes together will make certain things a lot easier and less awkward to do. Most of them were made while I was trying to make certain example objects and something was really awkward.
File Format
The file format has been changed a bit and the way data is saved has been made more secure\error free.
The format before was able to crash in certain cases, like if you changed a variable type from a number to a string. A file saved before the change would still be using the number type and load the wrong type of data, and if the instance used it in it's update event (event user 1)
(which is executed when the level is loading) it could crash due to having the wrong data type for the variable.
Indirect Lightmapping
I worked out a nice and fast way of doing indirect lightmapping mostly on the gpu, without making it cast billions of light rays.
The way it does it is actually quite simple: (similar to light probing)
It uses a 1x1 surface to render to each pixel\texel on the lightmap at a time.
For each texel on the lightmap, it sets the view position to the position it is on the triangle's surface, pointing in the direction of the triangle's normal (+ random scatter). Then it simply renders the whole model so that it renders what that texel can "see". Which simulates light rays bouncing off the other triangles onto that spot on the lightmap.
Then the 1x1 surface is drawn onto the main lightmap surface at the correct position.
I thought this would be really slow at first cus of how it has to submit the whole model vbuffer 512x512(262144) times per pass. But because it's rendering to a 1x1 surface most of the geometry is culled before it reaches the pixel shader. So it can do it about 500 times per step without lagging.
From testing with a 512x512 lightmap, it takes roughly 10 seconds to perform 1 pass. Which is really fast considering what it's doing.
I think for a smooth\none grainy result it probably needs about 32 passes. (5 minutes) so you can just go and make another coffee while it's rendering. And for a final game quality lightmap with 255 passes it would take about 40 minutes.
I'll make a video of this soon once it's set up properly.
It should look the same as unreal at high res, and quake and other early lightmapped games at lower res.
You'll also have control over the scatter amount, curve balance between light and dark. And the lightmap gets saved as a png so you can edit it in an image program.
It also pads by n pixels when finished, by simply drawing itself across multiple times. Cus empty texels that aren't used by a triangle have 0 alpha.
This approach could be used for light probing aswell. And that'd be really fast cus each probe would only need about 30 samples.
So maybe it could be done in realtime ?
I'm not that interested in light probing to be fair but, maybe I will be if I can use this technique with them..