Trouble with changing how the block is rendered

Marcin Sciesinski

Code ALL the Ages!
Contributor
World
Architecture
I'm working on "blockNetwork" and "signalling" mods. First one is a library-mod that helps developing new mods that require a network of blocks. If you ever played Minecraft - think of Buildcraft pipes, IC2 cables, etc. Second one is an example use of the "blockNetwork", which allows sending signals from one place in the block network to another - similar to redstone in Minecraft.

To be able to detect changes in the network, I've added event listeners in my SignalSystem class and I'm listening on Add/Changed/RemovedComponentEvent.

Now, imagine I have a block, that I want to change color and emit light, when it receives the signal - basically a lamp. Once my mod detects that this block is connected to powered network, it sets its property "isPowered=true".

In a LampSystem that knows what the lamp should do, now I need to change the look of the lamp, when the "isPowered" is changed to true. I was told, that the only way to do that is to replace the block with some other block. However, if I do that in my LampSystem, it will once again fire SignalSystem's event handlers, which in turn once again will set the property for the block, and so on...

I don't think replacing a block with another one is a clean solution, and will most definitely not fit all reasonable scenarios.

A few ideas - a block might have multiple textures/settings associated with it and code can modify some pre-defined property of the block/BlockComponent to define, which textures/settings should be used.
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
Yeah this has come up in the past under various scenarios. I think there was something about the lighting system needing work. And we've talked about texture variants before it just hasn't led to code yet . Another good example is the idea of a quantity-based bookcase. You could do that now with x different blocks showing bookcases with a different number of blocks each then use a system to swap the block when a book is added/removed.

But a better way probably would be including all x textures in a single PNG and letting the engine prep the variants. An "active" bookcase (one you can interact with - you could have non-interactive primitive bookcases too) would be backed by an entity holding a BookcaseComponent with a book count in it, so that way you can figure out what texture to pick (possibly only by which generated block ID to use).

You could also have plain variants like grass in different patterns, or walls with subtly different looking bricks. There and elsewhere the problem becomes how to persist an exact variant without changing the underlying block ID. Blocks backed by entities can cheat, although that still needs a decision of what texture/block to use, at some point in time.

Marcin Sciesinski - you put some sort of handling in for this, I'm thinking after you wrote that post - what solution did you go with for now?

begla or Immortius might remember / have feedback on the lighting part. Tricky part is how luminance cannot be changed at a per-block level right now. Each primitive block has a value that can't be changed without changing all for that type. So we just swap the whole block. Maybe suppress / cancel the follow-up event?
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
The fundamental limitation/optimisation of our underlying voxel system is that there is a block per possible appearance, and any change to the appearance means changing the block. Each block is associated with a byte, and each chunk of the world has a byte array or similar for the blocks it contains, which is what allows us to hold the millions of blocks in memory. However, the overlying entity handling is open to change if where necessary - we can change what events are sent out and how entities integrate with the world and so forth.

Firstly, add/changed/removedComponent events are very low level - working against higher level events should help avoid low level chatter of that sort. BlockChangedEvent would probably work better, although I know there are some issues with it in develop. Or is there a new event that can be added that would help?

Secondly, perhaps some improvements can be made to the BlockEntityRegistry. If we added the ability to change the underlying block without changing its entity, for instance, then you wouldn't get any add/changed/removed events from doing so - would that be of interest?

It seems some issues were introduced by https://github.com/MovingBlocks/Terasology/commit/e0f96cd9c92c235af2c43b6fc506198248b03193 - perhaps this commit needs a review and cleanup.
 

Marcin Sciesinski

Code ALL the Ages!
Contributor
World
Architecture
Cervator At the moment I just replace the block, and just let the events be triggered and handled by the SignalSystem - this introduces a lot of unnecessary processing, but does not cause any errors. This however introduces another problem. If I break the replaced block, in my inventory I get the block of the changed type, rather than the original. I'm not sure if there is an option to override what block is dropped when one is destroyed.

Immortius Replacing a block, without changing the components would be a good option.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Cervator At the moment I just replace the block, and just let the events be triggered and handled by the SignalSystem - this introduces a lot of unnecessary processing, but does not cause any errors. This however introduces another problem. If I break the replaced block, in my inventory I get the block of the changed type, rather than the original. I'm not sure if there is an option to override what block is dropped when one is destroyed.

Immortius Replacing a block, without changing the components would be a good option.
Ah, for picking up a block and getting a consistent type Terasology has the concept of a BlockFamily - the family is what is picked up, and when placed the block family determines which block from the family should be put in the world. This has mostly been used for various rotations, but it should work well for your use case as well - has the advantage you could have a block family place the correct lit/unlit block from the outset. Unfortunately there's no hooks for adding block families purely through modules at the moment - it requires some work in the engine to add the support to the block loader.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Ok, I've added both these features. BlockEntityRegister now has setBlockRetainEntity() methods for changing the block but leaving the entity alone (will send a BlockChangedEvent).

