1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 // Interface header.
31 #include "volume.h"
32
33 // appleseed.foundation headers.
34 #include "foundation/platform/types.h"
35 #include "foundation/utility/cc.h"
36
37 // Standard headers.
38 #include <cassert>
39 #include <cstdio>
40 #include <utility>
41
42 using namespace foundation;
43 using namespace std;
44
45 namespace renderer
46 {
47
48 //
49 // FluidChannels class implementation.
50 //
51
FluidChannels()52 FluidChannels::FluidChannels()
53 : m_color_index(NotPresent)
54 , m_density_index(NotPresent)
55 , m_temperature_index(NotPresent)
56 , m_fuel_index(NotPresent)
57 , m_falloff_index(NotPresent)
58 , m_pressure_index(NotPresent)
59 , m_coordinates_index(NotPresent)
60 , m_velocity_index(NotPresent)
61 {
62 }
63
64
65 //
66 // Voxel grid I/O.
67 //
68
69 namespace
70 {
71 struct FluidFileHeader
72 {
73 long m_id;
74 unsigned int m_xres;
75 unsigned int m_yres;
76 unsigned int m_zres;
77 unsigned int m_has_color;
78 unsigned int m_has_density;
79 unsigned int m_has_temperature;
80 unsigned int m_has_fuel;
81 unsigned int m_has_falloff;
82 unsigned int m_has_pressure;
83 unsigned int m_has_coordinates;
84 unsigned int m_has_velocity;
85 };
86
read_channels(FILE * file,VoxelGrid * grid,const size_t channel_index,const size_t channel_count)87 size_t read_channels(
88 FILE* file,
89 VoxelGrid* grid,
90 const size_t channel_index,
91 const size_t channel_count)
92 {
93 assert(file);
94 assert(grid);
95 assert(channel_count > 0);
96
97 size_t read = 0;
98
99 for (size_t z = 0; z < grid->get_zres(); ++z)
100 {
101 for (size_t y = 0; y < grid->get_yres(); ++y)
102 {
103 for (size_t x = 0; x < grid->get_xres(); ++x)
104 {
105 float* voxel = grid->voxel(x, y, z);
106 read +=
107 fread(
108 &voxel[channel_index],
109 sizeof(float),
110 channel_count,
111 file);
112 }
113 }
114 }
115
116 return read;
117 }
118 }
119
read_fluid_file(const char * filename,FluidChannels & channels)120 unique_ptr<VoxelGrid> read_fluid_file(
121 const char* filename,
122 FluidChannels& channels)
123 {
124 assert(filename);
125
126 FILE* file = fopen(filename, "rb");
127
128 if (file == nullptr)
129 return unique_ptr<VoxelGrid>(nullptr);
130
131 // Read the file header.
132 FluidFileHeader header;
133 if (fread(&header, sizeof(FluidFileHeader), 1, file) < 1)
134 {
135 fclose(file);
136 return unique_ptr<VoxelGrid>(nullptr);
137 }
138
139 // Check the validity of the file header.
140 if (header.m_id != CC32('F', 'L', 'D', '3'))
141 {
142 fclose(file);
143 return unique_ptr<VoxelGrid>(nullptr);
144 }
145
146 const size_t voxel_count = header.m_xres * header.m_yres * header.m_zres;
147
148 // Compute the number of channels, and set channel indices.
149 size_t channel_count = 0;
150 if (header.m_has_color)
151 {
152 channels.m_color_index = channel_count;
153 channel_count += 3;
154 }
155 if (header.m_has_density)
156 {
157 channels.m_density_index = channel_count;
158 channel_count += 1;
159 }
160 if (header.m_has_temperature)
161 {
162 channels.m_temperature_index = channel_count;
163 channel_count += 1;
164 }
165 if (header.m_has_fuel)
166 {
167 channels.m_fuel_index = channel_count;
168 channel_count += 1;
169 }
170 if (header.m_has_falloff)
171 {
172 channels.m_falloff_index = channel_count;
173 channel_count += 1;
174 }
175 if (header.m_has_pressure)
176 {
177 channels.m_pressure_index = channel_count;
178 channel_count += 1;
179 }
180 if (header.m_has_coordinates)
181 {
182 channels.m_coordinates_index = channel_count;
183 channel_count += 3;
184 }
185 if (header.m_has_velocity)
186 {
187 channels.m_velocity_index = channel_count;
188 channel_count += 3;
189 }
190
191 unique_ptr<VoxelGrid> grid(
192 new VoxelGrid(
193 header.m_xres,
194 header.m_yres,
195 header.m_zres,
196 channel_count));
197
198 size_t channel_index = 0;
199 size_t read = 0;
200 size_t needed = 0;
201
202 // Read fluid color.
203 if (header.m_has_color)
204 {
205 read += read_channels(file, grid.get(), channel_index, 3);
206 needed += voxel_count * 3;
207 channel_index += 3;
208 }
209
210 // Read fluid density.
211 if (header.m_has_density)
212 {
213 read += read_channels(file, grid.get(), channel_index, 1);
214 needed += voxel_count;
215 channel_index += 1;
216 }
217
218 // Read fluid temperature.
219 if (header.m_has_temperature)
220 {
221 read += read_channels(file, grid.get(), channel_index, 1);
222 needed += voxel_count;
223 channel_index += 1;
224 }
225
226 // Read fluid fuel.
227 if (header.m_has_fuel)
228 {
229 read += read_channels(file, grid.get(), channel_index, 1);
230 needed += voxel_count;
231 channel_index += 1;
232 }
233
234 // Read fluid falloff.
235 if (header.m_has_falloff)
236 {
237 read += read_channels(file, grid.get(), channel_index, 1);
238 needed += voxel_count;
239 channel_index += 1;
240 }
241
242 // Read fluid pressure.
243 if (header.m_has_pressure)
244 {
245 read += read_channels(file, grid.get(), channel_index, 1);
246 needed += voxel_count;
247 channel_index += 1;
248 }
249
250 // Read fluid coordinates.
251 if (header.m_has_coordinates)
252 {
253 read += read_channels(file, grid.get(), channel_index, 3);
254 needed += voxel_count * 3;
255 channel_index += 3;
256 }
257
258 // Read fluid velocity.
259 if (header.m_has_velocity)
260 {
261 // X.
262 read += read_channels(file, grid.get(), channel_index, 1);
263 needed += voxel_count;
264 channel_index += 1;
265
266 // Y.
267 read += read_channels(file, grid.get(), channel_index, 1);
268 needed += voxel_count;
269 channel_index += 1;
270
271 // Z.
272 read += read_channels(file, grid.get(), channel_index, 1);
273 needed += voxel_count;
274 channel_index += 1;
275 }
276
277 assert(channel_index == channel_count);
278
279 fclose(file);
280
281 return read == needed ? move(grid) : unique_ptr<VoxelGrid>(nullptr);
282 }
283
write_voxel_grid(const char * filename,const VoxelGrid & grid)284 void write_voxel_grid(
285 const char* filename,
286 const VoxelGrid& grid)
287 {
288 FILE* file = fopen(filename, "wt");
289
290 if (file == nullptr)
291 return;
292
293 const size_t xres = grid.get_xres();
294 const size_t yres = grid.get_yres();
295 const size_t zres = grid.get_zres();
296 const size_t channel_count = grid.get_channel_count();
297
298 for (size_t z = 0; z < zres; ++z)
299 {
300 fprintf(file, "z " FMT_SIZE_T "\n\n", z);
301
302 for (size_t y = 0; y < yres; ++y)
303 {
304 for (size_t x = 0; x < xres; ++x)
305 {
306 if (x > 0)
307 fprintf(file, " ");
308
309 const float* voxel = grid.voxel(x, y, z);
310
311 for (size_t i = 0; i < channel_count; ++i)
312 {
313 if (i > 0)
314 fprintf(file, ",");
315
316 fprintf(file, "%f", voxel[i]);
317 }
318 }
319
320 fprintf(file, "\n");
321 }
322
323 fprintf(file, "\n");
324 }
325
326 fclose(file);
327 }
328
329 } // namespace renderer
330