Blackvoxel > Programming with Blackvoxel
Custom VoxelTypes
d3x0r:
Success!
I have a block called 'fertile ground' that in a second or 2 from creation grows 'low foliage' aka food.
food will eventually age and become medium and tall; which will be unusable by the first animals... a block called rabbit and a block called fox....
------------
I implemented a type called ZVoxelRef
--- Code: ---#ifndef ZVOXEL_REF_DEFINED
#define ZVOXEL_REF_DEFINED
#include "ZVoxelSector.h"
#include "../ZVoxelExtension.h"
class ZVoxelWorld;
class ZVoxelRef
{
public:
ZVoxelSector * Sector;
int Offset;
int x, y, z;
UShort VoxelType;
ZVoxelWorld *World;
ZVoxelTypeManager *VoxelTypeManager;
ZVoxelExtension *VoxelExtension;
ZVoxelRef( ZVoxelWorld *world, ZVoxelTypeManager *vtm, long x = 0, long y = 0, long z = 0, ZVoxelSector *Sector=NULL, UShort VoxelType = 0, int offset = 0 )
{
this->x = x;
this->y = y;
this->z = z;
this->Sector = Sector;
this->Offset = offset;
this->World = world;
this->VoxelType = VoxelType;
VoxelTypeManager = vtm;
}
static int ForEachVoxel( ZVoxelWorld * World, ZVoxelRef *v1, ZVoxelRef *v2, int (*f)(ZVoxelRef *v) );
};
#endif
--- End code ---
that I for implemented for the result of raycast at maxiter... before I changed method to render selection cube in space in front of player...
// modified GetVoxel which just returned the UShort from a world coordinate
// to return the World, Sector, relative x,y,z within the sector, and the UShort type
// the sector has it's x,y,z scaled world coordinate... so the absolute coord is knowable from the reference.
... reviewing this a little... the ref could be used for all the temp variables themselves... this actually returns a copy of the world coordinate, React uses it as x,y,z within the sector...
--- Code: ---inline ZVoxelRef *ZVoxelWorld::GetVoxelRef(Long x, Long y, Long z)
{
ZVoxelSector * Sector;
Long Offset;
Sector = FindSector( x>>ZVOXELBLOCSHIFT_X , y>>ZVOXELBLOCSHIFT_Y , z>>ZVOXELBLOCSHIFT_Z );
if (!Sector) return NULL;
Offset = (y & ZVOXELBLOCMASK_Y)
+ ((x & ZVOXELBLOCMASK_X) << ZVOXELBLOCSHIFT_Y )
+ ((z & ZVOXELBLOCMASK_Z) << (ZVOXELBLOCSHIFT_Y + ZVOXELBLOCSHIFT_X));
return new ZVoxelRef( this, VoxelTypeManager, x, y, z, Sector, Sector->Data[Offset], Offset );
}
inline UShort ZVoxelWorld::GetVoxel(Long x, Long y, Long z)
{
ZVoxelSector * Sector;
Long Offset;
Sector = FindSector( x>>ZVOXELBLOCSHIFT_X , y>>ZVOXELBLOCSHIFT_Y , z>>ZVOXELBLOCSHIFT_Z );
if (!Sector) return(-1);
Offset = (y & ZVOXELBLOCMASK_Y)
+ ((x & ZVOXELBLOCMASK_X) << ZVOXELBLOCSHIFT_Y )
+ ((z & ZVOXELBLOCMASK_Z) << (ZVOXELBLOCSHIFT_Y + ZVOXELBLOCSHIFT_X));
return(Sector->Data[Offset]);
}
--- End code ---
-------------
Used this in VoxelReactor to call React virtual method in voxel table...
--- Code: --- Long RSx = Sector->Pos_x << ZVOXELBLOCSHIFT_X;
Long RSy = Sector->Pos_y << ZVOXELBLOCSHIFT_Y;
Long RSz = Sector->Pos_z << ZVOXELBLOCSHIFT_Z;
ZVoxelRef ref(World,VoxelTypeManager, 0,0,0,Sector );
// z, x, y should just be ref.x, ref.y, ref.z, ... redundant copy
for (z = 0; z < ZVOXELBLOCSIZE_Z; z++)
for (x = 0; x < ZVOXELBLOCSIZE_X; x++)
for (y = 0; y < ZVOXELBLOCSIZE_Y; y++)
{
VoxelType = *(VoxelP);
if (ActiveTable->Get(VoxelType))
{
if (!Sector->ModifTracker.Get( MainOffset ) ) // If voxel is already processed, don't process it once more in the same cycle.
{
switch(VoxelType)
{
default:
ref.x = x; ref.y = y; ref.z = z;
ref.Offset = VoxelP - DisplayData;
ref.VoxelType = VoxelType;
IsActiveVoxels = true;
ref.VoxelExtension = (ZVoxelExtension*)Sector->OtherInfos[MainOffset];
// not sure what St is ...
//St[i]->ModifTracker.Set(SecondaryOffset[i]);
VoxelTypeManager->VoxelTable[VoxelType]->React( ref, LastLoopTime);
break;
--- End code ---
olive:
--- Quote from: d3x0r on November 09, 2014, 01:26:37 am ---Success!
I have a block called 'fertile ground' that in a second or 2 from creation grows 'low foliage' aka food.
food will eventually age and become medium and tall; which will be unusable by the first animals... a block called rabbit and a block called fox....
--- End quote ---
Interesting ideas. We dreamed to make some agriculture and food in blackvoxel since a long time. But, be careful of animals that could be present in some other voxel games.
Maybe you could make it as some extra terrestrial animals. If you keep reference to some known animals, they could be very different of what we know. In blackvoxel, the idea is that one should be surprised from what he is expecting (when possible, of course).
--- Quote ---------------
I implemented a type called ZVoxelRef
--- Code: ---#ifndef ZVOXEL_REF_DEFINED
#define ZVOXEL_REF_DEFINED
#include "ZVoxelSector.h"
#include "../ZVoxelExtension.h"
class ZVoxelWorld;
class ZVoxelRef
{
public:
ZVoxelSector * Sector;
int Offset;
int x, y, z;
UShort VoxelType;
ZVoxelWorld *World;
ZVoxelTypeManager *VoxelTypeManager;
ZVoxelExtension *VoxelExtension;
ZVoxelRef( ZVoxelWorld *world, ZVoxelTypeManager *vtm, long x = 0, long y = 0, long z = 0, ZVoxelSector *Sector=NULL, UShort VoxelType = 0, int offset = 0 )
{
this->x = x;
this->y = y;
this->z = z;
this->Sector = Sector;
this->Offset = offset;
this->World = world;
this->VoxelType = VoxelType;
VoxelTypeManager = vtm;
}
static int ForEachVoxel( ZVoxelWorld * World, ZVoxelRef *v1, ZVoxelRef *v2, int (*f)(ZVoxelRef *v) );
};
#endif
--- End code ---
that I for implemented for the result of raycast at maxiter... before I changed method to render selection cube in space in front of player...
// modified GetVoxel which just returned the UShort from a world coordinate
// to return the World, Sector, relative x,y,z within the sector, and the UShort type
// the sector has it's x,y,z scaled world coordinate... so the absolute coord is knowable from the reference.
... reviewing this a little... the ref could be used for all the temp variables themselves... this actually returns a copy of the world coordinate, React uses it as x,y,z within the sector...
--- Code: ---inline ZVoxelRef *ZVoxelWorld::GetVoxelRef(Long x, Long y, Long z)
{
ZVoxelSector * Sector;
Long Offset;
Sector = FindSector( x>>ZVOXELBLOCSHIFT_X , y>>ZVOXELBLOCSHIFT_Y , z>>ZVOXELBLOCSHIFT_Z );
if (!Sector) return NULL;
Offset = (y & ZVOXELBLOCMASK_Y)
+ ((x & ZVOXELBLOCMASK_X) << ZVOXELBLOCSHIFT_Y )
+ ((z & ZVOXELBLOCMASK_Z) << (ZVOXELBLOCSHIFT_Y + ZVOXELBLOCSHIFT_X));
return new ZVoxelRef( this, VoxelTypeManager, x, y, z, Sector, Sector->Data[Offset], Offset );
}
inline UShort ZVoxelWorld::GetVoxel(Long x, Long y, Long z)
{
ZVoxelSector * Sector;
Long Offset;
Sector = FindSector( x>>ZVOXELBLOCSHIFT_X , y>>ZVOXELBLOCSHIFT_Y , z>>ZVOXELBLOCSHIFT_Z );
if (!Sector) return(-1);
Offset = (y & ZVOXELBLOCMASK_Y)
+ ((x & ZVOXELBLOCMASK_X) << ZVOXELBLOCSHIFT_Y )
+ ((z & ZVOXELBLOCMASK_Z) << (ZVOXELBLOCSHIFT_Y + ZVOXELBLOCSHIFT_X));
return(Sector->Data[Offset]);
}
--- End code ---
--- End quote ---
There is a function like this in ZVoxelWorld::GetVoxelLocation() and the VoxelLocation structure returned provide most of the informations about a Voxel. I'll explain how to use it.
There is a particularity in C++ games : the operator "new" should be avoided in "game loops" whenever possible because use of the system heap memory manager is always slow (and could lead to massive use causing memory fragmentation problems).
There is no garbage collector like in C# or Java, so that's very different in programming style.
We used some ways to avoid problem:
In Blackvoxel, we use a custom "recycling" memory manager for some classes which absolutely needed "on the fly" memory allocation.
But the simpler and best way in most case is to declare a "stack variable" structure in the caller and pass it to the a function that will fill it. That's what GetVoxelLocation() is doing.
--- Quote ----------------
Used this in VoxelReactor to call React virtual method in voxel table...
--- Code: --- Long RSx = Sector->Pos_x << ZVOXELBLOCSHIFT_X;
Long RSy = Sector->Pos_y << ZVOXELBLOCSHIFT_Y;
Long RSz = Sector->Pos_z << ZVOXELBLOCSHIFT_Z;
ZVoxelRef ref(World,VoxelTypeManager, 0,0,0,Sector );
// z, x, y should just be ref.x, ref.y, ref.z, ... redundant copy
for (z = 0; z < ZVOXELBLOCSIZE_Z; z++)
for (x = 0; x < ZVOXELBLOCSIZE_X; x++)
for (y = 0; y < ZVOXELBLOCSIZE_Y; y++)
{
VoxelType = *(VoxelP);
if (ActiveTable->Get(VoxelType))
{
if (!Sector->ModifTracker.Get( MainOffset ) ) // If voxel is already processed, don't process it once more in the same cycle.
{
switch(VoxelType)
{
default:
ref.x = x; ref.y = y; ref.z = z;
ref.Offset = VoxelP - DisplayData;
ref.VoxelType = VoxelType;
IsActiveVoxels = true;
ref.VoxelExtension = (ZVoxelExtension*)Sector->OtherInfos[MainOffset];
// not sure what St is ...
//St[i]->ModifTracker.Set(SecondaryOffset[i]);
VoxelTypeManager->VoxelTable[VoxelType]->React( ref, LastLoopTime);
break;
--- End code ---
--- End quote ---
As we said, an extension will be added to support this kind of stuff. Be patient as we'll finish it in the next few days (we understand you need it).
We need to think it well and not precipitate because there is some important considerations to make it useable easily.
--- Quote ---// not sure what St is ...
--- End quote ---
St is a table holding sector pointers for the neighbor points. The problem with MVI is accessing quickly neighbor voxels that could be in another sectors than the one where the voxel is located.
The modif tracker is a system recalling "processed" voxels in order to avoid moving these again in the same MVI cycle.
The Blackvoxel Team
d3x0r:
--- Quote from: olive on November 09, 2014, 11:19:41 pm ---
Interesting ideas. We dreamed to make some agriculture and food in blackvoxel since a long time. But, be careful of animals that could be present in some other voxel games.
Maybe you could make it as some extra terrestrial animals. If you keep reference to some known animals, they could be very different of what we know. In blackvoxel, the idea is that one should be surprised from what he is expecting (when possible, of course).
--- End quote ---
I agree; was going to make some cyber-space names for them.. but fell back to my idea of foxes and rabbits .... predator/prey and make the foliage be like data nodes... but the concept of growth didn't apply so much... am open to ideas ... maybe 'bit scav'/'virus' and 'antivirus'... so a virus consumes data and anti-virus consumes viruses...
general properties of an 'animal'
health/data size
damage/data consumption
level/entities consumed (improve damage application effectiveness)
propagation_threshold - if health > threshold spawn new X
age maybe - builtin obsolescence
scent?
----------
Something I've been considering; making transparent voxels have a base voxel extension that can store other voxels... fish in water; animals in plants, animals sharing spots... 1) animals could attack horizontally and just require nearness... 2) animals could be 'on top of' plants... 3) could be a meta voxel type 'fish in water' that behaves like water... but...
Something I'd like to implement is 'smell' that is empty (air) voxels will be like a textureless voxel that's not zero; and contain information about aromas in the air...
normally the player isn't a voxel; so voxels that are player-like should also respect 'can player pass through' ... but then they would be in the voxel.... and since there's only a current reprensentation of a single voxel type in any voxel, it could be an interface implemented on transparent voxels that allow being 'in' the voxel. Can do a multi-layered render of the voxel at some point to represent the contained voxels... thoughts? I will assert that from previous experience, I think you'd prefer to keep to a purist approach, and animals will just hop from ground to the top of foliage, and animals can attack animals nearby disregarding height differences.. and then I can't really populate air ... because eventually aroma voxels will exist above fertile land and prevent growth....
--- Quote ---
There is a particularity in C++ games : the operator "new" should be avoided in "game loops" whenever possible because use of the system heap memory manager is always slow (and could lead to massive use causing memory fragmentation problems).
There is no garbage collector like in C# or Java, so that's very different in programming style.
--- End quote ---
depends on your allocer; most malloc will return the same block as was last freed... so allocating short term storage doesn't fragment.. yes returning into a caller-owned struct solves it.... was more concerned with keeping the function signature the same for quickness; and when not deleted was what was causing my memory error :) ended up removing that.
well... memory deallocated is defragmented and allowed for reuse; so the things you throw away are collected, so there is garbage collection; just not automagic object reference increment/decrements... and garbage on the stack is collected...
And even so; you still have to intentionally throw away things like Datatables because of the circular references they won't auto implode.... so you should never count on things just going away.
free works kinda like ZMonoSizeMemoryPool::Free such that when released, it's immediately reused for new mallocs...
I find it's better to do a slight more work, and do 2 comparisons in the free list to order free blocks from least memory address to most; further reducing fragmentation... also if the allocer has a minimum memory block size there's less chance of having blocks that are not big enough laying around; my MUD terminal (dekware) improved greatly by setting minimum alloc at 60 bytes... at not that much increase in memory usage.
Also block types that are tiny like binary tree nodes come from a pool of structs so there's only a few allocs from physical memory.
so ya; I saw the techniques and recognized their purpose :)
----------------------------
And back on aromatics; could do seed dropping too... for vegetation... maybe fertile gruond is just ground until fertilized... I kind of see an evolution of foliage more.. so it's not just that it's A plant that grows, but really the plant type changes... so when it propagates, a 'tall/thick foliage' would cause more thick folliage to grow... that itself being an enemy to the vegetation consumer... I guess the diffusion function would be different for that...
d3x0r:
Hmm... does the mem pool really help? Is new and delete not really based on malloc/free? Glibc usually collects garbage pretty well... I had my own memory allocator that was in the core of my library; still do, just hard to initialize a dynamic program without having initialized memory; and sometimes core debugging becomes impossible....
In my library I made a set allocator which is a linked list of nodes that have a bitmap mask of used mono-sized nodes... also keep a counter of used nodes so can just check if all used, otherwise scan the bits 32 at a time until it's not ~0 ... otherwise allocates a new block of nodes... so all nodes are continuous in memory ...
even if you're recycling previously allocated things, they're still jumbled all over memory....
d3x0r:
Managed to add Aroma Generator and Aroma types.
Aroma diffuses itself such that if it is near itself, it moves away from itself, otherwise if it is not in free space it moves. I dunno basically it works pretty well... gave me something to think about regarding inter-mingling voxels, and various things... like 'wind' .... and what it actually is useful for... and what meaning it would have...
the particles also age-out so after 12 seconds they evaporate... but worked good for a long time without despawning... also had a really cool overflow that resulted in a solid growing bubble form... not sure if the inside was just solid transparent of it if was just the outside sheet somehow... but it's fixed now and that was a memory fault anyway...
https://github.com/d3x0r/Blackvoxel/blob/group_voxel_datas/src/ZVoxelType_AromaGenerator.cpp
https://github.com/d3x0r/Blackvoxel/blob/group_voxel_datas/src/ZVoxelType_Aroma.cpp
also; in consideration of making a container for voxels, needed to know what a voxel was, so I reimplemented Sector->Data,OtherInfos,TempInfos as VoxelData *Data;
https://github.com/d3x0r/Blackvoxel/blob/group_voxel_datas/src/ZVoxelSector.h#L203
------------
just messing around... it is rather stable backend for dynamic updates to voxels ...
noting that there should be some more standard routines... like to get the array of voxels around a single voxel...
----------
Oh; a sector reactor... to do computations between sectors... could use a point per sector as a air pressure, and create a gradient between sectors for 'wind'... wind as a velocity could represent a propability of motion in time rather than a linear motion....
1.0 = 1 voxel in (100ms?) a 1 tick... how long is a 'tick' target?
0.5 = 50% chance to move 1 voxel in 1 tick.. something... for partial motions... I guess use the fraction as a proabilty to move one more (if available )... might work
Navigation
[0] Message Index
[#] Next page
Go to full version