Volumes Of Fun
http://www.volumesoffun.com/phpBB3/

PolyVox and a Minecraft-esque Game
http://www.volumesoffun.com/phpBB3/viewtopic.php?f=14&t=236
Page 2 of 4

Author:  AuraofMana [ Thu Jun 23, 2011 8:54 pm ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

beyzend wrote:
That looks like off by one error, I think you are over extending the boundary. You have to really think about boundary conditions so you don't have off-by-one errors.

Oh wait I see it. I didn't check my partner's code and he was indeed off by 1 in x, y, and z :D

As for density, my partner just set every voxel to maxDensity. Obviously you can't do this anymore as a Material so I SetMaterial(1) instead. I will probably change this later because I may need at least 3 different density so I may do something like MaterialDensityPair<unsigned int, 30, 2>.

Thanks for your help!

Author:  DefiniteIntegral [ Fri Jun 24, 2011 3:53 am ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

Hello. In the images where you are using the number "10" with different colors on different faces, would you mind please posting your source image for the material? I am just interested to see how you did it.

Author:  AuraofMana [ Fri Jun 24, 2011 7:45 am ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

DefiniteIntegral wrote:
Hello. In the images where you are using the number "10" with different colors on different faces, would you mind please posting your source image for the material? I am just interested to see how you did it.


Sure, no problem.

VoxelTexture.material <-- Name doesn't matter for the material. You can set the name of the material in the material [name] loop. That's what you call in your ManualObject or Entity or whatever you are using.
Code:
vertex_program VoxelTexture_VP cg
{
   source VoxelTexture.cg
   entry_point voxeltexture_vp
   profiles vs_1_1

   default_params
    {
      param_named_auto world world_matrix
      param_named_auto viewProj viewproj_matrix
    }
}

fragment_program VoxelTexture_FP cg
{
   source VoxelTexture.cg
   entry_point voxeltexture_fp
   profiles ps_2_x ps_2_0
}

material VoxelTexture
{
   receive_shadows on
   technique
   {
      pass
      {
         vertex_program_ref VoxelTexture_VP
         {
         }
         
         fragment_program_ref VoxelTexture_FP
         {
         }
         
         texture_unit
         {
            texture Dirt.png 2d
            filtering anisotropic
                max_anisotropy 16
         }
      }
   }
}


For the image, I just grabbed the image in the materials/textures folder in the OGRESDK. It's called 10points.png and I just changed the name. It's a nice texture to check out how each face is being drawn.

VoxelTexture.cg <--This name does matter.
Code:
#define VOXELCENTEROFFSET 0.5

void voxeltexture_vp(
   float4 inPosition         : POSITION,

   out float4 outPosition     : POSITION,
   out float4 outWorldPosition   : TEXCOORD0,

   uniform float4x4 world,
   uniform float4x4 viewProj)
{
   outWorldPosition = mul(world, inPosition);
   
   outPosition = mul(viewProj, outWorldPosition);
}

void voxeltexture_fp(
   float4 inWorldPosition       : TEXCOORD0,

   out float4 color         : COLOR,

   uniform sampler2D texture)
{
   float3 worldNormal = cross(ddy(inWorldPosition.xyz), ddx(inWorldPosition.xyz));
   worldNormal = normalize(worldNormal);
   //Right
    if(worldNormal.x > 0.5)
    {
        color = tex2D(texture, float2((1 - inWorldPosition.z) + VOXELCENTEROFFSET, (1 - inWorldPosition.y) + VOXELCENTEROFFSET));
    }
   //Left
   if(worldNormal.x < -0.5)
    {
        color = tex2D(texture, float2(inWorldPosition.z + VOXELCENTEROFFSET, (1 - inWorldPosition.y) + VOXELCENTEROFFSET));
    }
   //Top
    if(worldNormal.y > 0.5)
    {
        color = tex2D(texture, inWorldPosition.xz + VOXELCENTEROFFSET);
    }
   //Bottom
    if(worldNormal.y < -0.5)
    {
        color = tex2D(texture, float2(inWorldPosition.x + VOXELCENTEROFFSET, (1 - inWorldPosition.z) + VOXELCENTEROFFSET));
    }
   //Front
    if(worldNormal.z > 0.5)
    {
        color = tex2D(texture, float2(inWorldPosition.x + VOXELCENTEROFFSET, (1 - inWorldPosition.y) + VOXELCENTEROFFSET));
    }
   //Back
    if(worldNormal.z < -0.5)
    {
        color = tex2D(texture, float2((1 - inWorldPosition.x) + VOXELCENTEROFFSET, (1 - inWorldPosition.y) + VOXELCENTEROFFSET));
    }
}


This does not color code the different faces however. I'll talk about that below.

A word of caution here. You can use:
Code:
if(worldNormal.x > 0.0)
if(worldNormal.x < 0.0)

as conditions for the x-axis (and same for y and z). This worked for me. I was running this in DX9 with an ATI card. OpenGL crashes Ogre for me (and not just in this project) so I have no way of knowing.

My partner has an nVidia card, and using 0.0 would cause the textures on the left/right and top/bottom images to look like static. Changing it to 0.5/-0.5 fixed it for him. He was also running in DX as OpenGL is causing problems. For his scenario with the OpenGL trouble it may be some other problem and not related to Ogre at all. But because of that I have no idea if this works for OpenGL. This is to let you and anyone who happens to find this know.

As for colors, inside one of the conditionals that test the normal, you can do something like this:

This is the original:
Code:
color = tex2D(texture, float2((1 - inWorldPosition.x) + VOXELCENTEROFFSET, (1 - inWorldPosition.y) + VOXELCENTEROFFSET));


Change it to:
Code:
float4 col; //More useful to define this outside of the conditional
col = tex2D(texture, float2((1 - inWorldPosition.x) + VOXELCENTEROFFSET, (1 - inWorldPosition.y) + VOXELCENTEROFFSET));
color = col + float4(0.0, 0.0, 0.0, 1.0);


Just replace the first 3 0.0's with your RGB color (last one is A). float4(1.0, 0.0, 0.0, 1.0) would give you red, float4(1.0, 1.0, 0.0, 1.0) would give you yellow and so on. I color coded the different faces when I was testing if I got their orientation right.

Lastly, you can also search through http://www.thermite3d.org/phpBB3/viewtopic.php?t=77 to find some additional things (like scaling, texture atlas). I based mine off of that with help. I'll end up doing things like those as well but right now I am messing with Libnoise to generate terrain so it will be a while.

Author:  DefiniteIntegral [ Fri Jun 24, 2011 11:15 am ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

Thanks.

To be honest I don't really know that much about materials since I have always just played around more with rendering systems and stuff. So I will probably copy some stuff from your material above to get started in working out materials if you don't mind.

Rather than a texture atlas, would it be possible to use multiple texture channels (1 for each face) and just choose which texture to sample from based on the face direction / normal? Graphics cards support 8 texture channels these days, right..... ?

I would have no idea how to do this though. I really should read up some noob tutorials on using .cg

Btw I have the same problem with Ogre and not being able to use OpenGL. Loading the OpenGL DLL crashes Ogre at startup.

Author:  beyzend [ Fri Jun 24, 2011 3:12 pm ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

The reason I'm using texture atlas is to reduce draw call since you can render an entire chunk in a single draw call. If you don't use texture atlas then you have to have N buffers ready each with it's own texture and write your mesh to these N buffers. I'm curious how this will performance so i'd appreciate it if someone posted their results here.

Author:  AuraofMana [ Sun Jun 26, 2011 8:19 am ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

Hey Beyzend (or anyone that has this working) can you show me how you did your texture atlas? I PM'd AndiNo and he gave me his C++ code and shader code, but he doesn't really remember what he did in the shader.

The idea of his code is to:
1. Find the col and row a texture is on. For example, (2, 1) is the texture on the second column, row 1 (columns and rows start at 1). He then passes this information inside a Ogre::ColourValue (specifically the r and g) which is then applied to the manual object. Since he has a 256x256 image and each texture is 16x16, there are 16 rows and columns. He will then multiply the column and row by 16/256 which are then stored in the r and g of the ColourValue (because it is [0.0, 1.0]).

2. Pass the color in the VP and into the FP. Now reconvert that color value to back to the column and row by dividing 16/256. After this he adds the uv coords you obtain using the world position.

I am failing somewhere and am just drawing the entire texture atlas as the image.

This is my vertex loop:
Code:
#define NUM_TEXTURES 256
#define NUM_NONTEX_MATERIALS 1 //Air is 0 for me, so the first texture is actually material 1, so materialID - 1 to offset this
#define NUM_TEXTURES_PER_ROW 16
#define TEX_WIDTH_NORMALIZED (NUM_TEXTURES_PER_ROW / NUM_TEXTURES) //This is 16/256

            vector<PositionMaterial>::iterator vecItr;
            for(vecItr = vecVertices.begin(); vecItr != vecVertices.end(); vecItr++)
            {
               Vector3DFloat pos = vecItr->getPosition() * worldScale;
               pos += (Vector3DFloat(j * chunkSize, k * chunkSize, i * chunkSize) * worldScale);
               obj->position(pos.getX(), pos.getY(), pos.getZ());

               Ogre::ColourValue colVal;
               uint8_t matID = vecItr->getMaterial(); //+ 0.5;
               colVal.r = ((matID - NUM_NONTEX_MATERIALS) % NUM_TEXTURES_PER_ROW) * TEX_WIDTH_NORMALIZED; // - pixelOffset;
               unsigned int matY = (unsigned int) (matID / NUM_TEXTURES_PER_ROW);
               colVal.g = (float) (matY * TEX_WIDTH_NORMALIZED);
               colVal.b = 0.0f;
               colVal.a = 1.0f;
               obj->colour(colVal);
            }


This is my shader code:
Code:
#define VOXELCENTEROFFSET 0.5
#define WORLDSCALE 2.0
//Pixel size of the texture atlas
#define TEXATLASSIZE 256
//Pixel per single texture
#define TEXATLAS_TEXSIZE 16
//Number of textures per row
#define TEXATLAS_NUMTEX (TEXATLASSIZE / TEXATLAS_TEXSIZE)
//Size of a single texture in 0.0 - 1.0 values
#define TEXATLAS_TEXSIZE_NORMALIZED ((float)TEXATLAS_TEXSIZE / (float)TEXATLASSIZE)

void voxeltexture_vp(
   float4 inPosition         : POSITION,
   float4 inColor            : COLOR,

   out float4 outPosition     : POSITION,
   out float4 outWorldPosition   : TEXCOORD0,
   out float4 outColor         : TEXCOORD1,

   uniform float4x4 world,
   uniform float4x4 viewProj)
{
   outWorldPosition = mul(world, inPosition);
   
   outPosition = mul(viewProj, outWorldPosition);
   
   outColor = inColor;
}

void voxeltexture_fp(
   float4 inWorldPosition       : TEXCOORD0,
   float4 inColor            : COLOR,

   out float4 color         : COLOR,

   uniform sampler2D texAtlas)
{
   float3 worldNormal = cross(ddy(inWorldPosition.xyz / WORLDSCALE), ddx(inWorldPosition.xyz / WORLDSCALE));
   worldNormal = normalize(worldNormal);
   
   float2 pos;
   
   //Handle texture atlas
   int2 posColor;
   // X coordinate
   inColor.x /= TEXATLAS_TEXSIZE_NORMALIZED;
   posColor.x = (int) inColor.x;
   pos.x = posColor.x;
   // Y coordinate
   inColor.y /= TEXATLAS_TEXSIZE_NORMALIZED;
   posColor.y = (int) inColor.y;
   pos.y = posColor.y;
   
   //Right
    if(worldNormal.x > 0.5)
    {
      pos += float2(1 - inWorldPosition.z / WORLDSCALE + VOXELCENTEROFFSET, 1 - inWorldPosition.y / WORLDSCALE + VOXELCENTEROFFSET);
    }
   //Left
   if(worldNormal.x < -0.5)
    {
        pos += float2(inWorldPosition.z / WORLDSCALE + VOXELCENTEROFFSET, 1 - inWorldPosition.y / WORLDSCALE + VOXELCENTEROFFSET);
    }
   //Top
    if(worldNormal.y > 0.5)
    {
        pos += float2(inWorldPosition.xz / WORLDSCALE + VOXELCENTEROFFSET);
    }
   //Bottom
    if(worldNormal.y < -0.5)
    {
        pos += float2(inWorldPosition.x / WORLDSCALE + VOXELCENTEROFFSET, 1 - inWorldPosition.z / WORLDSCALE + VOXELCENTEROFFSET);
    }
   //Front
    if(worldNormal.z > 0.5)
    {
        pos += float2(inWorldPosition.x / WORLDSCALE + VOXELCENTEROFFSET, 1 - inWorldPosition.y / WORLDSCALE + VOXELCENTEROFFSET);
    }
   //Back
    if(worldNormal.z < -0.5)
    {
        pos += float2(1 - inWorldPosition.x / WORLDSCALE + VOXELCENTEROFFSET, 1 - inWorldPosition.y / WORLDSCALE + VOXELCENTEROFFSET);
    }
   
   color = tex2D(texAtlas, pos);
}


Is the concept correct? Where am I failing? Do you have any other way to do this that may be better?

Author:  David Williams [ Mon Jun 27, 2011 11:37 pm ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

I don't have any code for you as I'm not using a texture atlas myself, but you seem to have the concept correct. Maybe you can debug it by replacing some of the variables with hard-coded values which you know are correct? And maybe also try a one dimensional texture atlas with only two textures to start with?

Author:  AuraofMana [ Wed Jun 29, 2011 11:55 pm ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

I'll work on texture atlas later. Right now I am trying to implement block removal. I am having setDynamic(true) on all manual objects I am constructing right now, and rebuilding the chunk the block belongs to through .beginUpdate(0) (each of my chunk is its own manual object so there's only 1 section). This stutters on 32^3 but runs smoothly on 16^3.

However when I remove a block, the voxels nearby do not get their texture displayed. This is very weird. Got some pictures of what I am talking about.

I removed the block in front of me and the block below it. This looks fine.

Image

I remove some block here, and the voxel below it doesn't display its up face:

Image

I know there is a valid block there because as soon as I remove it and the block behind it (I am thinking this is because the chunk gets rebuilt again and it comes up right).

Image

Why does this happen?

In addition it seems terribly inefficient to rebuild an entire chunk. Should I think about getting in the vertexBuffer myself and set it?

Here's my removal code just in case. The raycast function sets the material for the voxel to 0, and finds the chunk the block belongs to so I only have to rebuild that chunk. Also don't mind the ijk being set up weirdly in the string. This is because PolyVox and Havok sets their xyz differently.
Code:
void GraphicsManager::UpdateManualObject(SimpleVolume<VoxelMat>& volData, WorldTerrain wTerra, Vector3DInt32 chunkNum)
{
   int i = chunkNum.getX();
   int j = chunkNum.getY();
   int k = chunkNum.getZ();

   char str[50];
   sprintf(str, "%d-%d-%d", k, i, j);

   Vector3DInt32 start(i * chunkSize, j * chunkSize, k * chunkSize);
   Vector3DInt32 end((i + 1) * chunkSize - 1, (j + 1) * chunkSize - 1, (k + 1) * chunkSize - 1);

   SurfaceMesh<PositionMaterial> mesh;
   CubicSurfaceExtractor<SimpleVolume, VoxelMat> surfaceExtractor(&volData, Region(start, end), &mesh);
   surfaceExtractor.execute();

   ManualObject *obj = manualObjects[str];

   obj->estimateVertexCount(mesh.getNoOfVertices());
   obj->estimateIndexCount(mesh.getNoOfIndices());

   vector<uint32_t> vecIndices = mesh.getIndices();
   vector<PositionMaterial> vecVertices = mesh.getVertices();

   obj->beginUpdate(0);

   vector<PositionMaterial>::iterator vecItr;
   for(vecItr = vecVertices.begin(); vecItr != vecVertices.end(); vecItr++)
   {
      Vector3DFloat pos = vecItr->getPosition() * worldScale;
      pos += (Vector3DFloat(i * chunkSize, j * chunkSize, k * chunkSize) * worldScale);
      obj->position(pos.getX(), pos.getY(), pos.getZ());
   }

   vector<uint32_t>::iterator indVec;
   for(indVec = vecIndices.begin(); indVec != vecIndices.end(); indVec++)
   {
      obj->index(*indVec);
   }

   obj->end();
}


Hmm I Googled and asked around a bit and it seems it would be beneficial to turn D3DUSAGE_DYNAMIC on. Does anyone have any idea how to do this in Ogre? Googling this doesn't give me the results I want.

Author:  beyzend [ Thu Jun 30, 2011 2:33 am ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

I don't think there is a public way to set hardware buffer usage with manual object (could be wrong). Instead, you may want to skip the middle man and just work with Ogre hardware buffers, you can then set the buffer usage. http://www.ogre3d.org/docs/manual/manual_51.html#SEC275

I'm not sure how much this will affect performance. I meant to switch from manual object to hardware buffers to see but have not found the time to do so yet.

As for you border issues, well, that's one of the border issues you have to either resolve or made irrelevant (somehow). In my setup, because the way extractor works by needing to examine element x -1 at x, therefore on chunk borders you need to reextract a neighbor so the extracted state match. For example, let's say you delete element 31 on a chunk H, but it's neighbor (H+1)'s 0th element needs x - 1, which is element 31 on the chunk H. If you don't reextract (H+1) then you may see visual artifacts because both chunk no longer match.

Author:  AuraofMana [ Thu Jun 30, 2011 6:35 am ]
Post subject:  Re: PolyVox and a Minecraft-esque Game

I see what you mean about the neighbors thing. So you are saying that I could perform a check to see if the voxel is a bordering voxel. If so then just update the chunk(s) it is bordering as well. I'll probably just use this and see how the speed works. If it is unacceptable I may need to use the vertexBuffer directly, which would be a pain.

Page 2 of 4 All times are UTC
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/