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
Know what you're getting – Unlike many sites, all our code is clearly licensed. 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.