Add the current code of nanort + nanosg

This implementation will be based on commit
b31dc0bd92eb7ac896079849a2205fac4abbd087 from the nanort project

Signed-off by: Arthur Brainville (Ybalrid) <ybalrid@ybalrid.info>
This commit is contained in:
Arthur Brainville (Ybalrid) 2018-02-18 19:15:50 +01:00
parent fb7ebb955e
commit c81be8f87d
No known key found for this signature in database
GPG Key ID: BC05C4812A06BCF3
18 changed files with 4245 additions and 0 deletions

130
examples/raytrace/README.md Normal file
View File

@ -0,0 +1,130 @@
# NanoSG
Simple, minimal and header-only scene graph library for NanoRT.
NanoSG itself shoud be compiled with C++-03 compiler, but demo code uses C++11 features.
![](images/nanosg-demo.png)
![](https://media.giphy.com/media/l3JDO29fMFndyObHW/giphy.gif)
## Build
### Linux or macOS
```
$ premake5 gmake
$ make
```
### Windows
```
$ premake5 vs2015
```
## Data structure
### Node
Node represents scene graph node. Tansformation node or Mesh(shape) node.
Node is interpreted as transformation node when passing `nullptr` to Node class constructure.
Node can contain multiple children.
### Scene
Scene contains root nodes and provides the method to find an intersection of nodes.
## User defined data structure
Following are required in user application.
### Mesh class
Current example code assumes mesh is all composed of triangle meshes.
Following method must be implemented for `Scene::Traversal`.
```
///
/// Get the geometric normal and the shading normal at `face_idx' th face.
///
template<typename T>
void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u, const T v) const;
```
### Intersection class
Represents intersection(hit) information.
### Transform
Transformation is done in the following procedure.
`M' = parent_xform x local_xform x local_pivot`
## Memory management
`Scene` and `Node` does not create a copy of asset data(e.g. vertices, indices). Thus user must care about memory management of scene assets in user side.
## API
API is still subject to change.
### Node
```
void Node::SetName(const std::string &name);
```
Set (unique) name for the node.
```
void Node::AddChild(const type &child);
```
Add node as child node.
```
void Node::SetLocalXform(const T xform[4][4]) {
```
Set local transformation matrix. Default is identity matrix.
### Scene
```
bool Scene::AddNode(const Node<T, M> &node);
```
Add a node to the scene.
```
bool Scene::Commit() {
```
Commit the scene. After adding nodes to the scene or changed transformation matrix, call this `Commit` before tracing rays.
`Commit` triggers BVH build in each nodes and updates node's transformation matrix.
```
template<class H>
bool Scene::Traverse(nanort::Ray<T> &ray, H *isect, const bool cull_back_face = false) const;
```
Trace ray into the scene and find an intersection.
Returns `true` when there is an intersection and hit information is stored in `isect`.
## TODO
* [ ] Compute pivot point of each node(mesh).
## Third party libraries and its icenses.
* picojson : BSD license.
* bt3gui : zlib license.
* glew : BSD/MIT license.
* tinyobjloader : MIT license.
* glm : The Happy Bunny License (Modified MIT License). Copyright (c) 2005 - 2017 G-Truc Creation
* ImGui : The MIT License (MIT). Copyright (c) 2014-2015 Omar Cornut and ImGui contributors
* ImGuizmo : The MIT License (MIT). Copyright (c) 2016 Cedric Guillemet

Binary file not shown.

View File

