Merge pull request #40 from Ybalrid/pr_raytrace_example

Pr raytrace example
This commit is contained in:
Syoyo Fujita 2018-02-28 00:04:07 +09:00 committed by GitHub
commit 493cad4e64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 3840 additions and 541 deletions

22
.gitignore vendored
View File

@ -9,6 +9,28 @@ install_manifest.txt
compile_commands.json
CTestTestfile.cmake
#Files created by the CI scripts (downloading and installing premake)
premake5
premake5.tar.gz
#built examples
/examples/raytrace/bin/
#visual studio files
*.sln
*.vcxproj*
.vs
#binary directories
bin/
obj/
#runtime gui config
imgui.ini
#visual stuido code
.vscode
# Prerequisites
*.d

10
.travis-before-install.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
if [[ "$TRAVIS_OS_NAME" == "osx" ]]
then
brew upgrade
curl -o premake5.tar.gz https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-macosx.tar.gz
else
wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-linux.tar.gz -O premake5.tar.gz
fi
tar xzf premake5.tar.gz

View File

@ -33,7 +33,7 @@ matrix:
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi
- ./.travis-before-install.sh
script:
@ -42,3 +42,6 @@ script:
- ${CC} -v
- ${CXX} ${EXTRA_CXXFLAGS} -std=c++11 -Wall -g -o loader_example loader_example.cc
- ./loader_example ./models/Cube/Cube.gltf
- cd examples/raytrace
- ../../premake5 gmake
- make

View File

@ -13,3 +13,6 @@ configuration: Release
build:
parallel: true
project: TinyGLTFSolution.sln
after_build:
- examples.bat

3
examples.bat Normal file
View File

@ -0,0 +1,3 @@
cd examples\raytrace
..\..\tools\windows\premake5.exe vs2015
msbuild NanoSGSolution.sln /property:Configuration=Release

View File

