License GNU General Public License, version 2
Lines 195
Keywords
Fractal (1) Math (1) Menger Sponge (1) RenderMan (2)
Permissions
Owner: Stou S.
Viewable by Everyone
Editable by All Siafoo Users
Hide
Siafoo is here to make coding less frustrating and to save you time. Join Siafoo Now or Learn More

RenderMan procedural geometry - Menger Sponge Atom Feed 0

In Brief An excellent example of creating procedural geometry with RenderMan DSOs (RiProcDynamicLoad). In this case a Menger sponge:... more
# 's
  1//------------------------------------------------------------------------------
2// Menger Sponge primitive as renderman ProcDynamicLoad procedural geometry.
3//
4// Copyright (C) 2008 Chris Foster
5//
6// This library is free software; you can redistribute it and/or
7// modify it under the terms of the GNU General Public
8// License as published by the Free Software Foundation; either
9// version 2 of the License, or (at your option) any later version.
10//
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15//
16// You should have received a copy of the GNU General Public
17// License along with this library; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19//
20//
21// Author: Chris Foster [chris42f (at) gmail (d0t) com]
22//
23
24#include <ri.h>
25
26#include <sstream>
27#include <iostream>
28//#include <cstdlib>
29
30extern "C" AQSIS_EXPORT RtPointer ConvertParameters(char* initialdata);
31extern "C" AQSIS_EXPORT void Subdivide(RtPointer blinddata, RtFloat detailsize);
32extern "C" AQSIS_EXPORT void Free(RtPointer blinddata);
33
34
35/** A simple vector class with overloaded operators
36 */
37struct Vec3
38{
39 RtFloat x;
40 RtFloat y;
41 RtFloat z;
42
43 Vec3(RtFloat x, RtFloat y, RtFloat z) : x(x), y(y), z(z) {}
44 Vec3(RtFloat a) : x(a), y(a), z(a) {}
45
46 Vec3 operator+(const Vec3& rhs) const { return Vec3(x + rhs.x, y + rhs.y, z + rhs.z); }
47 Vec3 operator-(const Vec3& rhs) const { return Vec3(x - rhs.x, y - rhs.y, z - rhs.z); }
48 Vec3 operator*(float a) const { return Vec3(a*x, a*y, a*z); }
49};
50
51Vec3 operator*(float a, const Vec3& v) { return v*a; }
52
53
54/** Draw an axis-aligned cube, defined by the the minimum and maximum
55 * coordinates of the corners.
56 */
57void drawCube(Vec3 min, Vec3 max)
58{
59 RtInt nverts[] = {4, 4, 4, 4, 4, 4};
60 RtInt verts[] = {
61 0, 1, 2, 3,
62 4, 7, 6, 5,
63 0, 4, 5, 1,
64 3, 2, 6, 7,
65 1, 5, 6, 2,
66 0, 3, 7, 4
67 };
68 RtFloat P[] = {
69 min.x, min.y, min.z,
70 max.x, min.y, min.z,
71 max.x, min.y, max.z,
72 min.x, min.y, max.z,
73 min.x, max.y, min.z,
74 max.x, max.y, min.z,
75 max.x, max.y, max.z,
76 min.x, max.y, max.z
77 };
78
79 RiPointsPolygons(6, nverts, verts, "P", P, RI_NULL);
80}
81
82
83/** Class representing a menger sponge fractal, capable of recursively
84 * subdividing itself.
85 */
86class MengerSponge
87{
88 private:
89 // Level to which this sponge should be subdivided. 0 == no subdivision.
90 int m_level;
91 // minimum of axis-aligned box bounding the sponge
92 Vec3 m_min;
93 // maximum of axis-aligned box bounding the sponge
94 Vec3 m_max;
95
96 public:
97 MengerSponge(int level, const Vec3& min, const Vec3& max)
98 : m_level(level),
99 m_min(min),
100 m_max(max)
101 { }
102 /** Extract sponge parameters from a string
103 *
104 * initString is in the form "level x1 x2 y1 y1 z2 z2"
105 */
106 MengerSponge(const char* initString)
107 : m_level(1),
108 m_min(-1),
109 m_max(1)
110 {
111 std::istringstream(initString) >> m_level
112 >> m_min.x >> m_max.x
113 >> m_min.y >> m_max.y
114 >> m_min.z >> m_max.z;
115 }
116
117 /** Fill a renderman bound array with the bound for the current
118 * primitive.
119 */
120 void bound(RtBound bnd) const
121 {
122 bnd[0] = m_min.x;
123 bnd[1] = m_max.x;
124 bnd[2] = m_min.y;
125 bnd[3] = m_max.y;
126 bnd[4] = m_min.z;
127 bnd[5] = m_max.z;
128 }
129
130 /** Create a child of the current sponge
131 *
132 * xOff, yOff and zOff are the integer coordinates of the child within
133 * an even 3x3x3 subdivision of the current bounding box. RiProcedural()
134 * is called recursively to add the subdivided child primitives to the
135 * render pipeline.
136 */
137 void createChild(int xOff, int yOff, int zOff) const
138 {
139 RtBound bnd;
140 Vec3 diag = (1/3.0)*(m_max - m_min);
141 Vec3 newMin = m_min + Vec3(xOff*diag.x, yOff*diag.y, zOff*diag.z);
142 MengerSponge* pNew = new MengerSponge(m_level-1, newMin, newMin + diag);
143 pNew->bound(bnd);
144 RiProcedural(pNew, bnd, Subdivide, Free);
145 }
146
147 /** Split the procedural up according to the fractal subdivision rules.
148 *
149 * We iterate through all of the set of 3x3x3 sub-cubes, deciding which
150 * ones to draw based on the fractal recursion relation. Each drawn
151 * subcube is subdivided in the same way until leaf nodes of the
152 * subdivision tree are reached.
153 */
154 void subdivide() const
155 {
156 if(m_level <= 0) // || m_level/4.0 < std::rand()/float(RAND_MAX))
157 drawCube(m_min, m_max);
158 else
159 {
160 for(int zOff = 0; zOff < 3; ++zOff)
161 {
162 for(int yOff = 0; yOff < 3; ++yOff)
163 {
164 for(int xOff = 0; xOff < 3; ++xOff)
165 {
166 //if(xOff == 1 || yOff == 1 || zOff == 1)
167 if(xOff*yOff != 1 && xOff*zOff != 1 && yOff*zOff != 1)
168 createChild(xOff, yOff, zOff);
169 }
170 }
171 }
172 }
173 }
174};
175
176
177//------------------------------------------------------------------------------
178/**
179 * Any procedural which will be accessed via the RiProcDynamicLoad procedural
180 * type must provide three functions: ConvertParameters, Subdivide, and Free;
181 * here they are.
182 */
183
184/** ConvertParameters()
185 *
186 * Converts a string of initialization data for the procedural into whatever
187 * internal data the procedural needs. This data is sent back to the renderer
188 * as an opaque pointer, and will be passed onto the Subdivide() and Free()
189 * functions.
190 */
191extern "C" RtPointer ConvertParameters(char* initialdata)
192{
193 MengerSponge* params = new MengerSponge(initialdata);
194 return reinterpret_cast<RtPointer>(params);
195}
196
197/** Subdivide()
198 *
199 * Splits the procedural into smaller primitives which are inserted
200 * into the renderer pipeline via the RI. These can be any primitive type
201 * supported by the renderer, including further procedurals.
202 */
203extern "C" void Subdivide(RtPointer blinddata, RtFloat detailsize)
204{
205 const MengerSponge* p = reinterpret_cast<MengerSponge*>(blinddata);
206 p->subdivide();
207}
208
209/** Free()
210 *
211 * Frees the data pointed to by the handle which was allocated inside
212 * ConvertParameters().
213 */
214extern "C" void Free(RtPointer blinddata)
215{
216 delete reinterpret_cast<MengerSponge*>(blinddata);
217}

An excellent example of creating procedural geometry with RenderMan DSOs (RiProcDynamicLoad). In this case a Menger sponge:

http://www.siafoo.net/image/157

Compile with (something like):

g++ -Wall -fPIC -O3 -shared menger.cpp -I${AQSIS_INCLUDE_DIR} -o menger.so

Then use a RIB file like:

# 's
 1Display "menger.tif" "file" "rgb" "compression" "lzw"
2Display "+menger.tif" "framebuffer" "rgb"
3
4Format 512 512 1
5
6Projection "perspective" "fov" 45
7Translate 0 0 5
8Rotate -30 1 0 0
9Rotate 20 0 1 0
10
11WorldBegin
12 Procedural "DynamicLoad" ["menger" "4 -1 1 -1 1 -1 1"] [-1 1 -1 1 -1 1]
13WorldEnd

Note

The code for this plugin and the RIB file above are located inside the 'misc/menger' directory of the Aqsis source

Creating a DSO is very straight forward, simply implement three functions (as required by the RiSpec Section 5.7 pg 90): ConvertParameters performs the initialization for your drawing code (allocating objects, etc.), Free performs cleanups, Subdivide is where you issue all your Ri* commands.