1 /*
2 The MIT License (MIT)
3 
4 Copyright (c) 2015 - 2017 Light Transport Entertainment, Inc.
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 */
24 
25 #ifdef _WIN32
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <time.h>
29 #else
30 #include <fcntl.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #endif
34 
35 #ifdef __clang__
36 #pragma clang diagnostic push
37 #pragma clang diagnostic ignored "-Weverything"
38 #endif
39 
40 
41 #ifdef _MSC_VER
42 #pragma warning(push)
43 #pragma warning(disable : 4324)
44 #pragma warning(disable : 4457)
45 #pragma warning(disable : 4456)
46 #endif
47 
48 #include <embree2/rtcore.h>
49 #include <embree2/rtcore_ray.h>
50 
51 #define STB_IMAGE_WRITE_IMPLEMENTATION
52 #include "stb_image_write.h"
53 
54 #ifdef __clang__
55 #pragma clang diagnostic pop
56 #endif
57 
58 #ifdef _MSC_VER
59 #pragma warning(pop)
60 #endif
61 
62 #include <cstdio>
63 #include <cstdlib>
64 #include <iostream>
65 
66 #include "obj-loader.h"  // ../nanosg
67 
vnormalize(float dst[3],const float v[3])68 static inline void vnormalize(float dst[3], const float v[3]) {
69   dst[0] = v[0];
70   dst[1] = v[1];
71   dst[2] = v[2];
72   const float len = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
73   if (std::fabs(len) > std::numeric_limits<float>::epsilon()) {
74     const float inv_len = 1.0f / len;
75     dst[0] *= inv_len;
76     dst[1] *= inv_len;
77     dst[2] *= inv_len;
78   }
79 }
80 
MultV(float dst[3],const float m[4][4],const float v[3])81 static void MultV(float dst[3], const float m[4][4], const float v[3]) {
82   float tmp[3];
83   tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
84   tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
85   tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
86   dst[0] = tmp[0];
87   dst[1] = tmp[1];
88   dst[2] = tmp[2];
89 }
90 
91 // Create TriangleMesh
CreateTriangleMesh(const example::Mesh<float> & mesh,RTCScene scene)92 static unsigned int CreateTriangleMesh(const example::Mesh<float> &mesh,
93                                        RTCScene scene) {
94   unsigned int geom_id = rtcNewTriangleMesh(
95       scene, RTC_GEOMETRY_STATIC,
96       /* numTriangles */ mesh.faces.size() / 3, mesh.vertices.size() / 3, 1);
97 
98   float *vertices = reinterpret_cast<float *>(
99       rtcMapBuffer(scene, geom_id, RTC_VERTEX_BUFFER));
100   int *faces =
101       reinterpret_cast<int *>(rtcMapBuffer(scene, geom_id, RTC_INDEX_BUFFER));
102 
103   for (size_t i = 0; i < mesh.vertices.size() / 3; i++) {
104     // TODO(LTE): Use instanciation for applying xform.
105     float v[3];
106     MultV(v, mesh.pivot_xform, &mesh.vertices[3 * i + 0]);
107 
108     // Embree uses 4 floats(16 bytes stride)
109     vertices[4 * i + 0] = v[0];
110     vertices[4 * i + 1] = v[1];
111     vertices[4 * i + 2] = v[2];
112     vertices[4 * i + 3] = 0.0f;
113   }
114 
115   for (size_t i = 0; i < mesh.faces.size() / 3; i++) {
116     faces[3 * i + 0] = static_cast<int>(mesh.faces[3 * i + 0]);
117     faces[3 * i + 1] = static_cast<int>(mesh.faces[3 * i + 1]);
118     faces[3 * i + 2] = static_cast<int>(mesh.faces[3 * i + 2]);
119   }
120 
121   rtcUnmapBuffer(scene, geom_id, RTC_VERTEX_BUFFER);
122   rtcUnmapBuffer(scene, geom_id, RTC_INDEX_BUFFER);
123 
124   return geom_id;
125 }
126 
SaveImagePNG(const char * filename,const float * rgb,int width,int height)127 static void SaveImagePNG(const char *filename, const float *rgb, int width,
128                          int height) {
129   unsigned char *bytes = new unsigned char[size_t(width * height * 3)];
130   for (int y = 0; y < height; y++) {
131     for (int x = 0; x < width; x++) {
132       const int index = y * width + x;
133       bytes[index * 3 + 0] = static_cast<unsigned char>(
134           std::max(0.0f, std::min(rgb[index * 3 + 0] * 255.0f, 255.0f)));
135       bytes[index * 3 + 1] = static_cast<unsigned char>(
136           std::max(0.0f, std::min(rgb[index * 3 + 1] * 255.0f, 255.0f)));
137       bytes[index * 3 + 2] = static_cast<unsigned char>(
138           std::max(0.0f, std::min(rgb[index * 3 + 2] * 255.0f, 255.0f)));
139     }
140   }
141   stbi_write_png(filename, width, height, 3, bytes, width * 3);
142   delete[] bytes;
143 }
144 
145 #ifdef __clang__
146 // suppress RTC_INVALID_GEOMETRY_ID
147 #pragma clang diagnostic ignored "-Wold-style-cast"
148 #endif
149 
main(int argc,char ** argv)150 int main(int argc, char **argv) {
151   (void)argc;
152   (void)argv;
153 
154   if (argc < 2) {
155     std::cout << "render input.obj (obj_scale). " << std::endl;
156   }
157 
158   std::string obj_filename = "../common/cornellbox_suzanne_lucy.obj";
159 
160   if (argc > 1) {
161     obj_filename = argv[1];
162   }
163 
164   float obj_scale = 1.0f;
165   if (argc > 2) {
166     obj_scale = float(atof(argv[2]));
167   }
168 
169   RTCDevice device = rtcNewDevice(NULL);
170   RTCScene scene = rtcDeviceNewScene(
171       device, RTC_SCENE_STATIC | RTC_SCENE_INCOHERENT, RTC_INTERSECT1);
172 
173   std::vector<example::Mesh<float> > meshes;
174   std::vector<example::Material> materials;
175   std::vector<example::Texture> textures;
176   {
177     // load .obj
178 
179     bool ret = LoadObj(obj_filename, obj_scale, &meshes, &materials, &textures);
180     if (!ret) {
181       std::cerr << "Failed to load .obj [ " << obj_filename << " ]"
182                 << std::endl;
183       return EXIT_FAILURE;
184     }
185 
186     // Create meshes for ray tracing.
187     for (size_t i = 0; i < meshes.size(); i++) {
188       unsigned int mesh_id = CreateTriangleMesh(meshes[i], scene);
189       (void)mesh_id;
190     }
191 
192     rtcCommit(scene);
193 
194     RTCBounds bounds;
195     rtcGetBounds(scene, bounds);
196 
197     std::cout << "Scene bounding min: " << bounds.lower_x << ", "
198               << bounds.lower_y << ", " << bounds.lower_z << std::endl;
199     std::cout << "Scene bounding max: " << bounds.upper_x << ", "
200               << bounds.upper_y << ", " << bounds.upper_z << std::endl;
201   }
202 
203   int width = 512;
204   int height = 512;
205 
206   std::vector<float> rgb;
207   rgb.resize(size_t(width * height * 3));
208 
209 // Shoot rays.
210 #ifdef _OPENMP
211 #pragma omp parallel for
212 #endif
213   for (int y = 0; y < height; y++) {
214     std::cout << y << " / " << height << std::endl;
215     for (int x = 0; x < width; x++) {
216       // Simple camera. change eye pos and direction fit to .obj model.
217 
218       RTCRay ray;
219       ray.org[0] = 0.0f;
220       ray.org[1] = 5.0f;
221       ray.org[2] = 20.0f;
222 
223       float dir[3], ndir[3];
224       dir[0] = (x / float(width)) - 0.5f;
225       dir[1] = (y / float(height)) - 0.5f;
226       dir[2] = -1.0f;
227       vnormalize(ndir, dir);
228       ray.dir[0] = ndir[0];
229       ray.dir[1] = ndir[1];
230       ray.dir[2] = ndir[2];
231 
232       float kFar = 1.0e+30f;
233       ray.tnear = 0.0f;
234       ray.tfar = kFar;
235 
236       rtcIntersect(scene, ray);
237       if ((ray.tfar < kFar) && (ray.geomID != RTC_INVALID_GEOMETRY_ID) &&
238           (ray.primID != RTC_INVALID_GEOMETRY_ID)) {
239         const example::Mesh<float> &mesh = meshes[ray.geomID];
240         // std::cout << "tfar " << ray.tfar << std::endl;
241         // Write your shader here.
242         float normal[3];
243         normal[0] = 0.0f;
244         normal[1] = 0.0f;
245         normal[2] = 0.0f;
246         unsigned int fid = ray.primID;
247         if (mesh.facevarying_normals.size() > 0) {
248           // std::cout << "fid " << fid << std::endl;
249           normal[0] = mesh.facevarying_normals[9 * fid + 0];
250           normal[1] = mesh.facevarying_normals[9 * fid + 1];
251           normal[2] = mesh.facevarying_normals[9 * fid + 2];
252         }
253         // Flip Y
254         rgb[3 * size_t((height - y - 1) * width + x) + 0] =
255             0.5f * normal[0] + 0.5f;
256         rgb[3 * size_t((height - y - 1) * width + x) + 1] =
257             0.5f * normal[1] + 0.5f;
258         rgb[3 * size_t((height - y - 1) * width + x) + 2] =
259             0.5f * normal[2] + 0.5f;
260       }
261     }
262   }
263 
264   // Save image.
265   SaveImagePNG("render.png", &rgb.at(0), width, height);
266 
267   rtcDeleteScene(scene);
268   rtcDeleteDevice(device);
269 
270   return EXIT_SUCCESS;
271 }
272