@ -0,0 +1,9 @@
{ "obj_filename" : "../common/cornellbox_suzanne.obj",
"scene_scale" : 1.0,
"width" : 512,
"height" : 512,
"eye" : [0, 2.5, 15],
"up" : [0, 1, 0],
"look_at" : [0, 0, 0],
"dummy" : 0
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -0,0 +1,25 @@
[Debug]
Pos=60,60
Size=400,400
Collapsed=0
[UI]
Pos=-41,636
Size=469,191
Collapsed=0
[Render]
Pos=583,717
Size=410,425
Collapsed=0
[Scene]
Pos=60,60
Size=256,206
Collapsed=0
[Transform]
Pos=579,636
Size=235,167
Collapsed=0

1032
examples/raytrace/main.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
#ifndef EXAMPLE_MATERIAL_H_
#define EXAMPLE_MATERIAL_H_
#include <cstdlib>
#ifdef __clang__
#pragma clang diagnostic push
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
namespace example {
struct Material {
// float ambient[3];
float diffuse[3];
float specular[3];
// float reflection[3];
// float refraction[3];
int id;
int diffuse_texid;
int specular_texid;
// int reflection_texid;
// int transparency_texid;
// int bump_texid;
// int normal_texid; // normal map
// int alpha_texid; // alpha map
Material() {
// ambient[0] = 0.0;
// ambient[1] = 0.0;
// ambient[2] = 0.0;
diffuse[0] = 0.5;
diffuse[1] = 0.5;
diffuse[2] = 0.5;
specular[0] = 0.5;
specular[1] = 0.5;
specular[2] = 0.5;
// reflection[0] = 0.0;
// reflection[1] = 0.0;
// reflection[2] = 0.0;
// refraction[0] = 0.0;
// refraction[1] = 0.0;
// refraction[2] = 0.0;
id = -1;
diffuse_texid = -1;
specular_texid = -1;
// reflection_texid = -1;
// transparency_texid = -1;
// bump_texid = -1;
// normal_texid = -1;
// alpha_texid = -1;
}
};
struct Texture {
int width;
int height;
int components;
int _pad_;
unsigned char* image;
Texture() {
width = -1;
height = -1;
components = -1;
image = NULL;
}
};
} // namespace example
#endif // EXAMPLE_MATERIAL_H_

216
examples/raytrace/matrix.cc Normal file
View File

@ -0,0 +1,216 @@
#include <cstdio>
#include <cmath>
#include "matrix.h"
//using namespace mallie;
static inline float vdot(float a[3], float b[3]) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
static inline void vcross(float c[3], float a[3], float b[3]) {
c[0] = a[1] * b[2] - a[2] * b[1];
c[1] = a[2] * b[0] - a[0] * b[2];
c[2] = a[0] * b[1] - a[1] * b[0];
}
static inline float vlength(float v[3]) {
float len2 = vdot(v, v);
if (std::abs(len2) > 1.0e-30) {
return sqrt(len2);
}
return 0.0f;
}
static void vnormalize(float v[3]) {
float len = vlength(v);
if (std::abs(len) > 1.0e-30) {
float inv_len = 1.0f / len;
v[0] *= inv_len;
v[1] *= inv_len;
v[2] *= inv_len;
}
}
void Matrix::Print(float m[4][4]) {
for (int i = 0; i < 4; i++) {
printf("m[%d] = %f, %f, %f, %f\n", i, m[i][0], m[i][1], m[i][2], m[i][3]);
}
}
void Matrix::LookAt(float m[4][4], float eye[3], float lookat[3],
float up[3]) {
float u[3], v[3];
float look[3];
look[0] = lookat[0] - eye[0];
look[1] = lookat[1] - eye[1];
look[2] = lookat[2] - eye[2];
vnormalize(look);
vcross(u, look, up);
vnormalize(u);
vcross(v, u, look);
vnormalize(v);
#if 0
m[0][0] = u[0];
m[0][1] = v[0];
m[0][2] = -look[0];
m[0][3] = 0.0;
m[1][0] = u[1];
m[1][1] = v[1];
m[1][2] = -look[1];
m[1][3] = 0.0;
m[2][0] = u[2];
m[2][1] = v[2];
m[2][2] = -look[2];
m[2][3] = 0.0;
m[3][0] = eye[0];
m[3][1] = eye[1];
m[3][2] = eye[2];
m[3][3] = 1.0;
#else
m[0][0] = u[0];
m[1][0] = v[0];
m[2][0] = -look[0];
m[3][0] = eye[0];
m[0][1] = u[1];
m[1][1] = v[1];
m[2][1] = -look[1];
m[3][1] = eye[1];
m[0][2] = u[2];
m[1][2] = v[2];
m[2][2] = -look[2];
m[3][2] = eye[2];
m[0][3] = 0.0;
m[1][3] = 0.0;
m[2][3] = 0.0;
m[3][3] = 1.0;
#endif
}
void Matrix::Inverse(float m[4][4]) {
/*
* codes from intel web
* cramer's rule version
*/
int i, j;
float tmp[12]; /* tmp array for pairs */
float tsrc[16]; /* array of transpose source matrix */
float det; /* determinant */
/* transpose matrix */
for (i = 0; i < 4; i++) {
tsrc[i] = m[i][0];
tsrc[i + 4] = m[i][1];
tsrc[i + 8] = m[i][2];
tsrc[i + 12] = m[i][3];
}
/* calculate pair for first 8 elements(cofactors) */
tmp[0] = tsrc[10] * tsrc[15];
tmp[1] = tsrc[11] * tsrc[14];
tmp[2] = tsrc[9] * tsrc[15];
tmp[3] = tsrc[11] * tsrc[13];
tmp[4] = tsrc[9] * tsrc[14];
tmp[5] = tsrc[10] * tsrc[13];
tmp[6] = tsrc[8] * tsrc[15];
tmp[7] = tsrc[11] * tsrc[12];
tmp[8] = tsrc[8] * tsrc[14];
tmp[9] = tsrc[10] * tsrc[12];
tmp[10] = tsrc[8] * tsrc[13];
tmp[11] = tsrc[9] * tsrc[12];
/* calculate first 8 elements(cofactors) */
m[0][0] = tmp[0] * tsrc[5] + tmp[3] * tsrc[6] + tmp[4] * tsrc[7];
m[0][0] -= tmp[1] * tsrc[5] + tmp[2] * tsrc[6] + tmp[5] * tsrc[7];
m[0][1] = tmp[1] * tsrc[4] + tmp[6] * tsrc[6] + tmp[9] * tsrc[7];
m[0][1] -= tmp[0] * tsrc[4] + tmp[7] * tsrc[6] + tmp[8] * tsrc[7];
m[0][2] = tmp[2] * tsrc[4] + tmp[7] * tsrc[5] + tmp[10] * tsrc[7];
m[0][2] -= tmp[3] * tsrc[4] + tmp[6] * tsrc[5] + tmp[11] * tsrc[7];
m[0][3] = tmp[5] * tsrc[4] + tmp[8] * tsrc[5] + tmp[11] * tsrc[6];
m[0][3] -= tmp[4] * tsrc[4] + tmp[9] * tsrc[5] + tmp[10] * tsrc[6];
m[1][0] = tmp[1] * tsrc[1] + tmp[2] * tsrc[2] + tmp[5] * tsrc[3];
m[1][0] -= tmp[0] * tsrc[1] + tmp[3] * tsrc[2] + tmp[4] * tsrc[3];
m[1][1] = tmp[0] * tsrc[0] + tmp[7] * tsrc[2] + tmp[8] * tsrc[3];
m[1][1] -= tmp[1] * tsrc[0] + tmp[6] * tsrc[2] + tmp[9] * tsrc[3];
m[1][2] = tmp[3] * tsrc[0] + tmp[6] * tsrc[1] + tmp[11] * tsrc[3];
m[1][2] -= tmp[2] * tsrc[0] + tmp[7] * tsrc[1] + tmp[10] * tsrc[3];
m[1][3] = tmp[4] * tsrc[0] + tmp[9] * tsrc[1] + tmp[10] * tsrc[2];
m[1][3] -= tmp[5] * tsrc[0] + tmp[8] * tsrc[1] + tmp[11] * tsrc[2];
/* calculate pairs for second 8 elements(cofactors) */
tmp[0] = tsrc[2] * tsrc[7];
tmp[1] = tsrc[3] * tsrc[6];
tmp[2] = tsrc[1] * tsrc[7];
tmp[3] = tsrc[3] * tsrc[5];
tmp[4] = tsrc[1] * tsrc[6];
tmp[5] = tsrc[2] * tsrc[5];
tmp[6] = tsrc[0] * tsrc[7];
tmp[7] = tsrc[3] * tsrc[4];
tmp[8] = tsrc[0] * tsrc[6];
tmp[9] = tsrc[2] * tsrc[4];
tmp[10] = tsrc[0] * tsrc[5];
tmp[11] = tsrc[1] * tsrc[4];
/* calculate second 8 elements(cofactors) */
m[2][0] = tmp[0] * tsrc[13] + tmp[3] * tsrc[14] + tmp[4] * tsrc[15];
m[2][0] -= tmp[1] * tsrc[13] + tmp[2] * tsrc[14] + tmp[5] * tsrc[15];
m[2][1] = tmp[1] * tsrc[12] + tmp[6] * tsrc[14] + tmp[9] * tsrc[15];
m[2][1] -= tmp[0] * tsrc[12] + tmp[7] * tsrc[14] + tmp[8] * tsrc[15];
m[2][2] = tmp[2] * tsrc[12] + tmp[7] * tsrc[13] + tmp[10] * tsrc[15];
m[2][2] -= tmp[3] * tsrc[12] + tmp[6] * tsrc[13] + tmp[11] * tsrc[15];
m[2][3] = tmp[5] * tsrc[12] + tmp[8] * tsrc[13] + tmp[11] * tsrc[14];
m[2][3] -= tmp[4] * tsrc[12] + tmp[9] * tsrc[13] + tmp[10] * tsrc[14];
m[3][0] = tmp[2] * tsrc[10] + tmp[5] * tsrc[11] + tmp[1] * tsrc[9];
m[3][0] -= tmp[4] * tsrc[11] + tmp[0] * tsrc[9] + tmp[3] * tsrc[10];
m[3][1] = tmp[8] * tsrc[11] + tmp[0] * tsrc[8] + tmp[7] * tsrc[10];
m[3][1] -= tmp[6] * tsrc[10] + tmp[9] * tsrc[11] + tmp[1] * tsrc[8];
m[3][2] = tmp[6] * tsrc[9] + tmp[11] * tsrc[11] + tmp[3] * tsrc[8];
m[3][2] -= tmp[10] * tsrc[11] + tmp[2] * tsrc[8] + tmp[7] * tsrc[9];
m[3][3] = tmp[10] * tsrc[10] + tmp[4] * tsrc[8] + tmp[9] * tsrc[9];
m[3][3] -= tmp[8] * tsrc[9] + tmp[11] * tsrc[0] + tmp[5] * tsrc[8];
/* calculate determinant */
det = tsrc[0] * m[0][0] + tsrc[1] * m[0][1] + tsrc[2] * m[0][2] +
tsrc[3] * m[0][3];
/* calculate matrix inverse */
det = 1.0f / det;
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
m[j][i] *= det;
}
}
}
void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
dst[i][j] = 0;
for (int k = 0; k < 4; ++k) {
dst[i][j] += m0[k][j] * m1[i][k];
}
}
}
}
void Matrix::MultV(float dst[3], float m[4][4], float v[3]) {
// printf("v = %f, %f, %f\n", v[0], v[1], v[2]);
dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
dst[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
// printf("m = %f, %f, %f\n", m[3][0], m[3][1], m[3][2]);
// printf("dst = %f, %f, %f\n", dst[0], dst[1], dst[2]);
}

188
examples/raytrace/mesh.h Normal file
View File

@ -0,0 +1,188 @@
#ifndef EXAMPLE_MESH_H_
#define EXAMPLE_MESH_H_
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>
namespace example {
template<typename T>
inline void lerp(T dst[3], const T v0[3], const T v1[3], const T v2[3], float u, float v) {
dst[0] = (static_cast<T>(1.0) - u - v) * v0[0] + u * v1[0] + v * v2[0];
dst[1] = (static_cast<T>(1.0) - u - v) * v0[1] + u * v1[1] + v * v2[1];
dst[2] = (static_cast<T>(1.0) - u - v) * v0[2] + u * v1[2] + v * v2[2];
}
template <typename T>
inline T vlength(const T v[3]) {
const T d = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
if (std::fabs(d) > std::numeric_limits<T>::epsilon()) {
return std::sqrt(d);
} else {
return static_cast<T>(0.0);
}
}
template <typename T>
inline void vnormalize(T dst[3], const T v[3]) {
dst[0] = v[0];
dst[1] = v[1];
dst[2] = v[2];
const T len = vlength(v);
if (std::fabs(len) > std::numeric_limits<T>::epsilon()) {
const T inv_len = static_cast<T>(1.0) / len;
dst[0] *= inv_len;
dst[1] *= inv_len;
dst[2] *= inv_len;
}
}
template <typename T>
inline void vcross(T dst[3], const T a[3], const T b[3]) {
dst[0] = a[1] * b[2] - a[2] * b[1];
dst[1] = a[2] * b[0] - a[0] * b[2];
dst[2] = a[0] * b[1] - a[1] * b[0];
}
template <typename T>
inline void vsub(T dst[3], const T a[3], const T b[3]) {
dst[0] = a[0] - b[0];
dst[1] = a[1] - b[1];
dst[2] = a[2] - b[2];
}
template<typename T>
inline void calculate_normal(T Nn[3], const T v0[3], const T v1[3], const T v2[3]) {
T v10[3];
T v20[3];
vsub(v10, v1, v0);
vsub(v20, v2, v0);
T N[3];
vcross(N, v20, v10);
vnormalize(Nn, N);
}
template<typename T>
class Mesh {
public:
explicit Mesh(const size_t vertex_stride) :
stride(vertex_stride) {
}
std::string name;
std::vector<T> vertices; /// stride * num_vertices
std::vector<T> facevarying_normals; /// [xyz] * 3(triangle) * num_faces
std::vector<T> facevarying_tangents; /// [xyz] * 3(triangle) * num_faces
std::vector<T> facevarying_binormals; /// [xyz] * 3(triangle) * num_faces
std::vector<T> facevarying_uvs; /// [xy] * 3(triangle) * num_faces
std::vector<T>
facevarying_vertex_colors; /// [xyz] * 3(triangle) * num_faces
std::vector<unsigned int> faces; /// triangle x num_faces
std::vector<unsigned int> material_ids; /// index x num_faces
T pivot_xform[4][4];
size_t stride; /// stride for vertex data.
// --- Required methods in Scene::Traversal. ---
///
/// Get the geometric normal and the shading normal at `face_idx' th face.
///
void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u, const T v) const {
// Compute geometric normal.
unsigned int f0, f1, f2;
T v0[3], v1[3], v2[3];
f0 = faces[3 * face_idx + 0];
f1 = faces[3 * face_idx + 1];
f2 = faces[3 * face_idx + 2];
v0[0] = vertices[3 * f0 + 0];
v0[1] = vertices[3 * f0 + 1];
v0[2] = vertices[3 * f0 + 2];
v1[0] = vertices[3 * f1 + 0];
v1[1] = vertices[3 * f1 + 1];
v1[2] = vertices[3 * f1 + 2];
v2[0] = vertices[3 * f2 + 0];
v2[1] = vertices[3 * f2 + 1];
v2[2] = vertices[3 * f2 + 2];
calculate_normal(Ng, v0, v1, v2);
if (facevarying_normals.size() > 0) {
T n0[3], n1[3], n2[3];
n0[0] = facevarying_normals[9 * face_idx + 0];
n0[1] = facevarying_normals[9 * face_idx + 1];
n0[2] = facevarying_normals[9 * face_idx + 2];
n1[0] = facevarying_normals[9 * face_idx + 3];
n1[1] = facevarying_normals[9 * face_idx + 4];
n1[2] = facevarying_normals[9 * face_idx + 5];
n2[0] = facevarying_normals[9 * face_idx + 6];
n2[1] = facevarying_normals[9 * face_idx + 7];
n2[2] = facevarying_normals[9 * face_idx + 8];
lerp(Ns, n0, n1, n2, u, v);
} else {
// Use geometric normal.
Ns[0] = Ng[0];
Ns[1] = Ng[1];
Ns[2] = Ng[2];
}
}
// --- end of required methods in Scene::Traversal. ---
///
/// Get texture coordinate at `face_idx' th face.
///
void GetTexCoord(T tcoord[3], const unsigned int face_idx, const T u, const T v) {
if (facevarying_uvs.size() > 0) {
T t0[3], t1[3], t2[3];
t0[0] = facevarying_uvs[6 * face_idx + 0];
t0[1] = facevarying_uvs[6 * face_idx + 1];
t0[2] = static_cast<T>(0.0);
t1[0] = facevarying_uvs[6 * face_idx + 2];
t1[1] = facevarying_uvs[6 * face_idx + 3];
t1[2] = static_cast<T>(0.0);
t2[0] = facevarying_uvs[6 * face_idx + 4];
t2[1] = facevarying_uvs[6 * face_idx + 5];
t2[2] = static_cast<T>(0.0);
lerp(tcoord, t0, t1, t2, u, v);
} else {
tcoord[0] = static_cast<T>(0.0);
tcoord[1] = static_cast<T>(0.0);
tcoord[2] = static_cast<T>(0.0);
}
}
};
} // namespace example
#endif // EXAMPLE_MESH_H_

876
examples/raytrace/nanosg.h Normal file
View File

