| Trees | Indices | Help |
|---|
|
|
1 #!/usr/bin/env python
2
3 #! This file is a literate Python program. You can compile the documentation
4 #! using mylit (http://pypi.python.org/pypi/mylit/).
5 ## title = "glitter Example: Voxelization"
6 ## stylesheet = "pygments_style.css"
7
8 # <h1><i>glitter</i> Example: Voxelization</h1>
9
10 # <h2>Summary</h2>
11
12 # This program will create an invisible OpenGL context to voxelize a mesh given
13 # as an <a href="www.hdfgroup.org/HDF5/">HDF5</a> data file. The resulting
14 # volume is written to a file.
15
16 #! TODO describe voxelization algorithm
17 #! TODO include spreadbits.py so the data can be viewed in the volume viewer
18
19 # <h2>Front matter</h2>
20
21 # <h3>Module docstring</h3>
22
23 # The module docstring is used as a description of this example in the
24 # generated documentation:
25 """Voxelize a mesh.
26
27 @author: Stephan Wenger
28 @date: 2012-02-29
29 """
30
31 # We can usually import classes and functions contained in <i>glitter</i>
32 # submodules directly from glitter:
33 from glitter import VertexArray, TextureArray2D, uint32, Framebuffer, State, ShaderProgram
34
35 # <h2>Shaders</h2>
36
37 # The vertex shader transforms the position as usual.
38 vertex_code = """
39 #version 400 core
40
41 layout(location=0) in vec4 in_position;
42 out vec4 ex_position;
43
44 void main() {
45 gl_Position = ex_position = in_position;
46 }
47 """
48
49 # The fragment code will perform the actual work. It will quantize each
50 # fragment into a depth layer, compute a bitmask corresponding to that depth,
51 # and write the bit mask into the color attachments. The number of color
52 # attachments is only known at runtime, which is why a <code>%d</code>
53 # placeholder will have to be filled in just before the shader is compiled.
54 fragment_code = """
55 #version 400 core
56
57 in vec4 ex_position;
58 uniform bool solid;
59 layout(location=0) out uvec4 fragmentColor[%d];
60
61 uvec4 voxelize(uint z, uint offset, uint fill, uint base) {
62 if (z >= (offset + 1u) * 128u) return uvec4(0u);
63 if (z < offset * 128u) return uvec4(fill);
64 z -= offset * 128u;
65 return uvec4(
66 z < 128u ? (z >= 96u ? base << (z - 96u) : fill) : 0u,
67 z < 96u ? (z >= 64u ? base << (z - 64u) : fill) : 0u,
68 z < 64u ? (z >= 32u ? base << (z - 32u) : fill) : 0u,
69 z < 32u ? base << (z - 0u) : 0u
70 );
71 }
72
73 void main() { // if solid, switch on all bits >= z; else, switch on bit == z
74 uint z = uint(round(128.0 * float(fragmentColor.length()) * 0.5 * (ex_position.z + 1.0)));
75 for (int i = 0; i < fragmentColor.length(); ++i)
76 fragmentColor[i] = voxelize(z, i, solid ? 0xffffffffu : 0u, solid ? 0xffffffffu : 1u);
77 }
78 """
79
80 # <h2>Voxelization</h2>
81
82 # The function <code>voxelize()</code> performs solid or boundary voxelization
83 # of a mesh into a volume of given size.
85 """Voxelize a mesh into a cube of length C{size}.
86
87 @param mesh: The mesh to voxelize.
88 @type mesh: L{VertexArray}
89 @param size: The length of the resulting cube. Should be a multiple of 128.
90 @type size: C{int}
91 @param solid: Whether to perform solid voxelization instead of boundary voxelization.
92 @type solid: C{bool}
93
94 @attention: The result of boundary voxelization is likely to have holes. To
95 obtain a watertight volume, you can voxelize the volume from different
96 directions and combine the results, or use the gradient of a solid volume.
97 """
98
99 # An array of 2D integer textures will hold the output volume encoded in
100 # its bits:
101 volume = TextureArray2D(shape=(size // 128, size, size, 4), dtype=uint32)
102
103 # We create a framebuffer with all layers of the volume attached and
104 # directly bind it:
105 with Framebuffer(*[volume[i] for i in range(len(volume))]) as fbo:
106 fbo.clear()
107 # Depending on the voxelization mode, we set the logic op mode. We then
108 # create and activate a shader program and draw the mesh.
109 with State(logic_op_mode="XOR" if solid else "OR", color_logic_op=True):
110 with ShaderProgram(vertex=vertex_code, fragment=fragment_code % len(volume), solid=solid):
111 mesh.draw()
112
113 # Finally, the texture array is returned. The client code can decide to
114 # download the data or to process it further on the GPU.
115 return volume
116
117 # <h2>Initialization and main loop</h2>
118
119 # Finally, if this program is being run from the command line, we set up all
120 # the previously mentioned objects and start rendering. The program will expect
121 # two command line parameters: the name of an HDF5 input file, and the name of
122 # an HDF5 output file. An optional third parameter specifies the size of the
123 # resulting volume.
124 if __name__ == "__main__":
125 # We need to read a mesh filename from <code>sys.argv</code>, so import
126 # <code>sys</code>.
127 import sys
128
129 # We assume the mesh is stored in a <a
130 # href="http://www.hdfgroup.org/HDF5/">HDF5</a> file, so import <a
131 # href="h5py.alfven.org"><code>h5py</code></a>.
132 import h5py
133
134 # We open the HDF5 file specified on the command line for reading, extract
135 # the vertices and indices, and store them in a vertex array:
136 with h5py.File(sys.argv[1]) as f:
137 mesh = VertexArray(f["vertices"], elements=f["indices"])
138
139 # Now the voxelization is performed and the result is returned as an array
140 # of 2D integer textures.
141 volume = voxelize(mesh, int(sys.argv[3]) if len(sys.argv) > 3 else 128)
142
143 # Finally, we open the output file, download the data, and store it in a
144 # dataset. The dataset can typically be compressed a lot, which is why we
145 # enable LZF compression. However, this may take a long time, so you can
146 # disable it if you are not short on disk space.
147 with h5py.File(sys.argv[2], "w") as f:
148 f.create_dataset("data", data=volume.data, compression="lzf")
149
150 # Note how we did not need to create an OpenGL context explicitly. The
151 # first object that needs an OpenGL context (in this case the
152 # <code>VertexArray</code>) will create an invisible context as soon as it
153 # is initialized. This is very convenient; on the other hand, it means that
154 # if you create a context manually (e.g., because you need a visible
155 # window), you have to initialize it before any other objects. Otherwise,
156 # these objects may end up in the wrong context, and sharing data across
157 # context is a problem of its own.
158
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Fri Mar 16 17:56:07 2012 | http://epydoc.sourceforge.net |