This repository has been archived by the owner on Mar 11, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PlaceIceProcedure.cpp
259 lines (217 loc) · 9.64 KB
/
PlaceIceProcedure.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/// Includes all the necessary header files.
#include <ri.h>
#include <RixInterfaces.h>
#include <RiTypesHelper.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
// A RiProcedural must implement these functions. This is a fixed requirement.
extern "C"
{
PRMANEXPORT RtPointer ConvertParameters ( RtString paramStr );
PRMANEXPORT RtVoid Subdivide ( RtPointer data, RtFloat detail );
PRMANEXPORT RtVoid Free ( RtPointer data );
}
/**
* This struct is defined by the data passed through from the data field of the RenderMan procedural node.
*
* RtFloat size_variance: The variation in the scale of the ice cube.
* RtFloat spacing_variance: The variation in the x and z coordinates of the ice cube.
* std::string ice_path: The absolute path to the ice RIB archive.
* RtFloat initial_scale: The default scale of each ice cube.
* RtInt coordinate_count: The number of coordinates an ice cube can be placed at.
* RtFloat *surface_coordinates: The x, y, z coorindates of the top of the whiskey.
*/
typedef struct {
RtFloat size_variance;
RtFloat spacing_variance;
RtFloat rotation_variance;
std::string ice_path;
RtFloat initial_scale;
RtInt coordinate_count;
RtFloat *surface_coordinates;
} IceData;
RtFloat randBetween(RtFloat min, RtFloat max);
RtBoolean isOverlapping(RtFloat x, RtFloat y, RtFloat z, RtFloat other_x, RtFloat other_y, RtFloat other_z);
std::vector<std::string> winGetFiles(std::string pattern);
std::vector<std::string> globGetFiles(const std::string& pattern);
std::vector<std::string> getFiles(std::string pattern);
/**
* Converts the data to their proper data types.
*
* RtString paramStr: The string of the data from the RenderMan procedural node's data attribute.
*
* Returns a pointer to the data for the ice.
*/
RtPointer ConvertParameters(RtString paramStr) {
// The strtok() function cannot be used on the paramStr directly because
// it modifies the string.
long len = strlen(paramStr);
// We could directly create a copy of the input paramStr as an array and
// use the strcpy(), string copy, function.
//char copyStr[len];
//strcpy(copyStr, paramStr);
// However, because the paramStr can be very large we allocate memory
// from the main memory pool (the "heap") and then perform a block
// copy of the contents of paramStr.
char *copyStr = (char*)calloc(len + 1, sizeof(char));
memcpy(copyStr, paramStr, len + 1);
// Allocate a block of memory to store one instance of SpheresData.
IceData *dataPtr = (IceData*)calloc(1, sizeof(IceData));
// Irrespective of how many values are specified by the paramStr we
// know the first two values will specify the radius of the spheres
// and the number of coordinates that define their 3D locations.
char path_characters[512];
sscanf(copyStr, "%f %f %f %s %f %d", &dataPtr->size_variance, &dataPtr->spacing_variance, &dataPtr->rotation_variance, path_characters, &dataPtr->initial_scale, &dataPtr->coordinate_count);
std::string path(path_characters);
std::vector<std::string> paths = getFiles(path);
dataPtr->ice_path = paths[0];
// Allocate memory to store an array of coordinates
RtInt coordinate_count = dataPtr->coordinate_count;
dataPtr->surface_coordinates = (RtFloat*)calloc(coordinate_count, sizeof(RtFloat));
char *strPtr = strtok(copyStr, " ");
for(int deleted_values = 0; deleted_values < 6; deleted_values++) {
strPtr = strtok(NULL, " ");
}
long current_index = 0;
while(strPtr) {
// Convert each string to a double precision floating point number
dataPtr->surface_coordinates[current_index] = strtod(strPtr, NULL);
current_index++;
strPtr = strtok(NULL, " "); // grab the next part of copyStr.
}
// Don't forget to free the memory that was allocated for the copied text.
free(copyStr);
return (RtPointer)dataPtr;
}
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Subdivide(RtPointer data, RtFloat detail) {
/// Each value is pulled from the pointer to the instance of the IceData struct.
RtFloat size_variance = ((IceData*)data)->size_variance;
RtFloat spacing_variance = ((IceData*)data)->spacing_variance;
RtFloat rotation_variance = ((IceData*)data)->rotation_variance;
std::string ice_path = ((IceData*)data)->ice_path;
RtInt coordinate_count = ((IceData*)data)->coordinate_count;
RtFloat initial_scale = ((IceData*)data)->initial_scale;
RtFloat *surface_coordinates = ((IceData*)data)->surface_coordinates;
/// This keeps track of the coordinates that already have ice cubes placed there.
std::vector <RtFloat> other_coordinates;
for(int current_index = 0; current_index < coordinate_count; current_index += 3) {
RtFloat x = surface_coordinates[current_index] + randBetween(-spacing_variance, spacing_variance);
RtFloat y = surface_coordinates[current_index + 1];
RtFloat z = surface_coordinates[current_index + 2] + randBetween(-spacing_variance, spacing_variance);
/// Loops until there is a confirmed overlap of ice cubes.
RtBoolean overlaps_another = false;
for(int current_other_index = 0; current_other_index < other_coordinates.size(); current_other_index += 3) {
if(overlaps_another) {
break;
}
RtFloat other_x = other_coordinates[current_other_index];
RtFloat other_y = other_coordinates[current_other_index + 1];
RtFloat other_z = other_coordinates[current_other_index + 2];
overlaps_another = isOverlapping(x, y, z, other_x, other_y, other_z);
}
/// If there are no ice cubes at all or there was no overlap, create and place the ice cube as well as keep track of the coordinate where it was placed.
if(other_coordinates.size() == 0 || !overlaps_another) {
RiTransformBegin();
RiTranslate(x,y,z);
RiRotate(randBetween(-rotation_variance, rotation_variance), randBetween(0, 1), randBetween(0, 1), randBetween(0, 1));
RiScale(initial_scale + randBetween(0,size_variance), initial_scale + randBetween(0,size_variance), initial_scale + randBetween(0,size_variance));
RiReadArchiveV(ice_path.c_str(), NULL, 0, NULL, NULL);
RiTransformEnd();
other_coordinates.push_back(x);
other_coordinates.push_back(y);
other_coordinates.push_back(z);
}
}
}
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Free(RtPointer data) {
free(((IceData*)data)->surface_coordinates);
free(data);
}
/**
* Picks a random number between 2 values. Provided by Malcolm Kesson.
*
* RtFloat min: The smaller number in the range.
* RtFloat max: The larger number in the range.
*
* Returns a random number betwen the min and max.
*/
RtFloat randBetween(RtFloat min, RtFloat max) {
return ((RtFloat)rand()/RAND_MAX) * (max - min) + min;
}
/**
* Checks if the ice cube about to placed would overlap with one that is already placed.
*
* RtFloat x, y, z: The coordinates of the ice cube being placed.
* RtFloat other_x, other_y, other_z: The coordinates of the ice cube that is already placed.
*
* Returns whether or not the ice cubes are overlapping.
*/
RtBoolean isOverlapping(RtFloat x, RtFloat y, RtFloat z, RtFloat other_x, RtFloat other_y, RtFloat other_z) {
RtFloat distance_to_edge = .25;
RtFloat minX = x - distance_to_edge;
RtFloat minY = y - distance_to_edge;
RtFloat minZ = z - distance_to_edge;
RtFloat otherMinX = other_x - distance_to_edge;
RtFloat otherMinY = other_y - distance_to_edge;
RtFloat otherMinZ = other_z - distance_to_edge;
RtFloat maxX = x + distance_to_edge;
RtFloat maxY = y + distance_to_edge;
RtFloat maxZ = z + distance_to_edge;
RtFloat otherMaxX = other_x + distance_to_edge;
RtFloat otherMaxY = other_y + distance_to_edge;
RtFloat otherMaxZ = other_z + distance_to_edge;
RtBoolean xOverlaps = (minX <= otherMaxX && maxX >= otherMinX);
RtBoolean yOverlaps = (minY <= otherMaxY && maxY >= otherMinY);
RtBoolean zOverlaps = (minZ <= otherMaxZ && maxZ >= otherMinZ);
return xOverlaps && yOverlaps && zOverlaps;
}
/// THE FOLLOWING CODE WAS PROVIDED BY MALCOLM KESSON TO GET THE RIB ARCHIVE FILE
#ifdef _WIN32
#include <Windows.h>
std::vector<std::string> winGetFiles(std::string pattern) {
std::vector<std::string> paths;
std::string parentDir = pattern.substr(0, pattern.find_last_of("/\\"));
//printf("%s\n", parentDir.c_str());
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(pattern.c_str(), &fd);
if(hFind != INVALID_HANDLE_VALUE) {
do {
// read all (real) files in current folder
// , delete '!' read other 2 default folder . and ..
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
paths.push_back(parentDir + "/" + fd.cFileName);
}
} while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return paths;
}
#else
#include <glob.h>
std::vector<std::string> globGetFiles(const std::string& pattern){
glob_t glob_result;
glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
std::vector<std::string> paths;
for(unsigned int i=0;i<glob_result.gl_pathc;++i){
paths.push_back(std::string(glob_result.gl_pathv[i]));
}
globfree(&glob_result);
return paths;
}
#endif
std::vector<std::string> getFiles(std::string pattern) {
#ifdef _WIN32
return winGetFiles(pattern);
#else
return globGetFiles(pattern);
#endif
}