@ -0,0 +1,876 @@
/*
The MIT License (MIT)
Copyright (c) 2017 Light Transport Entertainment, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef NANOSG_H_
#define NANOSG_H_
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#include <iostream>
#include <limits>
#include <vector>
#include "nanort.h"
namespace nanosg {
template <class T>
class PrimitiveInterface;
template <class T>
class PrimitiveInterface {
public:
void print() { static_cast<T &>(this)->print(); }
};
class SpherePrimitive : PrimitiveInterface<SpherePrimitive> {
public:
void print() { std::cout << "Sphere" << std::endl; }
};
// 4x4 matrix
template <typename T>
class Matrix {
public:
Matrix();
~Matrix();
static void Print(const T m[4][4]) {
for (int i = 0; i < 4; i++) {
printf("m[%d] = %f, %f, %f, %f\n", i, m[i][0], m[i][1], m[i][2], m[i][3]);
}
}
static void Identity(T m[4][4]) {
m[0][0] = static_cast<T>(1);
m[0][1] = static_cast<T>(0);
m[0][2] = static_cast<T>(0);
m[0][3] = static_cast<T>(0);
m[1][0] = static_cast<T>(0);
m[1][1] = static_cast<T>(1);
m[1][2] = static_cast<T>(0);
m[1][3] = static_cast<T>(0);
m[2][0] = static_cast<T>(0);
m[2][1] = static_cast<T>(0);
m[2][2] = static_cast<T>(1);
m[2][3] = static_cast<T>(0);
m[3][0] = static_cast<T>(0);
m[3][1] = static_cast<T>(0);
m[3][2] = static_cast<T>(0);
m[3][3] = static_cast<T>(1);
}
static void Copy(T dst[4][4], const T src[4][4]) {
memcpy(dst, src, sizeof(T) * 16);
}
static void Inverse(T m[4][4]) {
/*
* codes from intel web
* cramer's rule version
*/
int i, j;
T tmp[12]; /* tmp array for pairs */
T tsrc[16]; /* array of transpose source matrix */
T det; /* determinant */
/* transpose matrix */
for (i = 0; i < 4; i++) {
tsrc[i] = m[i][0];
tsrc[i + 4] = m[i][1];
tsrc[i + 8] = m[i][2];
tsrc[i + 12] = m[i][3];
}
/* calculate pair for first 8 elements(cofactors) */
tmp[0] = tsrc[10] * tsrc[15];
tmp[1] = tsrc[11] * tsrc[14];
tmp[2] = tsrc[9] * tsrc[15];
tmp[3] = tsrc[11] * tsrc[13];
tmp[4] = tsrc[9] * tsrc[14];
tmp[5] = tsrc[10] * tsrc[13];
tmp[6] = tsrc[8] * tsrc[15];
tmp[7] = tsrc[11] * tsrc[12];
tmp[8] = tsrc[8] * tsrc[14];
tmp[9] = tsrc[10] * tsrc[12];
tmp[10] = tsrc[8] * tsrc[13];
tmp[11] = tsrc[9] * tsrc[12];
/* calculate first 8 elements(cofactors) */
m[0][0] = tmp[0] * tsrc[5] + tmp[3] * tsrc[6] + tmp[4] * tsrc[7];
m[0][0] -= tmp[1] * tsrc[5] + tmp[2] * tsrc[6] + tmp[5] * tsrc[7];
m[0][1] = tmp[1] * tsrc[4] + tmp[6] * tsrc[6] + tmp[9] * tsrc[7];
m[0][1] -= tmp[0] * tsrc[4] + tmp[7] * tsrc[6] + tmp[8] * tsrc[7];
m[0][2] = tmp[2] * tsrc[4] + tmp[7] * tsrc[5] + tmp[10] * tsrc[7];
m[0][2] -= tmp[3] * tsrc[4] + tmp[6] * tsrc[5] + tmp[11] * tsrc[7];
m[0][3] = tmp[5] * tsrc[4] + tmp[8] * tsrc[5] + tmp[11] * tsrc[6];
m[0][3] -= tmp[4] * tsrc[4] + tmp[9] * tsrc[5] + tmp[10] * tsrc[6];
m[1][0] = tmp[1] * tsrc[1] + tmp[2] * tsrc[2] + tmp[5] * tsrc[3];
m[1][0] -= tmp[0] * tsrc[1] + tmp[3] * tsrc[2] + tmp[4] * tsrc[3];
m[1][1] = tmp[0] * tsrc[0] + tmp[7] * tsrc[2] + tmp[8] * tsrc[3];
m[1][1] -= tmp[1] * tsrc[0] + tmp[6] * tsrc[2] + tmp[9] * tsrc[3];
m[1][2] = tmp[3] * tsrc[0] + tmp[6] * tsrc[1] + tmp[11] * tsrc[3];
m[1][2] -= tmp[2] * tsrc[0] + tmp[7] * tsrc[1] + tmp[10] * tsrc[3];
m[1][3] = tmp[4] * tsrc[0] + tmp[9] * tsrc[1] + tmp[10] * tsrc[2];
m[1][3] -= tmp[5] * tsrc[0] + tmp[8] * tsrc[1] + tmp[11] * tsrc[2];
/* calculate pairs for second 8 elements(cofactors) */
tmp[0] = tsrc[2] * tsrc[7];
tmp[1] = tsrc[3] * tsrc[6];
tmp[2] = tsrc[1] * tsrc[7];
tmp[3] = tsrc[3] * tsrc[5];
tmp[4] = tsrc[1] * tsrc[6];
tmp[5] = tsrc[2] * tsrc[5];
tmp[6] = tsrc[0] * tsrc[7];
tmp[7] = tsrc[3] * tsrc[4];
tmp[8] = tsrc[0] * tsrc[6];
tmp[9] = tsrc[2] * tsrc[4];
tmp[10] = tsrc[0] * tsrc[5];
tmp[11] = tsrc[1] * tsrc[4];
/* calculate second 8 elements(cofactors) */
m[2][0] = tmp[0] * tsrc[13] + tmp[3] * tsrc[14] + tmp[4] * tsrc[15];
m[2][0] -= tmp[1] * tsrc[13] + tmp[2] * tsrc[14] + tmp[5] * tsrc[15];
m[2][1] = tmp[1] * tsrc[12] + tmp[6] * tsrc[14] + tmp[9] * tsrc[15];
m[2][1] -= tmp[0] * tsrc[12] + tmp[7] * tsrc[14] + tmp[8] * tsrc[15];
m[2][2] = tmp[2] * tsrc[12] + tmp[7] * tsrc[13] + tmp[10] * tsrc[15];
m[2][2] -= tmp[3] * tsrc[12] + tmp[6] * tsrc[13] + tmp[11] * tsrc[15];
m[2][3] = tmp[5] * tsrc[12] + tmp[8] * tsrc[13] + tmp[11] * tsrc[14];
m[2][3] -= tmp[4] * tsrc[12] + tmp[9] * tsrc[13] + tmp[10] * tsrc[14];
m[3][0] = tmp[2] * tsrc[10] + tmp[5] * tsrc[11] + tmp[1] * tsrc[9];
m[3][0] -= tmp[4] * tsrc[11] + tmp[0] * tsrc[9] + tmp[3] * tsrc[10];
m[3][1] = tmp[8] * tsrc[11] + tmp[0] * tsrc[8] + tmp[7] * tsrc[10];
m[3][1] -= tmp[6] * tsrc[10] + tmp[9] * tsrc[11] + tmp[1] * tsrc[8];
m[3][2] = tmp[6] * tsrc[9] + tmp[11] * tsrc[11] + tmp[3] * tsrc[8];
m[3][2] -= tmp[10] * tsrc[11] + tmp[2] * tsrc[8] + tmp[7] * tsrc[9];
m[3][3] = tmp[10] * tsrc[10] + tmp[4] * tsrc[8] + tmp[9] * tsrc[9];
m[3][3] -= tmp[8] * tsrc[9] + tmp[11] * tsrc[0] + tmp[5] * tsrc[8];
/* calculate determinant */
det = tsrc[0] * m[0][0] + tsrc[1] * m[0][1] + tsrc[2] * m[0][2] +
tsrc[3] * m[0][3];
/* calculate matrix inverse */
det = static_cast<T>(1.0) / det;
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
m[j][i] *= det;
}
}
}
static void Transpose(T m[4][4]) {
T t[4][4];
// Transpose
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
t[j][i] = m[i][j];
}
}
// Copy
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
m[j][i] = t[j][i];
}
}
}
static void Mult(T dst[4][4], const T m0[4][4], const T m1[4][4]) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
dst[i][j] = 0;
for (int k = 0; k < 4; ++k) {
dst[i][j] += m0[k][j] * m1[i][k];
}
}
}
}
static void MultV(T dst[3], const T m[4][4], const T v[3]) {
T tmp[3];
tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
dst[0] = tmp[0];
dst[1] = tmp[1];
dst[2] = tmp[2];
}
static void MultV(nanort::real3<T> &dst, const T m[4][4], const T v[3]) {
T tmp[3];
tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
dst[0] = tmp[0];
dst[1] = tmp[1];
dst[2] = tmp[2];
}
};
// typedef Matrix<float> Matrixf;
// typedef Matrix<double> Matrixd;
template <typename T>
static void XformBoundingBox(T xbmin[3], // out
T xbmax[3], // out
T bmin[3], T bmax[3], T m[4][4]) {
// create bounding vertex from (bmin, bmax)
T b[8][3];
b[0][0] = bmin[0];
b[0][1] = bmin[1];
b[0][2] = bmin[2];
b[1][0] = bmax[0];
b[1][1] = bmin[1];
b[1][2] = bmin[2];
b[2][0] = bmin[0];
b[2][1] = bmax[1];
b[2][2] = bmin[2];
b[3][0] = bmax[0];
b[3][1] = bmax[1];
b[3][2] = bmin[2];
b[4][0] = bmin[0];
b[4][1] = bmin[1];
b[4][2] = bmax[2];
b[5][0] = bmax[0];
b[5][1] = bmin[1];
b[5][2] = bmax[2];
b[6][0] = bmin[0];
b[6][1] = bmax[1];
b[6][2] = bmax[2];
b[7][0] = bmax[0];
b[7][1] = bmax[1];
b[7][2] = bmax[2];
T xb[8][3];
for (int i = 0; i < 8; i++) {
Matrix<T>::MultV(xb[i], m, b[i]);
}
xbmin[0] = xb[0][0];
xbmin[1] = xb[0][1];
xbmin[2] = xb[0][2];
xbmax[0] = xb[0][0];
xbmax[1] = xb[0][1];
xbmax[2] = xb[0][2];
for (int i = 1; i < 8; i++) {
xbmin[0] = std::min(xb[i][0], xbmin[0]);
xbmin[1] = std::min(xb[i][1], xbmin[1]);
xbmin[2] = std::min(xb[i][2], xbmin[2]);
xbmax[0] = std::max(xb[i][0], xbmax[0]);
xbmax[1] = std::max(xb[i][1], xbmax[1]);
xbmax[2] = std::max(xb[i][2], xbmax[2]);
}
}
template <typename T>
struct Intersection {
// required fields.
T t; // hit distance
unsigned int prim_id; // primitive ID of the hit
float u;
float v;
unsigned int node_id; // node ID of the hit.
nanort::real3<T> P; // intersection point
nanort::real3<T> Ns; // shading normal
nanort::real3<T> Ng; // geometric normal
};
///
/// Renderable node
///
template <typename T, class M>
class Node {
public:
typedef Node<T, M> type;
explicit Node(const M *mesh) : mesh_(mesh) {
xbmin_[0] = xbmin_[1] = xbmin_[2] = std::numeric_limits<T>::max();
xbmax_[0] = xbmax_[1] = xbmax_[2] = -std::numeric_limits<T>::max();
lbmin_[0] = lbmin_[1] = lbmin_[2] = std::numeric_limits<T>::max();
lbmax_[0] = lbmax_[1] = lbmax_[2] = -std::numeric_limits<T>::max();
Matrix<T>::Identity(local_xform_);
Matrix<T>::Identity(xform_);
Matrix<T>::Identity(inv_xform_);
Matrix<T>::Identity(inv_xform33_);
inv_xform33_[3][3] = static_cast<T>(0.0);
Matrix<T>::Identity(inv_transpose_xform33_);
inv_transpose_xform33_[3][3] = static_cast<T>(0.0);
}
~Node() {}
void Copy(const type &rhs) {
Matrix<T>::Copy(local_xform_, rhs.local_xform_);
Matrix<T>::Copy(xform_, rhs.xform_);
Matrix<T>::Copy(inv_xform_, rhs.inv_xform_);
Matrix<T>::Copy(inv_xform33_, rhs.inv_xform33_);
Matrix<T>::Copy(inv_transpose_xform33_, rhs.inv_transpose_xform33_);
lbmin_[0] = rhs.lbmin_[0];
lbmin_[1] = rhs.lbmin_[1];
lbmin_[2] = rhs.lbmin_[2];
lbmax_[0] = rhs.lbmax_[0];
lbmax_[1] = rhs.lbmax_[1];
lbmax_[2] = rhs.lbmax_[2];
xbmin_[0] = rhs.xbmin_[0];
xbmin_[1] = rhs.xbmin_[1];
xbmin_[2] = rhs.xbmin_[2];
xbmax_[0] = rhs.xbmax_[0];
xbmax_[1] = rhs.xbmax_[1];
xbmax_[2] = rhs.xbmax_[2];
mesh_ = rhs.mesh_;
name_ = rhs.name_;
children_ = rhs.children_;
}
Node(const type &rhs) { Copy(rhs); }
const type &operator=(const type &rhs) {
Copy(rhs);
return (*this);
}
void SetName(const std::string &name) { name_ = name; }
const std::string &GetName() const { return name_; }
///
/// Add child node.
///
void AddChild(const type &child) { children_.push_back(child); }
///
/// Get chidren
///
const std::vector<type> &GetChildren() const { return children_; }
std::vector<type> &GetChildren() { return children_; }
///
/// Update internal state.
///
void Update(const T parent_xform[4][4]) {
if (!accel_.IsValid() && mesh_ && (mesh_->vertices.size() > 3) &&
(mesh_->faces.size() >= 3)) {
// Assume mesh is composed of triangle faces only.
nanort::TriangleMesh<float> triangle_mesh(
mesh_->vertices.data(), mesh_->faces.data(), mesh_->stride);
nanort::TriangleSAHPred<float> triangle_pred(
mesh_->vertices.data(), mesh_->faces.data(), mesh_->stride);
bool ret =
accel_.Build(static_cast<unsigned int>(mesh_->faces.size()) / 3,
triangle_mesh, triangle_pred);
// Update local bbox.
if (ret) {
accel_.BoundingBox(lbmin_, lbmax_);
}
}
// xform = parent_xform x local_xform
Matrix<T>::Mult(xform_, parent_xform, local_xform_);
// Compute the bounding box in world coordinate.
XformBoundingBox(xbmin_, xbmax_, lbmin_, lbmax_, xform_);
// Inverse(xform)
Matrix<T>::Copy(inv_xform_, xform_);
Matrix<T>::Inverse(inv_xform_);
// Clear translation, then inverse(xform)
Matrix<T>::Copy(inv_xform33_, xform_);
inv_xform33_[3][0] = static_cast<T>(0.0);
inv_xform33_[3][1] = static_cast<T>(0.0);
inv_xform33_[3][2] = static_cast<T>(0.0);
Matrix<T>::Inverse(inv_xform33_);
// Inverse transpose of xform33
Matrix<T>::Copy(inv_transpose_xform33_, inv_xform33_);
Matrix<T>::Transpose(inv_transpose_xform33_);
// Update children nodes
for (size_t i = 0; i < children_.size(); i++) {
children_[i].Update(xform_);
}
}
///
/// Set local transformation.
///
void SetLocalXform(const T xform[4][4]) {
memcpy(local_xform_, xform, sizeof(float) * 16);
}
const T *GetLocalXformPtr() const { return &local_xform_[0][0]; }
const T *GetXformPtr() const { return &xform_[0][0]; }
const M *GetMesh() const { return mesh_; }
const nanort::BVHAccel<T> &GetAccel() const { return accel_; }
inline void GetWorldBoundingBox(T bmin[3], T bmax[3]) const {
bmin[0] = xbmin_[0];
bmin[1] = xbmin_[1];
bmin[2] = xbmin_[2];
bmax[0] = xbmax_[0];
bmax[1] = xbmax_[1];
bmax[2] = xbmax_[2];
}
inline void GetLocalBoundingBox(T bmin[3], T bmax[3]) const {
bmin[0] = lbmin_[0];
bmin[1] = lbmin_[1];
bmin[2] = lbmin_[2];
bmax[0] = lbmax_[0];
bmax[1] = lbmax_[1];
bmax[2] = lbmax_[2];
}
T local_xform_[4][4]; // Node's local transformation matrix.
T xform_[4][4]; // Parent xform x local_xform.
T inv_xform_[4][4]; // inverse(xform). world -> local
T inv_xform33_[4][4]; // inverse(xform0 with upper-left 3x3 elemets only(for
// transforming direction vector)
T inv_transpose_xform33_[4][4]; // inverse(transpose(xform)) with upper-left
// 3x3 elements only(for transforming normal
// vector)
private:
// bounding box(local space)
T lbmin_[3];
T lbmax_[3];
// bounding box after xform(world space)
T xbmin_[3];
T xbmax_[3];
nanort::BVHAccel<T> accel_;
std::string name_;
const M *mesh_;
std::vector<type> children_;
};
// -------------------------------------------------
// Predefined SAH predicator for cube.
template <typename T, class M>
class NodeBBoxPred {
public:
NodeBBoxPred(const std::vector<Node<T, M> > *nodes)
: axis_(0), pos_(0.0f), nodes_(nodes) {}
void Set(int axis, float pos) const {
axis_ = axis;
pos_ = pos;
}
bool operator()(unsigned int i) const {
int axis = axis_;
float pos = pos_;
T bmin[3], bmax[3];
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
T center = bmax[axis] - bmin[axis];
return (center < pos);
}
private:
mutable int axis_;
mutable float pos_;
const std::vector<Node<T, M> > *nodes_;
};
template <typename T, class M>
class NodeBBoxGeometry {
public:
NodeBBoxGeometry(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
/// Compute bounding box for `prim_index`th cube.
/// This function is called for each primitive in BVH build.
void BoundingBox(nanort::real3<T> *bmin, nanort::real3<T> *bmax,
unsigned int prim_index) const {
T a[3], b[3];
(*nodes_)[prim_index].GetWorldBoundingBox(a, b);
(*bmin)[0] = a[0];
(*bmin)[1] = a[1];
(*bmin)[2] = a[2];
(*bmax)[0] = b[0];
(*bmax)[1] = b[1];
(*bmax)[2] = b[2];
}
const std::vector<Node<T, M> > *nodes_;
mutable nanort::real3<T> ray_org_;
mutable nanort::real3<T> ray_dir_;
mutable nanort::BVHTraceOptions trace_options_;
int _pad_;
};
class NodeBBoxIntersection {
public:
NodeBBoxIntersection() {}
float normal[3];
// Required member variables.
float t;
unsigned int prim_id;
};
template <typename T, class M>
class NodeBBoxIntersector {
public:
NodeBBoxIntersector(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
bool Intersect(float *out_t_min, float *out_t_max,
unsigned int prim_index) const {
T bmin[3], bmax[3];
(*nodes_)[prim_index].GetWorldBoundingBox(bmin, bmax);
float tmin, tmax;
const float min_x = ray_dir_sign_[0] ? bmax[0] : bmin[0];
const float min_y = ray_dir_sign_[1] ? bmax[1] : bmin[1];
const float min_z = ray_dir_sign_[2] ? bmax[2] : bmin[2];
const float max_x = ray_dir_sign_[0] ? bmin[0] : bmax[0];
const float max_y = ray_dir_sign_[1] ? bmin[1] : bmax[1];
const float max_z = ray_dir_sign_[2] ? bmin[2] : bmax[2];
// X
const float tmin_x = (min_x - ray_org_[0]) * ray_inv_dir_[0];
const float tmax_x = (max_x - ray_org_[0]) * ray_inv_dir_[0];
// Y
const float tmin_y = (min_y - ray_org_[1]) * ray_inv_dir_[1];
const float tmax_y = (max_y - ray_org_[1]) * ray_inv_dir_[1];
// Z
const float tmin_z = (min_z - ray_org_[2]) * ray_inv_dir_[2];
const float tmax_z = (max_z - ray_org_[2]) * ray_inv_dir_[2];
tmin = nanort::safemax(tmin_z, nanort::safemax(tmin_y, tmin_x));
tmax = nanort::safemin(tmax_z, nanort::safemin(tmax_y, tmax_x));
if (tmin <= tmax) {
(*out_t_min) = tmin;
(*out_t_max) = tmax;
return true;
}
return false;
}
/// Prepare BVH traversal(e.g. compute inverse ray direction)
/// This function is called only once in BVH traversal.
void PrepareTraversal(const nanort::Ray<float> &ray) const {
ray_org_[0] = ray.org[0];
ray_org_[1] = ray.org[1];
ray_org_[2] = ray.org[2];
ray_dir_[0] = ray.dir[0];
ray_dir_[1] = ray.dir[1];
ray_dir_[2] = ray.dir[2];
// FIXME(syoyo): Consider zero div case.
ray_inv_dir_[0] = static_cast<T>(1.0) / ray.dir[0];
ray_inv_dir_[1] = static_cast<T>(1.0) / ray.dir[1];
ray_inv_dir_[2] = static_cast<T>(1.0) / ray.dir[2];
ray_dir_sign_[0] = ray.dir[0] < static_cast<T>(0.0) ? 1 : 0;
ray_dir_sign_[1] = ray.dir[1] < static_cast<T>(0.0) ? 1 : 0;
ray_dir_sign_[2] = ray.dir[2] < static_cast<T>(0.0) ? 1 : 0;
}
const std::vector<Node<T, M> > *nodes_;
mutable nanort::real3<T> ray_org_;
mutable nanort::real3<T> ray_dir_;
mutable nanort::real3<T> ray_inv_dir_;
mutable int ray_dir_sign_[3];
};
template <typename T, class M>
class Scene {
public:
Scene() {
bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits<T>::max();
bmax_[0] = bmax_[1] = bmax_[2] = -std::numeric_limits<T>::max();
}
~Scene() {}
///
/// Add intersectable node to the scene.
///
bool AddNode(const Node<T, M> &node) {
nodes_.push_back(node);
return true;
}
const std::vector<Node<T, M> > &GetNodes() const { return nodes_; }
bool FindNode(const std::string &name, Node<T, M> **found_node) {
if (!found_node) {
return false;
}
if (name.empty()) {
return false;
}
// Simple exhaustive search.
for (size_t i = 0; i < nodes_.size(); i++) {
if (FindNodeRecursive(name, &(nodes_[i]), found_node)) {
return true;
}
}
return false;
}
///
/// Commit the scene. Must be called before tracing rays into the scene.
///
bool Commit() {
// Update nodes.
for (size_t i = 0; i < nodes_.size(); i++) {
T ident[4][4];
Matrix<T>::Identity(ident);
nodes_[i].Update(ident);
}
// Build toplevel BVH.
NodeBBoxGeometry<T, M> geom(&nodes_);
NodeBBoxPred<T, M> pred(&nodes_);
// FIXME(LTE): Limit one leaf contains one node bbox primitive. This would
// work, but would be inefficient.
// e.g. will miss some node when constructed BVH depth is larger than the
// value of BVHBuildOptions.
// Implement more better and efficient BVH build and traverse for Toplevel
// BVH.
nanort::BVHBuildOptions<T> build_options;
build_options.min_leaf_primitives = 1;
bool ret = toplevel_accel_.Build(static_cast<unsigned int>(nodes_.size()),
geom, pred, build_options);
nanort::BVHBuildStatistics stats = toplevel_accel_.GetStatistics();
(void)stats;
// toplevel_accel_.Debug();
if (ret) {
toplevel_accel_.BoundingBox(bmin_, bmax_);
} else {
// Set invalid bbox value.
bmin_[0] = std::numeric_limits<T>::max();
bmin_[1] = std::numeric_limits<T>::max();
bmin_[2] = std::numeric_limits<T>::max();
bmax_[0] = -std::numeric_limits<T>::max();
bmax_[1] = -std::numeric_limits<T>::max();
bmax_[2] = -std::numeric_limits<T>::max();
}
return ret;
}
///
/// Get the scene bounding box.
///
void GetBoundingBox(T bmin[3], T bmax[3]) const {
bmin[0] = bmin_[0];
bmin[1] = bmin_[1];
bmin[2] = bmin_[2];
bmax[0] = bmax_[0];
bmax[1] = bmax_[1];
bmax[2] = bmax_[2];
}
///
/// Trace the ray into the scene.
/// First find the intersection of nodes' bounding box using toplevel BVH.
/// Then, trace into the hit node to find the intersection of the primitive.
///
template <class H>
bool Traverse(nanort::Ray<T> &ray, H *isect,
const bool cull_back_face = false) const {
if (!toplevel_accel_.IsValid()) {
return false;
}
const int kMaxIntersections = 64;
bool has_hit = false;
NodeBBoxIntersector<T, M> isector(&nodes_);
nanort::StackVector<nanort::NodeHit<T>, 128> node_hits;
bool may_hit = toplevel_accel_.ListNodeIntersections(ray, kMaxIntersections,
isector, &node_hits);
if (may_hit) {
T t_max = std::numeric_limits<T>::max();
T t_nearest = t_max;
nanort::BVHTraceOptions trace_options;
trace_options.cull_back_face = cull_back_face;
// Find actual intersection point.
for (size_t i = 0; i < node_hits->size(); i++) {
// Early cull test.
if (t_nearest < node_hits[i].t_min) {
// printf("near: %f, t_min: %f, t_max: %f\n", t_nearest,
// node_hits[i].t_min, node_hits[i].t_max);
continue;
}
assert(node_hits[i].node_id < nodes_.size());
const Node<T, M> &node = nodes_[node_hits[i].node_id];
// Transform ray into node's local space
// TODO(LTE): Set ray tmin and tmax
nanort::Ray<T> local_ray;
Matrix<T>::MultV(local_ray.org, node.inv_xform_, ray.org);
Matrix<T>::MultV(local_ray.dir, node.inv_xform33_, ray.dir);
nanort::TriangleIntersector<T, H> triangle_intersector(
node.GetMesh()->vertices.data(), node.GetMesh()->faces.data(),
node.GetMesh()->stride);
H local_isect;
bool hit = node.GetAccel().Traverse(local_ray, triangle_intersector,
&local_isect);
if (hit) {
// Calulcate hit distance in world coordiante.
T local_P[3];
local_P[0] = local_ray.org[0] + local_isect.t * local_ray.dir[0];
local_P[1] = local_ray.org[1] + local_isect.t * local_ray.dir[1];
local_P[2] = local_ray.org[2] + local_isect.t * local_ray.dir[2];
T world_P[3];
Matrix<T>::MultV(world_P, node.xform_, local_P);
nanort::real3<T> po;
po[0] = world_P[0] - ray.org[0];
po[1] = world_P[1] - ray.org[1];
po[2] = world_P[2] - ray.org[2];
float t_world = vlength(po);
// printf("tworld %f, tnear %f\n", t_world, t_nearest);
if (t_world < t_nearest) {
t_nearest = t_world;
has_hit = true;
//(*isect) = local_isect;
isect->node_id = node_hits[i].node_id;
isect->prim_id = local_isect.prim_id;
isect->u = local_isect.u;
isect->v = local_isect.v;
// TODO(LTE): Implement
T Ng[3], Ns[3]; // geometric normal, shading normal.
node.GetMesh()->GetNormal(Ng, Ns, isect->prim_id, isect->u,
isect->v);
// Convert position and normal into world coordinate.
isect->t = t_world;
Matrix<T>::MultV(isect->P, node.xform_, local_P);
Matrix<T>::MultV(isect->Ng, node.inv_transpose_xform33_, Ng);
Matrix<T>::MultV(isect->Ns, node.inv_transpose_xform33_, Ns);
}
}
}
}
return has_hit;
}
private:
///
/// Find a node by name.
///
bool FindNodeRecursive(const std::string &name, Node<T, M> *root,
Node<T, M> **found_node) {
if (root->GetName().compare(name) == 0) {
(*found_node) = root;
return true;
}
// Simple exhaustive search.
for (size_t i = 0; i < root->GetChildren().size(); i++) {
if (FindNodeRecursive(name, &(root->GetChildren()[i]), found_node)) {
return true;
}
}
return false;
}
// Scene bounding box.
// Valid after calling `Commit()`.
T bmin_[3];
T bmax_[3];
// Toplevel BVH accel.
nanort::BVHAccel<T> toplevel_accel_;
std::vector<Node<T, M> > nodes_;
};
} // namespace nanosg
#endif // NANOSG_H_

View File

@ -0,0 +1,459 @@
#include "obj-loader.h"
#include "../../nanort.h" // for float3
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wcast-align"
#pragma clang diagnostic ignored "-Wpadded"
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wvariadic-macros"
#pragma clang diagnostic ignored "-Wc++11-extensions"
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#if __has_warning("-Wdouble-promotion")
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#if __has_warning("-Wcomma")
#pragma clang diagnostic ignored "-Wcomma"
#endif
#if __has_warning("-Wcast-qual")
#pragma clang diagnostic ignored "-Wcast-qual"
#endif
#endif
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include <iostream>
#ifdef NANOSG_USE_CXX11
#include <unordered_map>
#else
#include <map>
#endif
#define USE_TEX_CACHE 1
namespace example {
typedef nanort::real3<float> float3;
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
// TODO(LTE): Remove global static definition.
#ifdef NANOSG_USE_CXX11
static std::unordered_map<std::string, int> hashed_tex;
#else
static std::map<std::string, int> hashed_tex;
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
inline void CalcNormal(float3 &N, float3 v0, float3 v1, float3 v2) {
float3 v10 = v1 - v0;
float3 v20 = v2 - v0;
N = vcross(v20, v10);
N = vnormalize(N);
}
static std::string GetBaseDir(const std::string &filepath) {
if (filepath.find_last_of("/\\") != std::string::npos)
return filepath.substr(0, filepath.find_last_of("/\\"));
return "";
}
static int LoadTexture(const std::string &filename,
std::vector<Texture> *textures) {
int idx;
if (filename.empty()) return -1;
std::cout << " Loading texture : " << filename << std::endl;
Texture texture;
// tigra: find in cache. get index
if (USE_TEX_CACHE) {
if (hashed_tex.find(filename) != hashed_tex.end()) {
puts("from cache");
return hashed_tex[filename];
}
}
int w, h, n;
unsigned char *data = stbi_load(filename.c_str(), &w, &h, &n, 0);
if (data) {
texture.width = w;
texture.height = h;
texture.components = n;
size_t n_elem = size_t(w * h * n);
texture.image = new unsigned char[n_elem];
for (size_t i = 0; i < n_elem; i++) {
texture.image[i] = data[i];
}
free(data);
textures->push_back(texture);
idx = int(textures->size()) - 1;
// tigra: store index to cache
if (USE_TEX_CACHE) {
hashed_tex[filename] = idx;
}
return idx;
}
std::cout << " Failed to load : " << filename << std::endl;
return -1;
}
static void ComputeBoundingBoxOfMesh(float bmin[3], float bmax[3],
const example::Mesh<float> &mesh) {
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
for (size_t i = 0; i < mesh.vertices.size() / 3; i++) {
bmin[0] = std::min(bmin[0], mesh.vertices[3 * i + 0]);
bmin[1] = std::min(bmin[1], mesh.vertices[3 * i + 1]);
bmin[2] = std::min(bmin[1], mesh.vertices[3 * i + 2]);
bmax[0] = std::max(bmax[0], mesh.vertices[3 * i + 0]);
bmax[1] = std::max(bmax[1], mesh.vertices[3 * i + 1]);
bmax[2] = std::max(bmax[2], mesh.vertices[3 * i + 2]);
}
}
bool LoadObj(const std::string &filename, float scale,
std::vector<Mesh<float> > *meshes,
std::vector<Material> *out_materials,
std::vector<Texture> *out_textures) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
std::string basedir = GetBaseDir(filename) + "/";
const char *basepath = (basedir.compare("/") == 0) ? NULL : basedir.c_str();
// auto t_start = std::chrono::system_clock::now();
bool ret =
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(),
basepath, /* triangulate */ true);
// auto t_end = std::chrono::system_clock::now();
// std::chrono::duration<double, std::milli> ms = t_end - t_start;
if (!err.empty()) {
std::cerr << err << std::endl;
}
if (!ret) {
return false;
}
// std::cout << "[LoadOBJ] Parse time : " << ms.count() << " [msecs]"
// << std::endl;
std::cout << "[LoadOBJ] # of shapes in .obj : " << shapes.size() << std::endl;
std::cout << "[LoadOBJ] # of materials in .obj : " << materials.size()
<< std::endl;
{
size_t total_num_vertices = 0;
size_t total_num_faces = 0;
total_num_vertices = attrib.vertices.size() / 3;
std::cout << " vertices : " << attrib.vertices.size() / 3 << std::endl;
for (size_t i = 0; i < shapes.size(); i++) {
std::cout << " shape[" << i << "].name : " << shapes[i].name
<< std::endl;
std::cout << " shape[" << i
<< "].indices : " << shapes[i].mesh.indices.size() << std::endl;
assert((shapes[i].mesh.indices.size() % 3) == 0);
total_num_faces += shapes[i].mesh.indices.size() / 3;
// tigra: empty name convert to _id
if (shapes[i].name.length() == 0) {
#ifdef NANOSG_USE_CXX11
shapes[i].name = "_" + std::to_string(i);
#else
std::stringstream ss;
ss << i;
shapes[i].name = "_" + ss.str();
#endif
std::cout << " EMPTY shape[" << i << "].name, new : " << shapes[i].name
<< std::endl;
}
}
std::cout << "[LoadOBJ] # of faces: " << total_num_faces << std::endl;
std::cout << "[LoadOBJ] # of vertices: " << total_num_vertices << std::endl;
}
// TODO(LTE): Implement tangents and binormals
for (size_t i = 0; i < shapes.size(); i++) {
Mesh<float> mesh(/* stride */ sizeof(float) * 3);
mesh.name = shapes[i].name;
const size_t num_faces = shapes[i].mesh.indices.size() / 3;
mesh.faces.resize(num_faces * 3);
mesh.material_ids.resize(num_faces);
mesh.facevarying_normals.resize(num_faces * 3 * 3);
mesh.facevarying_uvs.resize(num_faces * 3 * 2);
mesh.vertices.resize(num_faces * 3 * 3);
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
// reorder vertices. may create duplicated vertices.
size_t f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
size_t f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
size_t f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
mesh.vertices[9 * f + 0] = scale * attrib.vertices[3 * f0 + 0];
mesh.vertices[9 * f + 1] = scale * attrib.vertices[3 * f0 + 1];
mesh.vertices[9 * f + 2] = scale * attrib.vertices[3 * f0 + 2];
mesh.vertices[9 * f + 3] = scale * attrib.vertices[3 * f1 + 0];
mesh.vertices[9 * f + 4] = scale * attrib.vertices[3 * f1 + 1];
mesh.vertices[9 * f + 5] = scale * attrib.vertices[3 * f1 + 2];
mesh.vertices[9 * f + 6] = scale * attrib.vertices[3 * f2 + 0];
mesh.vertices[9 * f + 7] = scale * attrib.vertices[3 * f2 + 1];
mesh.vertices[9 * f + 8] = scale * attrib.vertices[3 * f2 + 2];
mesh.faces[3 * f + 0] = static_cast<unsigned int>(3 * f + 0);
mesh.faces[3 * f + 1] = static_cast<unsigned int>(3 * f + 1);
mesh.faces[3 * f + 2] = static_cast<unsigned int>(3 * f + 2);
mesh.material_ids[f] =
static_cast<unsigned int>(shapes[i].mesh.material_ids[f]);
}
if (attrib.normals.size() > 0) {
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
size_t f0, f1, f2;
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].normal_index);
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].normal_index);
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].normal_index);
if (f0 > 0 && f1 > 0 && f2 > 0) {
float n0[3], n1[3], n2[3];
n0[0] = attrib.normals[3 * f0 + 0];
n0[1] = attrib.normals[3 * f0 + 1];
n0[2] = attrib.normals[3 * f0 + 2];
n1[0] = attrib.normals[3 * f1 + 0];
n1[1] = attrib.normals[3 * f1 + 1];
n1[2] = attrib.normals[3 * f1 + 2];
n2[0] = attrib.normals[3 * f2 + 0];
n2[1] = attrib.normals[3 * f2 + 1];
n2[2] = attrib.normals[3 * f2 + 2];
mesh.facevarying_normals[3 * (3 * f + 0) + 0] = n0[0];
mesh.facevarying_normals[3 * (3 * f + 0) + 1] = n0[1];
mesh.facevarying_normals[3 * (3 * f + 0) + 2] = n0[2];
mesh.facevarying_normals[3 * (3 * f + 1) + 0] = n1[0];
mesh.facevarying_normals[3 * (3 * f + 1) + 1] = n1[1];
mesh.facevarying_normals[3 * (3 * f + 1) + 2] = n1[2];
mesh.facevarying_normals[3 * (3 * f + 2) + 0] = n2[0];
mesh.facevarying_normals[3 * (3 * f + 2) + 1] = n2[1];
mesh.facevarying_normals[3 * (3 * f + 2) + 2] = n2[2];
} else { // face contains invalid normal index. calc geometric normal.
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
float3 v0, v1, v2;
v0[0] = attrib.vertices[3 * f0 + 0];
v0[1] = attrib.vertices[3 * f0 + 1];
v0[2] = attrib.vertices[3 * f0 + 2];
v1[0] = attrib.vertices[3 * f1 + 0];
v1[1] = attrib.vertices[3 * f1 + 1];
v1[2] = attrib.vertices[3 * f1 + 2];
v2[0] = attrib.vertices[3 * f2 + 0];
v2[1] = attrib.vertices[3 * f2 + 1];
v2[2] = attrib.vertices[3 * f2 + 2];
float3 N;
CalcNormal(N, v0, v1, v2);
mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0];
mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1];
mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2];
mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0];
mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1];
mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2];
mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0];
mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1];
mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2];
}
}
} else {
// calc geometric normal
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
size_t f0, f1, f2;
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
float3 v0, v1, v2;
v0[0] = attrib.vertices[3 * f0 + 0];
v0[1] = attrib.vertices[3 * f0 + 1];
v0[2] = attrib.vertices[3 * f0 + 2];
v1[0] = attrib.vertices[3 * f1 + 0];
v1[1] = attrib.vertices[3 * f1 + 1];
v1[2] = attrib.vertices[3 * f1 + 2];
v2[0] = attrib.vertices[3 * f2 + 0];
v2[1] = attrib.vertices[3 * f2 + 1];
v2[2] = attrib.vertices[3 * f2 + 2];
float3 N;
CalcNormal(N, v0, v1, v2);
mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0];
mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1];
mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2];
mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0];
mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1];
mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2];
mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0];
mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1];
mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2];
}
}
if (attrib.texcoords.size() > 0) {
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
size_t f0, f1, f2;
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].texcoord_index);
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].texcoord_index);
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].texcoord_index);
if (f0 > 0 && f1 > 0 && f2 > 0) {
float3 n0, n1, n2;
n0[0] = attrib.texcoords[2 * f0 + 0];
n0[1] = attrib.texcoords[2 * f0 + 1];
n1[0] = attrib.texcoords[2 * f1 + 0];
n1[1] = attrib.texcoords[2 * f1 + 1];
n2[0] = attrib.texcoords[2 * f2 + 0];
n2[1] = attrib.texcoords[2 * f2 + 1];
mesh.facevarying_uvs[2 * (3 * f + 0) + 0] = n0[0];
mesh.facevarying_uvs[2 * (3 * f + 0) + 1] = n0[1];
mesh.facevarying_uvs[2 * (3 * f + 1) + 0] = n1[0];
mesh.facevarying_uvs[2 * (3 * f + 1) + 1] = n1[1];
mesh.facevarying_uvs[2 * (3 * f + 2) + 0] = n2[0];
mesh.facevarying_uvs[2 * (3 * f + 2) + 1] = n2[1];
}
}
}
// Compute pivot translation and add offset to the vertices.
float bmin[3], bmax[3];
ComputeBoundingBoxOfMesh(bmin, bmax, mesh);
float bcenter[3];
bcenter[0] = 0.5f * (bmax[0] - bmin[0]) + bmin[0];
bcenter[1] = 0.5f * (bmax[1] - bmin[1]) + bmin[1];
bcenter[2] = 0.5f * (bmax[2] - bmin[2]) + bmin[2];
for (size_t v = 0; v < mesh.vertices.size() / 3; v++) {
mesh.vertices[3 * v + 0] -= bcenter[0];
mesh.vertices[3 * v + 1] -= bcenter[1];
mesh.vertices[3 * v + 2] -= bcenter[2];
}
mesh.pivot_xform[0][0] = 1.0f;
mesh.pivot_xform[0][1] = 0.0f;
mesh.pivot_xform[0][2] = 0.0f;
mesh.pivot_xform[0][3] = 0.0f;
mesh.pivot_xform[1][0] = 0.0f;
mesh.pivot_xform[1][1] = 1.0f;
mesh.pivot_xform[1][2] = 0.0f;
mesh.pivot_xform[1][3] = 0.0f;
mesh.pivot_xform[2][0] = 0.0f;
mesh.pivot_xform[2][1] = 0.0f;
mesh.pivot_xform[2][2] = 1.0f;
mesh.pivot_xform[2][3] = 0.0f;
mesh.pivot_xform[3][0] = bcenter[0];
mesh.pivot_xform[3][1] = bcenter[1];
mesh.pivot_xform[3][2] = bcenter[2];
mesh.pivot_xform[3][3] = 1.0f;
meshes->push_back(mesh);
}
// material_t -> Material and Texture
out_materials->resize(materials.size());
out_textures->resize(0);
for (size_t i = 0; i < materials.size(); i++) {
(*out_materials)[i].diffuse[0] = materials[i].diffuse[0];
(*out_materials)[i].diffuse[1] = materials[i].diffuse[1];
(*out_materials)[i].diffuse[2] = materials[i].diffuse[2];
(*out_materials)[i].specular[0] = materials[i].specular[0];
(*out_materials)[i].specular[1] = materials[i].specular[1];
(*out_materials)[i].specular[2] = materials[i].specular[2];
(*out_materials)[i].id = int(i);
// map_Kd
(*out_materials)[i].diffuse_texid =
LoadTexture(materials[i].diffuse_texname, out_textures);
// map_Ks
(*out_materials)[i].specular_texid =
LoadTexture(materials[i].specular_texname, out_textures);
}
return true;
}
} // namespace example

