Normalized line endings
This commit is contained in:
@ -1,49 +1,49 @@
|
||||
# Copyright (c) 2010-2012 Matt Williams
|
||||
#
|
||||
# This software is provided 'as-is', without any express or implied
|
||||
# warranty. In no event will the authors be held liable for any damages
|
||||
# arising from the use of this software.
|
||||
#
|
||||
# Permission is granted to anyone to use this software for any purpose,
|
||||
# including commercial applications, and to alter it and redistribute it
|
||||
# freely, subject to the following restrictions:
|
||||
#
|
||||
# 1. The origin of this software must not be misrepresented; you must not
|
||||
# claim that you wrote the original software. If you use this software
|
||||
# in a product, an acknowledgment in the product documentation would be
|
||||
# appreciated but is not required.
|
||||
#
|
||||
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||
# misrepresented as being the original software.
|
||||
#
|
||||
# 3. This notice may not be removed or altered from any source
|
||||
# distribution.
|
||||
|
||||
find_program(SPHINXBUILD_EXECUTABLE sphinx-build DOC "The location of the sphinx-build executable")
|
||||
|
||||
#if(SPHINXBUILD_EXECUTABLE AND QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
||||
if(SPHINXBUILD_EXECUTABLE)
|
||||
message(STATUS "Found `sphinx-build` at ${SPHINXBUILD_EXECUTABLE}")
|
||||
set(BUILD_MANUAL ON PARENT_SCOPE)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/conf.in.py ${CMAKE_CURRENT_BINARY_DIR}/conf.py @ONLY)
|
||||
#Generates the HTML docs and the Qt help file which can be opened with "assistant -collectionFile thermite.qhc"
|
||||
#add_custom_target(manual ${SPHINXBUILD_EXECUTABLE} -b qthelp ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${QT_QCOLLECTIONGENERATOR_EXECUTABLE} polyvox.qhcp -o polyvox.qhc)
|
||||
add_custom_target(manual
|
||||
${SPHINXBUILD_EXECUTABLE} -b html
|
||||
-c ${CMAKE_CURRENT_BINARY_DIR} #Load the conf.py from the binary dir
|
||||
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Building PolyVox manual"
|
||||
)
|
||||
add_dependencies(manual doc)
|
||||
set_target_properties(manual PROPERTIES PROJECT_LABEL "Manual") #Set label seen in IDE
|
||||
SET_PROPERTY(TARGET manual PROPERTY FOLDER "Documentation")
|
||||
else()
|
||||
if(NOT SPHINXBUILD_EXECUTABLE)
|
||||
message(STATUS "`sphinx-build` was not found. Try setting SPHINXBUILD_EXECUTABLE to its location.")
|
||||
endif()
|
||||
if(NOT QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
||||
message(STATUS "`qhelpgenerator` was not found. Try setting QT_QCOLLECTIONGENERATOR_EXECUTABLE to its location.")
|
||||
endif()
|
||||
set(BUILD_MANUAL OFF PARENT_SCOPE)
|
||||
endif()
|
||||
# Copyright (c) 2010-2012 Matt Williams
|
||||
#
|
||||
# This software is provided 'as-is', without any express or implied
|
||||
# warranty. In no event will the authors be held liable for any damages
|
||||
# arising from the use of this software.
|
||||
#
|
||||
# Permission is granted to anyone to use this software for any purpose,
|
||||
# including commercial applications, and to alter it and redistribute it
|
||||
# freely, subject to the following restrictions:
|
||||
#
|
||||
# 1. The origin of this software must not be misrepresented; you must not
|
||||
# claim that you wrote the original software. If you use this software
|
||||
# in a product, an acknowledgment in the product documentation would be
|
||||
# appreciated but is not required.
|
||||
#
|
||||
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||
# misrepresented as being the original software.
|
||||
#
|
||||
# 3. This notice may not be removed or altered from any source
|
||||
# distribution.
|
||||
|
||||
find_program(SPHINXBUILD_EXECUTABLE sphinx-build DOC "The location of the sphinx-build executable")
|
||||
|
||||
#if(SPHINXBUILD_EXECUTABLE AND QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
||||
if(SPHINXBUILD_EXECUTABLE)
|
||||
message(STATUS "Found `sphinx-build` at ${SPHINXBUILD_EXECUTABLE}")
|
||||
set(BUILD_MANUAL ON PARENT_SCOPE)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/conf.in.py ${CMAKE_CURRENT_BINARY_DIR}/conf.py @ONLY)
|
||||
#Generates the HTML docs and the Qt help file which can be opened with "assistant -collectionFile thermite.qhc"
|
||||
#add_custom_target(manual ${SPHINXBUILD_EXECUTABLE} -b qthelp ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${QT_QCOLLECTIONGENERATOR_EXECUTABLE} polyvox.qhcp -o polyvox.qhc)
|
||||
add_custom_target(manual
|
||||
${SPHINXBUILD_EXECUTABLE} -b html
|
||||
-c ${CMAKE_CURRENT_BINARY_DIR} #Load the conf.py from the binary dir
|
||||
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Building PolyVox manual"
|
||||
)
|
||||
add_dependencies(manual doc)
|
||||
set_target_properties(manual PROPERTIES PROJECT_LABEL "Manual") #Set label seen in IDE
|
||||
SET_PROPERTY(TARGET manual PROPERTY FOLDER "Documentation")
|
||||
else()
|
||||
if(NOT SPHINXBUILD_EXECUTABLE)
|
||||
message(STATUS "`sphinx-build` was not found. Try setting SPHINXBUILD_EXECUTABLE to its location.")
|
||||
endif()
|
||||
if(NOT QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
||||
message(STATUS "`qhelpgenerator` was not found. Try setting QT_QCOLLECTIONGENERATOR_EXECUTABLE to its location.")
|
||||
endif()
|
||||
set(BUILD_MANUAL OFF PARENT_SCOPE)
|
||||
endif()
|
||||
|
@ -1,25 +1,25 @@
|
||||
**************************
|
||||
Frequently Asked Questions
|
||||
**************************
|
||||
|
||||
Should I store my environment in a single big volume or break it down into several smaller ones?
|
||||
------------------------------------------------------------------------------------------------
|
||||
In most cases you should store you data in a single big volume, unless you are sure you understand the implications of doing otherwise.
|
||||
|
||||
Most algorithms in PolyVox operate on only a single volume (the exception to this are some of the image processing algorithms which use source and destination volumes, but even they use a *single* source and a *single* destination). The reason for this is that many algorithms require fast access to the neighbours of any given voxel. As an example, the surface extractors need to look at the neighbours of a voxel in order to determine whether triangles should be generated.
|
||||
|
||||
PolyVox volumes make it easy to access these neighbours, but the situation gets complex on the edge of a volume because the neighbouring voxels may not exist. It is possible to define a border value (which will be returned whenever you try to read a voxel outside the volume) but this doesn't handle all scenarios in the desired way. Often the most practical solution is to make the volume slightly larger than the data it needs to contain, and then avoid having your algorithms go right to the edge.
|
||||
|
||||
Having established that edge cases can be problematic, we can now see that storing your data as a set of adjacent volumes is undesirable because these edge cases then exist throughout the data set. This causes a lot of problems such as gaps between pieces of extracted terrain or discontinuities in the computed normals.
|
||||
|
||||
The usual reason why people attempt to break their terrain into separate volumes is so that they can perform some more advanced memory management for very big terrain, for example by only loading particular volumes into memory when they are within a certain distance from the camera. However, this kind of paging behaviour is already implemented by the LargeVolume class. The LargeVolume internally stores its data as a set of blocks, and does it in such a way that it is able to perform neighbourhood access across block boundaries. Whenever you find yourself trying to break terrain data into separate volumes you should probably use the LargeVolume instead.
|
||||
|
||||
Note that although you should only use a single volume for your data, it is still recommended that you split the mesh data into multiple pieces so that they can be culled against the view frustum, and so that you can update the smaller pieces more quickly when you need to. You can extract meshes from different parts of the volume by passing a Region to the surface extractor.
|
||||
|
||||
Lastly, note that there are exceptions to the 'one volume' rule. An example might be if you had a number of planets in space, in which case each planet could safely be a separate volume. These planets never touch, and so the artifacts which would occur on volume boundaries do not cause a problem.
|
||||
|
||||
Can I combine smooth meshes with cubic ones?
|
||||
--------------------------------------------
|
||||
We have never attempted to do this but in theory it should be possible. One option is simply to generate two meshes (one using the MarchingCubesSurfaceExtractor and the other using the CubicSurfaceExtractor) and render them on top of each other while allowing the depth buffer to resolve the intersections. Combining these two meshes into a single mesh is likely to be difficult as they use different vertex formats and have different texturing requirements (see the document on texture mapping).
|
||||
|
||||
**************************
|
||||
Frequently Asked Questions
|
||||
**************************
|
||||
|
||||
Should I store my environment in a single big volume or break it down into several smaller ones?
|
||||
------------------------------------------------------------------------------------------------
|
||||
In most cases you should store you data in a single big volume, unless you are sure you understand the implications of doing otherwise.
|
||||
|
||||
Most algorithms in PolyVox operate on only a single volume (the exception to this are some of the image processing algorithms which use source and destination volumes, but even they use a *single* source and a *single* destination). The reason for this is that many algorithms require fast access to the neighbours of any given voxel. As an example, the surface extractors need to look at the neighbours of a voxel in order to determine whether triangles should be generated.
|
||||
|
||||
PolyVox volumes make it easy to access these neighbours, but the situation gets complex on the edge of a volume because the neighbouring voxels may not exist. It is possible to define a border value (which will be returned whenever you try to read a voxel outside the volume) but this doesn't handle all scenarios in the desired way. Often the most practical solution is to make the volume slightly larger than the data it needs to contain, and then avoid having your algorithms go right to the edge.
|
||||
|
||||
Having established that edge cases can be problematic, we can now see that storing your data as a set of adjacent volumes is undesirable because these edge cases then exist throughout the data set. This causes a lot of problems such as gaps between pieces of extracted terrain or discontinuities in the computed normals.
|
||||
|
||||
The usual reason why people attempt to break their terrain into separate volumes is so that they can perform some more advanced memory management for very big terrain, for example by only loading particular volumes into memory when they are within a certain distance from the camera. However, this kind of paging behaviour is already implemented by the LargeVolume class. The LargeVolume internally stores its data as a set of blocks, and does it in such a way that it is able to perform neighbourhood access across block boundaries. Whenever you find yourself trying to break terrain data into separate volumes you should probably use the LargeVolume instead.
|
||||
|
||||
Note that although you should only use a single volume for your data, it is still recommended that you split the mesh data into multiple pieces so that they can be culled against the view frustum, and so that you can update the smaller pieces more quickly when you need to. You can extract meshes from different parts of the volume by passing a Region to the surface extractor.
|
||||
|
||||
Lastly, note that there are exceptions to the 'one volume' rule. An example might be if you had a number of planets in space, in which case each planet could safely be a separate volume. These planets never touch, and so the artifacts which would occur on volume boundaries do not cause a problem.
|
||||
|
||||
Can I combine smooth meshes with cubic ones?
|
||||
--------------------------------------------
|
||||
We have never attempted to do this but in theory it should be possible. One option is simply to generate two meshes (one using the MarchingCubesSurfaceExtractor and the other using the CubicSurfaceExtractor) and render them on top of each other while allowing the depth buffer to resolve the intersections. Combining these two meshes into a single mesh is likely to be difficult as they use different vertex formats and have different texturing requirements (see the document on texture mapping).
|
||||
|
||||
An alternative possibility may be to create a new surface extractor based on the Surface Nets (link) algorithm. The idea here is that the mesh would start with a cubic shape, but as the net was stretched it would be smoothed. The degree of smoothing could be controlled by a voxel property which could allow the full range from cubic to smooth to exist in a single mesh. As mentioned above, this may mean that some extra work has to be put into a fragment shader which is capable of texturing this kind of mesh. Come by the forums of you want to discuss this further.
|
@ -1,48 +1,48 @@
|
||||
***************
|
||||
Level of Detail
|
||||
***************
|
||||
When the PolyVox surface extractors are applied to volume data the resulting mesh can contain a very high number of triangles. For large voxel worlds this can cause both performance and memory problems. The performance problems occur due the the load on the vertex shader which has to process a large number of vertices, and also due to the setup costs of a large number of tiny (possibly sub-pixel) triangles. The memory costs result simply from having a large amount of data which does not actually contribute to the visual appearance of the scene.
|
||||
|
||||
For these reasons it is desirable to reduce the triangle count of the meshes as far as possible, especially as meshes move away from the camera. This document describes the various approaches which are available within PolyVox to achieve this. Generally these approaches are different for cubic meshes vs smooth meshes and so we address these cases separately.
|
||||
|
||||
Cubic Meshes
|
||||
============
|
||||
A naive implementation of a cubic surface extractor would generate a mesh containing a quad for voxel face which lies on the boundary between a solid and an empty voxel. The CubicSurfaceExtractor is indeed capable of generating such a mesh, but it also provides the option to merge adjacent quads into a single quad subject to various conditions being satisfied (e.g. the faces must have the same material). This merging process drastically reduces the amount of geometry which must be drawn but does not modify the shape of the mesh. Because of these desirable properties such merging is performed by default, but it can be disabled if necessary.
|
||||
|
||||
To our knowledge the only drawback of performing this quad merging is that it can create T-junctions in the resulting mesh. T-junctions are an undesirable property of mesh geometry because they can cause tiny cracks (usually just seen as flickering pixels) to occur between quads. The figure below shows a mesh before quad merging, a mesh where the merged quads have caused T-junctions, and how the resulting rendering might look (note the single pixel holes along the quad border).
|
||||
|
||||
*Add figure here...*
|
||||
|
||||
Vertices C and D are supposed to lie exactly along the line which has A and B as its end points, so in theory the mesh should be valid and should render correctly. The reason T-junctions cause a problem in practice is due to limitations of the floating point number representation. Depending on the transformations which are applied, it may be that the positions of C and/or D can not be represented precisely enough to exactly lie on the line between A and B.
|
||||
|
||||
*Demo correct mesh. mention we don't have a solution to generate it.*
|
||||
|
||||
Whether it's a problem in practice depends on hardware precision (16/32 bit), distance from origin, number of transforms which are applied, and probably a number of other factors. We have yet to investigate.
|
||||
|
||||
We don't currently have a real solution to this problem. In Voxeliens the borders between voxels were darkened to simulate ambient occlusion and this had the desirable side effect of making any flickering pixels very hard to see. It's also possible that anti-aliasing strategies can reduce the problem, and storing vertex positions as integers may help as well. Lastly, it may be possible to construct some kind of post-process which would repair the image where it identifies single pixel discontinuities in the depth buffer.
|
||||
|
||||
Smooth Meshes
|
||||
=============
|
||||
Level of detail for smooth meshes is a lot more complex than for cubic ones, and we'll admit upfront that we do not currently have a good solution to this problem. None the less, we do have a couple of partial solutions which you might be able to use or adapt for your specific scenario.
|
||||
|
||||
Techniques for performing level of detail on smooth meshes basically fall into two categories. The first category involves reducing the resolution of the volume data and then running the surface extractor on the smaller volume. This naturally generates a lower detail mesh which must then be scaled up to match the other meshes in the scene. The second category involves generating the mesh at full detail and then using traditional mesh simplification techniques to reduces the number of triangles. Both techniques are explored in more detail below.
|
||||
|
||||
Volume Reduction
|
||||
----------------
|
||||
The VolumeResampler class can be used to copy volume data from a source region to a destination region, and it handles the resampling of the voxel values in the event that the source and destination regions are not the same size. This is exactly what we need for implementing level of detail and the principle is demonstrated by the SmoothLOD sample (see the documentation for the SmoothLOD sample for more information).
|
||||
|
||||
One of the problems with this approach is that the lower resolution mesh does not *exactly* line up with the higher resolution mesh, and this can cause cracks to be visible where the two meshes meet. The SmoothLOD sample attempts to avoid this problem by overlapping the meshes slightly but this may not be effective in all situations or from all viewpoints.
|
||||
|
||||
An alternative is the Transvoxel algorithm (link) developed by Eric Lengyel. This essentially extends the original Marching Cubes lookup table with additional entries which handle seamless transitions between LOD levels, and it is a very promising solution to level of detail for voxel terrain. At this point in time we do not have an implementation of this algorithm but work is being undertaking in the area. For the latest developments see: http://www.volumesoffun.com/phpBB3/viewtopic.php?f=2&t=338
|
||||
|
||||
However, in all volume reduction approaches there is some uncertainty about how materials should be handled. Creating a lower resolution volume means that several voxel values from the high resolution volume need to be combined into a single value. For density values this is straightforward as a simple average gives good results, but it is not clear how this extends to material identifiers. Averaging them doesn't make sense, and it is hard to imagine an approach which would not lead to visible artifacts as LOD levels change. Perhaps the visible effects can be reduced by blending between two LOD levels, but more investigation needs to be done here.
|
||||
|
||||
Mesh Simplification
|
||||
-------------------
|
||||
The other main approach is to generate the mesh at the full resolution and then reduce the number of triangles using a postprocessing step. This can draw on the large body of mesh simplification research (link to survey) and typically involves merging adjacent faces or collapsing vertices. When using this approach there are a couple of additional complications compared to the implementations which are normally seen.
|
||||
|
||||
The first additional complication is that the decimation algorithm needs to preserve material boundaries so that they don't move between LOD levels. When choosing whether a particular simplification can be made (i.e deciding if one vertex can be collapsed on to another or whether two faces can be merged) a metric is usually used to determine how much the simplification would affect the visual appearance of the mesh. When working with smooth voxel meshes this metric needs to also consider the material identifiers.
|
||||
|
||||
We also need to ensure that the metric preserves the geometric boundary of the mesh, so that no cracks are visible when a simplified mesh is place next to an original one. Maintaining this geometric boundary can be difficult, as the straightforward approach of locking the edge vertices in place will tend to limit the amount of simplification which can be performed. Alternatively, cracks can be allowed to develop if they are later hidden through the use of 'skirts' around the resulting mesh.
|
||||
|
||||
PolyVox used to contain code for performing simplification of the smooth voxel meshes, but unfortunately it had significant performance and functionality issues. Therefore it has been deprecated and it will be removed in a future version of the library. We will instead investigate the use of external mesh simplification libraries and OpenMesh (link) may be a good candidate here.
|
||||
***************
|
||||
Level of Detail
|
||||
***************
|
||||
When the PolyVox surface extractors are applied to volume data the resulting mesh can contain a very high number of triangles. For large voxel worlds this can cause both performance and memory problems. The performance problems occur due the the load on the vertex shader which has to process a large number of vertices, and also due to the setup costs of a large number of tiny (possibly sub-pixel) triangles. The memory costs result simply from having a large amount of data which does not actually contribute to the visual appearance of the scene.
|
||||
|
||||
For these reasons it is desirable to reduce the triangle count of the meshes as far as possible, especially as meshes move away from the camera. This document describes the various approaches which are available within PolyVox to achieve this. Generally these approaches are different for cubic meshes vs smooth meshes and so we address these cases separately.
|
||||
|
||||
Cubic Meshes
|
||||
============
|
||||
A naive implementation of a cubic surface extractor would generate a mesh containing a quad for voxel face which lies on the boundary between a solid and an empty voxel. The CubicSurfaceExtractor is indeed capable of generating such a mesh, but it also provides the option to merge adjacent quads into a single quad subject to various conditions being satisfied (e.g. the faces must have the same material). This merging process drastically reduces the amount of geometry which must be drawn but does not modify the shape of the mesh. Because of these desirable properties such merging is performed by default, but it can be disabled if necessary.
|
||||
|
||||
To our knowledge the only drawback of performing this quad merging is that it can create T-junctions in the resulting mesh. T-junctions are an undesirable property of mesh geometry because they can cause tiny cracks (usually just seen as flickering pixels) to occur between quads. The figure below shows a mesh before quad merging, a mesh where the merged quads have caused T-junctions, and how the resulting rendering might look (note the single pixel holes along the quad border).
|
||||
|
||||
*Add figure here...*
|
||||
|
||||
Vertices C and D are supposed to lie exactly along the line which has A and B as its end points, so in theory the mesh should be valid and should render correctly. The reason T-junctions cause a problem in practice is due to limitations of the floating point number representation. Depending on the transformations which are applied, it may be that the positions of C and/or D can not be represented precisely enough to exactly lie on the line between A and B.
|
||||
|
||||
*Demo correct mesh. mention we don't have a solution to generate it.*
|
||||
|
||||
Whether it's a problem in practice depends on hardware precision (16/32 bit), distance from origin, number of transforms which are applied, and probably a number of other factors. We have yet to investigate.
|
||||
|
||||
We don't currently have a real solution to this problem. In Voxeliens the borders between voxels were darkened to simulate ambient occlusion and this had the desirable side effect of making any flickering pixels very hard to see. It's also possible that anti-aliasing strategies can reduce the problem, and storing vertex positions as integers may help as well. Lastly, it may be possible to construct some kind of post-process which would repair the image where it identifies single pixel discontinuities in the depth buffer.
|
||||
|
||||
Smooth Meshes
|
||||
=============
|
||||
Level of detail for smooth meshes is a lot more complex than for cubic ones, and we'll admit upfront that we do not currently have a good solution to this problem. None the less, we do have a couple of partial solutions which you might be able to use or adapt for your specific scenario.
|
||||
|
||||
Techniques for performing level of detail on smooth meshes basically fall into two categories. The first category involves reducing the resolution of the volume data and then running the surface extractor on the smaller volume. This naturally generates a lower detail mesh which must then be scaled up to match the other meshes in the scene. The second category involves generating the mesh at full detail and then using traditional mesh simplification techniques to reduces the number of triangles. Both techniques are explored in more detail below.
|
||||
|
||||
Volume Reduction
|
||||
----------------
|
||||
The VolumeResampler class can be used to copy volume data from a source region to a destination region, and it handles the resampling of the voxel values in the event that the source and destination regions are not the same size. This is exactly what we need for implementing level of detail and the principle is demonstrated by the SmoothLOD sample (see the documentation for the SmoothLOD sample for more information).
|
||||
|
||||
One of the problems with this approach is that the lower resolution mesh does not *exactly* line up with the higher resolution mesh, and this can cause cracks to be visible where the two meshes meet. The SmoothLOD sample attempts to avoid this problem by overlapping the meshes slightly but this may not be effective in all situations or from all viewpoints.
|
||||
|
||||
An alternative is the Transvoxel algorithm (link) developed by Eric Lengyel. This essentially extends the original Marching Cubes lookup table with additional entries which handle seamless transitions between LOD levels, and it is a very promising solution to level of detail for voxel terrain. At this point in time we do not have an implementation of this algorithm but work is being undertaking in the area. For the latest developments see: http://www.volumesoffun.com/phpBB3/viewtopic.php?f=2&t=338
|
||||
|
||||
However, in all volume reduction approaches there is some uncertainty about how materials should be handled. Creating a lower resolution volume means that several voxel values from the high resolution volume need to be combined into a single value. For density values this is straightforward as a simple average gives good results, but it is not clear how this extends to material identifiers. Averaging them doesn't make sense, and it is hard to imagine an approach which would not lead to visible artifacts as LOD levels change. Perhaps the visible effects can be reduced by blending between two LOD levels, but more investigation needs to be done here.
|
||||
|
||||
Mesh Simplification
|
||||
-------------------
|
||||
The other main approach is to generate the mesh at the full resolution and then reduce the number of triangles using a postprocessing step. This can draw on the large body of mesh simplification research (link to survey) and typically involves merging adjacent faces or collapsing vertices. When using this approach there are a couple of additional complications compared to the implementations which are normally seen.
|
||||
|
||||
The first additional complication is that the decimation algorithm needs to preserve material boundaries so that they don't move between LOD levels. When choosing whether a particular simplification can be made (i.e deciding if one vertex can be collapsed on to another or whether two faces can be merged) a metric is usually used to determine how much the simplification would affect the visual appearance of the mesh. When working with smooth voxel meshes this metric needs to also consider the material identifiers.
|
||||
|
||||
We also need to ensure that the metric preserves the geometric boundary of the mesh, so that no cracks are visible when a simplified mesh is place next to an original one. Maintaining this geometric boundary can be difficult, as the straightforward approach of locking the edge vertices in place will tend to limit the amount of simplification which can be performed. Alternatively, cracks can be allowed to develop if they are later hidden through the use of 'skirts' around the resulting mesh.
|
||||
|
||||
PolyVox used to contain code for performing simplification of the smooth voxel meshes, but unfortunately it had significant performance and functionality issues. Therefore it has been deprecated and it will be removed in a future version of the library. We will instead investigate the use of external mesh simplification libraries and OpenMesh (link) may be a good candidate here.
|
||||
|
@ -1,45 +1,45 @@
|
||||
********
|
||||
Lighting
|
||||
********
|
||||
Lighting is an important part of creating a realistic scene, and fortunately most common lighting solutions can be easily applied to PolyVox meshes. In this document we describe how to implement dynamic lighting and ambient occlusion with PolyVox.
|
||||
|
||||
Dynamic Lighting
|
||||
================
|
||||
In general, any lighting solution for real-time 3D graphics should be directly applicable to PolyVox meshes.
|
||||
|
||||
Normal Calculation for Smooth Meshes
|
||||
------------------------------------
|
||||
When working with smooth voxel terrain meshes, PolyVox provides vertex normals as part of the extracted surface mesh. A common approach for computing these normals would be to compute normals for each face in the mesh, and then compute the vertex normals as a weighted average of the normals of the faces which share it. Actually this is not the approach used by PolyVox, as PolyVox instead computes the vertex normals directly from the underlying volume data.
|
||||
|
||||
More specifically, PolyVox is able to compute the *gradient* of the volume data at any given point using well established image processing methods. The normalised gradient value is used as the vertex normal and in general it is smoother than the value computed by averaging neighbouring faces. Actually there are two approaches to this gradient computation known as *Central Differencing* and *Sobel Filter*. The central differencing approach is generally recommended but the Sobel filter can be used to obtain slightly smoother results but with lower performance. See the MarchingCubesSurfaceExtractor documentation for details on how to select between these (check this exists...).
|
||||
|
||||
Normal Calculation for Cubic Meshes
|
||||
-----------------------------------
|
||||
For cubic meshes PolyVox doesn't actually generate any vertex normals at all, and this is often a source of confusion for new users. The reason for this is that we wish to to perform per-face lighting rather than per-vertex lighting. Considering the case of a single cube, if we wanted to perform per-face lighting based on per-vertex normals then the normals cannot be shared between adjacent faces and so each vertex needs to be duplicated three times (one for each face which uses it). This means we would need 24 vertices to represent a cube which intuitively should only need eight vertices.
|
||||
|
||||
Therefore PolyVox does not generate per-vertex normals for cubic meshes, and as a result the cubic mesh's vertices are both smaller and less numerous. Of course, we still need a way to obtain normals for lighting calculations and so our suggestion is to compute the normals in a fragment program using the *derivative operations* which are provided by modern graphics hardware.
|
||||
|
||||
The description here is rather oversimplified, but the idea behind these operation is that they can tell you how much a variable has changed between two adjacent pixels. If we use our fragment world space position as the input to these derivative operations then we can obtain two vectors which lie on the surface of our face. The cross product of these then gives us a vector which is perpendicular to both and which is therefore our normal.
|
||||
|
||||
Further information about the derivative operations can be found in the OpenGL/Direct3D API documentation, but the implementation in code is quite simple. Firstly you need to make sure that you have access to the fragments world space position in your shader, which means you need to pass it through from the vertex shader. Then you can use the following code in your fragment shader:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
vec3 worldNormal = cross(dFdy(inWorldPosition.xyz), dFdx(inWorldPosition.xyz));
|
||||
worldNormal = normalize(worldNormal);
|
||||
|
||||
**TODO: Check the normal direction**
|
||||
|
||||
Similar code can be implemented in HLSL but you may need to invert the normal due to coordinate system differences between the two APIs. Also, be aware that it may be necessary to use OpenGL ES XXX extension in order to access this derivative functionality on mobile hardware.
|
||||
|
||||
Shadows
|
||||
-------
|
||||
To date we have only experimented with shadow maps as a solution to the real time shadowing problem and have found they work very well for both casting and receiving. The approach is essentially the same as for any other geometry and the usual approaches can be used for setting up the projection and for filtering the result. One PolyVox specific tip we can give is that you don't need to take account of materials when rendering into the shadow map as you always draw in black, so if you are splitting you geometry for material blending or for handling a large number of materials then you don't need to do this when rendering shadows. Using separate shadow geometry with all materials combined may decrease your batch count in this case.
|
||||
|
||||
The most widely used alternative to shadow maps is shadow volumes but we have not tested these with PolyVox. We do not expect these will provide a good solution because meshes usually require additional edge information to allow the shadow volume to be extruded from the silhouette and PolyVox does not provide this. Even if this edge information could be calculated, it would be invalidated each time the mesh changed which would make dynamic terrain more difficult.
|
||||
|
||||
Overall we would recommend you make use of shadow maps for dynamic shadows.
|
||||
|
||||
Ambient Occlusion
|
||||
=================
|
||||
********
|
||||
Lighting
|
||||
********
|
||||
Lighting is an important part of creating a realistic scene, and fortunately most common lighting solutions can be easily applied to PolyVox meshes. In this document we describe how to implement dynamic lighting and ambient occlusion with PolyVox.
|
||||
|
||||
Dynamic Lighting
|
||||
================
|
||||
In general, any lighting solution for real-time 3D graphics should be directly applicable to PolyVox meshes.
|
||||
|
||||
Normal Calculation for Smooth Meshes
|
||||
------------------------------------
|
||||
When working with smooth voxel terrain meshes, PolyVox provides vertex normals as part of the extracted surface mesh. A common approach for computing these normals would be to compute normals for each face in the mesh, and then compute the vertex normals as a weighted average of the normals of the faces which share it. Actually this is not the approach used by PolyVox, as PolyVox instead computes the vertex normals directly from the underlying volume data.
|
||||
|
||||
More specifically, PolyVox is able to compute the *gradient* of the volume data at any given point using well established image processing methods. The normalised gradient value is used as the vertex normal and in general it is smoother than the value computed by averaging neighbouring faces. Actually there are two approaches to this gradient computation known as *Central Differencing* and *Sobel Filter*. The central differencing approach is generally recommended but the Sobel filter can be used to obtain slightly smoother results but with lower performance. See the MarchingCubesSurfaceExtractor documentation for details on how to select between these (check this exists...).
|
||||
|
||||
Normal Calculation for Cubic Meshes
|
||||
-----------------------------------
|
||||
For cubic meshes PolyVox doesn't actually generate any vertex normals at all, and this is often a source of confusion for new users. The reason for this is that we wish to to perform per-face lighting rather than per-vertex lighting. Considering the case of a single cube, if we wanted to perform per-face lighting based on per-vertex normals then the normals cannot be shared between adjacent faces and so each vertex needs to be duplicated three times (one for each face which uses it). This means we would need 24 vertices to represent a cube which intuitively should only need eight vertices.
|
||||
|
||||
Therefore PolyVox does not generate per-vertex normals for cubic meshes, and as a result the cubic mesh's vertices are both smaller and less numerous. Of course, we still need a way to obtain normals for lighting calculations and so our suggestion is to compute the normals in a fragment program using the *derivative operations* which are provided by modern graphics hardware.
|
||||
|
||||
The description here is rather oversimplified, but the idea behind these operation is that they can tell you how much a variable has changed between two adjacent pixels. If we use our fragment world space position as the input to these derivative operations then we can obtain two vectors which lie on the surface of our face. The cross product of these then gives us a vector which is perpendicular to both and which is therefore our normal.
|
||||
|
||||
Further information about the derivative operations can be found in the OpenGL/Direct3D API documentation, but the implementation in code is quite simple. Firstly you need to make sure that you have access to the fragments world space position in your shader, which means you need to pass it through from the vertex shader. Then you can use the following code in your fragment shader:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
vec3 worldNormal = cross(dFdy(inWorldPosition.xyz), dFdx(inWorldPosition.xyz));
|
||||
worldNormal = normalize(worldNormal);
|
||||
|
||||
**TODO: Check the normal direction**
|
||||
|
||||
Similar code can be implemented in HLSL but you may need to invert the normal due to coordinate system differences between the two APIs. Also, be aware that it may be necessary to use OpenGL ES XXX extension in order to access this derivative functionality on mobile hardware.
|
||||
|
||||
Shadows
|
||||
-------
|
||||
To date we have only experimented with shadow maps as a solution to the real time shadowing problem and have found they work very well for both casting and receiving. The approach is essentially the same as for any other geometry and the usual approaches can be used for setting up the projection and for filtering the result. One PolyVox specific tip we can give is that you don't need to take account of materials when rendering into the shadow map as you always draw in black, so if you are splitting you geometry for material blending or for handling a large number of materials then you don't need to do this when rendering shadows. Using separate shadow geometry with all materials combined may decrease your batch count in this case.
|
||||
|
||||
The most widely used alternative to shadow maps is shadow volumes but we have not tested these with PolyVox. We do not expect these will provide a good solution because meshes usually require additional edge information to allow the shadow volume to be extruded from the silhouette and PolyVox does not provide this. Even if this edge information could be calculated, it would be invalidated each time the mesh changed which would make dynamic terrain more difficult.
|
||||
|
||||
Overall we would recommend you make use of shadow maps for dynamic shadows.
|
||||
|
||||
Ambient Occlusion
|
||||
=================
|
||||
This is an area in which we want to undertake more research in order to get effective ambient occlusion into PolyVox scenes. In the mean time SSAO has proved to be a popular solution.
|
@ -1,4 +1,4 @@
|
||||
=================
|
||||
Modifying Terrain
|
||||
=================
|
||||
=================
|
||||
Modifying Terrain
|
||||
=================
|
||||
This document has yet to be written.
|
@ -1,38 +1,38 @@
|
||||
*************
|
||||
Prerequisites
|
||||
*************
|
||||
The PolyVox library is aimed at experienced games and graphics programmers who wish to incorporate voxel environments into their applications. It is not a drop in solution, but instead provides a set of building blocks which you can use to construct a complete system. Most of the functionality could be considered quite low-level. For example, it provides mesh extractors to generate a surface from volume data but requires the application developer to handle all tasks related to scene management and rendering of such data.
|
||||
|
||||
As a result you will need a decent amount of graphics programming experience to effectively make use of the library. The purpose of this document is to highlight some of the core areas with which you will need to be familiar. In some cases we also provide links to places where you can find more information about the subject in question.
|
||||
|
||||
You should also be aware that voxel terrain is still an open research area and has not yet seen widespread adoption in games and simulations. There are many questions to which we do not currently know the best answer and so you may have to do some research and experimentation yourself when trying to obtain your desired result. Please do let us know if you come up with a trick or technique which you think could benefit other users.
|
||||
|
||||
Programming
|
||||
===========
|
||||
This section describes some of the programming concepts with which you will need to be familiar:
|
||||
|
||||
**C++:** PolyVox is written using the C++ language and we expect this is what the majority of our users will be developing in. You will need to be familiar with the basic process of building and linking against external libraries as well as setting up your development environment. Note that you do have the option of working with other languages via the SWIG bindings but you may not have as much flexibility with this approach.
|
||||
|
||||
**Templates:** PolyVox also makes heavy use of template programming in order to be both fast and generic, so familiarity with templates will be very useful. You shouldn't need to do much template programming yourself but an understanding of them will help you understand errors and resolve any problems.
|
||||
|
||||
**Callbacks:** Several of the algorithms in PolyVox can be customised through the use of callbacks. In general there are sensible defaults provided, but for maximum control you may wish to learn how to define you own. In general the principle is similar to the way in which callbacks are used in the STL.
|
||||
|
||||
Graphics Concepts
|
||||
=================
|
||||
Several core graphics principles will be useful in understanding and using PolyVox:
|
||||
|
||||
**Volume representation:** PolyVox revolves around the idea of storing and manipulating volume data and using this as a representation of a 3d world. This is a fairly intuitive extension of traditional heightmap terrain but does require the ability to 'think in 3D'. You will need to understand that data stored in this way can become very large, and understand the idea that paging and compression can be used to combat this.
|
||||
|
||||
**Mesh representation:** Most PolyVox projects will involve using one of the surface extractors, which output their data as index and vertex buffers. Therefore you will need to understand this representation in order to pass the data to your rendering engine or in order to perform further modifications to it. You can find out about more about this here: (ADD LINK)
|
||||
|
||||
**Image processing:** For certain advanced application an understanding of image processing methods can be useful. For example the process of blurring an image via a low pass filter can be used to effectively smooth out voxel terrain. There are plans to add more image processing operations to PolyVox particularly with regard to morphological operations which you might want to use to modify your environment.
|
||||
|
||||
Rendering
|
||||
=========
|
||||
**Runtime geometry creation:** PolyVox is independent of any particular graphics API which means it outputs its data using API-neutral structures such as index and vertex buffers (as mentioned above). You will need to write the code which converts these structures into a format which your API or engine can understand. This is not a difficult task but does require some knowledge of the rendering technology which you are using.
|
||||
|
||||
**Scene management:** PolyVox is only responsible for providing you with the mesh data to be displayed, so you application or engine will need to make sensible decisions about how this data should be organised in terms of a spatial structure (octree, bounding volumes tree, etc). It will also need to provide some approach to visibility determination such as frustum culling or a more advanced approach. If you are integrating PolyVox with an existing rendering engine then you should find that many of these aspects are already handled for you.
|
||||
|
||||
**Shader programming:** The meshes which are generated by PolyVox are very basic in terms of the vertex data they provide. You get vertex positions, a material identifier, and sometimes vertex normals. It is the responsibility of application programmer to decide how to use this data to create visually interesting renderings. This means you will almost certainly want to make use of shader programs. Of course, in our texturing (LINK) and lighting (LINK) documents you will find many ideas and common solutions, but you will need strong shader programming experience to make effective use of these.
|
||||
|
||||
*************
|
||||
Prerequisites
|
||||
*************
|
||||
The PolyVox library is aimed at experienced games and graphics programmers who wish to incorporate voxel environments into their applications. It is not a drop in solution, but instead provides a set of building blocks which you can use to construct a complete system. Most of the functionality could be considered quite low-level. For example, it provides mesh extractors to generate a surface from volume data but requires the application developer to handle all tasks related to scene management and rendering of such data.
|
||||
|
||||
As a result you will need a decent amount of graphics programming experience to effectively make use of the library. The purpose of this document is to highlight some of the core areas with which you will need to be familiar. In some cases we also provide links to places where you can find more information about the subject in question.
|
||||
|
||||
You should also be aware that voxel terrain is still an open research area and has not yet seen widespread adoption in games and simulations. There are many questions to which we do not currently know the best answer and so you may have to do some research and experimentation yourself when trying to obtain your desired result. Please do let us know if you come up with a trick or technique which you think could benefit other users.
|
||||
|
||||
Programming
|
||||
===========
|
||||
This section describes some of the programming concepts with which you will need to be familiar:
|
||||
|
||||
**C++:** PolyVox is written using the C++ language and we expect this is what the majority of our users will be developing in. You will need to be familiar with the basic process of building and linking against external libraries as well as setting up your development environment. Note that you do have the option of working with other languages via the SWIG bindings but you may not have as much flexibility with this approach.
|
||||
|
||||
**Templates:** PolyVox also makes heavy use of template programming in order to be both fast and generic, so familiarity with templates will be very useful. You shouldn't need to do much template programming yourself but an understanding of them will help you understand errors and resolve any problems.
|
||||
|
||||
**Callbacks:** Several of the algorithms in PolyVox can be customised through the use of callbacks. In general there are sensible defaults provided, but for maximum control you may wish to learn how to define you own. In general the principle is similar to the way in which callbacks are used in the STL.
|
||||
|
||||
Graphics Concepts
|
||||
=================
|
||||
Several core graphics principles will be useful in understanding and using PolyVox:
|
||||
|
||||
**Volume representation:** PolyVox revolves around the idea of storing and manipulating volume data and using this as a representation of a 3d world. This is a fairly intuitive extension of traditional heightmap terrain but does require the ability to 'think in 3D'. You will need to understand that data stored in this way can become very large, and understand the idea that paging and compression can be used to combat this.
|
||||
|
||||
**Mesh representation:** Most PolyVox projects will involve using one of the surface extractors, which output their data as index and vertex buffers. Therefore you will need to understand this representation in order to pass the data to your rendering engine or in order to perform further modifications to it. You can find out about more about this here: (ADD LINK)
|
||||
|
||||
**Image processing:** For certain advanced application an understanding of image processing methods can be useful. For example the process of blurring an image via a low pass filter can be used to effectively smooth out voxel terrain. There are plans to add more image processing operations to PolyVox particularly with regard to morphological operations which you might want to use to modify your environment.
|
||||
|
||||
Rendering
|
||||
=========
|
||||
**Runtime geometry creation:** PolyVox is independent of any particular graphics API which means it outputs its data using API-neutral structures such as index and vertex buffers (as mentioned above). You will need to write the code which converts these structures into a format which your API or engine can understand. This is not a difficult task but does require some knowledge of the rendering technology which you are using.
|
||||
|
||||
**Scene management:** PolyVox is only responsible for providing you with the mesh data to be displayed, so you application or engine will need to make sensible decisions about how this data should be organised in terms of a spatial structure (octree, bounding volumes tree, etc). It will also need to provide some approach to visibility determination such as frustum culling or a more advanced approach. If you are integrating PolyVox with an existing rendering engine then you should find that many of these aspects are already handled for you.
|
||||
|
||||
**Shader programming:** The meshes which are generated by PolyVox are very basic in terms of the vertex data they provide. You get vertex positions, a material identifier, and sometimes vertex normals. It is the responsibility of application programmer to decide how to use this data to create visually interesting renderings. This means you will almost certainly want to make use of shader programs. Of course, in our texturing (LINK) and lighting (LINK) documents you will find many ideas and common solutions, but you will need strong shader programming experience to make effective use of these.
|
||||
|
||||
If you don't have much experience with shader programming then there are many free resources available. The Cg Tutorial (LINK) provides a good introduction here (the concepts are applicable to other shader languages) as does the documentation for the main graphics (LINK to OpenGL docs) APIs (Link to D3D docs). there is nothing special about PolyVox meshes so you would be advised to practice development on simple meshes such as spheres and cubes before trying to apply it to the output of the mesh extractors.
|
@ -1,151 +1,151 @@
|
||||
***************
|
||||
Texture Mapping
|
||||
***************
|
||||
The PolyVox library is only concerned with operations on volume data (such as extracting a mesh from from a volume) and deliberately avoids the issue of rendering any resulting polygon meshes. This means PolyVox is not tied to any particular graphics API or rendering engine, and makes it much easier to integrate PolyVox with existing technology, because in general a PolyVox mesh can be treated the same as any other mesh. However, the texturing of a PolyVox mesh is usually handled a little differently, and so the purpose of this document is to provide some ideas about where to start with this process.
|
||||
|
||||
This document is aimed at readers in one of two positions:
|
||||
|
||||
1. You are trying to texture 'Minecraft-style' terrain with cubic blocks and a number of different materials.
|
||||
2. You are trying to texture smooth terrain produced by the Marching Cubes (or similar) algorithm.
|
||||
|
||||
These are certainly not the limit of PolyVox, and you can choose much more advanced texturing approaches if you wish. For example, in the past we have texture mapped a voxel Earth from a cube map and used an animated *procedural* texture (based on Perlin noise) for the magma at the center of the Earth. However, if you are aiming for such advanced techniques then we assume you understand the basics in this document and have enough knowledge to expand the ideas yourself. But do feel free to drop by and ask questions on our forum.
|
||||
|
||||
Traditionally meshes are textured by providing a pair of UV texture coordinates for each vertex, and these UV coordinates determine which parts of a texture maps to each vertex. The process of texturing PolyVox meshes is more complex for a couple of reasons:
|
||||
|
||||
1. PolyVox does not provide UV coordinates for each vertex.
|
||||
2. Voxel terrain (particularly Minecraft-style) often involves many more textures than the GPU can read at a time.
|
||||
|
||||
By reading this document you should learn how to work around the above problems, though you will almost certainly need to follow provided links and do some further reading as we have only summarised the key ideas here.
|
||||
|
||||
Mapping textures to mesh geometry
|
||||
=================================
|
||||
The lack of UV coordinates means some lateral thinking is required in order to apply texture maps to meshes. But before we get to that, we will first try to explain the rational behind PolyVox not providing UV coordinates in the first place. This rational is different for the smooth voxel meshes vs the cubic voxel meshes.
|
||||
|
||||
Rational
|
||||
--------
|
||||
The problem with texturing smooth voxel meshes is that the geometry can get very complex and it is not clear how the mapping between mesh geometry and a texture should be performed. In a traditional heightmap-based terrain this relationship is obvious as the texture map and heightmap simply line up directly. But for more complex shapes some form of 'UV unwrapping' is usually performed to define this relationship. This is usually done by an artist with the help of a 3D modeling package and so is a semi-automatic process, but it is time consuming and driven by the artists idea of what looks right for their particular scene. Even though fully automatic UV unwrapping is possible it is usually prohibitively slow.
|
||||
|
||||
Even if such an unwrapping were possible in a reasonable time frame, the next problem is that it would be invalidated as soon as the mesh changed. Enabling dynamic manipulation is one of the appealing factors of voxel terrain, and if this use case were discarded then the user may as well just model their terrain in an existing 3D modelling package and texture there. For these reasons we do not attempt to generate UV coordinates for smooth voxel meshes.
|
||||
|
||||
The rational in the cubic case is almost the opposite. For Minecraft style terrain you want to simply line up an instance of a texture with each face of a cube, and generating the texture coordinates for this is very easy. In fact it's so easy that there's no point in doing it - the logic can instead be implemented in a shader which in turn allows the amount of data in each vertex to be reduced.
|
||||
|
||||
Triplanar Texturing
|
||||
-------------------
|
||||
The most common approach to texture mapping smooth voxel terrain is to use *triplanar texturing*. The basic idea is to project a texture along all three main axes and blend between the three texture samples according to the surface normal. As an example, suppose that we wish to write a fragment shader to apply a single texture to our terrain, and assume that we have access to both the world space position of the fragment and also its normalised surface normal. Also, note that your textures should be set to wrap because the world space position will quickly go outside the bounds of 0.0-1.0. The world space position will need to have been passed through from earlier in the pipeline while the normal can be computed using one of the approaches in the lighting (link) document. The shader code would then look something like this [footnote: code is untested as is simplified compared to real world code. hopefully it compiles, but if not it should still give you an idea of how it works]:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
// Take the three texture samples
|
||||
vec4 sampleX = texture2d(inputTexture, worldSpacePos.yz); // Project along x axis
|
||||
vec4 sampleY = texture2d(inputTexture, worldSpacePos.xz); // Project along y axis
|
||||
vec4 sampleZ = texture2d(inputTexture, worldSpacePos.xy); // Project along z axis
|
||||
|
||||
// Blend the samples according to the normal
|
||||
vec4 blendedColour = sampleX * normal.x + sampleY * normal.y + sampleZ * normal.z;
|
||||
|
||||
Note that this approach will lead to the texture repeating once every world unit, and so in practice you may wish to scale the world space positions to make the texture appear the desired size. Also this technique can be extended to work with normal mapping though we won't go into the details here.
|
||||
|
||||
This idea of triplanar texturing can be applied to the cubic meshes as well, and in some ways it can be considered to be even simpler. With cubic meshes the normal always points exactly along one of the main axes, and so it is not necessary to sample the texture three times nor to blend the results. Instead you can use conditional branching in the fragment shader to determine which pair of values out of {x,y,z} should be used as the texture coordinates. Something like:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
vec4 sample = vec4(0, 0, 0, 0); // We'll fill this in below
|
||||
// Assume the normal is normalised.
|
||||
if(normal.x > 0.9) // x must be one while y and z are zero
|
||||
{
|
||||
//Project onto yz plane
|
||||
sample = texture2D(inputTexture, worldSpacePos.yz);
|
||||
}
|
||||
// Now similar logic for the other two axes.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
You might also choose to sample a different texture for each of the axes, in order to apply a different texture to each face of your cube. If so, you probably want to pack your different face textures together using an approach similar to those described later in this document for multiple material textures. Another (untested) idea would be to use the normal to select a face on a 1x1x1 cubemap, and have the cubemap face contain an index value for addressing the correct face texture. This could bypass the conditional logic above.
|
||||
|
||||
Using the material identifier
|
||||
-----------------------------
|
||||
So far we have assumed that only a single material is being used for the entire voxel world, but this is seldom the case. It is common to associate a particular material with each voxel so that it can represent rock, wood, sand or any other type of material as required. The usual approach is to store a simple integer identifier with each voxel, and then map this identifier to material properties within your application.
|
||||
|
||||
Both the CubicSurfaceExtractor and the MarchingCubesSurfacExtractor understand the concept of a material being associated with a voxel, and they will take this into account when generating a mesh. Specifically, they will both copy the material identifier into the vertex data of the output mesh, so you can pass it through to your shaders and use it to affect the way the surface is rendered.
|
||||
|
||||
The following code snippet assumes that you have passed the material identifier to your shaders and that you can access it in the fragment shader. It then chooses which colour to draw the polygon based on this identifier:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
vec4 fragmentColour = vec4(1, 1, 1, 1); // Default value
|
||||
if(materialId < 0.5) //Avoid '==' when working with floats.
|
||||
{
|
||||
fragmentColour = vec4(1, 0, 0, 1) // Draw material 0 as red.
|
||||
}
|
||||
else if(materialId < 1.5) //Avoid '==' when working with floats.
|
||||
{
|
||||
fragmentColour = vec4(0, 1, 0, 1) // Draw material 1 as green.
|
||||
}
|
||||
else if(materialId < 2.5) //Avoid '==' when working with floats.
|
||||
{
|
||||
fragmentColour = vec4(0, 0, 1, 1) // Draw material 2 as blue.
|
||||
}
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
This is a very simple example, and such use of conditional branching within the shader may not be the best approach as it incurs some performance overhead and becomes unwieldy with a large number of materials. Other approaches include encoding a colour directly into the material identifier, or using the identifier as an index into a texture atlas or array.
|
||||
|
||||
Note that PolyVox currently stores that material identifier for the vertex as a float, but this will probably change in the future to use the same type as is stored in the volume. It will then be up to you which type you pass to the GPU (older GPUs may not support integer values) but if you do use floats then watch out for precision issues and avoid equality comparisons.
|
||||
|
||||
Blending between materials
|
||||
--------------------------
|
||||
An additional complication when working with smooth voxel terrain is that it is usually desirable to blend smoothly between adjacent voxels with different materials. This situation does not occur with cubic meshes because the texture is considered to be per-face instead of per-vertex, and PolyVox enforces this by ensuring that all the vertices of a given face have the same material.
|
||||
|
||||
With a smooth mesh it is possible for each of the three vertices of any given triangle to have different material identifiers. If this is not explicitly handled then the graphics hardware will interpolate these material values across the face of the triangle. Fundamentally, the concept of interpolating between material identifiers does not make sense, because if we have (for example) 1='grass', 2='rock' and 3='sand' then it does not make sense to say rock is the average of grass and sand.
|
||||
|
||||
Correctly handling of this is a surprising difficult problem. For now, the best approach is described in our article 'Volumetric representation of virtual terrain' which appeared in Game Engine Gems Volume 1 and which is freely available through the Google Books preview here: http://books.google.com/books?id=WNfD2u8nIlIC&lpg=PR1&dq=game%20engine%20gems&pg=PA39#v=onepage&q&f=false
|
||||
|
||||
As off October 2012 we are actively researching alternative solutions to this problem though it will be some time before the results become available.
|
||||
|
||||
Actual implementation of these material blending approaches is left as an exercise to the reader, though it is possible that in the future we will add some utility functions to PolyVox to assist with tasks such as splitting the mesh or adding the required extra vertex attributes. Our test implementations have performed the mesh processing on the CPU before the mesh is uploaded to the graphics card, but it does seem like there is a lot of potential for implementing these approaches in the geometry shader.
|
||||
|
||||
Storage of textures
|
||||
===================
|
||||
The other major challenge in texturing voxel based geometry is handling the large number of textures which such environments often require. As an example, a game like Minecraft has hundreds of different material types each with their own texture. The traditional approach to mesh texturing is to bind textures to *texture units* on the GPU before rendering a batch, but even modern GPUs only allow between 16-64 textures to be bound at a time. In this section we discuss various solutions to overcoming this limitation.
|
||||
|
||||
There are various trade offs involved, but if you are targeting hardware with support for *texture arrays* (available from OpenGL 3 and Direct3D 10 on-wards) then we can save you some time and tell you that they are almost certainly the best solution. Otherwise you have to understand the various pros and cons of the other approaches described below.
|
||||
|
||||
Separate texture units
|
||||
----------------------
|
||||
Before we make things unnecessarily complicated, you should consider whether you do actually need the hundreds of textures discussed earlier. If you actually only need a few textures then the simplest solution may indeed be to pass them in via different texture units. You can then select the desired textures using a series of if statements, or a switch statement if the material identifiers are integer values. There is probably some performance overhead here, but you may find it is acceptable for a small number of textures. Keep in mind that you may need to reserve some texture units for additional texture data such as normal maps or shadow maps.
|
||||
|
||||
Splitting the mesh
|
||||
------------------
|
||||
If your required number of textures do indeed exceed the available number of textures units then one option is to break the mesh down into a number of pieces. Let's say you have a mesh which contains one hundred different materials. As an extreme solution you could break it down into one hundred separate meshes, and for each mesh you could then bind the required single texture before drawing the geometry. Obviously this will dramatically increase the batch count of your scene and so is not recommended.
|
||||
|
||||
A more practical approach would be to break the mesh into a smaller number of pieces such that each mesh uses several textures but less than the maximum number of texture units. For example, our mesh with one hundred materials could be split into ten meshes, the first of which contains those triangles using materials 0-9, the seconds contains those triangles using materials 10-19, and so forth. There is a trade off here between the number of batches and the number of textures units used per batch.
|
||||
|
||||
Furthermore, you could realise that although your terrain may use hundreds of different textures, any given region is likely to use only a small fraction of those. We have yet to experiment with this, but it seems if you region uses only (for example) materials 12, 47, and 231, then you could conceptually map these materials to the first three textures slots. This means that for each region you draw the mapping between material IDs and texture units would be different. This may require some complex logic in the application but could allow you to do much more with only a few texture units. We will investigate this further in the future.
|
||||
|
||||
Texture atlases
|
||||
---------------
|
||||
Probably the most widely used method is to pack a number of textures together into a single large texture, and to our knowledge this is the approach used by Minecraft. For example, if each of your textures are 256x256 texels, and if the maximum texture size supported by your target hardware is 4096x4096 texels, then you can pack 16 x 16 = 256 small textures into the larger one. If this isn't enough (or if your input textures are larger than 256x256) then you can also combine this approach with multiple texture units or with the mesh splitting described previously.
|
||||
|
||||
However, there are a number of problems with packing textures like this. Most obviously, it limits the size of your textures as they now have to be significantly smaller then the maximum texture size. Whether this is a problem will really depend on your application.
|
||||
|
||||
Next, it means you have to adjust your UV coordinates to correctly address a given texture inside the atlas. UV coordinates for a single texture would normally vary between 0.0 and 1.0 in both dimensions, but when packed into a texture atlas each texture uses only a small part of this range. You will need to apply offsets and scaling factors to your UV coordinates to address your texture correctly.
|
||||
|
||||
However, the biggest problem with texture atlases is that they causes problems with texture filtering and with mipmaps. The filtering problem occurs because graphics hardware usually samples the surrounding texels and performs linear interpolation to compute the colour of a given sample point, but when multiple textures are packed together these surrounding texels can actually come from a neighbouring packed texture rather than wrapping round to sample on the other side of the same packed texture. The mipmap problem occurs because for the highest mipmap levels (such as 1x1 or 2x2) multiple textures are being are being averaged together.
|
||||
|
||||
It is possible to combat these problems but the solutions are non-trivial. You will want to limit the number of miplevels which you use, and probably provide custom shader code to handle the wrapping of texture coordinates, the sampling of MIP maps, and the calculation of interpolated values. You can also try adding a border around all your packed textures, perhaps by duplicating each texture and offsetting by half its size. Even so, it's not clear to us at this point whether the the various artifacts can be completely removed. Minecraft handles it by completely disabling texture filtering and using the resulting pixelated look as part of its aesthetic.
|
||||
|
||||
3D texture slices
|
||||
-----------------
|
||||
The idea here is similar to the texture atlas approach, but rather than packing texture side-by-side in an atlas they are instead packed as slices in a 3D texture. We haven't actually tested this but in theory it may have a couple of benefits. Firstly, it simplifies the addressing of the texture as there is no need to offset/scale the UV coordinates, and the W coordinate (the slice index) can be more easily computed from the material identifier. Secondly, a single volume texture will usually be able to hold more texels than a single 2D texture (for example, 512x512x512 is bigger than 4096x4096). Lastly, it should simplify the filtering problem as packed textures are no longer tiled and so should wrap correctly.
|
||||
|
||||
However, MIP mapping will probably be more complex than the texture atlas case because even the first MIP level will involve combining adjacent slices. Volume textures are also not so widely supported and may be particularly problematic on mobile hardware.
|
||||
|
||||
Texture arrays
|
||||
--------------
|
||||
These provide the perfect solution to the problem of handling a large number of textures... at least if they are supported by your hardware. They were introduced with OpenGL 3 and Direct3D 10 but older versions of OpenGL may still be able to access the functionality via extensions. They allow you to bind an array of textures to the shader, and the advantage compared to a texture atlas is that the hardware understands that the textures are separate and so avoids the filtering and mipmapping issues. Beyond the hardware requirements, the only real limitation is that all the textures must be the same size.
|
||||
|
||||
Bindless rendering
|
||||
------------------
|
||||
***************
|
||||
Texture Mapping
|
||||
***************
|
||||
The PolyVox library is only concerned with operations on volume data (such as extracting a mesh from from a volume) and deliberately avoids the issue of rendering any resulting polygon meshes. This means PolyVox is not tied to any particular graphics API or rendering engine, and makes it much easier to integrate PolyVox with existing technology, because in general a PolyVox mesh can be treated the same as any other mesh. However, the texturing of a PolyVox mesh is usually handled a little differently, and so the purpose of this document is to provide some ideas about where to start with this process.
|
||||
|
||||
This document is aimed at readers in one of two positions:
|
||||
|
||||
1. You are trying to texture 'Minecraft-style' terrain with cubic blocks and a number of different materials.
|
||||
2. You are trying to texture smooth terrain produced by the Marching Cubes (or similar) algorithm.
|
||||
|
||||
These are certainly not the limit of PolyVox, and you can choose much more advanced texturing approaches if you wish. For example, in the past we have texture mapped a voxel Earth from a cube map and used an animated *procedural* texture (based on Perlin noise) for the magma at the center of the Earth. However, if you are aiming for such advanced techniques then we assume you understand the basics in this document and have enough knowledge to expand the ideas yourself. But do feel free to drop by and ask questions on our forum.
|
||||
|
||||
Traditionally meshes are textured by providing a pair of UV texture coordinates for each vertex, and these UV coordinates determine which parts of a texture maps to each vertex. The process of texturing PolyVox meshes is more complex for a couple of reasons:
|
||||
|
||||
1. PolyVox does not provide UV coordinates for each vertex.
|
||||
2. Voxel terrain (particularly Minecraft-style) often involves many more textures than the GPU can read at a time.
|
||||
|
||||
By reading this document you should learn how to work around the above problems, though you will almost certainly need to follow provided links and do some further reading as we have only summarised the key ideas here.
|
||||
|
||||
Mapping textures to mesh geometry
|
||||
=================================
|
||||
The lack of UV coordinates means some lateral thinking is required in order to apply texture maps to meshes. But before we get to that, we will first try to explain the rational behind PolyVox not providing UV coordinates in the first place. This rational is different for the smooth voxel meshes vs the cubic voxel meshes.
|
||||
|
||||
Rational
|
||||
--------
|
||||
The problem with texturing smooth voxel meshes is that the geometry can get very complex and it is not clear how the mapping between mesh geometry and a texture should be performed. In a traditional heightmap-based terrain this relationship is obvious as the texture map and heightmap simply line up directly. But for more complex shapes some form of 'UV unwrapping' is usually performed to define this relationship. This is usually done by an artist with the help of a 3D modeling package and so is a semi-automatic process, but it is time consuming and driven by the artists idea of what looks right for their particular scene. Even though fully automatic UV unwrapping is possible it is usually prohibitively slow.
|
||||
|
||||
Even if such an unwrapping were possible in a reasonable time frame, the next problem is that it would be invalidated as soon as the mesh changed. Enabling dynamic manipulation is one of the appealing factors of voxel terrain, and if this use case were discarded then the user may as well just model their terrain in an existing 3D modelling package and texture there. For these reasons we do not attempt to generate UV coordinates for smooth voxel meshes.
|
||||
|
||||
The rational in the cubic case is almost the opposite. For Minecraft style terrain you want to simply line up an instance of a texture with each face of a cube, and generating the texture coordinates for this is very easy. In fact it's so easy that there's no point in doing it - the logic can instead be implemented in a shader which in turn allows the amount of data in each vertex to be reduced.
|
||||
|
||||
Triplanar Texturing
|
||||
-------------------
|
||||
The most common approach to texture mapping smooth voxel terrain is to use *triplanar texturing*. The basic idea is to project a texture along all three main axes and blend between the three texture samples according to the surface normal. As an example, suppose that we wish to write a fragment shader to apply a single texture to our terrain, and assume that we have access to both the world space position of the fragment and also its normalised surface normal. Also, note that your textures should be set to wrap because the world space position will quickly go outside the bounds of 0.0-1.0. The world space position will need to have been passed through from earlier in the pipeline while the normal can be computed using one of the approaches in the lighting (link) document. The shader code would then look something like this [footnote: code is untested as is simplified compared to real world code. hopefully it compiles, but if not it should still give you an idea of how it works]:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
// Take the three texture samples
|
||||
vec4 sampleX = texture2d(inputTexture, worldSpacePos.yz); // Project along x axis
|
||||
vec4 sampleY = texture2d(inputTexture, worldSpacePos.xz); // Project along y axis
|
||||
vec4 sampleZ = texture2d(inputTexture, worldSpacePos.xy); // Project along z axis
|
||||
|
||||
// Blend the samples according to the normal
|
||||
vec4 blendedColour = sampleX * normal.x + sampleY * normal.y + sampleZ * normal.z;
|
||||
|
||||
Note that this approach will lead to the texture repeating once every world unit, and so in practice you may wish to scale the world space positions to make the texture appear the desired size. Also this technique can be extended to work with normal mapping though we won't go into the details here.
|
||||
|
||||
This idea of triplanar texturing can be applied to the cubic meshes as well, and in some ways it can be considered to be even simpler. With cubic meshes the normal always points exactly along one of the main axes, and so it is not necessary to sample the texture three times nor to blend the results. Instead you can use conditional branching in the fragment shader to determine which pair of values out of {x,y,z} should be used as the texture coordinates. Something like:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
vec4 sample = vec4(0, 0, 0, 0); // We'll fill this in below
|
||||
// Assume the normal is normalised.
|
||||
if(normal.x > 0.9) // x must be one while y and z are zero
|
||||
{
|
||||
//Project onto yz plane
|
||||
sample = texture2D(inputTexture, worldSpacePos.yz);
|
||||
}
|
||||
// Now similar logic for the other two axes.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
You might also choose to sample a different texture for each of the axes, in order to apply a different texture to each face of your cube. If so, you probably want to pack your different face textures together using an approach similar to those described later in this document for multiple material textures. Another (untested) idea would be to use the normal to select a face on a 1x1x1 cubemap, and have the cubemap face contain an index value for addressing the correct face texture. This could bypass the conditional logic above.
|
||||
|
||||
Using the material identifier
|
||||
-----------------------------
|
||||
So far we have assumed that only a single material is being used for the entire voxel world, but this is seldom the case. It is common to associate a particular material with each voxel so that it can represent rock, wood, sand or any other type of material as required. The usual approach is to store a simple integer identifier with each voxel, and then map this identifier to material properties within your application.
|
||||
|
||||
Both the CubicSurfaceExtractor and the MarchingCubesSurfacExtractor understand the concept of a material being associated with a voxel, and they will take this into account when generating a mesh. Specifically, they will both copy the material identifier into the vertex data of the output mesh, so you can pass it through to your shaders and use it to affect the way the surface is rendered.
|
||||
|
||||
The following code snippet assumes that you have passed the material identifier to your shaders and that you can access it in the fragment shader. It then chooses which colour to draw the polygon based on this identifier:
|
||||
|
||||
.. sourcecode:: glsl
|
||||
|
||||
vec4 fragmentColour = vec4(1, 1, 1, 1); // Default value
|
||||
if(materialId < 0.5) //Avoid '==' when working with floats.
|
||||
{
|
||||
fragmentColour = vec4(1, 0, 0, 1) // Draw material 0 as red.
|
||||
}
|
||||
else if(materialId < 1.5) //Avoid '==' when working with floats.
|
||||
{
|
||||
fragmentColour = vec4(0, 1, 0, 1) // Draw material 1 as green.
|
||||
}
|
||||
else if(materialId < 2.5) //Avoid '==' when working with floats.
|
||||
{
|
||||
fragmentColour = vec4(0, 0, 1, 1) // Draw material 2 as blue.
|
||||
}
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
This is a very simple example, and such use of conditional branching within the shader may not be the best approach as it incurs some performance overhead and becomes unwieldy with a large number of materials. Other approaches include encoding a colour directly into the material identifier, or using the identifier as an index into a texture atlas or array.
|
||||
|
||||
Note that PolyVox currently stores that material identifier for the vertex as a float, but this will probably change in the future to use the same type as is stored in the volume. It will then be up to you which type you pass to the GPU (older GPUs may not support integer values) but if you do use floats then watch out for precision issues and avoid equality comparisons.
|
||||
|
||||
Blending between materials
|
||||
--------------------------
|
||||
An additional complication when working with smooth voxel terrain is that it is usually desirable to blend smoothly between adjacent voxels with different materials. This situation does not occur with cubic meshes because the texture is considered to be per-face instead of per-vertex, and PolyVox enforces this by ensuring that all the vertices of a given face have the same material.
|
||||
|
||||
With a smooth mesh it is possible for each of the three vertices of any given triangle to have different material identifiers. If this is not explicitly handled then the graphics hardware will interpolate these material values across the face of the triangle. Fundamentally, the concept of interpolating between material identifiers does not make sense, because if we have (for example) 1='grass', 2='rock' and 3='sand' then it does not make sense to say rock is the average of grass and sand.
|
||||
|
||||
Correctly handling of this is a surprising difficult problem. For now, the best approach is described in our article 'Volumetric representation of virtual terrain' which appeared in Game Engine Gems Volume 1 and which is freely available through the Google Books preview here: http://books.google.com/books?id=WNfD2u8nIlIC&lpg=PR1&dq=game%20engine%20gems&pg=PA39#v=onepage&q&f=false
|
||||
|
||||
As off October 2012 we are actively researching alternative solutions to this problem though it will be some time before the results become available.
|
||||
|
||||
Actual implementation of these material blending approaches is left as an exercise to the reader, though it is possible that in the future we will add some utility functions to PolyVox to assist with tasks such as splitting the mesh or adding the required extra vertex attributes. Our test implementations have performed the mesh processing on the CPU before the mesh is uploaded to the graphics card, but it does seem like there is a lot of potential for implementing these approaches in the geometry shader.
|
||||
|
||||
Storage of textures
|
||||
===================
|
||||
The other major challenge in texturing voxel based geometry is handling the large number of textures which such environments often require. As an example, a game like Minecraft has hundreds of different material types each with their own texture. The traditional approach to mesh texturing is to bind textures to *texture units* on the GPU before rendering a batch, but even modern GPUs only allow between 16-64 textures to be bound at a time. In this section we discuss various solutions to overcoming this limitation.
|
||||
|
||||
There are various trade offs involved, but if you are targeting hardware with support for *texture arrays* (available from OpenGL 3 and Direct3D 10 on-wards) then we can save you some time and tell you that they are almost certainly the best solution. Otherwise you have to understand the various pros and cons of the other approaches described below.
|
||||
|
||||
Separate texture units
|
||||
----------------------
|
||||
Before we make things unnecessarily complicated, you should consider whether you do actually need the hundreds of textures discussed earlier. If you actually only need a few textures then the simplest solution may indeed be to pass them in via different texture units. You can then select the desired textures using a series of if statements, or a switch statement if the material identifiers are integer values. There is probably some performance overhead here, but you may find it is acceptable for a small number of textures. Keep in mind that you may need to reserve some texture units for additional texture data such as normal maps or shadow maps.
|
||||
|
||||
Splitting the mesh
|
||||
------------------
|
||||
If your required number of textures do indeed exceed the available number of textures units then one option is to break the mesh down into a number of pieces. Let's say you have a mesh which contains one hundred different materials. As an extreme solution you could break it down into one hundred separate meshes, and for each mesh you could then bind the required single texture before drawing the geometry. Obviously this will dramatically increase the batch count of your scene and so is not recommended.
|
||||
|
||||
A more practical approach would be to break the mesh into a smaller number of pieces such that each mesh uses several textures but less than the maximum number of texture units. For example, our mesh with one hundred materials could be split into ten meshes, the first of which contains those triangles using materials 0-9, the seconds contains those triangles using materials 10-19, and so forth. There is a trade off here between the number of batches and the number of textures units used per batch.
|
||||
|
||||
Furthermore, you could realise that although your terrain may use hundreds of different textures, any given region is likely to use only a small fraction of those. We have yet to experiment with this, but it seems if you region uses only (for example) materials 12, 47, and 231, then you could conceptually map these materials to the first three textures slots. This means that for each region you draw the mapping between material IDs and texture units would be different. This may require some complex logic in the application but could allow you to do much more with only a few texture units. We will investigate this further in the future.
|
||||
|
||||
Texture atlases
|
||||
---------------
|
||||
Probably the most widely used method is to pack a number of textures together into a single large texture, and to our knowledge this is the approach used by Minecraft. For example, if each of your textures are 256x256 texels, and if the maximum texture size supported by your target hardware is 4096x4096 texels, then you can pack 16 x 16 = 256 small textures into the larger one. If this isn't enough (or if your input textures are larger than 256x256) then you can also combine this approach with multiple texture units or with the mesh splitting described previously.
|
||||
|
||||
However, there are a number of problems with packing textures like this. Most obviously, it limits the size of your textures as they now have to be significantly smaller then the maximum texture size. Whether this is a problem will really depend on your application.
|
||||
|
||||
Next, it means you have to adjust your UV coordinates to correctly address a given texture inside the atlas. UV coordinates for a single texture would normally vary between 0.0 and 1.0 in both dimensions, but when packed into a texture atlas each texture uses only a small part of this range. You will need to apply offsets and scaling factors to your UV coordinates to address your texture correctly.
|
||||
|
||||
However, the biggest problem with texture atlases is that they causes problems with texture filtering and with mipmaps. The filtering problem occurs because graphics hardware usually samples the surrounding texels and performs linear interpolation to compute the colour of a given sample point, but when multiple textures are packed together these surrounding texels can actually come from a neighbouring packed texture rather than wrapping round to sample on the other side of the same packed texture. The mipmap problem occurs because for the highest mipmap levels (such as 1x1 or 2x2) multiple textures are being are being averaged together.
|
||||
|
||||
It is possible to combat these problems but the solutions are non-trivial. You will want to limit the number of miplevels which you use, and probably provide custom shader code to handle the wrapping of texture coordinates, the sampling of MIP maps, and the calculation of interpolated values. You can also try adding a border around all your packed textures, perhaps by duplicating each texture and offsetting by half its size. Even so, it's not clear to us at this point whether the the various artifacts can be completely removed. Minecraft handles it by completely disabling texture filtering and using the resulting pixelated look as part of its aesthetic.
|
||||
|
||||
3D texture slices
|
||||
-----------------
|
||||
The idea here is similar to the texture atlas approach, but rather than packing texture side-by-side in an atlas they are instead packed as slices in a 3D texture. We haven't actually tested this but in theory it may have a couple of benefits. Firstly, it simplifies the addressing of the texture as there is no need to offset/scale the UV coordinates, and the W coordinate (the slice index) can be more easily computed from the material identifier. Secondly, a single volume texture will usually be able to hold more texels than a single 2D texture (for example, 512x512x512 is bigger than 4096x4096). Lastly, it should simplify the filtering problem as packed textures are no longer tiled and so should wrap correctly.
|
||||
|
||||
However, MIP mapping will probably be more complex than the texture atlas case because even the first MIP level will involve combining adjacent slices. Volume textures are also not so widely supported and may be particularly problematic on mobile hardware.
|
||||
|
||||
Texture arrays
|
||||
--------------
|
||||
These provide the perfect solution to the problem of handling a large number of textures... at least if they are supported by your hardware. They were introduced with OpenGL 3 and Direct3D 10 but older versions of OpenGL may still be able to access the functionality via extensions. They allow you to bind an array of textures to the shader, and the advantage compared to a texture atlas is that the hardware understands that the textures are separate and so avoids the filtering and mipmapping issues. Beyond the hardware requirements, the only real limitation is that all the textures must be the same size.
|
||||
|
||||
Bindless rendering
|
||||
------------------
|
||||
We don't have much to say about this option as it needs significant research, but bindless rendering is one of the new OpenGL extensions to come out of Nvidia. The idea is that it removes the abstraction of needing to 'bind' a texture to a particular texture unit, and instead allows more direct access to the texture data on the GPU. This means you can have access to a much larger number of textures from your shader. Sounds useful, but we've yet to investigate it.
|
@ -1,69 +1,69 @@
|
||||
*********
|
||||
Threading
|
||||
*********
|
||||
Modern computing hardware typically contains a large number of processors, and so users of PolyVox often want to know how they can best make use of these from their applications.
|
||||
|
||||
PolyVox does not make any guarantees about thread-safety, and does not contain any threading primitives to protect access to data structures. You can still make use of PolyVox from multiple threads, but you will have to take responsibility for enforcing thread safety yourself (e.g. by providing thread safe wrappers around the volume classes). If you do want to use PolyVox is a multi-threaded context then this document provides some tips and tricks that you might find useful.
|
||||
|
||||
However, be aware that we do not have a lot of expertise in threading, and this is part of the reason why it is not explicitly addressed within PolyVox. If you do have more experience and believe any of this information to be misleading then please do post on the forums to discuss it.
|
||||
|
||||
Volumes
|
||||
=======
|
||||
Volumes are a core aspect of PolyVox, and so a natural question is whether it is safe to simultaneously access a given volume from multiple threads. The answer to this is actually fairly complex and is also dependent on the type of volume which is being used.
|
||||
|
||||
RawVolume
|
||||
---------
|
||||
The RawVolume has a very simple internal structure in which the data is stored as a single array which is never moved or resized. Because of this property it is indeed safe to perform multiple simultaneous *reads* of the data from different threads. However, it is generally not safe to perform simultaneous writes from different threads, or even to write from only one thread while other threads are reading.
|
||||
|
||||
The reason why simultaneous writes can be problematic should be fairly obvious - if two different threads try to write to the same voxel then the result will depend on which thread writes first. But why can't we write from one thread and read from another one? The problem here is that the the CPU may implement some kind of caching mechanism and/or choose to store data in registers. If a write operation occurs before a read, then the read *may* still obtain the old value because the cache has not been updated yet. There may even be a cache for each thread (particularly if running on multiple processors).
|
||||
|
||||
If we assume for a moment that each voxel is a simple integer, then the rules for accessing a single voxel from multiple threads are the same as the rules for accessing integers from multiple threads. There is some useful information about this available on the web, including a discussion on StackOverflow: http://stackoverflow.com/questions/4588915/can-an-integer-be-shared-between-threads-safely
|
||||
|
||||
If nothing else, this serves to illustrate that multi-threaded access even to something as simple as an integer can be surprisingly complex and architecture dependant.
|
||||
|
||||
However, all this has been in the context of accessing a *single* voxel from multiple threads... what if we carefully design our algorithm such that different threads access different parts of the volume which never overlap? Unfortunately I don't believe this is a solution either. Caching mechanisms seldom operate on individual elements but instead tend to cache larger chunks of data on the grounds that data accesses are usually localised. So it's quite possible that accessing a given voxel will cause another voxel to be cached.
|
||||
|
||||
C++ does provide the 'volatile' keyword which can be used to ensure a variable is updated immediately (rather than being cached) but this is still not sufficient for thread safe code. It also has performance implications which we would like to avoid. More information about volatile and multitheaded programming can be found here: http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/
|
||||
|
||||
Lastly, note that PolyVox volumes are templatised which means the voxel type might be something other than a simple int. However we don't think this actually makes a difference given that so few guarantees are made anyway, and it should still be safe to perform multiple concurrent reads for more complex types.
|
||||
|
||||
PagedVolume
|
||||
-----------
|
||||
NOTE: The info below is based on LargeVolume, which PagedVolume has replaced. It is likely that the same limitations apply but this has not been well tested. We do intend to improve the thread safty of PagedVolume in the future.
|
||||
|
||||
The LargeVolume provides even less thread safety than the RawVolume, in that even concurrent read operations can cause problems. The reason for this is the more complex memory management which is performed behind the scenes, and which allows pieces of volume data to be moved around and deleted. For example, a read of a single voxel may mean that the block of data associated with that voxel has to be paged in to memory, which in turn may mean that another block of data has to be paged out of memory. If second thread was halfway through reading a voxel in this second block of data then a problem will occur.
|
||||
|
||||
In the future we may do a more comprehensive analysis of thread safety in the LargeVolume, but for now you should assume that any multithreaded access can cause problems.
|
||||
|
||||
Consequences of abuse
|
||||
---------------------
|
||||
We have outlined above the rules for multithreaded access of volumes, but what actually happens if you violate these? There's a couple of things to watch out for:
|
||||
|
||||
- As mentioned, performing unprotected writes to the volume can cause problems because the data may be copied into the CPU cache and/or registers, and so a subsequent read could retrieve the old value. This is not what you want but probably won't be fatal (i.e. it shouldn't crash). It would basically manifest itself as data corruption.
|
||||
- If you access the LargeVolume in a multithreaded fashion then you risk trying to access data which has been removed by another thread, and in this case you will get undefined behaviour. This will probably be a crash (out of bounds access) but really anything could happen.
|
||||
|
||||
Surface Extraction
|
||||
==================
|
||||
Despite the lack of thread safety built in to PolyVox, it is still possible and often desirable to make use of multiple threads for tasks such as surface extraction. Performing surface extraction does not require write access to the data, and we've already established that you can safely perform reads from different threads *provided you are not using the LargeVolume*.
|
||||
|
||||
Combining multiple surface extraction threads with the *LargeVolume* is something we will need to experiment with in the future, to determine how it can be improved.
|
||||
|
||||
In the future we will expand this section to discuss how to split surface extraction across a number of threads, but for now please see Section XX of the book chapter 'Volumetric Representation of Virtual environments', available for free here: http://books.google.nl/books?id=WNfD2u8nIlIC&lpg=PR1&dq=game+engine+gems&pg=PA39&redir_esc=y#v=onepage&q&f=false
|
||||
|
||||
GPU thread safety
|
||||
=================
|
||||
Be aware that even if you successfully perform surface across multiple threads you still need to take care when uploading the data to the GPU. For Direct3D 9.0 and OpenGL 2.0 it is only possible to upload data from the main thread (or more accurately the one which owns the rendering context). So after you have performed your multi-threaded surface extraction you need to bring the data back to the main thread for uploading to the GPU.
|
||||
|
||||
More recent versions of the Direct3D and OpenGL APIs lift this restriction and provide means of accessing GPU resources from multiple threads. Please consult the documentation for your API for details.
|
||||
|
||||
Future work
|
||||
===========
|
||||
Threading support is not a high priority for PolyVox because it can be implemented by the user at a higher level. However, there are a couple of areas we may investigate in the future.
|
||||
|
||||
Thread safe volume wrapper
|
||||
--------------------------
|
||||
It might be useful to provide a thread safe wrapper around the volume classes, and this could possibly be included in the PolyVox utilities or as a extra library. This thread safe wrapper could be templatised to work with any internal volume type, and could itself be a volume so that it can be used directly with the existing algorithms.
|
||||
|
||||
OpenMP
|
||||
------
|
||||
*********
|
||||
Threading
|
||||
*********
|
||||
Modern computing hardware typically contains a large number of processors, and so users of PolyVox often want to know how they can best make use of these from their applications.
|
||||
|
||||
PolyVox does not make any guarantees about thread-safety, and does not contain any threading primitives to protect access to data structures. You can still make use of PolyVox from multiple threads, but you will have to take responsibility for enforcing thread safety yourself (e.g. by providing thread safe wrappers around the volume classes). If you do want to use PolyVox is a multi-threaded context then this document provides some tips and tricks that you might find useful.
|
||||
|
||||
However, be aware that we do not have a lot of expertise in threading, and this is part of the reason why it is not explicitly addressed within PolyVox. If you do have more experience and believe any of this information to be misleading then please do post on the forums to discuss it.
|
||||
|
||||
Volumes
|
||||
=======
|
||||
Volumes are a core aspect of PolyVox, and so a natural question is whether it is safe to simultaneously access a given volume from multiple threads. The answer to this is actually fairly complex and is also dependent on the type of volume which is being used.
|
||||
|
||||
RawVolume
|
||||
---------
|
||||
The RawVolume has a very simple internal structure in which the data is stored as a single array which is never moved or resized. Because of this property it is indeed safe to perform multiple simultaneous *reads* of the data from different threads. However, it is generally not safe to perform simultaneous writes from different threads, or even to write from only one thread while other threads are reading.
|
||||
|
||||
The reason why simultaneous writes can be problematic should be fairly obvious - if two different threads try to write to the same voxel then the result will depend on which thread writes first. But why can't we write from one thread and read from another one? The problem here is that the the CPU may implement some kind of caching mechanism and/or choose to store data in registers. If a write operation occurs before a read, then the read *may* still obtain the old value because the cache has not been updated yet. There may even be a cache for each thread (particularly if running on multiple processors).
|
||||
|
||||
If we assume for a moment that each voxel is a simple integer, then the rules for accessing a single voxel from multiple threads are the same as the rules for accessing integers from multiple threads. There is some useful information about this available on the web, including a discussion on StackOverflow: http://stackoverflow.com/questions/4588915/can-an-integer-be-shared-between-threads-safely
|
||||
|
||||
If nothing else, this serves to illustrate that multi-threaded access even to something as simple as an integer can be surprisingly complex and architecture dependant.
|
||||
|
||||
However, all this has been in the context of accessing a *single* voxel from multiple threads... what if we carefully design our algorithm such that different threads access different parts of the volume which never overlap? Unfortunately I don't believe this is a solution either. Caching mechanisms seldom operate on individual elements but instead tend to cache larger chunks of data on the grounds that data accesses are usually localised. So it's quite possible that accessing a given voxel will cause another voxel to be cached.
|
||||
|
||||
C++ does provide the 'volatile' keyword which can be used to ensure a variable is updated immediately (rather than being cached) but this is still not sufficient for thread safe code. It also has performance implications which we would like to avoid. More information about volatile and multitheaded programming can be found here: http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/
|
||||
|
||||
Lastly, note that PolyVox volumes are templatised which means the voxel type might be something other than a simple int. However we don't think this actually makes a difference given that so few guarantees are made anyway, and it should still be safe to perform multiple concurrent reads for more complex types.
|
||||
|
||||
PagedVolume
|
||||
-----------
|
||||
NOTE: The info below is based on LargeVolume, which PagedVolume has replaced. It is likely that the same limitations apply but this has not been well tested. We do intend to improve the thread safty of PagedVolume in the future.
|
||||
|
||||
The LargeVolume provides even less thread safety than the RawVolume, in that even concurrent read operations can cause problems. The reason for this is the more complex memory management which is performed behind the scenes, and which allows pieces of volume data to be moved around and deleted. For example, a read of a single voxel may mean that the block of data associated with that voxel has to be paged in to memory, which in turn may mean that another block of data has to be paged out of memory. If second thread was halfway through reading a voxel in this second block of data then a problem will occur.
|
||||
|
||||
In the future we may do a more comprehensive analysis of thread safety in the LargeVolume, but for now you should assume that any multithreaded access can cause problems.
|
||||
|
||||
Consequences of abuse
|
||||
---------------------
|
||||
We have outlined above the rules for multithreaded access of volumes, but what actually happens if you violate these? There's a couple of things to watch out for:
|
||||
|
||||
- As mentioned, performing unprotected writes to the volume can cause problems because the data may be copied into the CPU cache and/or registers, and so a subsequent read could retrieve the old value. This is not what you want but probably won't be fatal (i.e. it shouldn't crash). It would basically manifest itself as data corruption.
|
||||
- If you access the LargeVolume in a multithreaded fashion then you risk trying to access data which has been removed by another thread, and in this case you will get undefined behaviour. This will probably be a crash (out of bounds access) but really anything could happen.
|
||||
|
||||
Surface Extraction
|
||||
==================
|
||||
Despite the lack of thread safety built in to PolyVox, it is still possible and often desirable to make use of multiple threads for tasks such as surface extraction. Performing surface extraction does not require write access to the data, and we've already established that you can safely perform reads from different threads *provided you are not using the LargeVolume*.
|
||||
|
||||
Combining multiple surface extraction threads with the *LargeVolume* is something we will need to experiment with in the future, to determine how it can be improved.
|
||||
|
||||
In the future we will expand this section to discuss how to split surface extraction across a number of threads, but for now please see Section XX of the book chapter 'Volumetric Representation of Virtual environments', available for free here: http://books.google.nl/books?id=WNfD2u8nIlIC&lpg=PR1&dq=game+engine+gems&pg=PA39&redir_esc=y#v=onepage&q&f=false
|
||||
|
||||
GPU thread safety
|
||||
=================
|
||||
Be aware that even if you successfully perform surface across multiple threads you still need to take care when uploading the data to the GPU. For Direct3D 9.0 and OpenGL 2.0 it is only possible to upload data from the main thread (or more accurately the one which owns the rendering context). So after you have performed your multi-threaded surface extraction you need to bring the data back to the main thread for uploading to the GPU.
|
||||
|
||||
More recent versions of the Direct3D and OpenGL APIs lift this restriction and provide means of accessing GPU resources from multiple threads. Please consult the documentation for your API for details.
|
||||
|
||||
Future work
|
||||
===========
|
||||
Threading support is not a high priority for PolyVox because it can be implemented by the user at a higher level. However, there are a couple of areas we may investigate in the future.
|
||||
|
||||
Thread safe volume wrapper
|
||||
--------------------------
|
||||
It might be useful to provide a thread safe wrapper around the volume classes, and this could possibly be included in the PolyVox utilities or as a extra library. This thread safe wrapper could be templatised to work with any internal volume type, and could itself be a volume so that it can be used directly with the existing algorithms.
|
||||
|
||||
OpenMP
|
||||
------
|
||||
This is a standard for extending C++ with compiler directives which allow the compiler to automatically parallelise sections of code. Most likely this could be used to parallelise some of the loops which occur in image processing tasks.
|
@ -1,4 +1,4 @@
|
||||
Changelog
|
||||
#########
|
||||
|
||||
.. include:: ../CHANGELOG.txt
|
||||
Changelog
|
||||
#########
|
||||
|
||||
.. include:: ../CHANGELOG.txt
|
||||
|
@ -1,232 +1,232 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PolyVox documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Jul 6 14:46:57 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append('@CMAKE_CURRENT_SOURCE_DIR@/_extensions')
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinxcontrib.doxylink']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'PolyVox'
|
||||
copyright = u'2013, David Williams, Matt Williams'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '@POLYVOX_VERSION@'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '@POLYVOX_VERSION@'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = "en"
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
#htmlhelp_basename = 'PolyVoxdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'PolyVox.tex', u'PolyVox Documentation',
|
||||
u'David Williams, Matt Williams', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'polyvox', u'PolyVox Documentation',
|
||||
[u'David Williams, Matt Williams'], 1)
|
||||
]
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
|
||||
doxylink = {
|
||||
'polyvox' : ('@CMAKE_CURRENT_BINARY_DIR@/../library/PolyVox.tag', '../library/doc/html/') #Make this '../api/' for uploading
|
||||
}
|
||||
|
||||
#This must be lowercase for QtHelp
|
||||
qthelp_basename = "polyvox"
|
||||
|
||||
primary_domain = "c++"
|
||||
|
||||
# Default higlighting scheme to use for `code-block` directives
|
||||
highlight_language = "c++"
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PolyVox documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Jul 6 14:46:57 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append('@CMAKE_CURRENT_SOURCE_DIR@/_extensions')
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinxcontrib.doxylink']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'PolyVox'
|
||||
copyright = u'2013, David Williams, Matt Williams'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '@POLYVOX_VERSION@'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '@POLYVOX_VERSION@'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = "en"
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
#htmlhelp_basename = 'PolyVoxdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'PolyVox.tex', u'PolyVox Documentation',
|
||||
u'David Williams, Matt Williams', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'polyvox', u'PolyVox Documentation',
|
||||
[u'David Williams, Matt Williams'], 1)
|
||||
]
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
|
||||
doxylink = {
|
||||
'polyvox' : ('@CMAKE_CURRENT_BINARY_DIR@/../library/PolyVox.tag', '../library/doc/html/') #Make this '../api/' for uploading
|
||||
}
|
||||
|
||||
#This must be lowercase for QtHelp
|
||||
qthelp_basename = "polyvox"
|
||||
|
||||
primary_domain = "c++"
|
||||
|
||||
# Default higlighting scheme to use for `code-block` directives
|
||||
highlight_language = "c++"
|
||||
|
@ -1,41 +1,41 @@
|
||||
Welcome to PolyVox's documentation!
|
||||
===================================
|
||||
|
||||
User Guide:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Prerequisites
|
||||
install
|
||||
principles
|
||||
Lighting
|
||||
TextureMapping
|
||||
ModifyingTerrain
|
||||
LevelOfDetail
|
||||
Threading
|
||||
ErrorHandling
|
||||
|
||||
Examples:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
tutorial1
|
||||
python-bindings
|
||||
|
||||
Other Information:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
changelog
|
||||
FAQ
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
Welcome to PolyVox's documentation!
|
||||
===================================
|
||||
|
||||
User Guide:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Prerequisites
|
||||
install
|
||||
principles
|
||||
Lighting
|
||||
TextureMapping
|
||||
ModifyingTerrain
|
||||
LevelOfDetail
|
||||
Threading
|
||||
ErrorHandling
|
||||
|
||||
Examples:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
tutorial1
|
||||
python-bindings
|
||||
|
||||
Other Information:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
changelog
|
||||
FAQ
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
@ -1 +1 @@
|
||||
.. include:: ../INSTALL.txt
|
||||
.. include:: ../INSTALL.txt
|
||||
|
@ -1,155 +1,155 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PolyVox.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PolyVox.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PolyVox.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PolyVox.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
|
@ -1,14 +1,14 @@
|
||||
**********************
|
||||
Principles of PolyVox
|
||||
**********************
|
||||
|
||||
.. warning ::
|
||||
This section is being written and is just a skeleton for now
|
||||
|
||||
PolyVox provides a library for managing 3D arrays (volumes) of data (voxels). It gives you the tools to iterate through, randomly access, read and write volumes. It supports any type you'd like to represent each voxel whether it's just an ``int`` or a class which encapsulates both density and colour.
|
||||
|
||||
Once you have a volume, PolyVox provides a number of tools for turning it into something that you can pass to your rendering engine. These are called `surface extractors`.
|
||||
|
||||
Each surface extractor needs to be told how to interperet your voxel type and that is done using...
|
||||
|
||||
Link to a page describing how to write your own voxel type and link it to a surface extractor...
|
||||
**********************
|
||||
Principles of PolyVox
|
||||
**********************
|
||||
|
||||
.. warning ::
|
||||
This section is being written and is just a skeleton for now
|
||||
|
||||
PolyVox provides a library for managing 3D arrays (volumes) of data (voxels). It gives you the tools to iterate through, randomly access, read and write volumes. It supports any type you'd like to represent each voxel whether it's just an ``int`` or a class which encapsulates both density and colour.
|
||||
|
||||
Once you have a volume, PolyVox provides a number of tools for turning it into something that you can pass to your rendering engine. These are called `surface extractors`.
|
||||
|
||||
Each surface extractor needs to be told how to interperet your voxel type and that is done using...
|
||||
|
||||
Link to a page describing how to write your own voxel type and link it to a surface extractor...
|
||||
|
@ -1,166 +1,166 @@
|
||||
**********************
|
||||
Tutorial 1 - Basic use
|
||||
**********************
|
||||
|
||||
Introduction
|
||||
============
|
||||
This tutorial covers the basic use of the PolyVox API. After reading this tutorial you should have a good idea how to create a PolyVox volume and fill it with data, extract a triangle mesh representing the surface, and render the result. This tutorial assumes you are already familiar with the basic concepts behind PolyVox (see the :doc:`principles of polyvox <principles>` document if not), and are reasonably confident with 3D graphics and C++. It also assumes you have already got PolyVox installed on your system, if this is not the case then please consult :doc:`installation guide <install>`.
|
||||
|
||||
The code samples and text in this tutorial correspond directly to the BasicExample which comes with PolyVox. This example uses the Qt toolkit for window and input handling, and for providing an OpenGL context to render into. In this tutorial we will omit code which performs these tasks and will instead focus on on the PolyVox code. You can consult the `Qt documentation <http://doc.qt.nokia.com/latest/>`_ if you want more information about these other aspects of the system.
|
||||
|
||||
Creating a volume
|
||||
=================
|
||||
To get started, we need to include the following headers:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
#include "PolyVoxCore/CubicSurfaceExtractorWithNormals.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/SurfaceMesh.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 which stores a 3D grid of voxels. Our basic example application creates a volume with the following line of code:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
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 of type `uint8_t` which is an unsigned 8-bit integer.
|
||||
|
||||
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:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
createSphereInVolume(volData, 30);
|
||||
|
||||
Note that this function is part of the BasicExample (rather than being part of the PolyVox library) and is implemented as follows:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
|
||||
{
|
||||
//This vector hold the position of the center of the volume
|
||||
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
|
||||
|
||||
//This three-level for loop iterates over every voxel in the volume
|
||||
for (int z = 0; z < volData.getDepth(); z++)
|
||||
{
|
||||
for (int y = 0; y < volData.getHeight(); y++)
|
||||
{
|
||||
for (int x = 0; x < volData.getWidth(); x++)
|
||||
{
|
||||
//Store our current position as a vector...
|
||||
Vector3DFloat v3dCurrentPos(x,y,z);
|
||||
//And compute how far the current position is from the center of the volume
|
||||
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(fDistToCenter <= fRadius)
|
||||
{
|
||||
//Our new voxel value
|
||||
uVoxelValue = 255;
|
||||
}
|
||||
|
||||
//Wrte the voxel value into the volume
|
||||
volData.setVoxelAt(x, y, z, uVoxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
This function takes as input the :polyvox:`SimpleVolume` in which we want to create the sphere, and also a radius specifying how large we want the sphere to be. In our case we have specified a radius of 30 voxels, which will fit nicely inside our :polyvox:`SimpleVolume` of dimensions 64x64x64.
|
||||
|
||||
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 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
|
||||
======================
|
||||
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:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
SurfaceMesh<PositionMaterialNormal> 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 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.
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
surfaceExtractor.execute();
|
||||
|
||||
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 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.
|
||||
|
||||
The OpenGLWidget::setSurfaceMeshToRender() function is implemented as follows:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
void OpenGLWidget::setSurfaceMeshToRender(const PolyVox::SurfaceMesh<PositionMaterialNormal>& surfaceMesh)
|
||||
{
|
||||
//Convienient access to the vertices and indices
|
||||
const vector<uint32_t>& vecIndices = surfaceMesh.getIndices();
|
||||
const vector<PositionMaterialNormal>& vecVertices = surfaceMesh.getVertices();
|
||||
|
||||
//Build an OpenGL index buffer
|
||||
glGenBuffers(1, &indexBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
const GLvoid* pIndices = static_cast<const GLvoid*>(&(vecIndices[0]));
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), pIndices, GL_STATIC_DRAW);
|
||||
|
||||
//Build an OpenGL vertex buffer
|
||||
glGenBuffers(1, &vertexBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
const GLvoid* pVertices = static_cast<const GLvoid*>(&(vecVertices[0]));
|
||||
glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PositionMaterialNormal), pVertices, GL_STATIC_DRAW);
|
||||
|
||||
m_uBeginIndex = 0;
|
||||
m_uEndIndex = vecIndices.size();
|
||||
}
|
||||
|
||||
We begin by obtaining direct access to the index and vertex buffer in the :polyvox:`SurfaceMesh` class in order to make the following code slightly cleaner. Both the :polyvox:`SurfaceMesh::getIndices` and :polyvox:`SurfaceMesh::getVertices` functions return an std::vector containing the relevant data.
|
||||
|
||||
The OpenGL functions which are called to construct the index and vertex buffer are best explained by the OpenGL documentation. In both cases we are making an exact copy of the data stored in the :polyvox:`SurfaceMesh`.
|
||||
|
||||
The begin and end indices are used in the OpenGLWidget::paintGL() to control what part of the index buffer is actually rendered. For this simple example we will render the whole buffer from '0' to 'vecIndices.size()'.
|
||||
|
||||
With the OpenGL index and vertex buffers set up, we can now look at the code which is called each frame to render them:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
void OpenGLWidget::paintGL()
|
||||
{
|
||||
//Clear the screen
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//Set up the viewing transformation
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back
|
||||
glRotatef(-m_xRotation, 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
|
||||
|
||||
//Bind the index buffer
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
|
||||
//Bind the vertex buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(PositionMaterialNormal), 0);
|
||||
glNormalPointer(GL_FLOAT, sizeof(PositionMaterialNormal), (GLvoid*)12);
|
||||
|
||||
glDrawRangeElements(GL_TRIANGLES, m_uBeginIndex, m_uEndIndex-1, m_uEndIndex - m_uBeginIndex, GL_UNSIGNED_INT, 0);
|
||||
|
||||
//Error checking code here...
|
||||
}
|
||||
|
||||
**********************
|
||||
Tutorial 1 - Basic use
|
||||
**********************
|
||||
|
||||
Introduction
|
||||
============
|
||||
This tutorial covers the basic use of the PolyVox API. After reading this tutorial you should have a good idea how to create a PolyVox volume and fill it with data, extract a triangle mesh representing the surface, and render the result. This tutorial assumes you are already familiar with the basic concepts behind PolyVox (see the :doc:`principles of polyvox <principles>` document if not), and are reasonably confident with 3D graphics and C++. It also assumes you have already got PolyVox installed on your system, if this is not the case then please consult :doc:`installation guide <install>`.
|
||||
|
||||
The code samples and text in this tutorial correspond directly to the BasicExample which comes with PolyVox. This example uses the Qt toolkit for window and input handling, and for providing an OpenGL context to render into. In this tutorial we will omit code which performs these tasks and will instead focus on on the PolyVox code. You can consult the `Qt documentation <http://doc.qt.nokia.com/latest/>`_ if you want more information about these other aspects of the system.
|
||||
|
||||
Creating a volume
|
||||
=================
|
||||
To get started, we need to include the following headers:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
#include "PolyVoxCore/CubicSurfaceExtractorWithNormals.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/SurfaceMesh.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 which stores a 3D grid of voxels. Our basic example application creates a volume with the following line of code:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
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 of type `uint8_t` which is an unsigned 8-bit integer.
|
||||
|
||||
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:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
createSphereInVolume(volData, 30);
|
||||
|
||||
Note that this function is part of the BasicExample (rather than being part of the PolyVox library) and is implemented as follows:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
|
||||
{
|
||||
//This vector hold the position of the center of the volume
|
||||
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
|
||||
|
||||
//This three-level for loop iterates over every voxel in the volume
|
||||
for (int z = 0; z < volData.getDepth(); z++)
|
||||
{
|
||||
for (int y = 0; y < volData.getHeight(); y++)
|
||||
{
|
||||
for (int x = 0; x < volData.getWidth(); x++)
|
||||
{
|
||||
//Store our current position as a vector...
|
||||
Vector3DFloat v3dCurrentPos(x,y,z);
|
||||
//And compute how far the current position is from the center of the volume
|
||||
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(fDistToCenter <= fRadius)
|
||||
{
|
||||
//Our new voxel value
|
||||
uVoxelValue = 255;
|
||||
}
|
||||
|
||||
//Wrte the voxel value into the volume
|
||||
volData.setVoxelAt(x, y, z, uVoxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
This function takes as input the :polyvox:`SimpleVolume` in which we want to create the sphere, and also a radius specifying how large we want the sphere to be. In our case we have specified a radius of 30 voxels, which will fit nicely inside our :polyvox:`SimpleVolume` of dimensions 64x64x64.
|
||||
|
||||
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 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
|
||||
======================
|
||||
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:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
SurfaceMesh<PositionMaterialNormal> 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 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.
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
surfaceExtractor.execute();
|
||||
|
||||
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 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.
|
||||
|
||||
The OpenGLWidget::setSurfaceMeshToRender() function is implemented as follows:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
void OpenGLWidget::setSurfaceMeshToRender(const PolyVox::SurfaceMesh<PositionMaterialNormal>& surfaceMesh)
|
||||
{
|
||||
//Convienient access to the vertices and indices
|
||||
const vector<uint32_t>& vecIndices = surfaceMesh.getIndices();
|
||||
const vector<PositionMaterialNormal>& vecVertices = surfaceMesh.getVertices();
|
||||
|
||||
//Build an OpenGL index buffer
|
||||
glGenBuffers(1, &indexBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
const GLvoid* pIndices = static_cast<const GLvoid*>(&(vecIndices[0]));
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), pIndices, GL_STATIC_DRAW);
|
||||
|
||||
//Build an OpenGL vertex buffer
|
||||
glGenBuffers(1, &vertexBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
const GLvoid* pVertices = static_cast<const GLvoid*>(&(vecVertices[0]));
|
||||
glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PositionMaterialNormal), pVertices, GL_STATIC_DRAW);
|
||||
|
||||
m_uBeginIndex = 0;
|
||||
m_uEndIndex = vecIndices.size();
|
||||
}
|
||||
|
||||
We begin by obtaining direct access to the index and vertex buffer in the :polyvox:`SurfaceMesh` class in order to make the following code slightly cleaner. Both the :polyvox:`SurfaceMesh::getIndices` and :polyvox:`SurfaceMesh::getVertices` functions return an std::vector containing the relevant data.
|
||||
|
||||
The OpenGL functions which are called to construct the index and vertex buffer are best explained by the OpenGL documentation. In both cases we are making an exact copy of the data stored in the :polyvox:`SurfaceMesh`.
|
||||
|
||||
The begin and end indices are used in the OpenGLWidget::paintGL() to control what part of the index buffer is actually rendered. For this simple example we will render the whole buffer from '0' to 'vecIndices.size()'.
|
||||
|
||||
With the OpenGL index and vertex buffers set up, we can now look at the code which is called each frame to render them:
|
||||
|
||||
.. sourcecode:: c++
|
||||
|
||||
void OpenGLWidget::paintGL()
|
||||
{
|
||||
//Clear the screen
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//Set up the viewing transformation
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back
|
||||
glRotatef(-m_xRotation, 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
|
||||
|
||||
//Bind the index buffer
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
|
||||
//Bind the vertex buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(PositionMaterialNormal), 0);
|
||||
glNormalPointer(GL_FLOAT, sizeof(PositionMaterialNormal), (GLvoid*)12);
|
||||
|
||||
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.
|
Reference in New Issue
Block a user