Updated Tutorial 1 to match BasicExample.
This commit is contained in:
parent
03cd1ada6d
commit
a67f9f86fa
@ -14,20 +14,18 @@ To get started, we need to include the following headers:
|
|||||||
|
|
||||||
.. code-block:: c++
|
.. code-block:: c++
|
||||||
|
|
||||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
|
||||||
#include "PolyVoxCore/CubicSurfaceExtractorWithNormals.h"
|
#include "PolyVoxCore/CubicSurfaceExtractorWithNormals.h"
|
||||||
|
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||||
#include "PolyVoxCore/SurfaceMesh.h"
|
#include "PolyVoxCore/SurfaceMesh.h"
|
||||||
#include "PolyVoxCore/SimpleVolume.h"
|
#include "PolyVoxCore/SimpleVolume.h"
|
||||||
|
|
||||||
The most fundamental construct when working with PolyVox is that of the volume. This is represented by the :polyvox:`SimpleVolume` class and stores a 3D grid of voxels. Our basic example application creates a volume with the following line of code:
|
The most fundamental construct when working with PolyVox is that of the volume. This is represented by the :polyvox:`SimpleVolume` class which stores a 3D grid of voxels. Our basic example application creates a volume with the following line of code:
|
||||||
|
|
||||||
.. code-block:: c++
|
.. code-block:: c++
|
||||||
|
|
||||||
SimpleVolume<MaterialDensityPair44> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
|
SimpleVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
|
||||||
|
|
||||||
As can be seen, the SimpleVolume class is templated upon the voxel type. This means it is straightforward to create a volume of integers, floats, or a custom voxel type (see the :polyvox:`SimpleVolume documentation <PolyVox::SimpleVolume>` for more details). In this particular case we have created a volume in which each voxel is an instance of :polyvox:`MaterialDensityPair44`. Each instance of :polyvox:`MaterialDensityPair44` holds both a material and a density and uses four bits of data for each. This means that both the material and the density have a range of 0-15, and each voxel requires one byte of storage. For more information about materials and densities please consult the :doc:`principles of polyvox <principles>` document.
|
As can be seen, the SimpleVolume class is templated upon the voxel type. This means it is straightforward to create a volume of integers, floats, or a custom voxel type (see the :polyvox:`SimpleVolume documentation <PolyVox::SimpleVolume>` for more details). In this particular case we have created a volume in which each voxel is of type `uint8_t` which is an unsigned 8-bit integer.
|
||||||
|
|
||||||
Each voxel is initialised using its default constructor, which in the case of :polyvox:`MaterialDensityPair44` will mean that both the material and the density are set to zero. This corresponds to a volume full of empty space because the density of each voxel is below the threshold.
|
|
||||||
|
|
||||||
Next, we set some of the voxels in the volume to be 'solid' in order to create a large sphere in the centre of the volume. We do this with the following function call:
|
Next, we set some of the voxels in the volume to be 'solid' in order to create a large sphere in the centre of the volume. We do this with the following function call:
|
||||||
|
|
||||||
@ -39,38 +37,34 @@ Note that this function is part of the BasicExample (rather than being part of t
|
|||||||
|
|
||||||
.. code-block:: c++
|
.. code-block:: c++
|
||||||
|
|
||||||
void createSphereInVolume(SimpleVolume<MaterialDensityPair44>& volData, float fRadius)
|
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
|
||||||
{
|
{
|
||||||
//This vector hold the position of the center of the volume
|
//This vector hold the position of the center of the volume
|
||||||
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
|
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
|
||||||
|
|
||||||
//This three-level for loop iterates over every voxel in the volume
|
//This three-level for loop iterates over every voxel in the volume
|
||||||
for (int z = 0; z < volData.getWidth(); z++)
|
for (int z = 0; z < volData.getDepth(); z++)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < volData.getHeight(); y++)
|
for (int y = 0; y < volData.getHeight(); y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < volData.getDepth(); x++)
|
for (int x = 0; x < volData.getWidth(); x++)
|
||||||
{
|
{
|
||||||
//Store our current position as a vector...
|
//Store our current position as a vector...
|
||||||
Vector3DFloat v3dCurrentPos(x,y,z);
|
Vector3DFloat v3dCurrentPos(x,y,z);
|
||||||
//And compute how far the current position is from the center of the volume
|
//And compute how far the current position is from the center of the volume
|
||||||
float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length();
|
float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length();
|
||||||
|
|
||||||
|
uint8_t uVoxelValue = 0;
|
||||||
|
|
||||||
//If the current voxel is less than 'radius' units from the center then we make it solid.
|
//If the current voxel is less than 'radius' units from the center then we make it solid.
|
||||||
if(fDistToCenter <= fRadius)
|
if(fDistToCenter <= fRadius)
|
||||||
{
|
{
|
||||||
//Our new density value
|
//Our new voxel value
|
||||||
uint8_t uDensity = VoxelTypeTraits<MaterialDensityPair44>::MaxDensity;
|
uVoxelValue = 255;
|
||||||
|
|
||||||
//Get the old voxel
|
|
||||||
MaterialDensityPair44 voxel = volData.getVoxelAt(x,y,z);
|
|
||||||
|
|
||||||
//Modify the density
|
|
||||||
voxel.setDensity(uDensity);
|
|
||||||
|
|
||||||
//Wrte the voxel value into the volume
|
|
||||||
volData.setVoxelAt(x, y, z, voxel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Wrte the voxel value into the volume
|
||||||
|
volData.setVoxelAt(x, y, z, uVoxelValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,18 +74,18 @@ This function takes as input the :polyvox:`SimpleVolume` in which we want to cre
|
|||||||
|
|
||||||
Because this is a simple example function it always places the sphere at the centre of the volume. It computes this centre by halving the dimensions of the volume as given by the functions :polyvox:`SimpleVolume::getWidth`, :polyvox:`SimpleVolume::getHeight` and :polyvox:`SimpleVolume::getDepth`. The resulting position is stored using a :polyvox:`Vector3DFloat`. This is simply a typedef from our templatised :polyvox:`Vector` class, meaning that other sizes and storage types are available if you need them.
|
Because this is a simple example function it always places the sphere at the centre of the volume. It computes this centre by halving the dimensions of the volume as given by the functions :polyvox:`SimpleVolume::getWidth`, :polyvox:`SimpleVolume::getHeight` and :polyvox:`SimpleVolume::getDepth`. The resulting position is stored using a :polyvox:`Vector3DFloat`. This is simply a typedef from our templatised :polyvox:`Vector` class, meaning that other sizes and storage types are available if you need them.
|
||||||
|
|
||||||
Next, the function uses a three-level 'for' loop to iterate over each voxel in the volume. For each voxel it computes the distance from the voxel to the centre of the volume. If this distance is less than or equal to the specified radius then the voxel form part of the sphere and is made solid. During surface extraction, the voxel will be considered solid if it's density is set to any value greater than its threshold, which can be obtained by calling :polyvox:`MaterialDensityPair44::getThreshold <MaterialDensityPair::getThreshold>`. In our case we simply set it to the largest possible value by calling :polyvox:`VoxelTypeTraits<MaterialDensityPair44>::MaxDensity <VoxelTypeTraits<MaterialDensityPair44>::MaxDensity>`.
|
Next, the function uses a three-level 'for' loop to iterate over each voxel in the volume. For each voxel it computes the distance from the voxel to the centre of the volume. If this distance is less than or equal to the specified radius then the voxel forms part of the sphere and is made solid. During surface extraction, the voxel will be considered empty if it has a value of zero, and otherwise it will be considered solid. In our case we simply set it to 255 which is the largest value a uint8_t can contain.
|
||||||
|
|
||||||
Extracting the surface
|
Extracting the surface
|
||||||
======================
|
======================
|
||||||
Now that we have built our volume we need to convert it into a triangle mesh for rendering. This process is performed by the :polyvox:`CubicSurfaceExtractorWithNormals` class. An instance of the :polyvox:`CubicSurfaceExtractorWithNormals` is created as follows:
|
Now that we have built our volume we need to convert it into a triangle mesh for rendering. This process can be performed by the :polyvox:`CubicSurfaceExtractorWithNormals` class. An instance of the :polyvox:`CubicSurfaceExtractorWithNormals` is created as follows:
|
||||||
|
|
||||||
.. code-block:: c++
|
.. code-block:: c++
|
||||||
|
|
||||||
SurfaceMesh<PositionMaterialNormal> mesh;
|
SurfaceMesh<PositionMaterialNormal> mesh;
|
||||||
CubicSurfaceExtractorWithNormals<SimpleVolume, MaterialDensityPair44 > surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
|
CubicSurfaceExtractorWithNormals< SimpleVolume<uint8_t> > surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
|
||||||
|
|
||||||
The :polyvox:`CubicSurfaceExtractorWithNormals` takes a pointer to the volume data, and also it needs to be told which :polyvox:`Region` of the volume the extraction should be performed on (in more advanced application this is useful for extracting only those parts of the volume which have been modified since the last extraction). For our purposes the :polyvox:`SimpleVolume` class provides a convenient :polyvox:`SimpleVolume::getEnclosingRegion` function which returns a :polyvox:`Region` representing the whole volume. The constructor also takes a pointer to a :polyvox:`SurfaceMesh` object where it will store the result, so we need to create one of these before we can construct the :polyvox:`CubicSurfaceExtractorWithNormals`.
|
The :polyvox:`CubicSurfaceExtractorWithNormals` takes a pointer to the volume data, and also it needs to be told which :polyvox:`Region` of the volume the extraction should be performed on (in more advanced applications this is useful for extracting only those parts of the volume which have been modified since the last extraction). For our purpose the :polyvox:`SimpleVolume` class provides a convenient :polyvox:`SimpleVolume::getEnclosingRegion` function which returns a :polyvox:`Region` representing the whole volume. The constructor also takes a pointer to a :polyvox:`SurfaceMesh` object where it will store the result, so we need to create one of these before we can construct the :polyvox:`CubicSurfaceExtractorWithNormals`.
|
||||||
|
|
||||||
The actual extraction happens in the :polyvox:`CubicSurfaceExtractorWithNormals::execute` function. This means you can set up a :polyvox:`CubicSurfaceExtractorWithNormals` with the required parameters and then actually execute it later. For this example we just call it straight away.
|
The actual extraction happens in the :polyvox:`CubicSurfaceExtractorWithNormals::execute` function. This means you can set up a :polyvox:`CubicSurfaceExtractorWithNormals` with the required parameters and then actually execute it later. For this example we just call it straight away.
|
||||||
|
|
||||||
@ -101,6 +95,8 @@ The actual extraction happens in the :polyvox:`CubicSurfaceExtractorWithNormals:
|
|||||||
|
|
||||||
This fills in our :polyvox:`SurfaceMesh` object, which basically contains an index and vertex buffer representing the desired triangle mesh.
|
This fills in our :polyvox:`SurfaceMesh` object, which basically contains an index and vertex buffer representing the desired triangle mesh.
|
||||||
|
|
||||||
|
Note: If you like you can try swapping the :polyvox:`CubicSurfaceExtractorWithNormals` for :polyvox:`MarchingCubesSurfaceExtractor`. We have already included the relevant header, and in the BasicExample you just need to change which line in commented out. The :polyvox:`MarchingCubesSurfaceExtractor` makes use of a smooth density field and will consider a voxel to be solid if it is above a threshold of half the voxel's maximum value (so in this case that's half of 255, which is 127).
|
||||||
|
|
||||||
Rendering the surface
|
Rendering the surface
|
||||||
=====================
|
=====================
|
||||||
Rendering the surface with OpenGL is handled by the OpenGLWidget class. Again, this is not part of PolyVox, it is simply an example based on Qt and OpenGL which demonstrates how rendering can be performed. Within this class there are mainly two functions which are of interest - the OpenGLWidget::setSurfaceMeshToRender() function which constructs OpenGL buffers from our :polyvox:`SurfaceMesh` and the OpenGLWidget::paintGL() function which is called each frame to perform the rendering.
|
Rendering the surface with OpenGL is handled by the OpenGLWidget class. Again, this is not part of PolyVox, it is simply an example based on Qt and OpenGL which demonstrates how rendering can be performed. Within this class there are mainly two functions which are of interest - the OpenGLWidget::setSurfaceMeshToRender() function which constructs OpenGL buffers from our :polyvox:`SurfaceMesh` and the OpenGLWidget::paintGL() function which is called each frame to perform the rendering.
|
||||||
@ -150,8 +146,8 @@ With the OpenGL index and vertex buffers set up, we can now look at the code whi
|
|||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back
|
glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back
|
||||||
glRotatef(m_xRotation, 1.0f, 0.0f, 0.0f);
|
glRotatef(-m_xRotation, 0.0f, 1.0f, 0.0f);
|
||||||
glRotatef(m_yRotation, 0.0f, 1.0f, 0.0f);
|
glRotatef(-m_yRotation, 1.0f, 0.0f, 0.0f);
|
||||||
glTranslatef(-32.0f,-32.0f,-32.0f); //Centre volume and move back
|
glTranslatef(-32.0f,-32.0f,-32.0f); //Centre volume and move back
|
||||||
|
|
||||||
//Bind the index buffer
|
//Bind the index buffer
|
||||||
@ -163,6 +159,8 @@ With the OpenGL index and vertex buffers set up, we can now look at the code whi
|
|||||||
glNormalPointer(GL_FLOAT, sizeof(PositionMaterialNormal), (GLvoid*)12);
|
glNormalPointer(GL_FLOAT, sizeof(PositionMaterialNormal), (GLvoid*)12);
|
||||||
|
|
||||||
glDrawRangeElements(GL_TRIANGLES, m_uBeginIndex, m_uEndIndex-1, m_uEndIndex - m_uBeginIndex, GL_UNSIGNED_INT, 0);
|
glDrawRangeElements(GL_TRIANGLES, m_uBeginIndex, m_uEndIndex-1, m_uEndIndex - m_uBeginIndex, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
|
//Error checking code here...
|
||||||
}
|
}
|
||||||
|
|
||||||
Again, the explanation of this code is best left to the OpenGL documentation. Note that is is called automatically by Qt each time the display needs to be updated.
|
Again, the explanation of this code is best left to the OpenGL documentation. Note that is is called automatically by Qt each time the display needs to be updated.
|
Loading…
x
Reference in New Issue
Block a user