View File

@ -0,0 +1,19 @@
#ifndef EXAMPLE_OBJ_LOADER_H_
#define EXAMPLE_OBJ_LOADER_H_
#include <vector>
#include <string>
#include "mesh.h"
#include "material.h"
namespace example {
///
/// Loads wavefront .obj mesh
///
bool LoadObj(const std::string &filename, float scale, std::vector<Mesh<float> > *meshes, std::vector<Material> *materials, std::vector<Texture> *textures);
}
#endif // EXAMPLE_OBJ_LOADER_H_

View File

@ -0,0 +1,114 @@
newoption {
trigger = "with-gtk3nfd",
description = "Build with native file dialog support(GTK3 required. Linux only)"
}
newoption {
trigger = "asan",
description = "Enable Address Sanitizer(gcc5+ ang clang only)"
}
sources = {
"main.cc",
"render.cc",
"render-config.cc",
"obj-loader.cc",
"matrix.cc",
"../common/trackball.cc",
"../common/imgui/imgui.cpp",
"../common/imgui/imgui_draw.cpp",
"../common/imgui/imgui_impl_btgui.cpp",
"../common/imgui/ImGuizmo.cpp",
}
solution "NanoSGSolution"
configurations { "Release", "Debug" }
if os.is("Windows") then
platforms { "x64", "x32" }
else
platforms { "native", "x64", "x32" }
end
-- RootDir for OpenGLWindow
projectRootDir = os.getcwd() .. "/../common/"
dofile ("../common/findOpenGLGlewGlut.lua")
initOpenGL()
initGlew()
-- Use c++11
flags { "c++11" }
-- A project defines one build target
project "viwewer"
kind "ConsoleApp"
language "C++"
files { sources }
includedirs { "./", "../../" }
includedirs { "../common" }
includedirs { "../common/imgui" }
includedirs { "../common/glm" }
--includedirs { "../common/nativefiledialog/src/include" }
if _OPTIONS['asan'] then
buildoptions { "-fsanitize=address" }
linkoptions { "-fsanitize=address" }
end
if os.is("Windows") then
flags { "FatalCompileWarnings" }
warnings "Extra" -- /W4
defines { "NOMINMAX" }
defines { "USE_NATIVEFILEDIALOG" }
buildoptions { "/W4" } -- raise compile error level.
files{
"../common/OpenGLWindow/Win32OpenGLWindow.cpp",
"../common/OpenGLWindow/Win32OpenGLWindow.h",
"../common/OpenGLWindow/Win32Window.cpp",
"../common/OpenGLWindow/Win32Window.h",
}
includedirs { "./../common/nativefiledialog/src/include" }
files { "../common/nativefiledialog/src/nfd_common.c",
"../common/nativefiledialog/src/nfd_win.cpp" }
end
if os.is("Linux") then
files {
"../common/OpenGLWindow/X11OpenGLWindow.cpp",
"../common/OpenGLWindow/X11OpenGLWindows.h"
}
links {"X11", "pthread", "dl"}
if _OPTIONS["with-gtk3nfd"] then
defines { "USE_NATIVEFILEDIALOG" }
includedirs { "./../common/nativefiledialog/src/include" }
files { "../common/nativefiledialog/src/nfd_gtk.c",
"../common/nativefiledialog/src/nfd_common.c"
}
buildoptions { "`pkg-config --cflags gtk+-3.0`" }
linkoptions { "`pkg-config --libs gtk+-3.0`" }
end
end
if os.is("MacOSX") then
defines { "USE_NATIVEFILEDIALOG" }
links {"Cocoa.framework"}
files {
"../common/OpenGLWindow/MacOpenGLWindow.h",
"../common/OpenGLWindow/MacOpenGLWindow.mm",
}
includedirs { "./../common/nativefiledialog/src/include" }
files { "../common/nativefiledialog/src/nfd_cocoa.m",
"../common/nativefiledialog/src/nfd_common.c" }
end
configuration "Debug"
defines { "DEBUG" } -- -DDEBUG
symbols "On"
targetname "view_debug"
configuration "Release"
-- defines { "NDEBUG" } -- -NDEBUG
symbols "On"
optimize "On"
targetname "view"