@ -1,31 +1,128 @@
# Raytrace example
# NanoSG
Simple raytracing example with OpenGL preview
Simple, minimal and header-only scene graph library for NanoRT.
## Status
NanoSG itself shoud be compiled with C++-03 compiler, but demo code uses C++11 features.
Not working yet. Still in work in progress.
![screenshot of the demo program](images/nanosg-demo.png)
![Animation showing node manipulation](https://media.giphy.com/media/l3JDO29fMFndyObHW/giphy.gif)
## Build
### Linux or macOS
```
$ premake5 gmake
$ make
```bash
premake5 gmake
make
```
### Windows
```
$ premake5 vs2015
```bash
premake5 vs2015
```
## Data structure
## Third party libraries and its icenses.
### 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`.
```cpp
///
/// 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
```cpp
void Node::SetName(const std::string &name);
```
Set (unique) name for the node.
```cpp
void Node::AddChild(const type &child);
```
Add node as child node.
```cpp
void Node::SetLocalXform(const T xform[4][4]) {
```
Set local transformation matrix. Default is identity matrix.
### Scene
```cpp
bool Scene::AddNode(const Node<T, M> &node);
```
Add a node to the scene.
```cpp
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.
```cpp
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.
* 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

View File

@ -1,9 +1,23 @@
{ "gltf_filename" : "../../models/Cube/Cube.gltf",
"scene_scale" : 1.0,
"width" : 512,
"height" : 512,
"eye" : [0, 2.5, 15],
"up" : [0, 1, 0],
"look_at" : [0, 0, 0],
"dummy" : 0
{
"commented_out_obj_filename": "cornellbox_suzanne.obj",
"gltf_filename": "../../models/Cube/Cube.gltf",
"scene_scale": 1.0,
"width": 512,
"height": 512,
"eye": [
0,
2.5,
15
],
"up": [
0,
1,
0
],
"look_at": [
0,
0,
0
],
"dummy": 0
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,11 @@
#include "gltf-loader.h"
#include <iostream>
#include <memory> // c++11
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "tiny_gltf.h"
#include <tiny_gltf.h>
namespace example {
static std::string GetFilePathExtension(const std::string &FileName) {
if (FileName.find_last_of(".") != std::string::npos)
return FileName.substr(FileName.find_last_of(".") + 1);
@ -17,17 +15,17 @@ static std::string GetFilePathExtension(const std::string &FileName) {
///
/// Loads glTF 2.0 mesh
///
bool LoadGLTF(const std::string &filename, float scale, std::vector<Mesh<float> > *meshes, std::vector<Material> *materials, std::vector<Texture> *textures)
{
// TODO(syoyo): Implement
bool LoadGLTF(const std::string &filename, float scale,
std::vector<Mesh<float> > *meshes,
std::vector<Material> *materials,
std::vector<Texture> *textures) {
// TODO(syoyo): Texture
// TODO(syoyo): Material
tinygltf::Model model;
tinygltf::TinyGLTF loader;
std::string err;
std::string ext = GetFilePathExtension(filename);
const std::string ext = GetFilePathExtension(filename);
bool ret = false;
if (ext.compare("glb") == 0) {
@ -46,8 +44,433 @@ bool LoadGLTF(const std::string &filename, float scale, std::vector<Mesh<float>
return false;
}
std::cerr << "LoadGLTF() function is not yet implemented!" << std::endl;
return false;
}
std::cout << "loaded glTF file has:\n"
<< model.accessors.size() << " accessors\n"
<< model.animations.size() << " animations\n"
<< model.buffers.size() << " buffers\n"
<< model.bufferViews.size() << " bufferViews\n"
<< model.materials.size() << " materials\n"
<< model.meshes.size() << " meshes\n"
<< model.nodes.size() << " nodes\n"
<< model.textures.size() << " textures\n"
<< model.images.size() << " images\n"
<< model.skins.size() << " skins\n"
<< model.samplers.size() << " samplers\n"
<< model.cameras.size() << " cameras\n"
<< model.scenes.size() << " scenes\n"
<< model.lights.size() << " lights\n";
} // namespace example
// Iterate through all the meses in the glTF file
for (const auto &gltfMesh : model.meshes) {
std::cout << "Current mesh has " << gltfMesh.primitives.size()
<< " primitives:\n";
// Create a mesh object
Mesh<float> loadedMesh(sizeof(float) * 3);
// To store the min and max of the buffer (as 3D vector of floats)
v3f pMin = {}, pMax = {};
// Store the name of the glTF mesh (if defined)
loadedMesh.name = gltfMesh.name;
// For each primitive
for (const auto &meshPrimitive : gltfMesh.primitives) {
// Boolean used to check if we have converted the vertex buffer format
bool convertedToTriangleList = false;
// This permit to get a type agnostic way of reading the index buffer
std::unique_ptr<intArrayBase> indicesArrayPtr = nullptr;
{
const auto &indicesAccessor = model.accessors[meshPrimitive.indices];
const auto &bufferView = model.bufferViews[indicesAccessor.bufferView];
const auto &buffer = model.buffers[bufferView.buffer];
const auto dataAddress = buffer.data.data() + bufferView.byteOffset +
indicesAccessor.byteOffset;
const auto byteStride = indicesAccessor.ByteStride(bufferView);
const auto count = indicesAccessor.count;
// Allocate the index array in the pointer-to-base declared in the
// parent scope
switch (indicesAccessor.componentType) {
case TINYGLTF_COMPONENT_TYPE_BYTE:
indicesArrayPtr =
std::unique_ptr<intArray<char> >(new intArray<char>(
arrayAdapter<char>(dataAddress, count, byteStride)));
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
indicesArrayPtr = std::unique_ptr<intArray<unsigned char> >(
new intArray<unsigned char>(arrayAdapter<unsigned char>(
dataAddress, count, byteStride)));
break;
case TINYGLTF_COMPONENT_TYPE_SHORT:
indicesArrayPtr =
std::unique_ptr<intArray<short> >(new intArray<short>(
arrayAdapter<short>(dataAddress, count, byteStride)));
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
indicesArrayPtr = std::unique_ptr<intArray<unsigned short> >(
new intArray<unsigned short>(arrayAdapter<unsigned short>(
dataAddress, count, byteStride)));
break;
case TINYGLTF_COMPONENT_TYPE_INT:
indicesArrayPtr = std::unique_ptr<intArray<int> >(new intArray<int>(
arrayAdapter<int>(dataAddress, count, byteStride)));
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
indicesArrayPtr = std::unique_ptr<intArray<unsigned int> >(
new intArray<unsigned int>(arrayAdapter<unsigned int>(
dataAddress, count, byteStride)));
break;
default:
break;
}
}
const auto &indices = *indicesArrayPtr;
if (indicesArrayPtr) {
std::cout << "indices: ";
for (size_t i(0); i < indicesArrayPtr->size(); ++i) {
std::cout << indices[i] << " ";
loadedMesh.faces.push_back(indices[i]);
}
std::cout << '\n';
}
switch (meshPrimitive.mode) {
// We re-arrange the indices so that it describe a simple list of
// triangles
case TINYGLTF_MODE_TRIANGLE_FAN:
if (!convertedToTriangleList) {
std::cout << "TRIANGLE_FAN\n";
// This only has to be done once per primitive
convertedToTriangleList = true;
// We steal the guts of the vector
auto triangleFan = std::move(loadedMesh.faces);
loadedMesh.faces.clear();
// Push back the indices that describe just one triangle one by one
for (size_t i{2}; i < triangleFan.size(); ++i) {
loadedMesh.faces.push_back(triangleFan[0]);
loadedMesh.faces.push_back(triangleFan[i - 1]);
loadedMesh.faces.push_back(triangleFan[i]);
}
}
case TINYGLTF_MODE_TRIANGLE_STRIP:
if (!convertedToTriangleList) {
std::cout << "TRIANGLE_STRIP\n";
// This only has to be done once per primitive
convertedToTriangleList = true;
auto triangleStrip = std::move(loadedMesh.faces);
loadedMesh.faces.clear();
for (size_t i{2}; i < triangleStrip.size(); ++i) {
loadedMesh.faces.push_back(triangleStrip[i - 2]);
loadedMesh.faces.push_back(triangleStrip[i - 1]);
loadedMesh.faces.push_back(triangleStrip[i]);
}
}
case TINYGLTF_MODE_TRIANGLES: // this is the simpliest case to handle
{
std::cout << "TRIANGLES\n";
for (const auto &attribute : meshPrimitive.attributes) {
const auto attribAccessor = model.accessors[attribute.second];
const auto &bufferView =
model.bufferViews[attribAccessor.bufferView];
const auto &buffer = model.buffers[bufferView.buffer];
const auto dataPtr = buffer.data.data() + bufferView.byteOffset +
attribAccessor.byteOffset;
const auto byte_stride = attribAccessor.ByteStride(bufferView);
const auto count = attribAccessor.count;
std::cout << "current attribute has count " << count
<< " and stride " << byte_stride << " bytes\n";
std::cout << "attribute string is : " << attribute.first << '\n';
if (attribute.first == "POSITION") {
std::cout << "found position attribute\n";
// get the position min/max for computing the boundingbox
pMin.x = attribAccessor.minValues[0];
pMin.y = attribAccessor.minValues[1];
pMin.z = attribAccessor.minValues[2];
pMax.x = attribAccessor.maxValues[0];
pMax.y = attribAccessor.maxValues[1];
pMax.z = attribAccessor.maxValues[2];
switch (attribAccessor.type) {
case TINYGLTF_TYPE_VEC3: {
switch (attribAccessor.componentType) {
case TINYGLTF_COMPONENT_TYPE_FLOAT:
std::cout << "Type is FLOAT\n";
// 3D vector of float
v3fArray positions(
arrayAdapter<v3f>(dataPtr, count, byte_stride));
std::cout << "positions's size : " << positions.size()
<< '\n';
for (size_t i{0}; i < positions.size(); ++i) {
const auto v = positions[i];
std::cout << "positions[" << i << "]: (" << v.x << ", "
<< v.y << ", " << v.z << ")\n";
loadedMesh.vertices.push_back(v.x * scale);
loadedMesh.vertices.push_back(v.y * scale);
loadedMesh.vertices.push_back(v.z * scale);
}
}
break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
std::cout << "Type is DOUBLE\n";
switch (attribAccessor.type) {
case TINYGLTF_TYPE_VEC3: {
v3dArray positions(
arrayAdapter<v3d>(dataPtr, count, byte_stride));
for (size_t i{0}; i < positions.size(); ++i) {
const auto v = positions[i];
std::cout << "positions[" << i << "]: (" << v.x
<< ", " << v.y << ", " << v.z << ")\n";
loadedMesh.vertices.push_back(v.x * scale);
loadedMesh.vertices.push_back(v.y * scale);
loadedMesh.vertices.push_back(v.z * scale);
}
} break;
default:
// TODO Handle error
break;
}
break;
default:
break;
}
} break;
}
}
if (attribute.first == "NORMAL") {
std::cout << "found normal attribute\n";
switch (attribAccessor.type) {
case TINYGLTF_TYPE_VEC3: {
std::cout << "Normal is VEC3\n";
switch (attribAccessor.componentType) {
case TINYGLTF_COMPONENT_TYPE_FLOAT: {
std::cout << "Normal is FLOAT\n";
v3fArray normals(
arrayAdapter<v3f>(dataPtr, count, byte_stride));
// IMPORTANT: We need to reorder normals (and texture
// coordinates into "facevarying" order) for each face
// For each triangle :
for (size_t i{0}; i < indices.size() / 3; ++i) {
// get the i'th triange's indexes
auto f0 = indices[3 * i + 0];
auto f1 = indices[3 * i + 1];
auto f2 = indices[3 * i + 2];
// get the 3 normal vectors for that face
v3f n0, n1, n2;
n0 = normals[f0];
n1 = normals[f1];
n2 = normals[f2];
// Put them in the array in the correct order
loadedMesh.facevarying_normals.push_back(n0.x);
loadedMesh.facevarying_normals.push_back(n0.y);
loadedMesh.facevarying_normals.push_back(n0.z);
loadedMesh.facevarying_normals.push_back(n1.x);
loadedMesh.facevarying_normals.push_back(n1.y);
loadedMesh.facevarying_normals.push_back(n2.z);
loadedMesh.facevarying_normals.push_back(n2.x);
loadedMesh.facevarying_normals.push_back(n2.y);
loadedMesh.facevarying_normals.push_back(n2.z);
}
} break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
std::cout << "Normal is DOUBLE\n";
v3dArray normals(
arrayAdapter<v3d>(dataPtr, count, byte_stride));
// IMPORTANT: We need to reorder normals (and texture
// coordinates into "facevarying" order) for each face
// For each triangle :
for (size_t i{0}; i < indices.size() / 3; ++i) {
// get the i'th triange's indexes
auto f0 = indices[3 * i + 0];
auto f1 = indices[3 * i + 1];
auto f2 = indices[3 * i + 2];
// get the 3 normal vectors for that face
v3d n0, n1, n2;
n0 = normals[f0];
n1 = normals[f1];
n2 = normals[f2];
// Put them in the array in the correct order
loadedMesh.facevarying_normals.push_back(n0.x);
loadedMesh.facevarying_normals.push_back(n0.y);
loadedMesh.facevarying_normals.push_back(n0.z);
loadedMesh.facevarying_normals.push_back(n1.x);
loadedMesh.facevarying_normals.push_back(n1.y);
loadedMesh.facevarying_normals.push_back(n2.z);
loadedMesh.facevarying_normals.push_back(n2.x);
loadedMesh.facevarying_normals.push_back(n2.y);
loadedMesh.facevarying_normals.push_back(n2.z);
}
} break;
default:
std::cerr << "Unhandeled componant type for normal\n";
}
} break;
default:
std::cerr << "Unhandeled vector type for normal\n";
}
// Face varying comment on the normals is also true for the UVs
if (attribute.first == "TEXCOORD_0") {
std::cout << "Found texture coordinates\n";
switch (attribAccessor.type) {
case TINYGLTF_TYPE_VEC2: {
std::cout << "TEXTCOORD is VEC2\n";
switch (attribAccessor.componentType) {
case TINYGLTF_COMPONENT_TYPE_FLOAT: {
std::cout << "TEXTCOORD is FLOAT\n";
v2fArray uvs(
arrayAdapter<v2f>(dataPtr, count, byte_stride));
for (size_t i{0}; i < indices.size() / 3; ++i) {
// get the i'th triange's indexes
auto f0 = indices[3 * i + 0];
auto f1 = indices[3 * i + 1];
auto f2 = indices[3 * i + 2];
// get the texture coordinates for each triangle's
// vertices
v2f uv0, uv1, uv2;
uv0 = uvs[f0];
uv1 = uvs[f1];
uv2 = uvs[f2];
// push them in order into the mesh data
loadedMesh.facevarying_uvs.push_back(uv0.x);
loadedMesh.facevarying_uvs.push_back(uv0.y);
loadedMesh.facevarying_uvs.push_back(uv1.x);
loadedMesh.facevarying_uvs.push_back(uv1.y);
loadedMesh.facevarying_uvs.push_back(uv2.x);
loadedMesh.facevarying_uvs.push_back(uv2.y);
}
} break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
std::cout << "TEXTCOORD is DOUBLE\n";
v2dArray uvs(
arrayAdapter<v2d>(dataPtr, count, byte_stride));
for (size_t i{0}; i < indices.size() / 3; ++i) {
// get the i'th triange's indexes
auto f0 = indices[3 * i + 0];
auto f1 = indices[3 * i + 1];
auto f2 = indices[3 * i + 2];
v2d uv0, uv1, uv2;
uv0 = uvs[f0];
uv1 = uvs[f1];
uv2 = uvs[f2];
loadedMesh.facevarying_uvs.push_back(uv0.x);
loadedMesh.facevarying_uvs.push_back(uv0.y);
loadedMesh.facevarying_uvs.push_back(uv1.x);
loadedMesh.facevarying_uvs.push_back(uv1.y);
loadedMesh.facevarying_uvs.push_back(uv2.x);
loadedMesh.facevarying_uvs.push_back(uv2.y);
}
} break;
default:
std::cerr << "unrecognized vector type for UV";
}
} break;
default:
std::cerr << "unreconized componant type for UV";
}
}
}
}
break;
default:
std::cerr << "primitive mode not implemented";
break;
// These aren't triangles:
case TINYGLTF_MODE_POINTS:
case TINYGLTF_MODE_LINE:
case TINYGLTF_MODE_LINE_LOOP:
std::cerr << "primitive is not triangle based, ignoring";
}
}
// bbox :
v3f bCenter;
bCenter.x = 0.5f * (pMax.x - pMin.x) + pMin.x;
bCenter.y = 0.5f * (pMax.y - pMin.y) + pMin.y;
bCenter.z = 0.5f * (pMax.z - pMin.z) + pMin.z;
for (size_t v = 0; v < loadedMesh.vertices.size() / 3; v++) {
loadedMesh.vertices[3 * v + 0] -= bCenter.x;
loadedMesh.vertices[3 * v + 1] -= bCenter.y;
loadedMesh.vertices[3 * v + 2] -= bCenter.z;
}
loadedMesh.pivot_xform[0][0] = 1.0f;
loadedMesh.pivot_xform[0][1] = 0.0f;
loadedMesh.pivot_xform[0][2] = 0.0f;
loadedMesh.pivot_xform[0][3] = 0.0f;
loadedMesh.pivot_xform[1][0] = 0.0f;
loadedMesh.pivot_xform[1][1] = 1.0f;
loadedMesh.pivot_xform[1][2] = 0.0f;
loadedMesh.pivot_xform[1][3] = 0.0f;
loadedMesh.pivot_xform[2][0] = 0.0f;
loadedMesh.pivot_xform[2][1] = 0.0f;
loadedMesh.pivot_xform[2][2] = 1.0f;
loadedMesh.pivot_xform[2][3] = 0.0f;
loadedMesh.pivot_xform[3][0] = bCenter.x;
loadedMesh.pivot_xform[3][1] = bCenter.y;
loadedMesh.pivot_xform[3][2] = bCenter.z;
loadedMesh.pivot_xform[3][3] = 1.0f;
// TODO handle materials
for (size_t i{0}; i < loadedMesh.faces.size(); ++i)
loadedMesh.material_ids.push_back(materials->at(0).id);
meshes->push_back(loadedMesh);
ret = true;
}
return ret;
}
}
} // namespace example

View File

@ -1,19 +1,166 @@
#ifndef EXAMPLE_GLTF_LOADER_H_
#define EXAMPLE_GLTF_LOADER_H_
#include <vector>
#include <stdexcept>
#include <string>
#include <vector>
#include "mesh.h"
#include "material.h"
#include "mesh.h"
namespace example {
/// Adapts an array of bytes to an array of T. Will advace of byte_stride each
/// elements.
template <typename T>
struct arrayAdapter {
/// Pointer to the bytes
const unsigned char *dataPtr;
/// Number of elements in the array
const size_t elemCount;
/// Stride in bytes between two elements
const size_t stride;
/// Construct an array adapter.
/// \param ptr Pointer to the start of the data, with offset applied
/// \param count Number of elements in the array
/// \param byte_stride Stride betweens elements in the array
arrayAdapter(const unsigned char *ptr, size_t count, size_t byte_stride)
: dataPtr(ptr), elemCount(count), stride(byte_stride) {}
/// Returns a *copy* of a single element. Can't be used to modify it.
T operator[](size_t pos) const {
if (pos >= elemCount)
throw std::out_of_range(
"Tried to access beyond the last element of an array adapter with "
"count " +
std::to_string(elemCount) + " while getting elemnet number " +
std::to_string(pos));
return *(reinterpret_cast<const T *>(dataPtr + pos * stride));
}
};
/// Interface of any adapted array that returns ingeger data
struct intArrayBase {
virtual ~intArrayBase() = default;
virtual unsigned int operator[](size_t) const = 0;
virtual size_t size() const = 0;
};
/// Interface of any adapted array that returns float data
struct floatArrayBase {
virtual ~floatArrayBase() = default;
virtual float operator[](size_t) const = 0;
virtual size_t size() const = 0;
};
/// An array that loads interger types, returns them as int
template <class T>
struct intArray : public intArrayBase {
arrayAdapter<T> adapter;
intArray(const arrayAdapter<T> &a) : adapter(a) {}
unsigned int operator[](size_t position) const override {
return static_cast<unsigned int>(adapter[position]);
}
size_t size() const override { return adapter.elemCount; }
};
template <class T>
struct floatArray : public floatArrayBase {
arrayAdapter<T> adapter;
floatArray(const arrayAdapter<T> &a) : adapter(a) {}
float operator[](size_t position) const override {
return static_cast<float>(adapter[position]);
}
size_t size() const override { return adapter.elemCount; }
};
#pragma pack(push, 1)
template <typename T>
struct v2 {
T x, y;
};
/// 3D vector of floats without padding
template <typename T>
struct v3 {
T x, y, z;
};
/// 4D vector of floats without padding
template <typename T>
struct v4 {
T x, y, z, w;
};
#pragma pack(pop)
using v2f = v2<float>;
using v3f = v3<float>;
using v4f = v4<float>;
using v2d = v2<double>;
using v3d = v3<double>;
using v4d = v4<double>;
struct v2fArray {
arrayAdapter<v2f> adapter;
v2fArray(const arrayAdapter<v2f> &a) : adapter(a) {}
v2f operator[](size_t position) const { return adapter[position]; }
size_t size() const { return adapter.elemCount; }
};
struct v3fArray {
arrayAdapter<v3f> adapter;
v3fArray(const arrayAdapter<v3f> &a) : adapter(a) {}
v3f operator[](size_t position) const { return adapter[position]; }
size_t size() const { return adapter.elemCount; }
};
struct v4fArray {
arrayAdapter<v4f> adapter;
v4fArray(const arrayAdapter<v4f> &a) : adapter(a) {}
v4f operator[](size_t position) const { return adapter[position]; }
size_t size() const { return adapter.elemCount; }
};
struct v2dArray {
arrayAdapter<v2d> adapter;
v2dArray(const arrayAdapter<v2d> &a) : adapter(a) {}
v2d operator[](size_t position) const { return adapter[position]; }
size_t size() const { return adapter.elemCount; }
};
struct v3dArray {
arrayAdapter<v3d> adapter;
v3dArray(const arrayAdapter<v3d> &a) : adapter(a) {}
v3d operator[](size_t position) const { return adapter[position]; }
size_t size() const { return adapter.elemCount; }
};
struct v4dArray {
arrayAdapter<v4d> adapter;
v4dArray(const arrayAdapter<v4d> &a) : adapter(a) {}
v4d operator[](size_t position) const { return adapter[position]; }
size_t size() const { return adapter.elemCount; }
};
///
/// Loads glTF 2.0 mesh
///
bool LoadGLTF(const std::string &filename, float scale, std::vector<Mesh<float> > *meshes, std::vector<Material> *materials, std::vector<Texture> *textures);
bool LoadGLTF(const std::string &filename, float scale,
std::vector<Mesh<float> > *meshes,
std::vector<Material> *materials, std::vector<Texture> *textures);
}
} // namespace example
#endif // EXAMPLE_GLTF_LOADER_H_
#endif // EXAMPLE_GLTF_LOADER_H_

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -55,10 +55,10 @@ THE SOFTWARE.
#include <cstring>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <sstream>
#include <map>
#include <atomic> // C++11
#include <chrono> // C++11
@ -72,22 +72,23 @@ THE SOFTWARE.
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4201)
#pragma warning(disable : 4201)
#endif
#include "glm/mat4x4.hpp"
#include "glm/gtc/quaternion.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/quaternion.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "glm/mat4x4.hpp"
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#include "gltf-loader.h"
#include "nanosg.h"
#include "obj-loader.h"
#include "render-config.h"
#include "render.h"
#include "gltf-loader.h"
#include "trackball.h"
#ifdef WIN32
@ -96,19 +97,17 @@ THE SOFTWARE.
#endif
#endif
#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)
b3gDefaultOpenGLWindow* window = 0;
b3gDefaultOpenGLWindow *window = 0;
int gWidth = 512;
int gHeight = 512;
int gMousePosX = -1, gMousePosY = -1;
bool gMouseLeftDown = false;
// FIX issue when max passes is done - no modes is switched. pass must be set to
// 0 when mode is changed
int gShowBufferMode_prv = SHOW_BUFFER_COLOR;
int gShowBufferMode = SHOW_BUFFER_COLOR;
bool gTabPressed = false;
bool gShiftPressed = false;
float gShowPositionScale = 1.0f;
@ -128,8 +127,7 @@ std::atomic<bool> gSceneDirty;
example::RenderConfig gRenderConfig;
std::mutex gMutex;
struct RenderLayer
{
struct RenderLayer {
std::vector<float> displayRGBA; // Accumurated image.
std::vector<float> rgba;
std::vector<float> auxRGBA; // Auxiliary buffer
@ -143,14 +141,12 @@ struct RenderLayer
RenderLayer gRenderLayer;
struct Camera
{
struct Camera {
glm::mat4 view;
glm::mat4 projection;
};
struct ManipConfig
{
struct ManipConfig {
glm::vec3 snapTranslation;
glm::vec3 snapRotation;
glm::vec3 snapScale;
@ -201,9 +197,12 @@ void RenderThread() {
// gRenderCancel may be set to true in main loop.
// Render() will repeatedly check this flag inside the rendering loop.
bool ret =
example::Renderer::Render(&gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0), &gRenderLayer.sampleCounts.at(0),
gCurrQuat, gScene, gAsset, gRenderConfig, gRenderCancel);
bool ret = example::Renderer::Render(
&gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0),
&gRenderLayer.sampleCounts.at(0), gCurrQuat, gScene, gAsset,
gRenderConfig, gRenderCancel,
gShowBufferMode // added mode passing
);
if (ret) {
std::lock_guard<std::mutex> guard(gMutex);
@ -219,16 +218,18 @@ void RenderThread() {
}
}
void InitRender(example::RenderConfig* rc) {
void InitRender(example::RenderConfig *rc) {
rc->pass = 0;
rc->max_passes = 128;
gRenderLayer.sampleCounts.resize(rc->width * rc->height);
std::fill(gRenderLayer.sampleCounts.begin(), gRenderLayer.sampleCounts.end(), 0.0);
std::fill(gRenderLayer.sampleCounts.begin(), gRenderLayer.sampleCounts.end(),
0.0);
gRenderLayer.displayRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.displayRGBA.begin(), gRenderLayer.displayRGBA.end(), 0.0);
std::fill(gRenderLayer.displayRGBA.begin(), gRenderLayer.displayRGBA.end(),
0.0);
gRenderLayer.rgba.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.rgba.begin(), gRenderLayer.rgba.end(), 0.0);
@ -237,19 +238,23 @@ void InitRender(example::RenderConfig* rc) {
std::fill(gRenderLayer.auxRGBA.begin(), gRenderLayer.auxRGBA.end(), 0.0);
gRenderLayer.normalRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.normalRGBA.begin(), gRenderLayer.normalRGBA.end(), 0.0);
std::fill(gRenderLayer.normalRGBA.begin(), gRenderLayer.normalRGBA.end(),
0.0);
gRenderLayer.positionRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.positionRGBA.begin(), gRenderLayer.positionRGBA.end(), 0.0);
std::fill(gRenderLayer.positionRGBA.begin(), gRenderLayer.positionRGBA.end(),
0.0);
gRenderLayer.depthRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.depthRGBA.begin(), gRenderLayer.depthRGBA.end(), 0.0);
gRenderLayer.texCoordRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.texCoordRGBA.begin(), gRenderLayer.texCoordRGBA.end(), 0.0);
std::fill(gRenderLayer.texCoordRGBA.begin(), gRenderLayer.texCoordRGBA.end(),
0.0);
gRenderLayer.varyCoordRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.varyCoordRGBA.begin(), gRenderLayer.varyCoordRGBA.end(), 0.0);
std::fill(gRenderLayer.varyCoordRGBA.begin(),
gRenderLayer.varyCoordRGBA.end(), 0.0);
rc->normalImage = &gRenderLayer.normalRGBA.at(0);
rc->positionImage = &gRenderLayer.positionRGBA.at(0);
@ -268,19 +273,18 @@ void checkErrors(std::string desc) {
}
}
static int CreateDisplayTextureGL(const float *data, int width, int height,
int components) {
GLuint id;
glGenTextures(1, &id);
static int CreateDisplayTextureGL(const float *data, int width,
int height, int components) {
GLuint id;
glGenTextures(1, &id);
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLenum format = GL_RGBA;
if (components == 1) {
format = GL_LUMINANCE;
@ -290,15 +294,15 @@ static int CreateDisplayTextureGL(const float *data, int width,
format = GL_RGB;
} else if (components == 4) {
format = GL_RGBA;
} else {
assert(0); // "Invalid components"
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format,
GL_FLOAT, data);
} else {
assert(0); // "Invalid components"
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, GL_FLOAT,
data);
glBindTexture(GL_TEXTURE_2D, last_texture);
return static_cast<int>(id);
}
@ -312,8 +316,10 @@ void keyboardCallback(int keycode, int state) {
// reset.
trackball(gCurrQuat, 0.0f, 0.0f, 0.0f, 0.0f);
// clear buffer.
memset(gRenderLayer.rgba.data(), 0, sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4);
memset(gRenderLayer.sampleCounts.data(), 0, sizeof(int) * gRenderConfig.width * gRenderConfig.height);
memset(gRenderLayer.rgba.data(), 0,
sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4);
memset(gRenderLayer.sampleCounts.data(), 0,
sizeof(int) * gRenderConfig.width * gRenderConfig.height);
} else if (keycode == 9) {
gTabPressed = (state == 1);
} else if (keycode == B3G_SHIFT) {
@ -330,7 +336,6 @@ void keyboardCallback(int keycode, int state) {
}
void mouseMoveCallback(float x, float y) {
if (gMouseLeftDown) {
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
} else {
@ -371,7 +376,7 @@ void mouseButtonCallback(int button, int state, float x, float y) {
(void)y;
ImGui_ImplBtGui_SetMouseButtonState(button, (state == 1));
ImGuiIO& io = ImGui::GetIO();
ImGuiIO &io = ImGui::GetIO();
if (io.WantCaptureMouse || io.WantCaptureKeyboard) {
return;
}
@ -395,7 +400,7 @@ void mouseButtonCallback(int button, int state, float x, float y) {
}
void resizeCallback(float width, float height) {
//GLfloat h = (GLfloat)height / (GLfloat)width;
// GLfloat h = (GLfloat)height / (GLfloat)width;
GLfloat xmax, znear, zfar;
znear = 1.0f;
@ -406,7 +411,7 @@ void resizeCallback(float width, float height) {
gHeight = static_cast<int>(height);
}
inline float pesudoColor(float v, int ch) {
inline float pseudoColor(float v, int ch) {
if (ch == 0) { // red
if (v <= 0.5f)
return 0.f;
@ -471,7 +476,7 @@ void UpdateDisplayTextureGL(GLint tex_id, int width, int height) {
for (size_t i = 0; i < buf.size(); i++) {
float v = (gRenderLayer.depthRGBA[i] - d_min) / d_diff;
if (gShowDepthPeseudoColor) {
buf[i] = pesudoColor(v, i % 4);
buf[i] = pseudoColor(v, i % 4);
} else {
buf[i] = v;
}
@ -491,31 +496,31 @@ void UpdateDisplayTextureGL(GLint tex_id, int width, int height) {
disp.resize(width * height * 4);
{
for (size_t y = 0; y < height; y++) {
memcpy(&disp[4 * (y * width)], &buf[4 * ((height - y - 1) * width)], sizeof(float) * 4 * width);
memcpy(&disp[4 * (y * width)], &buf[4 * ((height - y - 1) * width)],
sizeof(float) * 4 * width);
}
}
glBindTexture(GL_TEXTURE_2D, tex_id);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, disp.data());
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT,
disp.data());
glBindTexture(GL_TEXTURE_2D, 0);
//glRasterPos2i(-1, -1);
//glDrawPixels(width, height, GL_RGBA, GL_FLOAT,
// glRasterPos2i(-1, -1);
// glDrawPixels(width, height, GL_RGBA, GL_FLOAT,
// static_cast<const GLvoid*>(&buf.at(0)));
}
void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& matrix)
{
void EditTransform(const ManipConfig &config, const Camera &camera,
glm::mat4 &matrix) {
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
if (ImGui::IsKeyPressed(90)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69)) mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
if (ImGui::RadioButton("Translate",
mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
@ -524,14 +529,15 @@ void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& m
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(&matrix[0][0], matrixTranslation, matrixRotation, matrixScale);
ImGuizmo::DecomposeMatrixToComponents(&matrix[0][0], matrixTranslation,
matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, &matrix[0][0]);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation,
matrixScale, &matrix[0][0]);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (mCurrentGizmoOperation != ImGuizmo::SCALE) {
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
@ -539,33 +545,32 @@ void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& m
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
if (ImGui::IsKeyPressed(83)) useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
glm::vec3 snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.snapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.snapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.snapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
switch (mCurrentGizmoOperation) {
case ImGuizmo::TRANSLATE:
snap = config.snapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.snapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.snapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuiIO &io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(&camera.view[0][0], &camera.projection[0][0], mCurrentGizmoOperation, mCurrentGizmoMode, &matrix[0][0], NULL, useSnap ? &snap.x : NULL);
ImGuizmo::Manipulate(&camera.view[0][0], &camera.projection[0][0],
mCurrentGizmoOperation, mCurrentGizmoMode, &matrix[0][0],
NULL, useSnap ? &snap.x : NULL);
}
void DrawMesh(const example::Mesh<float> *mesh)
{
void DrawMesh(const example::Mesh<float> *mesh) {
// TODO(LTE): Use vertex array or use display list.
glBegin(GL_TRIANGLES);
@ -579,20 +584,17 @@ void DrawMesh(const example::Mesh<float> *mesh)
glNormal3f(mesh->facevarying_normals[9 * i + 0],
mesh->facevarying_normals[9 * i + 1],
mesh->facevarying_normals[9 * i + 2]);
glVertex3f(mesh->vertices[3 * f0 + 0],
mesh->vertices[3 * f0 + 1],
glVertex3f(mesh->vertices[3 * f0 + 0], mesh->vertices[3 * f0 + 1],
mesh->vertices[3 * f0 + 2]);
glNormal3f(mesh->facevarying_normals[9 * i + 3],
mesh->facevarying_normals[9 * i + 4],
mesh->facevarying_normals[9 * i + 5]);
glVertex3f(mesh->vertices[3 * f1 + 0],
mesh->vertices[3 * f1 + 1],
glVertex3f(mesh->vertices[3 * f1 + 0], mesh->vertices[3 * f1 + 1],
mesh->vertices[3 * f1 + 2]);
glNormal3f(mesh->facevarying_normals[9 * i + 6],
mesh->facevarying_normals[9 * i + 7],
mesh->facevarying_normals[9 * i + 8]);
glVertex3f(mesh->vertices[3 * f2 + 0],
mesh->vertices[3 * f2 + 1],
glVertex3f(mesh->vertices[3 * f2 + 0], mesh->vertices[3 * f2 + 1],
mesh->vertices[3 * f2 + 2]);
}
@ -602,23 +604,19 @@ void DrawMesh(const example::Mesh<float> *mesh)
unsigned int f1 = mesh->faces[3 * i + 1];
unsigned int f2 = mesh->faces[3 * i + 2];
glVertex3f(mesh->vertices[3 * f0 + 0],
mesh->vertices[3 * f0 + 1],
glVertex3f(mesh->vertices[3 * f0 + 0], mesh->vertices[3 * f0 + 1],
mesh->vertices[3 * f0 + 2]);
glVertex3f(mesh->vertices[3 * f1 + 0],
mesh->vertices[3 * f1 + 1],
glVertex3f(mesh->vertices[3 * f1 + 0], mesh->vertices[3 * f1 + 1],
mesh->vertices[3 * f1 + 2]);
glVertex3f(mesh->vertices[3 * f2 + 0],
mesh->vertices[3 * f2 + 1],
glVertex3f(mesh->vertices[3 * f2 + 0], mesh->vertices[3 * f2 + 1],
mesh->vertices[3 * f2 + 2]);
}
}
glEnd();
glEnd();
}
void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node)
{
void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node) {
glPushMatrix();
glMultMatrixf(node.GetLocalXformPtr());
@ -634,8 +632,8 @@ void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node)
}
// Draw scene with OpenGL
void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene, const Camera &camera)
{
void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene,
const Camera &camera) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
@ -649,11 +647,12 @@ void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene, const C
const float light_diffuse[4] = {0.5f, 0.5f, 0.5f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, &light0_pos[0]);
glLightfv(GL_LIGHT0, GL_DIFFUSE, &light_diffuse[0]);
glLightfv(GL_LIGHT0, GL_DIFFUSE, &light_diffuse[0]);
glLightfv(GL_LIGHT1, GL_POSITION, &light1_pos[0]);
glLightfv(GL_LIGHT1, GL_DIFFUSE, &light_diffuse[0]);
glLightfv(GL_LIGHT1, GL_DIFFUSE, &light_diffuse[0]);
const std::vector<nanosg::Node<float, example::Mesh<float> > > &root_nodes = scene.GetNodes();
const std::vector<nanosg::Node<float, example::Mesh<float> > > &root_nodes =
scene.GetNodes();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
@ -675,15 +674,12 @@ void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene, const C
glDisable(GL_LIGHT1);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
}
void BuildSceneItems(
std::vector<std::string> *display_names,
std::vector<std::string> *names,
const nanosg::Node<float, example::Mesh<float> > &node,
int indent)
{
void BuildSceneItems(std::vector<std::string> *display_names,
std::vector<std::string> *names,
const nanosg::Node<float, example::Mesh<float> > &node,
int indent) {
if (node.GetName().empty()) {
// Skip a node with empty name.
return;
@ -699,14 +695,16 @@ void BuildSceneItems(
display_names->push_back(display_name);
names->push_back(node.GetName());
for (size_t i = 0; i < node.GetChildren().size(); i++) {
BuildSceneItems(display_names, names, node.GetChildren()[i], indent + 1);
}
}
int main(int argc, char** argv) {
// tigra: add default material
example::Material default_material;
int main(int argc, char **argv) {
std::string config_filename = "config.json";
if (argc > 1) {
@ -729,13 +727,43 @@ int main(int argc, char** argv) {
std::vector<example::Material> materials;
std::vector<example::Texture> textures;
bool ret = LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale, &meshes, &materials, &textures);
if (!ret) {
std::cerr << "Failed to load glTF [ " << gRenderConfig.gltf_filename << " ]" << std::endl;
return -1;
// tigra: set default material to 95% white diffuse
default_material.diffuse[0] = 0.95f;
default_material.diffuse[1] = 0.95f;
default_material.diffuse[2] = 0.95f;
default_material.specular[0] = 0;
default_material.specular[1] = 0;
default_material.specular[2] = 0;
// Material pushed as first material on the list
materials.push_back(default_material);
if (!gRenderConfig.obj_filename.empty()) {
bool ret = LoadObj(gRenderConfig.obj_filename, gRenderConfig.scene_scale,
&meshes, &materials, &textures);
if (!ret) {
std::cerr << "Failed to load .obj [ " << gRenderConfig.obj_filename
<< " ]" << std::endl;
return -1;
}
}
if (!gRenderConfig.gltf_filename.empty()) {
std::cout << "Found gltf file : " << gRenderConfig.gltf_filename << "\n";
bool ret =
LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale,
&meshes, &materials, &textures);
if (!ret) {
std::cerr << "Failed to load glTF file [ "
<< gRenderConfig.gltf_filename << " ]" << std::endl;
return -1;
}
}
gAsset.materials = materials;
gAsset.default_material = default_material;
gAsset.textures = textures;
for (size_t n = 0; n < meshes.size(); n++) {
@ -744,12 +772,20 @@ int main(int argc, char** argv) {
}
for (size_t n = 0; n < gAsset.meshes.size(); n++) {
nanosg::Node<float, example::Mesh<float> > node(&gAsset.meshes[n]);
// case where the name of a mesh isn't defined in the loaded file
if (gAsset.meshes[n].name.empty()) {
std::string generatedName = "unnamed_" + std::to_string(n);
gAsset.meshes[n].name = generatedName;
meshes[n].name = generatedName;
}
node.SetName(meshes[n].name);
node.SetLocalXform(meshes[n].pivot_xform); // Use mesh's pivot transform as node's local transform.
node.SetLocalXform(meshes[n].pivot_xform); // Use mesh's pivot transform
// as node's local transform.
gNodes.push_back(node);
gScene.AddNode(node);
}
@ -761,11 +797,12 @@ int main(int argc, char** argv) {
float bmin[3], bmax[3];
gScene.GetBoundingBox(bmin, bmax);
printf(" # of nodes : %d\n", int(gNodes.size()));
printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1],
bmin[2]);
printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1],
bmax[2]);
}
std::vector<const char *> imgui_node_names;
std::vector<std::string> display_node_names;
std::vector<std::string> node_names;
@ -773,13 +810,14 @@ int main(int argc, char** argv) {
{
for (size_t i = 0; i < gScene.GetNodes().size(); i++) {
BuildSceneItems(&display_node_names, &node_names, gScene.GetNodes()[i], /* indent */0);
BuildSceneItems(&display_node_names, &node_names, gScene.GetNodes()[i],
/* indent */ 0);
}
// List of strings for imgui.
// Assume nodes in the scene does not change.
for (size_t i = 0; i < display_node_names.size(); i++) {
//std::cout << "name : " << display_node_names[i] << std::endl;
// std::cout << "name : " << display_node_names[i] << std::endl;
imgui_node_names.push_back(display_node_names[i].c_str());
}
@ -788,13 +826,13 @@ int main(int argc, char** argv) {
nanosg::Node<float, example::Mesh<float> > *node;
if (gScene.FindNode(node_names[i], &node)) {
//std::cout << "id : " << i << ", name : " << node_names[i] << std::endl;
// std::cout << "id : " << i << ", name : " << node_names[i] <<
// std::endl;
node_map[i] = node;
}
}
}
window = new b3gDefaultOpenGLWindow;
b3gWindowConstructionInfo ci;
#ifdef USE_OPENGL2
@ -836,9 +874,9 @@ int main(int argc, char** argv) {
ImGui_ImplBtGui_Init(window);
ImGuiIO& io = ImGui::GetIO();
ImGuiIO &io = ImGui::GetIO();
io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f);
// io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f);
glm::mat4 projection(1.0f);
glm::mat4 view(1.0f);
@ -861,7 +899,7 @@ int main(int argc, char** argv) {
ImGuizmo::BeginFrame();
ImGuizmo::Enable(true);
//ImGuiIO &io = ImGui::GetIO();
// ImGuiIO &io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGui::Begin("UI");
@ -897,7 +935,7 @@ int main(int argc, char** argv) {
ImGui::InputFloat("show pos scale", &gShowPositionScale);
ImGui::InputFloat2("show depth range", gShowDepthRange);
ImGui::Checkbox("show depth pesudo color", &gShowDepthPeseudoColor);
ImGui::Checkbox("show depth pseudo color", &gShowDepthPeseudoColor);
}
ImGui::End();
@ -908,38 +946,47 @@ int main(int argc, char** argv) {
checkErrors("clear");
// fix max passes issue
if (gShowBufferMode_prv != gShowBufferMode) {
gRenderConfig.pass = 0;
gShowBufferMode_prv = gShowBufferMode;
}
// Render display window
{
static GLint gl_texid = -1;
if (gl_texid < 0) {
gl_texid = CreateDisplayTextureGL(NULL, gRenderConfig.width, gRenderConfig.height, 4);
gl_texid = CreateDisplayTextureGL(NULL, gRenderConfig.width,
gRenderConfig.height, 4);
}
// Refresh texture until rendering finishes.
if (gRenderConfig.pass < gRenderConfig.max_passes) {
// FIXME(LTE): Do not update GL texture frequently.
UpdateDisplayTextureGL(gl_texid, gRenderConfig.width, gRenderConfig.height);
UpdateDisplayTextureGL(gl_texid, gRenderConfig.width,
gRenderConfig.height);
}
ImGui::Begin("Render");
ImTextureID tex_id = reinterpret_cast<void *>(
static_cast<intptr_t>(gl_texid));
ImTextureID tex_id =
reinterpret_cast<void *>(static_cast<intptr_t>(gl_texid));
ImGui::Image(tex_id, ImVec2(256, 256), ImVec2(0, 0),
ImVec2(1, 1));// Setup camera and draw imguizomo
ImVec2(1, 1)); // Setup camera and draw imguizomo
ImGui::End();
}
// scene graph tree
glm::mat4 node_matrix;
static int node_selected = 0;
static int node_selected_index = 0;
{
ImGui::Begin("Scene");
ImGui::ListBox("Node list", &node_selected, imgui_node_names.data(), imgui_node_names.size(), 16);
node_matrix = glm::make_mat4(node_map[node_selected]->GetLocalXformPtr());
ImGui::ListBox("Node list", &node_selected_index, imgui_node_names.data(),
imgui_node_names.size(), 16);
auto node_selected = node_map.at(node_selected_index);
node_matrix = glm::make_mat4(node_selected->GetLocalXformPtr());
ImGui::End();
}
@ -964,11 +1011,14 @@ int main(int argc, char** argv) {
up[2] = gRenderConfig.up[2];
// NOTE(LTE): w, then (x,y,z) for glm::quat.
glm::quat rot = glm::quat(gCurrQuat[3], gCurrQuat[0], gCurrQuat[1], gCurrQuat[2]);
glm::quat rot =
glm::quat(gCurrQuat[3], gCurrQuat[0], gCurrQuat[1], gCurrQuat[2]);
glm::mat4 rm = glm::mat4_cast(rot);
view = glm::lookAt(eye, lookat, up) * glm::inverse(glm::mat4_cast(rot));
projection = glm::perspective (45.0f, float(window->getWidth()) / float(window->getHeight()), 0.01f, 1000.0f);
projection = glm::perspective(
45.0f, float(window->getWidth()) / float(window->getHeight()), 0.01f,
1000.0f);
camera.view = view;
camera.projection = projection;
@ -978,10 +1028,10 @@ int main(int argc, char** argv) {
float mat[4][4];
memcpy(mat, &node_matrix[0][0], sizeof(float) * 16);
node_map[node_selected]->SetLocalXform(mat);
node_map[node_selected_index]->SetLocalXform(mat);
checkErrors("edit_transform");
ImGui::End();
}

View File

@ -3,9 +3,14 @@
#include <cstdlib>
namespace example {
#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
// TODO(syoyo): Support PBR material.
namespace example {
struct Material {
// float ambient[3];
@ -53,6 +58,7 @@ struct Texture {
int width;
int height;
int components;
int _pad_;
unsigned char* image;
Texture() {

View File

@ -4,6 +4,7 @@
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>
namespace example {
@ -69,9 +70,13 @@ inline void calculate_normal(T Nn[3], const T v0[3], const T v1[3], const T v2[3
template<typename T>
class Mesh {
public:
explicit Mesh(const size_t vertex_stride) :
stride(vertex_stride) {
}
std::string name;
std::vector<T> vertices; /// [xyz] * num_vertices
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
@ -82,6 +87,7 @@ class Mesh {
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. ---

View File

@ -0,0 +1 @@
#include "nanort.h"

View File

@ -44,6 +44,13 @@ THE SOFTWARE.
namespace nanort {
#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
// Parallelized BVH build is not yet fully tested,
// thus turn off if you face a problem when building BVH.
#define NANORT_ENABLE_PARALLEL_BUILD (1)
@ -301,9 +308,7 @@ class real3 {
real3 operator/(const real3 &f2) const {
return real3(x() / f2.x(), y() / f2.y(), z() / f2.z());
}
real3 operator-() const {
return real3(-x(), -y(), -z());
}
real3 operator-() const { return real3(-x(), -y(), -z()); }
T operator[](int i) const { return v[i]; }
T &operator[](int i) { return v[i]; }
@ -330,8 +335,8 @@ template <typename T>
inline real3<T> vnormalize(const real3<T> &rhs) {
real3<T> v = rhs;
T len = vlength(rhs);
if (fabs(len) > 1.0e-6f) {
float inv_len = 1.0f / len;
if (std::fabs(len) > static_cast<T>(1.0e-6)) {
T inv_len = static_cast<T>(1.0) / len;
v.v[0] *= inv_len;
v.v[1] *= inv_len;
v.v[2] *= inv_len;
@ -363,9 +368,7 @@ inline const real *get_vertex_addr(const real *p, const size_t idx,
template <typename T = float>
class Ray {
public:
Ray()
: min_t(static_cast<T>(0.0))
, max_t(std::numeric_limits<T>::max()) {
Ray() : min_t(static_cast<T>(0.0)), max_t(std::numeric_limits<T>::max()) {
org[0] = static_cast<T>(0.0);
org[1] = static_cast<T>(0.0);
org[2] = static_cast<T>(0.0);
@ -374,11 +377,11 @@ class Ray {
dir[2] = static_cast<T>(-1.0);
}
T org[3]; // must set
T dir[3]; // must set
T min_t; // minium ray hit distance.
T max_t; // maximum ray hit distance.
T inv_dir[3]; // filled internally
T org[3]; // must set
T dir[3]; // must set
T min_t; // minimum ray hit distance.
T max_t; // maximum ray hit distance.
T inv_dir[3]; // filled internally
int dir_sign[3]; // filled internally
};
@ -386,6 +389,38 @@ template <typename T = float>
class BVHNode {
public:
BVHNode() {}
BVHNode(const BVHNode &rhs) {
bmin[0] = rhs.bmin[0];
bmin[1] = rhs.bmin[1];
bmin[2] = rhs.bmin[2];
flag = rhs.flag;
bmax[0] = rhs.bmax[0];
bmax[1] = rhs.bmax[1];
bmax[2] = rhs.bmax[2];
axis = rhs.axis;
data[0] = rhs.data[0];
data[1] = rhs.data[1];
}
BVHNode &operator=(const BVHNode &rhs) {
bmin[0] = rhs.bmin[0];
bmin[1] = rhs.bmin[1];
bmin[2] = rhs.bmin[2];
flag = rhs.flag;
bmax[0] = rhs.bmax[0];
bmax[1] = rhs.bmax[1];
bmax[2] = rhs.bmax[2];
axis = rhs.axis;
data[0] = rhs.data[0];
data[1] = rhs.data[1];
return (*this);
}
~BVHNode() {}
T bmin[3];
@ -480,14 +515,26 @@ class BBox {
}
};
template<typename T>
class NodeHit
{
template <typename T>
class NodeHit {
public:
NodeHit()
: t_min(std::numeric_limits<T>::max())
, t_max(-std::numeric_limits<T>::max())
, node_id(static_cast<unsigned int>(-1)) {
: t_min(std::numeric_limits<T>::max()),
t_max(-std::numeric_limits<T>::max()),
node_id(static_cast<unsigned int>(-1)) {}
NodeHit(const NodeHit<T> &rhs) {
t_min = rhs.t_min;
t_max = rhs.t_max;
node_id = rhs.node_id;
}
NodeHit &operator=(const NodeHit<T> &rhs) {
t_min = rhs.t_min;
t_max = rhs.t_max;
node_id = rhs.node_id;
return (*this);
}
~NodeHit() {}
@ -497,9 +544,8 @@ class NodeHit
unsigned int node_id;
};
template<typename T>
class NodeHitComparator
{
template <typename T>
class NodeHitComparator {
public:
inline bool operator()(const NodeHit<T> &a, const NodeHit<T> &b) {
return a.t_min < b.t_min;
@ -512,29 +558,37 @@ class BVHAccel {
BVHAccel() : pad0_(0) { (void)pad0_; }
~BVHAccel() {}
///
/// Build BVH for input primitives.
template<class P, class Pred>
bool Build(const unsigned int num_primitives,
const P &p, const Pred &pred, const BVHBuildOptions<T> &options = BVHBuildOptions<T>());
///
template <class P, class Pred>
bool Build(const unsigned int num_primitives, const P &p, const Pred &pred,
const BVHBuildOptions<T> &options = BVHBuildOptions<T>());
///
/// Get statistics of built BVH tree. Valid after Build()
///
BVHBuildStatistics GetStatistics() const { return stats_; }
///
/// Dump built BVH to the file.
///
bool Dump(const char *filename);
///
/// Load BVH binary
///
bool Load(const char *filename);
void Debug();
///
/// Traverse into BVH along ray and find closest hit point & primitive if
/// found
template<class I, class H>
bool Traverse(const Ray<T> &ray,
const I &intersector,
H *isect,
const BVHTraceOptions& options = BVHTraceOptions()) const;
///
template <class I, class H>
bool Traverse(const Ray<T> &ray, const I &intersector, H *isect,
const BVHTraceOptions &options = BVHTraceOptions()) const;
#if 0
/// Multi-hit ray traversal
@ -551,16 +605,17 @@ class BVHAccel {
/// List up nodes which intersects along the ray.
/// This function is useful for two-level BVH traversal.
///
template<class I>
bool ListNodeIntersections(const Ray<T> &ray,
int max_intersections,
template <class I>
bool ListNodeIntersections(const Ray<T> &ray, int max_intersections,
const I &intersector,
StackVector<NodeHit<T>, 128> *hits) const;
const std::vector<BVHNode<T> > &GetNodes() const { return nodes_; }
const std::vector<unsigned int> &GetIndices() const { return indices_; }
///
/// Returns bounding box of built BVH.
///
void BoundingBox(T bmin[3], T bmax[3]) const {
if (nodes_.empty()) {
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<T>::max();
@ -589,7 +644,7 @@ class BVHAccel {
std::vector<ShallowNodeInfo> shallow_node_infos_;
/// Builds shallow BVH tree recursively.
template<class P, class Pred>
template <class P, class Pred>
unsigned int BuildShallowTree(std::vector<BVHNode<T> > *out_nodes,
unsigned int left_idx, unsigned int right_idx,
unsigned int depth,
@ -598,22 +653,22 @@ class BVHAccel {
#endif
/// Builds BVH tree recursively.
template<class P, class Pred>
template <class P, class Pred>
unsigned int BuildTree(BVHBuildStatistics *out_stat,
std::vector<BVHNode<T> > *out_nodes,
unsigned int left_idx, unsigned int right_idx,
unsigned int depth, const P &p, const Pred &pred);
template<class I>
template <class I>
bool TestLeafNode(const BVHNode<T> &node, const Ray<T> &ray,
const I &intersector) const;
template<class I>
bool TestLeafNodeIntersections(const BVHNode<T> &node,
const Ray<T> &ray,
const int max_intersections,
const I &intersector,
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >, NodeHitComparator<T> > *isect_pq) const;
template <class I>
bool TestLeafNodeIntersections(
const BVHNode<T> &node, const Ray<T> &ray, const int max_intersections,
const I &intersector,
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >,
NodeHitComparator<T> > *isect_pq) const;
#if 0
template<class I, class H, class Comp>
@ -663,7 +718,7 @@ class TriangleSAHPred {
T center = p0[axis] + p1[axis] + p2[axis];
return (center < pos * 3.0);
return (center < pos * static_cast<T>(3.0));
}
private:
@ -762,7 +817,7 @@ class TriangleIntersector {
/// distance `t`,
/// varycentric coordinate `u` and `v`.
/// Returns true if there's intersection.
bool Intersect(T *t_inout, unsigned int prim_index) const {
bool Intersect(T *t_inout, const unsigned int prim_index) const {
if ((prim_index < trace_options_.prim_ids_range[0]) ||
(prim_index >= trace_options_.prim_ids_range[1])) {
return false;
@ -791,8 +846,13 @@ class TriangleIntersector {
T V = Ax * Cy - Ay * Cx;
T W = Bx * Ay - By * Ax;
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#endif
// Fall back to test against edges using double precision.
if (U == 0.0 || V == 0.0 || W == 0.0) {
if (U == static_cast<T>(0.0) || V == static_cast<T>(0.0) || W == static_cast<T>(0.0)) {
double CxBy = static_cast<double>(Cx) * static_cast<double>(By);
double CyBx = static_cast<double>(Cy) * static_cast<double>(Bx);
U = static_cast<T>(CxBy - CyBx);
@ -807,15 +867,19 @@ class TriangleIntersector {
}
if (trace_options_.cull_back_face) {
if (U < 0.0 || V < 0.0 || W < 0.0) return false;
if (U < static_cast<T>(0.0) || V < static_cast<T>(0.0) || W < static_cast<T>(0.0)) return false;
} else {
if ((U < 0.0 || V < 0.0 || W < 0.0) && (U > 0.0 || V > 0.0 || W > 0.0)) {
if ((U < static_cast<T>(0.0) || V < static_cast<T>(0.0) || W < static_cast<T>(0.0)) && (U > static_cast<T>(0.0) || V > static_cast<T>(0.0) || W > static_cast<T>(0.0))) {
return false;
}
}
T det = U + V + W;
if (det == 0.0) return false;
if (det == static_cast<T>(0.0)) return false;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
const T Az = ray_coeff_.Sz * A[ray_coeff_.kz];
const T Bz = ray_coeff_.Sz * B[ray_coeff_.kz];
@ -829,6 +893,10 @@ class TriangleIntersector {
return false;
}
if (tt < t_min_) {
return false;
}
(*t_inout) = tt;
// Use Thomas-Mueller style barycentric coord.
// U + V + W = 1.0 and interp(p) = U * p0 + V * p1 + W * p2
@ -884,6 +952,8 @@ class TriangleIntersector {
trace_options_ = trace_options;
t_min_ = ray.min_t;
u_ = 0.0f;
v_ = 0.0f;
}
@ -901,19 +971,20 @@ class TriangleIntersector {
}
private:
const T *vertices_;
const unsigned int *faces_;
const size_t vertex_stride_bytes_;
mutable real3<T> ray_org_;
mutable RayCoeff ray_coeff_;
mutable BVHTraceOptions trace_options_;
mutable T t_min_;
mutable T t_;
mutable T u_;
mutable T v_;
mutable T t_;
mutable T u_;
mutable T v_;
mutable unsigned int prim_id_;
int _pad_;
};
//
@ -950,7 +1021,8 @@ struct BinBuffer {
template <typename T>
inline T CalculateSurfaceArea(const real3<T> &min, const real3<T> &max) {
real3<T> box = max - min;
return static_cast<T>(2.0) * (box[0] * box[1] + box[1] * box[2] + box[2] * box[0]);
return static_cast<T>(2.0) *
(box[0] * box[1] + box[1] * box[2] + box[2] * box[0]);
}
template <typename T>
@ -994,12 +1066,12 @@ inline void ContributeBinBuffer(BinBuffer *bins, // [out]
real3<T> scene_size, scene_inv_size;
scene_size = scene_max - scene_min;
for (int i = 0; i < 3; ++i) {
assert(scene_size[i] >= 0.0);
assert(scene_size[i] >= static_cast<T>(0.0));
if (scene_size[i] > 0.0) {
if (scene_size[i] > static_cast<T>(0.0)) {
scene_inv_size[i] = bin_size / scene_size[i];
} else {
scene_inv_size[i] = 0.0;
scene_inv_size[i] = static_cast<T>(0.0);
}
}
@ -1057,7 +1129,8 @@ inline T SAH(size_t ns1, T leftArea, size_t ns2, T rightArea, T invS, T Taabb,
T Ttri) {
T sah;
sah = static_cast<T>(2.0) * Taabb + (leftArea * invS) * static_cast<T>(ns1) * Ttri +
sah = static_cast<T>(2.0) * Taabb +
(leftArea * invS) * static_cast<T>(ns1) * Ttri +
(rightArea * invS) * static_cast<T>(ns2) * Ttri;
return sah;
@ -1079,22 +1152,22 @@ inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz
T pos;
T minCost[3];
T costTtri = 1.0f - costTaabb;
T costTtri = static_cast<T>(1.0) - costTaabb;
(*minCostAxis) = 0;
bsize = bmax - bmin;
bstep = bsize * (1.0f / bins->bin_size);
bstep = bsize * (static_cast<T>(1.0) / bins->bin_size);
saTotal = CalculateSurfaceArea(bmin, bmax);
T invSaTotal = 0.0f;
T invSaTotal = static_cast<T>(0.0);
if (saTotal > kEPS) {
invSaTotal = 1.0f / saTotal;
invSaTotal = static_cast<T>(1.0) / saTotal;
}
for (int j = 0; j < 3; ++j) {
//
// Compute SAH cost for right side of each cell of the bbox.
// Compute SAH cost for the right side of each cell of the bbox.
// Exclude both extreme side of the bbox.
//
// i: 0 1 2 3
@ -1103,7 +1176,7 @@ inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz
// +----+----+----+----+----+
//
T minCostPos = bmin[j] + 0.5f * bstep[j];
T minCostPos = bmin[j] + static_cast<T>(1.0) * bstep[j];
minCost[j] = std::numeric_limits<T>::max();
left = 0;
@ -1127,7 +1200,7 @@ inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz
// +1 for i since we want a position on right side of the cell.
//
pos = bmin[j] + (i + 0.5f) * bstep[j];
pos = bmin[j] + (i + static_cast<T>(1.0)) * bstep[j];
bmaxLeft[j] = pos;
bminRight[j] = pos;
@ -1280,11 +1353,14 @@ inline void GetBoundingBox(real3<T> *bmin, real3<T> *bmax,
//
#if NANORT_ENABLE_PARALLEL_BUILD
template <typename T> template<class P, class Pred>
unsigned int BVHAccel<T>::BuildShallowTree(
std::vector<BVHNode<T> > *out_nodes, unsigned int left_idx,
unsigned int right_idx, unsigned int depth, unsigned int max_shallow_depth,
const P &p, const Pred &pred) {
template <typename T>
template <class P, class Pred>
unsigned int BVHAccel<T>::BuildShallowTree(std::vector<BVHNode<T> > *out_nodes,
unsigned int left_idx,
unsigned int right_idx,
unsigned int depth,
unsigned int max_shallow_depth,
const P &p, const Pred &pred) {
assert(left_idx <= right_idx);
unsigned int offset = static_cast<unsigned int>(out_nodes->size());
@ -1424,11 +1500,13 @@ unsigned int BVHAccel<T>::BuildShallowTree(
}
#endif
template <typename T> template<class P, class Pred>
unsigned int BVHAccel<T>::BuildTree(
BVHBuildStatistics *out_stat, std::vector<BVHNode<T> > *out_nodes,
unsigned int left_idx, unsigned int right_idx, unsigned int depth,
const P &p, const Pred &pred) {
template <typename T>
template <class P, class Pred>
unsigned int BVHAccel<T>::BuildTree(BVHBuildStatistics *out_stat,
std::vector<BVHNode<T> > *out_nodes,
unsigned int left_idx,
unsigned int right_idx, unsigned int depth,
const P &p, const Pred &pred) {
assert(left_idx <= right_idx);
unsigned int offset = static_cast<unsigned int>(out_nodes->size());
@ -1554,10 +1632,10 @@ unsigned int BVHAccel<T>::BuildTree(
return offset;
}
template <typename T> template<class P, class Pred>
bool BVHAccel<T>::Build(unsigned int num_primitives,
const P &p, const Pred &pred,
const BVHBuildOptions<T> &options) {
template <typename T>
template <class P, class Pred>
bool BVHAccel<T>::Build(unsigned int num_primitives, const P &p,
const Pred &pred, const BVHBuildOptions<T> &options) {
options_ = options;
stats_ = BVHBuildStatistics();
@ -1566,6 +1644,10 @@ bool BVHAccel<T>::Build(unsigned int num_primitives,
assert(options_.bin_size > 1);
if (num_primitives == 0) {
return false;
}
unsigned int n = num_primitives;
//
@ -1697,15 +1779,9 @@ void BVHAccel<T>::Debug() {
}
for (size_t i = 0; i < nodes_.size(); i++) {
printf("node[%d] : bmin %f, %f, %f, bmax %f, %f, %f\n",
int(i),
nodes_[i].bmin[0],
nodes_[i].bmin[1],
nodes_[i].bmin[1],
nodes_[i].bmax[0],
nodes_[i].bmax[1],
nodes_[i].bmax[1]);
printf("node[%d] : bmin %f, %f, %f, bmax %f, %f, %f\n", int(i),
nodes_[i].bmin[0], nodes_[i].bmin[1], nodes_[i].bmin[1],
nodes_[i].bmax[0], nodes_[i].bmax[1], nodes_[i].bmax[1]);
}
}
@ -1713,7 +1789,7 @@ template <typename T>
bool BVHAccel<T>::Dump(const char *filename) {
FILE *fp = fopen(filename, "wb");
if (!fp) {
//fprintf(stderr, "[BVHAccel] Cannot write a file: %s\n", filename);
// fprintf(stderr, "[BVHAccel] Cannot write a file: %s\n", filename);
return false;
}
@ -1744,7 +1820,7 @@ template <typename T>
bool BVHAccel<T>::Load(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) {
//fprintf(stderr, "Cannot open file: %s\n", filename);
// fprintf(stderr, "Cannot open file: %s\n", filename);
return false;
}
@ -1815,10 +1891,10 @@ inline bool IntersectRayAABB(T *tminOut, // [out]
return false; // no hit
}
template <typename T> template<class I>
inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node,
const Ray<T> &ray,
const I &intersector) const {
template <typename T>
template <class I>
inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node, const Ray<T> &ray,
const I &intersector) const {
bool hit = false;
unsigned int num_primitives = node.data[0];
@ -1841,20 +1917,18 @@ inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node,
T local_t = t;
if (intersector.Intersect(&local_t, prim_idx)) {
if (local_t > ray.min_t) {
// Update isect state
t = local_t;
// Update isect state
t = local_t;
intersector.Update(t, prim_idx);
hit = true;
}
intersector.Update(t, prim_idx);
hit = true;
}
}
return hit;
}
#if 0 // TODO(LTE): Implement
#if 0 // TODO(LTE): Implement
template <typename T> template<class I, class H, class Comp>
bool BVHAccel<T>::MultiHitTestLeafNode(
std::priority_queue<H, std::vector<H>, Comp> *isect_pq,
@ -1928,11 +2002,10 @@ bool BVHAccel<T>::MultiHitTestLeafNode(
}
#endif
template <typename T> template<class I, class H>
bool BVHAccel<T>::Traverse(const Ray<T> &ray,
const I &intersector,
H *isect,
const BVHTraceOptions &options) const {
template <typename T>
template <class I, class H>
bool BVHAccel<T>::Traverse(const Ray<T> &ray, const I &intersector, H *isect,
const BVHTraceOptions &options) const {
const int kMaxStackDepth = 512;
T hit_t = ray.max_t;
@ -1999,13 +2072,14 @@ bool BVHAccel<T>::Traverse(const Ray<T> &ray,
return hit;
}
template <typename T> template<class I>
inline bool BVHAccel<T>::TestLeafNodeIntersections(const BVHNode<T> &node,
const Ray<T> &ray,
const int max_intersections,
const I &intersector,
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >, NodeHitComparator<T> > *isect_pq) const {
template <typename T>
template <class I>
inline bool BVHAccel<T>::TestLeafNodeIntersections(
const BVHNode<T> &node, const Ray<T> &ray, const int max_intersections,
const I &intersector,
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >,
NodeHitComparator<T> > *isect_pq) const {
bool hit = false;
unsigned int num_primitives = node.data[0];
@ -2043,7 +2117,6 @@ inline bool BVHAccel<T>::TestLeafNodeIntersections(const BVHNode<T> &node,
isect_pq->pop();
isect_pq->push(isect);
}
}
}
@ -2052,11 +2125,11 @@ inline bool BVHAccel<T>::TestLeafNodeIntersections(const BVHNode<T> &node,
return hit;
}
template <typename T> template<class I>
bool BVHAccel<T>::ListNodeIntersections(const Ray<T> &ray,
int max_intersections,
const I &intersector,
StackVector<NodeHit<T>, 128> *hits) const {
template <typename T>
template <class I>
bool BVHAccel<T>::ListNodeIntersections(
const Ray<T> &ray, int max_intersections, const I &intersector,
StackVector<NodeHit<T>, 128> *hits) const {
const int kMaxStackDepth = 512;
T hit_t = ray.max_t;
@ -2066,14 +2139,19 @@ bool BVHAccel<T>::ListNodeIntersections(const Ray<T> &ray,
node_stack[0] = 0;
// Stores furthest intersection at top
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >, NodeHitComparator<T> > isect_pq;
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >,
NodeHitComparator<T> >
isect_pq;
(*hits)->clear();
int dir_sign[3];
dir_sign[0] = ray.dir[0] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
dir_sign[1] = ray.dir[1] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
dir_sign[2] = ray.dir[2] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
dir_sign[0] =
ray.dir[0] < static_cast<T>(0.0) ? 1 : 0;
dir_sign[1] =
ray.dir[1] < static_cast<T>(0.0) ? 1 : 0;
dir_sign[2] =
ray.dir[2] < static_cast<T>(0.0) ? 1 : 0;
// @fixme { Check edge case; i.e., 1/0 }
real3<T> ray_inv_dir;
@ -2108,7 +2186,8 @@ bool BVHAccel<T>::ListNodeIntersections(const Ray<T> &ray,
} else { // leaf node
if (hit) {
TestLeafNodeIntersections(node, ray, max_intersections, intersector, &isect_pq);
TestLeafNodeIntersections(node, ray, max_intersections, intersector,
&isect_pq);
}
}
}
@ -2132,7 +2211,7 @@ bool BVHAccel<T>::ListNodeIntersections(const Ray<T> &ray,
return false;
}
#if 0 // TODO(LTE): Implement
#if 0 // TODO(LTE): Implement
template <typename T> template<class I, class H, class Comp>
bool BVHAccel<T>::MultiHitTraverse(const Ray<T> &ray,
int max_intersections,
@ -2225,6 +2304,10 @@ bool BVHAccel<T>::MultiHitTraverse(const Ray<T> &ray,
}
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace nanort
#endif // NANORT_H_

View File

@ -31,35 +31,36 @@ THE SOFTWARE.
#endif
#endif
#include <iostream>
#include <limits>
#include <vector>
#include <iostream>
#include "nanort.h"
namespace nanosg {
template<class T>
template <class T>
class PrimitiveInterface;
template<class T>
class PrimitiveInterface{
public:
void print(){ static_cast<T &>(this)->print(); }
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; }
public:
void print() { std::cout << "Sphere" << std::endl; }
};
// 4x4 matrix
template <typename T> class Matrix {
public:
template <typename T>
class Matrix {
public:
Matrix();
~Matrix();
static void Print(T m[4][4]) {
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]);
}
@ -232,18 +233,15 @@ public:
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]) {
// 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];
@ -286,7 +284,6 @@ static void XformBoundingBox(T xbmin[3], // out
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]);
@ -297,48 +294,45 @@ static void XformBoundingBox(T xbmin[3], // out
}
}
template<typename T>
struct Intersection
{
template <typename T>
struct Intersection {
// required fields.
T t; // hit distance
unsigned int prim_id; // primitive ID of the hit
float u;
float v;
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
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
{
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();
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();
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);
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() {}
~Node() {}
void Copy(const type &rhs) {
Matrix<T>::Copy(local_xform_, rhs.local_xform_);
@ -369,53 +363,44 @@ class Node
children_ = rhs.children_;
}
Node(const type &rhs) {
Copy(rhs);
}
Node(const type &rhs) { Copy(rhs); }
const type &operator=(const type &rhs) {
Copy(rhs);
return (*this);
}
void SetName(const std::string &name) {
name_ = name;
}
void SetName(const std::string &name) { name_ = name; }
const std::string &GetName() const {
return name_;
}
const std::string &GetName() const { return name_; }
///
/// Add child node.
///
void AddChild(const type &child) {
children_.push_back(child);
}
void AddChild(const type &child) { children_.push_back(child); }
///
/// Get chidren
///
const std::vector<type> &GetChildren() const {
return children_;
}
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)) {
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(), sizeof(float) * 3);
nanort::TriangleSAHPred<float> triangle_pred(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3);
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(int(mesh_->faces.size()) / 3, triangle_mesh, triangle_pred);
bool ret =
accel_.Build(static_cast<unsigned int>(mesh_->faces.size()) / 3,
triangle_mesh, triangle_pred);
// Update local bbox.
if (ret) {
@ -433,7 +418,7 @@ class Node
Matrix<T>::Copy(inv_xform_, xform_);
Matrix<T>::Inverse(inv_xform_);
// Clear translation, then inverse(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);
@ -448,67 +433,61 @@ class Node
for (size_t i = 0; i < children_.size(); i++) {
children_[i].Update(xform_);
}
}
}
///
/// Set local transformation.
///
///
/// 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 *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];
}
const T *GetXformPtr() const {
return &xform_[0][0];
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];
}
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)
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(local space)
T lbmin_[3];
T lbmax_[3];
// bounding box after xform(world space)
T xbmin_[3];
T xbmax_[3];
// bounding box after xform(world space)
T xbmin_[3];
T xbmax_[3];
nanort::BVHAccel<T> accel_;
std::string name_;
@ -516,16 +495,16 @@ class Node
const M *mesh_;
std::vector<type> children_;
};
// -------------------------------------------------
// Predefined SAH predicator for cube.
template<typename T, class M>
template <typename T, class M>
class NodeBBoxPred {
public:
NodeBBoxPred(const std::vector<Node<T, M> >* nodes) : axis_(0), pos_(0.0f), nodes_(nodes) {}
NodeBBoxPred(const std::vector<Node<T, M> > *nodes)
: axis_(0), pos_(0.0f), nodes_(nodes) {}
void Set(int axis, float pos) const {
axis_ = axis;
@ -538,8 +517,8 @@ class NodeBBoxPred {
T bmin[3], bmax[3];
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
T center = bmax[axis] - bmin[axis];
return (center < pos);
@ -551,15 +530,15 @@ class NodeBBoxPred {
const std::vector<Node<T, M> > *nodes_;
};
template<typename T, class M>
template <typename T, class M>
class NodeBBoxGeometry {
public:
NodeBBoxGeometry(const std::vector<Node<T, M> >* nodes)
: nodes_(nodes) {}
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 {
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];
@ -570,10 +549,11 @@ class NodeBBoxGeometry {
(*bmax)[2] = b[2];
}
const std::vector<Node<T, M> >* nodes_;
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 {
@ -587,14 +567,13 @@ class NodeBBoxIntersection {
unsigned int prim_id;
};
template<typename T, class M>
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 {
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);
@ -634,7 +613,7 @@ class NodeBBoxIntersector {
/// 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 {
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];
@ -648,40 +627,37 @@ class NodeBBoxIntersector {
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) ? static_cast<T>(1) : static_cast<T>(0);
ray_dir_sign_[1] = ray.dir[1] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
ray_dir_sign_[2] = ray.dir[2] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
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_;
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
{
template <typename T, class M>
class Scene {
public:
Scene() {
Scene() {
bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits<T>::max();
bmax_[0] = bmax_[1] = bmax_[2] = -std::numeric_limits<T>::max();
}
~Scene() {};
~Scene() {}
///
/// Add intersectable node to the scene.
///
bool AddNode(const Node<T, M> &node) {
bool AddNode(const Node<T, M> &node) {
nodes_.push_back(node);
return true;
}
const std::vector<Node<T, M> > &GetNodes() const {
return nodes_;
}
const std::vector<Node<T, M> > &GetNodes() const { return nodes_; }
bool FindNode(const std::string &name, Node<T, M> **found_node) {
if (!found_node) {
@ -702,11 +678,15 @@ class Scene
return false;
}
///
/// Commit the scene. Must be called before tracing rays into the scene.
///
bool Commit() {
// the scene should contains something
if (nodes_.size() == 0) {
std::cerr << "You are attempting to commit an empty scene!\n";
return false;
}
// Update nodes.
for (size_t i = 0; i < nodes_.size(); i++) {
@ -719,21 +699,26 @@ class Scene
// 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.
// 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);
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();
// toplevel_accel_.Debug();
if (ret) {
toplevel_accel_.BoundingBox(bmin_, bmax_);
toplevel_accel_.BoundingBox(bmin_, bmax_);
} else {
// Set invalid bbox value.
bmin_[0] = std::numeric_limits<T>::max();
@ -766,51 +751,54 @@ class 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 {
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;
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);
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;
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.
// 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);
// 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
// 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(), sizeof(T) * 3);
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);
bool hit = node.GetAccel().Traverse(local_ray, triangle_intersector,
&local_isect);
if (hit) {
// Calulcate hit distance in world coordiante.
@ -828,6 +816,7 @@ class Scene
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;
@ -839,36 +828,30 @@ class Scene
isect->v = local_isect.v;
// TODO(LTE): Implement
T Ng[3], Ns[3]; // geometric normal, shading normal.
T Ng[3], Ns[3]; // geometric normal, shading normal.
node.GetMesh()->GetNormal(Ng, Ns, isect->prim_id, isect->u,
isect->v);
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);
}
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) {
///
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;
@ -882,19 +865,18 @@ class Scene
}
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_;
std::vector<Node<T, M> > nodes_;
};
} // namespace nanosg
} // namespace nanosg
#endif // NANOSG_H_
#endif // NANOSG_H_

View File

@ -0,0 +1,458 @@
#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
#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

@ -1,12 +1,19 @@
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 = {
"stbi-impl.cc",
"main.cc",
"render.cc",
"render-config.cc",
"obj-loader.cc",
"gltf-loader.cc",
"matrix.cc",
"../common/trackball.cc",
@ -16,7 +23,7 @@ sources = {
"../common/imgui/ImGuizmo.cpp",
}
solution "RaytraceSolution"
solution "NanoSGSolution"
configurations { "Release", "Debug" }
if os.is("Windows") then
@ -53,7 +60,6 @@ solution "RaytraceSolution"
end
if os.is("Windows") then
flags { "FatalCompileWarnings" }
warnings "Extra" -- /W4
defines { "NOMINMAX" }

View File

@ -29,12 +29,24 @@ bool LoadRenderConfig(example::RenderConfig* config, const char* filename) {
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("gltf_filename") != o.end()) {
if (o["gltf_filename"].is<std::string>()) {
config->gltf_filename = o["gltf_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>()) {
@ -107,4 +119,4 @@ bool LoadRenderConfig(example::RenderConfig* config, const char* filename) {
return true;
}
}
} // namespace example

View File

@ -28,7 +28,9 @@ typedef struct {
float *varycoordImage;
// Scene input info
std::string obj_filename;
std::string gltf_filename;
std::string eson_filename;
float scene_scale;
} RenderConfig;

View File

@ -200,6 +200,7 @@ void BuildCameraFrame(float3* origin, float3* corner, float3* u, float3* v,
}
}
#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) {
@ -217,9 +218,12 @@ nanort::Ray<float> GenerateRay(const float3& origin, const float3& corner,
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;
@ -235,10 +239,15 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
const nanosg::Scene<float, example::Mesh<float>> &scene,
const example::Asset &asset,
const RenderConfig& config,
std::atomic<bool>& cancelFlag) {
std::atomic<bool>& cancelFlag,
int &_showBufferMode
) {
//if (!gAccel.IsValid()) {
// return false;
//}
int width = config.width;
int height = config.height;
@ -300,6 +309,19 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
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);
@ -320,6 +342,9 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
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] =
@ -410,26 +435,49 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
// 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];
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];
}
float specular_col[3];
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];
}
//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));

View File

@ -3,6 +3,15 @@
#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"
@ -13,6 +22,9 @@ namespace example {
struct Asset {
std::vector<Mesh<float> > meshes;
std::vector<Material> materials;
//tigra: add default material
Material default_material;
std::vector<Texture> textures;
};
@ -23,7 +35,10 @@ class 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);
const nanosg::Scene<float, Mesh<float>> &scene, const Asset &asset, const RenderConfig& config,
std::atomic<bool>& cancel_flag,
int& _showBufferMode
);
};
};

View File

@ -0,0 +1,3 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

View File

@ -0,0 +1,357 @@
# 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)/gltf-loader.o \
$(OBJDIR)/main.o \
$(OBJDIR)/matrix.o \
$(OBJDIR)/obj-loader.o \
$(OBJDIR)/render-config.o \
$(OBJDIR)/render.o \
$(OBJDIR)/stbi-impl.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)/gltf-loader.o: gltf-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)/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 "$<"
$(OBJDIR)/stbi-impl.o: stbi-impl.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