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