View File

@ -0,0 +1,116 @@
#include "render-config.h"
#include "picojson.h"
#include <fstream>
#include <istream>
namespace example {
bool LoadRenderConfig(example::RenderConfig* config, const char* filename) {
std::ifstream is(filename);
if (is.fail()) {
std::cerr << "Cannot open " << filename << std::endl;
return false;
}
std::istream_iterator<char> input(is);
std::string err;
picojson::value v;
input = picojson::parse(v, input, std::istream_iterator<char>(), &err);
if (!err.empty()) {
std::cerr << err << std::endl;
}
if (!v.is<picojson::object>()) {
std::cerr << "Not a JSON object" << std::endl;
return false;
}
picojson::object o = v.get<picojson::object>();
if (o.find("obj_filename") != o.end()) {
if (o["obj_filename"].is<std::string>()) {
config->obj_filename = o["obj_filename"].get<std::string>();
}
}
if (o.find("eson_filename") != o.end()) {
if (o["eson_filename"].is<std::string>()) {
config->eson_filename = o["eson_filename"].get<std::string>();
}
}
config->scene_scale = 1.0f;
if (o.find("scene_scale") != o.end()) {
if (o["scene_scale"].is<double>()) {
config->scene_scale = static_cast<float>(o["scene_scale"].get<double>());
}
}
config->eye[0] = 0.0f;
config->eye[1] = 0.0f;
config->eye[2] = 5.0f;
if (o.find("eye") != o.end()) {
if (o["eye"].is<picojson::array>()) {
picojson::array arr = o["eye"].get<picojson::array>();
if (arr.size() == 3) {
config->eye[0] = static_cast<float>(arr[0].get<double>());
config->eye[1] = static_cast<float>(arr[1].get<double>());
config->eye[2] = static_cast<float>(arr[2].get<double>());
}
}
}
config->up[0] = 0.0f;
config->up[1] = 1.0f;
config->up[2] = 0.0f;
if (o.find("up") != o.end()) {
if (o["up"].is<picojson::array>()) {
picojson::array arr = o["up"].get<picojson::array>();
if (arr.size() == 3) {
config->up[0] = static_cast<float>(arr[0].get<double>());
config->up[1] = static_cast<float>(arr[1].get<double>());
config->up[2] = static_cast<float>(arr[2].get<double>());
}
}
}
config->look_at[0] = 0.0f;
config->look_at[1] = 0.0f;
config->look_at[2] = 0.0f;
if (o.find("look_at") != o.end()) {
if (o["look_at"].is<picojson::array>()) {
picojson::array arr = o["look_at"].get<picojson::array>();
if (arr.size() == 3) {
config->look_at[0] = static_cast<float>(arr[0].get<double>());
config->look_at[1] = static_cast<float>(arr[1].get<double>());
config->look_at[2] = static_cast<float>(arr[2].get<double>());
}
}
}
config->fov = 45.0f;
if (o.find("fov") != o.end()) {
if (o["fov"].is<double>()) {
config->fov = static_cast<float>(o["fov"].get<double>());
}
}
config->width = 512;
if (o.find("width") != o.end()) {
if (o["width"].is<double>()) {
config->width = static_cast<int>(o["width"].get<double>());
}
}
config->height = 512;
if (o.find("height") != o.end()) {
if (o["height"].is<double>()) {
config->height = static_cast<int>(o["height"].get<double>());
}
}
return true;
}
}