You can now also include a "pickupFamily" setting in a block definition, which is the uri of the block family to pickup instead of the block's family. e.g.

Code:
{
     "inventory" : {
        "pickupFamily" : "engine:dirt"
    }
}
would cause the block to be picked up as dirt. This also changes the dropped block's appearance to match.
 

Marcin Sciesinski

Code ALL the Ages!
Contributor
World
Architecture
Ok, for now I got all I wanted. I can replace a block without touching an entity. I can also specify what block should be dropped when my replacing block is harvested.

I'm not sure if this will cover all future cases, so I think going further we should have an option to register special renderers to blocks. Simpler option would be to be able to define multiple meshes, and a way to switch, which one is displayed.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Technically this is already possible - what you do is make the block invisible, and then use the entity to render it with a mesh component. This should be used sparingly though.

It is not feasible to have special renderers for blocks otherwise because we don't render individual blocks - we render chunks composed of the visible surfaces of a region of blocks. Multiple variations per block may be possible, but would require extra memory per voxel so I would be reluctant to go that way (instead I'ld prefer to increase the number of types of blocks supported to 64k and continue to have one appearance per block as this has greater utility). Essentially this all comes down to the low level optimisations that make Terasology playable at more than 1fps.

Block families are the intended mechanism for addressing this need, and having a variations block family would have the advantage that all the variations could be defied in a single block definition and you could easily check whether a block is part of the family. Picking a block out of the family is the same as picking a variation of the block.
 

Marcin Sciesinski

Code ALL the Ages!
Contributor
World
Architecture
Immortius I'd like to refactor the code (in separate branch in my repo) to allow mods to add new BlockFamilies, as at the moment they are hard coded in an enumeration. Leave the details to me - I know you will be fine with the changes. The reason for it, I will need a special class for handling cable-type blocks that will be able to connect to other blocks, depending on presence of a specific component in the neighboring block and properties in the component.

However, I need some guidance, as I have a problem with figuring out one thing. In case of the ConnectToAdjacentBlockFamily, there is one block created for each variation of connecting (cross, T-section, etc). However I'm not entirely sure, if a separate block is needed for each rotation of the variation, for example there should be four different corners. Does each corner rotation require a separate block, or is the rotation handled internally by the rendering and the block has some rotation property?

I would really appreciate for you to shed some light on this.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Every rotation is a separate block. Remember, the only information we store per voxel is the block id - we're effectively packing rotation/etc information into that block id by having each variation a separate block. This allows us to avoid wasting memory with rotation information for every voxel when many blocks don't rotate.
 

Marcin Sciesinski

Code ALL the Ages!
Contributor
World
Architecture
Aaah, I found it now. The BlockLoader in the applyShape does the rotation over horizontal axis, which is a bit of a waste, as some of the shape variations (cross, line), do not require rotations at all, or require a limited rotations only. I hope to fix that as well.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Keep in mind that it can depend on the texturing - I haven't looked into ConnectToAdjacentBlockFamily's implementation in detail though, so you are quite likely correct.
 

UberWaffe

Member
Contributor
Design
Speaking out of total ignorance of graphics engines:
Would it not make sense to allow blocks to specify a 'special rendering' flag, which excludes it from the 'making a single mesh + texture from all visible blocks for displaying'?

Then these special rendering blocks (hopefully few in number) can use their extra-data-bytes to store the data they need (or attached entity data, or world clock, or whatever) to do whatever rendering they want. (I.e. rotation/current frame of animation/current angle of mesh/whatever).

Surely something similar is already happening for entities?
As long as the vast majority of blocks in a rendered chunk (whether air or stone or ocean) is covered by the 'single mesh and texture' approach, then overlaying other rendering shouldn't kill the performance... right?:cautious:
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
You can do pretty much that - you make them invisible and let their entity render them. I mentioned a few posts up, but I guess it can be easy to miss. I had forgotten about that technique in my initial post - it is a last resort to an extent though. It also wouldn't work for the use case (lamps) because lighting is part of the block definition and that cannot be handled by the entity.

So essentially, options for handling block appearance where systems are changing them are:
- Different blocks/block families (if they should act fundamentally differently)
- Single Block family (if they are just different appearances of the same block)
- Invisible + entity renders (if there are some advanced behaviors like animation or lots of transformation - I would use this for stockpile style blocks as well)
 
Top