View File

@ -0,0 +1,42 @@
#ifndef RENDER_CONFIG_H
#define RENDER_CONFIG_H
#include <string>
namespace example {
typedef struct {
// framebuffer
int width;
int height;
// camera
float eye[3];
float up[3];
float look_at[3];
float fov; // vertical fov in degree.
// render pass
int pass;
int max_passes;
// For debugging. Array size = width * height * 4.
float *normalImage;
float *positionImage;
float *depthImage;
float *texcoordImage;
float *varycoordImage;
// Scene input info
std::string obj_filename;
std::string eson_filename;
float scene_scale;
} RenderConfig;
/// Loads config from JSON file.
bool LoadRenderConfig(example::RenderConfig *config, const char *filename);
} // namespace
#endif // RENDER_CONFIG_H

560
examples/raytrace/render.cc Normal file
View File

@ -0,0 +1,560 @@
/*
The MIT License (MIT)
Copyright (c) 2015 - 2016 Light Transport Entertainment, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifdef _MSC_VER
#pragma warning(disable : 4018)
#pragma warning(disable : 4244)
#pragma warning(disable : 4189)
#pragma warning(disable : 4996)
#pragma warning(disable : 4267)
#pragma warning(disable : 4477)
#endif
#include "render.h"
#include <chrono> // C++11
#include <sstream>
#include <thread> // C++11
#include <vector>
#include <iostream>
#include "../../nanort.h"
#include "matrix.h"
#include "material.h"
#include "mesh.h"
#include "trackball.h"
#ifdef WIN32
#undef min
#undef max
#endif
namespace example {
// PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
// http://www.pcg-random.org/
typedef struct {
unsigned long long state;
unsigned long long inc; // not used?
} pcg32_state_t;
#define PCG32_INITIALIZER \
{ 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
float pcg32_random(pcg32_state_t* rng) {
unsigned long long oldstate = rng->state;
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
unsigned int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
unsigned int rot = oldstate >> 59u;
unsigned int ret = (xorshifted >> rot) | (xorshifted << ((-static_cast<int>(rot)) & 31));
return (float)((double)ret / (double)4294967296.0);
}
void pcg32_srandom(pcg32_state_t* rng, uint64_t initstate, uint64_t initseq) {
rng->state = 0U;
rng->inc = (initseq << 1U) | 1U;
pcg32_random(rng);
rng->state += initstate;
pcg32_random(rng);
}
const float kPI = 3.141592f;
typedef nanort::real3<float> float3;
inline float3 Lerp3(float3 v0, float3 v1, float3 v2, float u, float v) {
return (1.0f - u - v) * v0 + u * v1 + v * v2;
}
inline void CalcNormal(float3& N, float3 v0, float3 v1, float3 v2) {
float3 v10 = v1 - v0;
float3 v20 = v2 - v0;
N = vcross(v20, v10);
N = vnormalize(N);
}
void BuildCameraFrame(float3* origin, float3* corner, float3* u, float3* v,
float quat[4], float eye[3], float lookat[3], float up[3],
float fov, int width, int height) {
float e[4][4];
Matrix::LookAt(e, eye, lookat, up);
float r[4][4];
build_rotmatrix(r, quat);
float3 lo;
lo[0] = lookat[0] - eye[0];
lo[1] = lookat[1] - eye[1];
lo[2] = lookat[2] - eye[2];
float dist = vlength(lo);
float dir[3];
dir[0] = 0.0;
dir[1] = 0.0;
dir[2] = dist;
Matrix::Inverse(r);
float rr[4][4];
float re[4][4];
float zero[3] = {0.0f, 0.0f, 0.0f};
float localUp[3] = {0.0f, 1.0f, 0.0f};
Matrix::LookAt(re, dir, zero, localUp);
// translate
re[3][0] += eye[0]; // 0.0; //lo[0];
re[3][1] += eye[1]; // 0.0; //lo[1];
re[3][2] += (eye[2] - dist);
// rot -> trans
Matrix::Mult(rr, r, re);
float m[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
m[j][i] = rr[j][i];
}
}
float vzero[3] = {0.0f, 0.0f, 0.0f};
float eye1[3];
Matrix::MultV(eye1, m, vzero);
float lookat1d[3];
dir[2] = -dir[2];
Matrix::MultV(lookat1d, m, dir);
float3 lookat1(lookat1d[0], lookat1d[1], lookat1d[2]);
float up1d[3];
Matrix::MultV(up1d, m, up);
float3 up1(up1d[0], up1d[1], up1d[2]);
// absolute -> relative
up1[0] -= eye1[0];
up1[1] -= eye1[1];
up1[2] -= eye1[2];
// printf("up1(after) = %f, %f, %f\n", up1[0], up1[1], up1[2]);
// Use original up vector
// up1[0] = up[0];
// up1[1] = up[1];
// up1[2] = up[2];
{
float flen =
(0.5f * (float)height / tanf(0.5f * (float)(fov * kPI / 180.0f)));
float3 look1;
look1[0] = lookat1[0] - eye1[0];
look1[1] = lookat1[1] - eye1[1];
look1[2] = lookat1[2] - eye1[2];
// vcross(u, up1, look1);
// flip
(*u) = nanort::vcross(look1, up1);
(*u) = vnormalize((*u));
(*v) = vcross(look1, (*u));
(*v) = vnormalize((*v));
look1 = vnormalize(look1);
look1[0] = flen * look1[0] + eye1[0];
look1[1] = flen * look1[1] + eye1[1];
look1[2] = flen * look1[2] + eye1[2];
(*corner)[0] = look1[0] - 0.5f * (width * (*u)[0] + height * (*v)[0]);
(*corner)[1] = look1[1] - 0.5f * (width * (*u)[1] + height * (*v)[1]);
(*corner)[2] = look1[2] - 0.5f * (width * (*u)[2] + height * (*v)[2]);
(*origin)[0] = eye1[0];
(*origin)[1] = eye1[1];
(*origin)[2] = eye1[2];
}
}
#if 0 // TODO(LTE): Not used method. Delete.
nanort::Ray<float> GenerateRay(const float3& origin, const float3& corner,
const float3& du, const float3& dv, float u,
float v) {
float3 dir;
dir[0] = (corner[0] + u * du[0] + v * dv[0]) - origin[0];
dir[1] = (corner[1] + u * du[1] + v * dv[1]) - origin[1];
dir[2] = (corner[2] + u * du[2] + v * dv[2]) - origin[2];
dir = vnormalize(dir);
float3 org;
nanort::Ray<float> ray;
ray.org[0] = origin[0];
ray.org[1] = origin[1];
ray.org[2] = origin[2];
ray.dir[0] = dir[0];
ray.dir[1] = dir[1];
ray.dir[2] = dir[2];
return ray;
}
#endif
void FetchTexture(const Texture &texture, float u, float v, float* col) {
int tx = u * texture.width;
int ty = (1.0f - v) * texture.height;
int idx_offset = (ty * texture.width + tx) * texture.components;
col[0] = texture.image[idx_offset + 0] / 255.f;
col[1] = texture.image[idx_offset + 1] / 255.f;
col[2] = texture.image[idx_offset + 2] / 255.f;
}
bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
float quat[4],
const nanosg::Scene<float, example::Mesh<float>> &scene,
const example::Asset &asset,
const RenderConfig& config,
std::atomic<bool>& cancelFlag,
int &_showBufferMode
) {
//if (!gAccel.IsValid()) {
// return false;
//}
int width = config.width;
int height = config.height;
// camera
float eye[3] = {config.eye[0], config.eye[1], config.eye[2]};
float look_at[3] = {config.look_at[0], config.look_at[1], config.look_at[2]};
float up[3] = {config.up[0], config.up[1], config.up[2]};
float fov = config.fov;
float3 origin, corner, u, v;
BuildCameraFrame(&origin, &corner, &u, &v, quat, eye, look_at, up, fov, width,
height);
auto kCancelFlagCheckMilliSeconds = 300;
std::vector<std::thread> workers;
std::atomic<int> i(0);
uint32_t num_threads = std::max(1U, std::thread::hardware_concurrency());
auto startT = std::chrono::system_clock::now();
// Initialize RNG.
for (auto t = 0; t < num_threads; t++) {
workers.emplace_back(std::thread([&, t]() {
pcg32_state_t rng;
pcg32_srandom(&rng, config.pass,
t); // seed = combination of render pass + thread no.
int y = 0;
while ((y = i++) < config.height) {
auto currT = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> ms = currT - startT;
// Check cancel flag
if (ms.count() > kCancelFlagCheckMilliSeconds) {
if (cancelFlag) {
break;
}
}
// draw dash line to aux buffer for progress.
// for (int x = 0; x < config.width; x++) {
// float c = (x / 8) % 2;
// aux_rgba[4*(y*config.width+x)+0] = c;
// aux_rgba[4*(y*config.width+x)+1] = c;
// aux_rgba[4*(y*config.width+x)+2] = c;
// aux_rgba[4*(y*config.width+x)+3] = 0.0f;
//}
for (int x = 0; x < config.width; x++) {
nanort::Ray<float> ray;
ray.org[0] = origin[0];
ray.org[1] = origin[1];
ray.org[2] = origin[2];
float u0 = pcg32_random(&rng);
float u1 = pcg32_random(&rng);
float3 dir;
//for modes not a "color"
if(_showBufferMode != SHOW_BUFFER_COLOR)
{
//only one pass
if(config.pass > 0)
continue;
//to the center of pixel
u0 = 0.5f;
u1 = 0.5f;
}
dir = corner + (float(x) + u0) * u +
(float(config.height - y - 1) + u1) * v;
dir = vnormalize(dir);
ray.dir[0] = dir[0];
ray.dir[1] = dir[1];
ray.dir[2] = dir[2];
float kFar = 1.0e+30f;
ray.min_t = 0.0f;
ray.max_t = kFar;
nanosg::Intersection<float> isect;
bool hit = scene.Traverse(ray, &isect, /* cull_back_face */false);
if (hit) {
const std::vector<Material> &materials = asset.materials;
const std::vector<Texture> &textures = asset.textures;
const Mesh<float> &mesh = asset.meshes[isect.node_id];
//tigra: add default material
const Material &default_material = asset.default_material;
float3 p;
p[0] =
ray.org[0] + isect.t * ray.dir[0];
p[1] =
ray.org[1] + isect.t * ray.dir[1];
p[2] =
ray.org[2] + isect.t * ray.dir[2];
config.positionImage[4 * (y * config.width + x) + 0] = p.x();
config.positionImage[4 * (y * config.width + x) + 1] = p.y();
config.positionImage[4 * (y * config.width + x) + 2] = p.z();
config.positionImage[4 * (y * config.width + x) + 3] = 1.0f;
config.varycoordImage[4 * (y * config.width + x) + 0] =
isect.u;
config.varycoordImage[4 * (y * config.width + x) + 1] =
isect.v;
config.varycoordImage[4 * (y * config.width + x) + 2] = 0.0f;
config.varycoordImage[4 * (y * config.width + x) + 3] = 1.0f;
unsigned int prim_id = isect.prim_id;
float3 N;
if (mesh.facevarying_normals.size() > 0) {
float3 n0, n1, n2;
n0[0] = mesh.facevarying_normals[9 * prim_id + 0];
n0[1] = mesh.facevarying_normals[9 * prim_id + 1];
n0[2] = mesh.facevarying_normals[9 * prim_id + 2];
n1[0] = mesh.facevarying_normals[9 * prim_id + 3];
n1[1] = mesh.facevarying_normals[9 * prim_id + 4];
n1[2] = mesh.facevarying_normals[9 * prim_id + 5];
n2[0] = mesh.facevarying_normals[9 * prim_id + 6];
n2[1] = mesh.facevarying_normals[9 * prim_id + 7];
n2[2] = mesh.facevarying_normals[9 * prim_id + 8];
N = Lerp3(n0, n1, n2, isect.u, isect.v);
} else {
unsigned int f0, f1, f2;
f0 = mesh.faces[3 * prim_id + 0];
f1 = mesh.faces[3 * prim_id + 1];
f2 = mesh.faces[3 * prim_id + 2];
float3 v0, v1, v2;
v0[0] = mesh.vertices[3 * f0 + 0];
v0[1] = mesh.vertices[3 * f0 + 1];
v0[2] = mesh.vertices[3 * f0 + 2];
v1[0] = mesh.vertices[3 * f1 + 0];
v1[1] = mesh.vertices[3 * f1 + 1];
v1[2] = mesh.vertices[3 * f1 + 2];
v2[0] = mesh.vertices[3 * f2 + 0];
v2[1] = mesh.vertices[3 * f2 + 1];
v2[2] = mesh.vertices[3 * f2 + 2];
CalcNormal(N, v0, v1, v2);
}
config.normalImage[4 * (y * config.width + x) + 0] =
0.5f * N[0] + 0.5f;
config.normalImage[4 * (y * config.width + x) + 1] =
0.5f * N[1] + 0.5f;
config.normalImage[4 * (y * config.width + x) + 2] =
0.5f * N[2] + 0.5f;
config.normalImage[4 * (y * config.width + x) + 3] = 1.0f;
config.depthImage[4 * (y * config.width + x) + 0] =
isect.t;
config.depthImage[4 * (y * config.width + x) + 1] =
isect.t;
config.depthImage[4 * (y * config.width + x) + 2] =
isect.t;
config.depthImage[4 * (y * config.width + x) + 3] = 1.0f;
float3 UV;
if (mesh.facevarying_uvs.size() > 0) {
float3 uv0, uv1, uv2;
uv0[0] = mesh.facevarying_uvs[6 * prim_id + 0];
uv0[1] = mesh.facevarying_uvs[6 * prim_id + 1];
uv1[0] = mesh.facevarying_uvs[6 * prim_id + 2];
uv1[1] = mesh.facevarying_uvs[6 * prim_id + 3];
uv2[0] = mesh.facevarying_uvs[6 * prim_id + 4];
uv2[1] = mesh.facevarying_uvs[6 * prim_id + 5];
UV = Lerp3(uv0, uv1, uv2, isect.u, isect.v);
config.texcoordImage[4 * (y * config.width + x) + 0] = UV[0];
config.texcoordImage[4 * (y * config.width + x) + 1] = UV[1];
}
// Fetch texture
unsigned int material_id =
mesh.material_ids[isect.prim_id];
//printf("material_id=%d materials=%lld\n", material_id, materials.size());
float diffuse_col[3];
float specular_col[3];
//tigra: material_id is ok
if(material_id>=0 && material_id<materials.size())
{
//printf("ok mat\n");
int diffuse_texid = materials[material_id].diffuse_texid;
if (diffuse_texid >= 0) {
FetchTexture(textures[diffuse_texid], UV[0], UV[1], diffuse_col);
} else {
diffuse_col[0] = materials[material_id].diffuse[0];
diffuse_col[1] = materials[material_id].diffuse[1];
diffuse_col[2] = materials[material_id].diffuse[2];
}
int specular_texid = materials[material_id].specular_texid;
if (specular_texid >= 0) {
FetchTexture(textures[specular_texid], UV[0], UV[1], specular_col);
} else {
specular_col[0] = materials[material_id].specular[0];
specular_col[1] = materials[material_id].specular[1];
specular_col[2] = materials[material_id].specular[2];
}
}
else
//tigra: wrong material_id, use default_material
{
//printf("default_material\n");
diffuse_col[0] = default_material.diffuse[0];
diffuse_col[1] = default_material.diffuse[1];
diffuse_col[2] = default_material.diffuse[2];
specular_col[0] = default_material.specular[0];
specular_col[1] = default_material.specular[1];
specular_col[2] = default_material.specular[2];
}
// Simple shading
float NdotV = fabsf(vdot(N, dir));
if (config.pass == 0) {
rgba[4 * (y * config.width + x) + 0] = NdotV * diffuse_col[0];
rgba[4 * (y * config.width + x) + 1] = NdotV * diffuse_col[1];
rgba[4 * (y * config.width + x) + 2] = NdotV * diffuse_col[2];
rgba[4 * (y * config.width + x) + 3] = 1.0f;
sample_counts[y * config.width + x] =
1; // Set 1 for the first pass
} else { // additive.
rgba[4 * (y * config.width + x) + 0] += NdotV * diffuse_col[0];
rgba[4 * (y * config.width + x) + 1] += NdotV * diffuse_col[1];
rgba[4 * (y * config.width + x) + 2] += NdotV * diffuse_col[2];
rgba[4 * (y * config.width + x) + 3] += 1.0f;
sample_counts[y * config.width + x]++;
}
} else {
{
if (config.pass == 0) {
// clear pixel
rgba[4 * (y * config.width + x) + 0] = 0.0f;
rgba[4 * (y * config.width + x) + 1] = 0.0f;
rgba[4 * (y * config.width + x) + 2] = 0.0f;
rgba[4 * (y * config.width + x) + 3] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 0] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 1] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 2] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 3] = 0.0f;
sample_counts[y * config.width + x] =
1; // Set 1 for the first pass
} else {
sample_counts[y * config.width + x]++;
}
// No super sampling
config.normalImage[4 * (y * config.width + x) + 0] = 0.0f;
config.normalImage[4 * (y * config.width + x) + 1] = 0.0f;
config.normalImage[4 * (y * config.width + x) + 2] = 0.0f;
config.normalImage[4 * (y * config.width + x) + 3] = 0.0f;
config.positionImage[4 * (y * config.width + x) + 0] = 0.0f;
config.positionImage[4 * (y * config.width + x) + 1] = 0.0f;
config.positionImage[4 * (y * config.width + x) + 2] = 0.0f;
config.positionImage[4 * (y * config.width + x) + 3] = 0.0f;
config.depthImage[4 * (y * config.width + x) + 0] = 0.0f;
config.depthImage[4 * (y * config.width + x) + 1] = 0.0f;
config.depthImage[4 * (y * config.width + x) + 2] = 0.0f;
config.depthImage[4 * (y * config.width + x) + 3] = 0.0f;
config.texcoordImage[4 * (y * config.width + x) + 0] = 0.0f;
config.texcoordImage[4 * (y * config.width + x) + 1] = 0.0f;
config.texcoordImage[4 * (y * config.width + x) + 2] = 0.0f;
config.texcoordImage[4 * (y * config.width + x) + 3] = 0.0f;
config.varycoordImage[4 * (y * config.width + x) + 0] = 0.0f;
config.varycoordImage[4 * (y * config.width + x) + 1] = 0.0f;
config.varycoordImage[4 * (y * config.width + x) + 2] = 0.0f;
config.varycoordImage[4 * (y * config.width + x) + 3] = 0.0f;
}
}
}
for (int x = 0; x < config.width; x++) {
aux_rgba[4 * (y * config.width + x) + 0] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 1] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 2] = 0.0f;
aux_rgba[4 * (y * config.width + x) + 3] = 0.0f;
}
}
}));
}
for (auto& t : workers) {
t.join();
}
return (!cancelFlag);
};
} // namespace example

View File

@ -0,0 +1,45 @@
#ifndef EXAMPLE_RENDER_H_
#define EXAMPLE_RENDER_H_
#include <atomic> // C++11
//mode definitions now here
#define SHOW_BUFFER_COLOR (0)
#define SHOW_BUFFER_NORMAL (1)
#define SHOW_BUFFER_POSITION (2)
#define SHOW_BUFFER_DEPTH (3)
#define SHOW_BUFFER_TEXCOORD (4)
#define SHOW_BUFFER_VARYCOORD (5)
#include "render-config.h"
#include "nanosg.h"
#include "mesh.h"
#include "material.h"
namespace example {
struct Asset {
std::vector<Mesh<float> > meshes;
std::vector<Material> materials;
//tigra: add default material
Material default_material;
std::vector<Texture> textures;
};
class Renderer {
public:
Renderer() {}
~Renderer() {}
/// Returns false when the rendering was canceled.
static bool Render(float* rgba, float* aux_rgba, int *sample_counts, float quat[4],
const nanosg::Scene<float, Mesh<float>> &scene, const Asset &asset, const RenderConfig& config,
std::atomic<bool>& cancel_flag,
int& _showBufferMode
);
};
};
#endif // EXAMPLE_RENDER_H_

View File

@ -0,0 +1,339 @@
# GNU Make project makefile autogenerated by Premake
ifndef config
config=release_native
endif
ifndef verbose
SILENT = @
endif
.PHONY: clean prebuild prelink
ifeq ($(config),release_native)
RESCOMP = windres
TARGETDIR = bin/native/Release
TARGET = $(TARGETDIR)/view
OBJDIR = obj/native/Release
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O2 -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O2 -g -std=c++11
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += -lX11 -lpthread -ldl
LDDEPS +=
ALL_LDFLAGS += $(LDFLAGS)
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
all: prebuild prelink $(TARGET)
@:
endif
ifeq ($(config),release_x64)
RESCOMP = windres
TARGETDIR = bin/x64/Release
TARGET = $(TARGETDIR)/view
OBJDIR = obj/x64/Release
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -O2 -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -O2 -g -std=c++11
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += -lX11 -lpthread -ldl
LDDEPS +=
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
all: prebuild prelink $(TARGET)
@:
endif
ifeq ($(config),release_x32)
RESCOMP = windres
TARGETDIR = bin/x32/Release
TARGET = $(TARGETDIR)/view
OBJDIR = obj/x32/Release
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -O2 -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m32 -O2 -g -std=c++11
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += -lX11 -lpthread -ldl
LDDEPS +=
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -m32
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
all: prebuild prelink $(TARGET)
@:
endif
ifeq ($(config),debug_native)
RESCOMP = windres
TARGETDIR = bin/native/Debug
TARGET = $(TARGETDIR)/view_debug
OBJDIR = obj/native/Debug
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g -std=c++11
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += -lX11 -lpthread -ldl
LDDEPS +=
ALL_LDFLAGS += $(LDFLAGS)
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
all: prebuild prelink $(TARGET)
@:
endif
ifeq ($(config),debug_x64)
RESCOMP = windres
TARGETDIR = bin/x64/Debug
TARGET = $(TARGETDIR)/view_debug
OBJDIR = obj/x64/Debug
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -g -std=c++11
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += -lX11 -lpthread -ldl
LDDEPS +=
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
all: prebuild prelink $(TARGET)
@:
endif
ifeq ($(config),debug_x32)
RESCOMP = windres
TARGETDIR = bin/x32/Debug
TARGET = $(TARGETDIR)/view_debug
OBJDIR = obj/x32/Debug
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m32 -g -std=c++11
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += -lX11 -lpthread -ldl
LDDEPS +=
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -m32
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
all: prebuild prelink $(TARGET)
@:
endif
OBJECTS := \
$(OBJDIR)/X11OpenGLWindow.o \
$(OBJDIR)/glew.o \
$(OBJDIR)/ImGuizmo.o \
$(OBJDIR)/imgui.o \
$(OBJDIR)/imgui_draw.o \
$(OBJDIR)/imgui_impl_btgui.o \
$(OBJDIR)/trackball.o \
$(OBJDIR)/main.o \
$(OBJDIR)/matrix.o \
$(OBJDIR)/obj-loader.o \
$(OBJDIR)/render-config.o \
$(OBJDIR)/render.o \
RESOURCES := \
CUSTOMFILES := \
SHELLTYPE := msdos
ifeq (,$(ComSpec)$(COMSPEC))
SHELLTYPE := posix
endif
ifeq (/bin,$(findstring /bin,$(SHELL)))
SHELLTYPE := posix
endif
$(TARGET): $(GCH) ${CUSTOMFILES} $(OBJECTS) $(LDDEPS) $(RESOURCES)
@echo Linking viwewer
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(TARGETDIR)
else
$(SILENT) mkdir $(subst /,\\,$(TARGETDIR))
endif
$(SILENT) $(LINKCMD)
$(POSTBUILDCMDS)
clean:
@echo Cleaning viwewer
ifeq (posix,$(SHELLTYPE))
$(SILENT) rm -f $(TARGET)
$(SILENT) rm -rf $(OBJDIR)
else
$(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET))
$(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR))
endif
prebuild:
$(PREBUILDCMDS)
prelink:
$(PRELINKCMDS)
ifneq (,$(PCH))
$(OBJECTS): $(GCH) $(PCH)
$(GCH): $(PCH)
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<"
endif
$(OBJDIR)/X11OpenGLWindow.o: ../common/OpenGLWindow/X11OpenGLWindow.cpp
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/glew.o: ../common/ThirdPartyLibs/Glew/glew.c
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/ImGuizmo.o: ../common/imgui/ImGuizmo.cpp
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/imgui.o: ../common/imgui/imgui.cpp
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/imgui_draw.o: ../common/imgui/imgui_draw.cpp
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/imgui_impl_btgui.o: ../common/imgui/imgui_impl_btgui.cpp
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/trackball.o: ../common/trackball.cc
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/main.o: main.cc
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/matrix.o: matrix.cc
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/obj-loader.o: obj-loader.cc
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/render-config.o: render-config.cc
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/render.o: render.cc
@echo $(notdir $<)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
-include $(OBJECTS:%.o=%.d)
ifneq (,$(PCH))
-include $(OBJDIR)/$(notdir $(PCH)).d
endif