Merge branch 'devel' of github.com:syoyo/tinygltf into devel

This commit is contained in:
Syoyo Fujita 2018-03-21 18:52:53 +09:00
commit ba809ff52f
46 changed files with 15604 additions and 5036 deletions

22
.gitignore vendored
View File

@ -9,6 +9,28 @@ install_manifest.txt
compile_commands.json compile_commands.json
CTestTestfile.cmake 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 # Prerequisites
*.d *.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" env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - ./.travis-before-install.sh
script: script:
@ -42,3 +42,6 @@ script:
- ${CC} -v - ${CC} -v
- ${CXX} ${EXTRA_CXXFLAGS} -std=c++11 -Wall -g -o loader_example loader_example.cc - ${CXX} ${EXTRA_CXXFLAGS} -std=c++11 -Wall -g -o loader_example loader_example.cc
- ./loader_example ./models/Cube/Cube.gltf - ./loader_example ./models/Cube/Cube.gltf
- cd examples/raytrace
- ../../premake5 gmake
- make

View File

@ -107,6 +107,7 @@ if (!ret) {
## Compile options ## Compile options
* `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF. * `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF.
* `TINYGLTF_NO_STB_IMAGE` : Do not load images with stb_image. Instead use `TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)` to set a callback for loading images.
### Saving gltTF 2.0 model ### Saving gltTF 2.0 model

View File

@ -13,3 +13,6 @@ configuration: Release
build: build:
parallel: true parallel: true
project: TinyGLTFSolution.sln 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

@ -21,7 +21,9 @@
// SOFTWARE. // SOFTWARE.
#include "imgui.h" #include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui_internal.h" #include "imgui_internal.h"
#include "ImGuizmo.h" #include "ImGuizmo.h"
@ -65,6 +67,8 @@ namespace ImGuizmo
//template <typename T> T LERP(T x, T y, float z) { return (x + (y - x)*z); } //template <typename T> T LERP(T x, T y, float z) { return (x + (y - x)*z); }
template <typename T> T Clamp(T x, T y, T z) { return ((x<y) ? y : ((x>z) ? z : x)); } template <typename T> T Clamp(T x, T y, T z) { return ((x<y) ? y : ((x>z) ? z : x)); }
template <typename T> T max(T x, T y) { return (x > y) ? x : y; } template <typename T> T max(T x, T y) { return (x > y) ? x : y; }
template <typename T> T min(T x, T y) { return (x < y) ? x : y; }
template <typename T> bool IsWithin(T x, T y, T z) { return (x>=y) && (x<=z); }
struct matrix_t; struct matrix_t;
struct vec_t struct vec_t
@ -86,7 +90,7 @@ namespace ImGuizmo
vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; }
vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; }
vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; } vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; }
vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; } vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; }
vec_t operator * (float f) const; vec_t operator * (float f) const;
vec_t operator - () const; vec_t operator - () const;
@ -99,7 +103,7 @@ namespace ImGuizmo
float LengthSq() const { return (x*x + y*y + z*z); }; float LengthSq() const { return (x*x + y*y + z*z); };
vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); } vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); }
vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); } vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); }
vec_t Abs() const; vec_t Abs() const;
void Cross(const vec_t& v) void Cross(const vec_t& v)
{ {
vec_t res; vec_t res;
@ -147,7 +151,6 @@ namespace ImGuizmo
vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); } vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); }
vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); } vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); }
vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); } vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); }
ImVec2 operator+ (const ImVec2& a, const ImVec2& b) { return ImVec2(a.x + b.x, a.y + b.y); }
vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; } vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; }
vec_t Cross(const vec_t& v1, const vec_t& v2) vec_t Cross(const vec_t& v1, const vec_t& v2)
@ -188,7 +191,7 @@ namespace ImGuizmo
{ {
vec_t right, up, dir, position; vec_t right, up, dir, position;
} v; } v;
vec_t component[4]; vec_t component[4];
}; };
matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); } matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); }
@ -491,7 +494,7 @@ namespace ImGuizmo
SCALE_Y, SCALE_Y,
SCALE_Z, SCALE_Z,
SCALE_XYZ, SCALE_XYZ,
BOUNDS BOUNDS
}; };
struct Context struct Context
@ -520,6 +523,7 @@ namespace ImGuizmo
vec_t mRayOrigin; vec_t mRayOrigin;
vec_t mRayVector; vec_t mRayVector;
float mRadiusSquareCenter;
ImVec2 mScreenSquareCenter; ImVec2 mScreenSquareCenter;
ImVec2 mScreenSquareMin; ImVec2 mScreenSquareMin;
ImVec2 mScreenSquareMax; ImVec2 mScreenSquareMax;
@ -551,23 +555,25 @@ namespace ImGuizmo
bool mBelowPlaneLimit[3]; bool mBelowPlaneLimit[3];
float mAxisFactor[3]; float mAxisFactor[3];
// bounds stretching // bounds stretching
vec_t mBoundsPivot; vec_t mBoundsPivot;
vec_t mBoundsAnchor; vec_t mBoundsAnchor;
vec_t mBoundsPlan; vec_t mBoundsPlan;
vec_t mBoundsLocalPivot; vec_t mBoundsLocalPivot;
int mBoundsBestAxis; int mBoundsBestAxis;
int mBoundsAxis[2]; int mBoundsAxis[2];
bool mbUsingBounds; bool mbUsingBounds;
matrix_t mBoundsMatrix; matrix_t mBoundsMatrix;
// //
int mCurrentOperation; int mCurrentOperation;
float mX = 0.f; float mX = 0.f;
float mY = 0.f; float mY = 0.f;
float mWidth = 0.f; float mWidth = 0.f;
float mHeight = 0.f; float mHeight = 0.f;
float mXMax = 0.f;
float mYMax = 0.f;
}; };
static Context gContext; static Context gContext;
@ -577,7 +583,11 @@ namespace ImGuizmo
static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) }; static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) };
static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 }; static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 };
static const ImU32 selectionColor = 0xFF1080FF;
// Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F
static const ImU32 planeBorderColor[3] = { 0xFFAA0000, 0xFF0000AA, 0xFF00AA00 };
static const ImU32 planeColor[3] = { 0x610000AA, 0x6100AA00, 0x61AA0000 };
static const ImU32 selectionColor = 0x8A1080FF;
static const ImU32 inactiveColor = 0x99999999; static const ImU32 inactiveColor = 0x99999999;
static const ImU32 translationLineColor = 0xAAAAAAAA; static const ImU32 translationLineColor = 0xAAAAAAAA;
static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "X : %5.3f Y : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" }; static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "X : %5.3f Y : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" };
@ -598,18 +608,16 @@ namespace ImGuizmo
static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat) static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat)
{ {
//ImGuiIO& io = ImGui::GetIO();
vec_t trans; vec_t trans;
trans.TransformPoint(worldPos, mat); trans.TransformPoint(worldPos, mat);
trans *= 0.5f / trans.w; trans *= 0.5f / trans.w;
trans += makeVect(0.5f, 0.5f); trans += makeVect(0.5f, 0.5f);
trans.y = 1.f - trans.y; trans.y = 1.f - trans.y;
trans.x *= gContext.mWidth; trans.x *= gContext.mWidth;
trans.y *= gContext.mHeight; trans.y *= gContext.mHeight;
trans.x += gContext.mX; trans.x += gContext.mX;
trans.y += gContext.mY; trans.y += gContext.mY;
return ImVec2(trans.x, trans.y); return ImVec2(trans.x, trans.y);
} }
static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir) static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir)
@ -619,9 +627,9 @@ namespace ImGuizmo
matrix_t mViewProjInverse; matrix_t mViewProjInverse;
mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat); mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat);
float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f; float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f;
float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f; float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f;
rayOrigin.Transform(makeVect(mox, moy, 0.f, 1.f), mViewProjInverse); rayOrigin.Transform(makeVect(mox, moy, 0.f, 1.f), mViewProjInverse);
rayOrigin *= 1.f / rayOrigin.w; rayOrigin *= 1.f / rayOrigin.w;
vec_t rayEnd; vec_t rayEnd;
@ -641,23 +649,38 @@ namespace ImGuizmo
return -(numer / denom); return -(numer / denom);
} }
static bool IsInContextRect( ImVec2 p )
{
return IsWithin( p.x, gContext.mX, gContext.mXMax ) && IsWithin(p.y, gContext.mY, gContext.mYMax );
}
void SetRect(float x, float y, float width, float height) void SetRect(float x, float y, float width, float height)
{ {
gContext.mX = x; gContext.mX = x;
gContext.mY = y; gContext.mY = y;
gContext.mWidth = width; gContext.mWidth = width;
gContext.mHeight = height; gContext.mHeight = height;
gContext.mXMax = gContext.mX + gContext.mWidth;
gContext.mYMax = gContext.mY + gContext.mXMax;
}
void SetDrawlist()
{
gContext.mDrawList = ImGui::GetWindowDrawList();
} }
void BeginFrame() void BeginFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("gizmo", NULL, io.DisplaySize, 0, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus); const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
ImGui::SetNextWindowSize(io.DisplaySize);
gContext.mDrawList = ImGui::GetWindowDrawList();
ImGui::PushStyleColor(ImGuiCol_WindowBg, 0);
ImGui::Begin("gizmo", NULL, flags);
gContext.mDrawList = ImGui::GetWindowDrawList();
ImGui::End(); ImGui::End();
ImGui::PopStyleColor();
} }
bool IsUsing() bool IsUsing()
@ -673,11 +696,11 @@ namespace ImGuizmo
void Enable(bool enable) void Enable(bool enable)
{ {
gContext.mbEnable = enable; gContext.mbEnable = enable;
if (!enable) if (!enable)
{ {
gContext.mbUsing = false; gContext.mbUsing = false;
gContext.mbUsingBounds = false; gContext.mbUsingBounds = false;
} }
} }
static float GetUniform(const vec_t& position, const matrix_t& mat) static float GetUniform(const vec_t& position, const matrix_t& mat)
@ -738,7 +761,8 @@ namespace ImGuizmo
{ {
int colorPlaneIndex = (i + 2) % 3; int colorPlaneIndex = (i + 2) % 3;
colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i]; colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i];
colors[i + 4] = (type == (int)(MOVE_XY + i)) ? selectionColor : directionColor[colorPlaneIndex]; colors[i + 4] = (type == (int)(MOVE_XY + i)) ? selectionColor : planeColor[colorPlaneIndex];
colors[i + 4] = (type == MOVE_SCREEN) ? selectionColor : colors[i + 4];
} }
break; break;
case ROTATE: case ROTATE:
@ -751,8 +775,6 @@ namespace ImGuizmo
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i]; colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i];
break; break;
default:
break;
} }
} }
else else
@ -848,7 +870,6 @@ namespace ImGuizmo
static void DrawRotationGizmo(int type) static void DrawRotationGizmo(int type)
{ {
ImDrawList* drawList = gContext.mDrawList; ImDrawList* drawList = gContext.mDrawList;
//ImGuiIO& io = ImGui::GetIO();
// colors // colors
ImU32 colors[7]; ImU32 colors[7];
@ -857,6 +878,7 @@ namespace ImGuizmo
vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
cameraToModelNormalized.TransformVector(gContext.mModelInverse); cameraToModelNormalized.TransformVector(gContext.mModelInverse);
gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight;
for (int axis = 0; axis < 3; axis++) for (int axis = 0; axis < 3; axis++)
{ {
ImVec2 circlePos[halfCircleSegmentCount]; ImVec2 circlePos[halfCircleSegmentCount];
@ -870,9 +892,14 @@ namespace ImGuizmo
vec_t pos = makeVect(axisPos[axis], axisPos[(axis+1)%3], axisPos[(axis+2)%3]) * gContext.mScreenFactor; vec_t pos = makeVect(axisPos[axis], axisPos[(axis+1)%3], axisPos[(axis+2)%3]) * gContext.mScreenFactor;
circlePos[i] = worldToPos(pos, gContext.mMVP); circlePos[i] = worldToPos(pos, gContext.mMVP);
} }
drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2, true);
float radiusAxis = sqrtf( (ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]) ));
if(radiusAxis > gContext.mRadiusSquareCenter)
gContext.mRadiusSquareCenter = radiusAxis;
drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2);
} }
drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), screenRotateSize * gContext.mHeight, colors[0], 64); drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f);
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
@ -889,8 +916,8 @@ namespace ImGuizmo
pos *= gContext.mScreenFactor; pos *= gContext.mScreenFactor;
circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection); circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection);
} }
drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF, true); drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF);
drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2, true); drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2);
ImVec2 destinationPosOnScreen = circlePos[1]; ImVec2 destinationPosOnScreen = circlePos[1];
char tmps[512]; char tmps[512];
@ -918,9 +945,6 @@ namespace ImGuizmo
ImU32 colors[7]; ImU32 colors[7];
ComputeColors(colors, type, SCALE); ComputeColors(colors, type, SCALE);
// draw screen cirle
drawList->AddCircleFilled(gContext.mScreenSquareCenter, 12.f, colors[0], 32);
// draw // draw
vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
@ -942,17 +966,20 @@ namespace ImGuizmo
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 6.f); drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 3.f);
drawList->AddCircleFilled(worldDirSSpaceNoScale, 10.f, 0xFF404040); drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, 0xFF404040);
} }
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 6.f); drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
drawList->AddCircleFilled(worldDirSSpace, 10.f, colors[i + 1]); drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]);
if (gContext.mAxisFactor[i] < 0.f) if (gContext.mAxisFactor[i] < 0.f)
DrawHatchedAxis(dirPlaneX * scaleDisplay[i]); DrawHatchedAxis(dirPlaneX * scaleDisplay[i]);
} }
} }
// draw screen cirle
drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
@ -978,21 +1005,21 @@ namespace ImGuizmo
static void DrawTranslationGizmo(int type) static void DrawTranslationGizmo(int type)
{ {
ImDrawList* drawList = gContext.mDrawList; ImDrawList* drawList = gContext.mDrawList;
if (!drawList) if (!drawList)
return; return;
// colors // colors
ImU32 colors[7]; ImU32 colors[7];
ComputeColors(colors, type, TRANSLATE); ComputeColors(colors, type, TRANSLATE);
// draw screen quad const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
drawList->AddRectFilled(gContext.mScreenSquareMin, gContext.mScreenSquareMax, colors[0], 2.f);
// draw // draw
for (unsigned int i = 0; i < 3; i++) bool belowAxisLimit = false;
bool belowPlaneLimit = false;
for (unsigned int i = 0; i < 3; ++i)
{ {
vec_t dirPlaneX, dirPlaneY; vec_t dirPlaneX, dirPlaneY;
bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
// draw axis // draw axis
@ -1001,8 +1028,20 @@ namespace ImGuizmo
ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP); ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpace = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP);
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 6.f); drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
// Arrow head begin
ImVec2 dir(origin - worldDirSSpace);
float d = sqrtf(ImLengthSqr(dir));
dir /= d; // Normalize
dir *= 6.0f;
ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector
ImVec2 a(worldDirSSpace + dir);
drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]);
// Arrow head end
if (gContext.mAxisFactor[i] < 0.f) if (gContext.mAxisFactor[i] < 0.f)
DrawHatchedAxis(dirPlaneX); DrawHatchedAxis(dirPlaneX);
} }
@ -1011,15 +1050,18 @@ namespace ImGuizmo
if (belowPlaneLimit) if (belowPlaneLimit)
{ {
ImVec2 screenQuadPts[4]; ImVec2 screenQuadPts[4];
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; ++j)
{ {
vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor; vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP); screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
} }
drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4], true); drawList->AddPolyline(screenQuadPts, 4, planeBorderColor[i], true, 1.0f);
drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
} }
} }
drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
@ -1040,176 +1082,233 @@ namespace ImGuizmo
} }
} }
static bool CanActivate()
{
if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive())
return true;
return false;
}
static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues) static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImDrawList* drawList = gContext.mDrawList; ImDrawList* drawList = gContext.mDrawList;
// compute best projection axis // compute best projection axis
vec_t bestAxisWorldDirection; vec_t axesWorldDirections[3];
int bestAxis = gContext.mBoundsBestAxis; vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f };
if (!gContext.mbUsingBounds) int axes[3];
{ unsigned int numAxes = 1;
axes[0] = gContext.mBoundsBestAxis;
float bestDot = 0.f; int bestAxis = axes[0];
for (unsigned int i = 0; i < 3; i++) if (!gContext.mbUsingBounds)
{ {
vec_t dirPlaneNormalWorld; numAxes = 0;
dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource); float bestDot = 0.f;
dirPlaneNormalWorld.Normalize(); for (unsigned int i = 0; i < 3; i++)
{
vec_t dirPlaneNormalWorld;
dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource);
dirPlaneNormalWorld.Normalize();
float dt = Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld); float dt = fabsf( Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld) );
if (fabsf(dt) >= bestDot) if ( dt >= bestDot )
{ {
bestDot = fabsf(dt); bestDot = dt;
bestAxis = i; bestAxis = i;
bestAxisWorldDirection = dirPlaneNormalWorld; bestAxisWorldDirection = dirPlaneNormalWorld;
} }
}
}
// corners if( dt >= 0.1f )
vec_t aabb[4]; {
axes[numAxes] = i;
axesWorldDirections[numAxes] = dirPlaneNormalWorld;
++numAxes;
}
}
}
int secondAxis = (bestAxis + 1) % 3; if( numAxes == 0 )
int thirdAxis = (bestAxis + 2) % 3; {
axes[0] = bestAxis;
axesWorldDirections[0] = bestAxisWorldDirection;
numAxes = 1;
}
else if( bestAxis != axes[0] )
{
unsigned int bestIndex = 0;
for (unsigned int i = 0; i < numAxes; i++)
{
if( axes[i] == bestAxis )
{
bestIndex = i;
break;
}
}
int tempAxis = axes[0];
axes[0] = axes[bestIndex];
axes[bestIndex] = tempAxis;
vec_t tempDirection = axesWorldDirections[0];
axesWorldDirections[0] = axesWorldDirections[bestIndex];
axesWorldDirections[bestIndex] = tempDirection;
}
for (int i = 0; i < 4; i++) for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex)
{ {
aabb[i][3] = aabb[i][bestAxis] = 0.f; bestAxis = axes[axisIndex];
aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; bestAxisWorldDirection = axesWorldDirections[axisIndex];
aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
}
// draw bounds // corners
unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000; vec_t aabb[4];
matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; int secondAxis = (bestAxis + 1) % 3;
for (int i = 0; i < 4;i++) int thirdAxis = (bestAxis + 2) % 3;
{
ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP);
ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP);
float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
int stepCount = (int)(boundDistance / 10.f);
float stepLength = 1.f / (float)stepCount;
for (int j = 0; j < stepCount; j++)
{
float t1 = (float)j * stepLength;
float t2 = (float)j * stepLength + stepLength * 0.5f;
ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 3.f);
}
vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f;
ImVec2 midBound = worldToPos(midPoint, boundsMVP);
static const float AnchorBigRadius = 8.f;
static const float AnchorSmallRadius = 6.f;
bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
for (int i = 0; i < 4; i++)
unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); {
unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); aabb[i][3] = aabb[i][bestAxis] = 0.f;
aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor); aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor); }
int oppositeIndex = (i + 2) % 4;
// big anchor on corners
if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && io.MouseDown[0])
{
gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource);
gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource);
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
gContext.mBoundsBestAxis = bestAxis;
gContext.mBoundsAxis[0] = secondAxis;
gContext.mBoundsAxis[1] = thirdAxis;
gContext.mBoundsLocalPivot.Set(0.f); // draw bounds
gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis]; unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000;
gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
gContext.mbUsingBounds = true; matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection;
gContext.mBoundsMatrix = gContext.mModelSource; for (int i = 0; i < 4;i++)
} {
// small anchor on middle of segment ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP);
if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && io.MouseDown[0]) ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP);
{ if( !IsInContextRect( worldBound1 ) || !IsInContextRect( worldBound2 ) )
vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; {
gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); continue;
gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); }
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
gContext.mBoundsBestAxis = bestAxis; int stepCount = (int)(boundDistance / 10.f);
int indices[] = { secondAxis , thirdAxis }; stepCount = min( stepCount, 1000 );
gContext.mBoundsAxis[0] = indices[i%2]; float stepLength = 1.f / (float)stepCount;
gContext.mBoundsAxis[1] = -1; for (int j = 0; j < stepCount; j++)
{
float t1 = (float)j * stepLength;
float t2 = (float)j * stepLength + stepLength * 0.5f;
ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 3.f);
}
vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f;
ImVec2 midBound = worldToPos(midPoint, boundsMVP);
static const float AnchorBigRadius = 8.f;
static const float AnchorSmallRadius = 6.f;
bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
gContext.mBoundsLocalPivot.Set(0.f);
gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor);
drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor);
int oppositeIndex = (i + 2) % 4;
// big anchor on corners
if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate())
{
gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource);
gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource);
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
gContext.mBoundsBestAxis = bestAxis;
gContext.mBoundsAxis[0] = secondAxis;
gContext.mBoundsAxis[1] = thirdAxis;
gContext.mbUsingBounds = true; gContext.mBoundsLocalPivot.Set(0.f);
gContext.mBoundsMatrix = gContext.mModelSource; gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis];
} gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
}
if (gContext.mbUsingBounds)
{
matrix_t scale;
scale.SetToIdentity();
// compute projected mouse position on plan gContext.mbUsingBounds = true;
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan); gContext.mBoundsMatrix = gContext.mModelSource;
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; }
// small anchor on middle of segment
if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate())
{
vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f;
gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource);
gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource);
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
gContext.mBoundsBestAxis = bestAxis;
int indices[] = { secondAxis , thirdAxis };
gContext.mBoundsAxis[0] = indices[i%2];
gContext.mBoundsAxis[1] = -1;
// compute a reference and delta vectors base on mouse move gContext.mBoundsLocalPivot.Set(0.f);
vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
// for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length gContext.mbUsingBounds = true;
for (int i = 0; i < 2; i++) gContext.mBoundsMatrix = gContext.mModelSource;
{ }
int axisIndex = gContext.mBoundsAxis[i]; }
if (axisIndex == -1)
continue; if (gContext.mbUsingBounds)
{
matrix_t scale;
scale.SetToIdentity();
float ratioAxis = 1.f; // compute projected mouse position on plan
vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex].Abs(); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan);
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
float dtAxis = axisDir.Dot(referenceVector); // compute a reference and delta vectors base on mouse move
float boundSize = bounds[axisIndex + 3] - bounds[axisIndex]; vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs();
if (dtAxis > FLT_EPSILON) vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
ratioAxis = axisDir.Dot(deltaVector) / dtAxis;
if (snapValues) // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length
{ for (int i = 0; i < 2; i++)
float length = boundSize * ratioAxis; {
ComputeSnap(&length, snapValues[axisIndex]); int axisIndex1 = gContext.mBoundsAxis[i];
if (boundSize > FLT_EPSILON) if (axisIndex1 == -1)
ratioAxis = length / boundSize; continue;
}
scale.component[axisIndex] *= ratioAxis;
}
// transform matrix float ratioAxis = 1.f;
matrix_t preScale, postScale; vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs();
preScale.Translation(-gContext.mBoundsLocalPivot);
postScale.Translation(gContext.mBoundsLocalPivot);
matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix;
*matrix = res;
// info text float dtAxis = axisDir.Dot(referenceVector);
char tmps[512]; float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1];
ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); if (dtAxis > FLT_EPSILON)
ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f" ratioAxis = axisDir.Dot(deltaVector) / dtAxis;
, (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length()
, (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length()
, (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length()
);
drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
}
if (!io.MouseDown[0]) if (snapValues)
gContext.mbUsingBounds = false; {
float length = boundSize * ratioAxis;
ComputeSnap(&length, snapValues[axisIndex1]);
if (boundSize > FLT_EPSILON)
ratioAxis = length / boundSize;
}
scale.component[axisIndex1] *= ratioAxis;
}
// transform matrix
matrix_t preScale, postScale;
preScale.Translation(-gContext.mBoundsLocalPivot);
postScale.Translation(gContext.mBoundsLocalPivot);
matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix;
*matrix = res;
// info text
char tmps[512];
ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f"
, (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length()
, (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length()
, (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length()
);
drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
}
if (!io.MouseDown[0])
gContext.mbUsingBounds = false;
if( gContext.mbUsingBounds )
break;
}
} }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1255,7 +1354,7 @@ namespace ImGuizmo
vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f };
float dist = deltaScreen.Length(); float dist = deltaScreen.Length();
if (dist >= (screenRotateSize - 0.002f) * gContext.mHeight && dist < (screenRotateSize + 0.002f) * gContext.mHeight) if (dist >= (gContext.mRadiusSquareCenter - 1.0f) && dist < (gContext.mRadiusSquareCenter + 1.0f))
type = ROTATE_SCREEN; type = ROTATE_SCREEN;
const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir}; const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir};
@ -1327,6 +1426,7 @@ namespace ImGuizmo
// move // move
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
ImGui::CaptureMouseFromApp();
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
@ -1385,8 +1485,9 @@ namespace ImGuizmo
// find new possible way to move // find new possible way to move
vec_t gizmoHitProportion; vec_t gizmoHitProportion;
type = GetMoveType(&gizmoHitProportion); type = GetMoveType(&gizmoHitProportion);
if (io.MouseDown[0] && type != NONE) if (CanActivate() && type != NONE)
{ {
ImGui::CaptureMouseFromApp();
gContext.mbUsing = true; gContext.mbUsing = true;
gContext.mCurrentOperation = type; gContext.mCurrentOperation = type;
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, -gContext.mCameraDir }; const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, -gContext.mCameraDir };
@ -1409,8 +1510,9 @@ namespace ImGuizmo
{ {
// find new possible way to scale // find new possible way to scale
type = GetScaleType(); type = GetScaleType();
if (io.MouseDown[0] && type != NONE) if (CanActivate() && type != NONE)
{ {
ImGui::CaptureMouseFromApp();
gContext.mbUsing = true; gContext.mbUsing = true;
gContext.mCurrentOperation = type; gContext.mCurrentOperation = type;
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir }; const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir };
@ -1429,6 +1531,7 @@ namespace ImGuizmo
// scale // scale
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
ImGui::CaptureMouseFromApp();
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
@ -1448,7 +1551,7 @@ namespace ImGuizmo
gContext.mScale[axisIndex] = max(ratio, 0.001f); gContext.mScale[axisIndex] = max(ratio, 0.001f);
} }
else else
{ {
float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f; float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f;
gContext.mScale.Set(max(1.f + scaleDelta, 0.001f)); gContext.mScale.Set(max(1.f + scaleDelta, 0.001f));
} }
@ -1492,14 +1595,15 @@ namespace ImGuizmo
if (!gContext.mbUsing) if (!gContext.mbUsing)
{ {
type = GetRotateType(); type = GetRotateType();
if (type == ROTATE_SCREEN) if (type == ROTATE_SCREEN)
{ {
applyRotationLocaly = true; applyRotationLocaly = true;
} }
if (io.MouseDown[0] && type != NONE) if (CanActivate() && type != NONE)
{ {
ImGui::CaptureMouseFromApp();
gContext.mbUsing = true; gContext.mbUsing = true;
gContext.mCurrentOperation = type; gContext.mCurrentOperation = type;
const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
@ -1523,6 +1627,7 @@ namespace ImGuizmo
// rotation // rotation
if (gContext.mbUsing) if (gContext.mbUsing)
{ {
ImGui::CaptureMouseFromApp();
gContext.mRotationAngle = ComputeAngleOnPlan(); gContext.mRotationAngle = ComputeAngleOnPlan();
if (snap) if (snap)
{ {
@ -1627,41 +1732,41 @@ namespace ImGuizmo
int type = NONE; int type = NONE;
if (gContext.mbEnable) if (gContext.mbEnable)
{ {
if (!gContext.mbUsingBounds) if (!gContext.mbUsingBounds)
{ {
switch (operation) switch (operation)
{ {
case ROTATE: case ROTATE:
HandleRotation(matrix, deltaMatrix, type, snap); HandleRotation(matrix, deltaMatrix, type, snap);
break; break;
case TRANSLATE: case TRANSLATE:
HandleTranslation(matrix, deltaMatrix, type, snap); HandleTranslation(matrix, deltaMatrix, type, snap);
break; break;
case SCALE: case SCALE:
HandleScale(matrix, deltaMatrix, type, snap); HandleScale(matrix, deltaMatrix, type, snap);
break; break;
} }
} }
} }
if (localBounds && !gContext.mbUsing) if (localBounds && !gContext.mbUsing)
HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap); HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap);
if (!gContext.mbUsingBounds) if (!gContext.mbUsingBounds)
{ {
switch (operation) switch (operation)
{ {
case ROTATE: case ROTATE:
DrawRotationGizmo(type); DrawRotationGizmo(type);
break; break;
case TRANSLATE: case TRANSLATE:
DrawTranslationGizmo(type); DrawTranslationGizmo(type);
break; break;
case SCALE: case SCALE:
DrawScaleGizmo(type); DrawScaleGizmo(type);
break; break;
} }
} }
} }
void DrawCube(const float *view, const float *projection, float *matrix) void DrawCube(const float *view, const float *projection, float *matrix)
@ -1713,7 +1818,7 @@ namespace ImGuizmo
continue; continue;
// draw face with lighter color // draw face with lighter color
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080, true); gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080);
} }
} }
}; };

View File

@ -112,6 +112,9 @@ void EditTransform(const Camera& camera, matrix_t& matrix)
namespace ImGuizmo namespace ImGuizmo
{ {
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
IMGUI_API void SetDrawlist();
// call BeginFrame right after ImGui_XXXX_NewFrame(); // call BeginFrame right after ImGui_XXXX_NewFrame();
IMGUI_API void BeginFrame(); IMGUI_API void BeginFrame();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
#include "OpenGLWindow/X11OpenGLWindow.h" #include "OpenGLWindow/X11OpenGLWindow.h"
#endif #endif
#include <imgui_internal.h>
#include <cstdio> #include <cstdio>
#define IMGUI_B3G_CONTROL (300) #define IMGUI_B3G_CONTROL (300)
@ -122,12 +123,12 @@ void ImGui_ImplBtGui_RenderDrawLists(ImDrawData* draw_data) {
(GLsizei)last_viewport[3]); (GLsizei)last_viewport[3]);
} }
static const char* ImGui_ImplBtGui_GetClipboardText() { static const char* ImGui_ImplBtGui_GetClipboardText(void* user_ctx) {
// @todo // @todo
return NULL; // glfwGetClipboardString(g_Window); return NULL; // glfwGetClipboardString(g_Window);
} }
static void ImGui_ImplBtGui_SetClipboardText(const char* text) { static void ImGui_ImplBtGui_SetClipboardText(void* user_ctx, const char* text) {
// @todo // @todo
return; return;
// glfwSetClipboardString(g_Window, text); // glfwSetClipboardString(g_Window, text);
@ -259,7 +260,7 @@ bool ImGui_ImplBtGui_Init(b3gDefaultOpenGLWindow* window) {
io.SetClipboardTextFn = ImGui_ImplBtGui_SetClipboardText; io.SetClipboardTextFn = ImGui_ImplBtGui_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplBtGui_GetClipboardText; io.GetClipboardTextFn = ImGui_ImplBtGui_GetClipboardText;
#ifdef _WIN32 #ifdef _WIN32
//io.ImeWindowHandle = glfwGetWin32Window(g_Window); // io.ImeWindowHandle = glfwGetWin32Window(g_Window);
#endif #endif
#if 0 #if 0
@ -277,7 +278,7 @@ bool ImGui_ImplBtGui_Init(b3gDefaultOpenGLWindow* window) {
void ImGui_ImplBtGui_Shutdown() { void ImGui_ImplBtGui_Shutdown() {
ImGui_ImplBtGui_InvalidateDeviceObjects(); ImGui_ImplBtGui_InvalidateDeviceObjects();
ImGui::Shutdown(); ImGui::Shutdown(ImGui::GetCurrentContext());
} }
void ImGui_ImplBtGui_NewFrame(int mouse_x, int mouse_y) { void ImGui_ImplBtGui_NewFrame(int mouse_x, int mouse_y) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.6)
project(gltfutil)
set(CMAKE_CXX_STANDARD 11)
include_directories(../../)
file(GLOB gltfutil_sources *.cc *.h)
add_executable(gltfutil ${gltfutil_sources})

View File

@ -0,0 +1,60 @@
#include <algorithm>
#include <array>
#include <fstream>
#include <iostream>
#include <string>
#include "texture_dumper.h"
namespace gltfutil {
enum class ui_mode { cli, interactive };
enum class cli_action { not_set, help, dump };
enum class FileType { Ascii, Binary, Unknown };
/// Probe inside the file, or check the extension to determine if we have to
/// load a text file, or a binary file
FileType detectType(const std::string& path) {
// Quickly open the file as binary and chekc if there's the gltf binary magic
// number
{
auto probe = std::ifstream(path, std::ios_base::binary);
if (!probe) throw std::runtime_error("Could not open " + path);
std::array<char, 5> buffer;
for (size_t i{0}; i < 4; ++i) probe >> buffer[i];
buffer[4] = 0;
if (std::string("glTF") == std::string(buffer.data())) {
std::cout
<< "Detected binary file thanks to the magic number at the start!\n";
return FileType::Binary;
}
}
// If we don't have any better, check the file extension.
auto extension = path.substr(path.find_last_of('.') + 1);
std::transform(std::begin(extension), std::end(extension),
std::begin(extension),
[](char c) { return char(::tolower(int(c))); });
if (extension == "gltf") return FileType::Ascii;
if (extension == "glb") return FileType::Binary;
return FileType::Unknown;
}
struct configuration {
std::string input_path, output_dir;
ui_mode mode;
cli_action action = cli_action::not_set;
texture_dumper::texture_output_format requested_format =
texture_dumper::texture_output_format::not_specified;
bool has_output_dir;
bool is_valid() {
// TODO impl check
return true;
}
};
} // namespace gltfutil

123
examples/gltfutil/main.cc Normal file
View File

@ -0,0 +1,123 @@
#include <iostream>
#include <string>
#include "gltfuilconfig.h"
#include "texture_dumper.h"
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include <tiny_gltf.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
namespace gltfutil {
int usage(int ret = 0) {
using std::cout;
cout << "gltfutil: tool for manipulating gltf files\n"
<< " usage information:\n\n"
<< "\t gltfutil (-d|-h|) (-f [png|bmp|tga]) [path to .gltf/glb] (-o "
"[path to output directory])\n\n"
//<< "\t\t -i: start in interactive mode\n"
<< "\t\t -d: dump enclosed content (image assets)\n"
<< "\t\t -f: file format for image output"
<< "\t\t -o: ouptput directory path"
<< "\t\t -h: print this help\n";
return ret;
}
int arg_error() {
(void)usage();
return -1;
}
int parse_args(int argc, char** argv) {
gltfutil::configuration config;
for (size_t i = 1; i < size_t(argc); ++i) {
char* arg = argv[i];
if (arg[0] == '-') switch (arg[1]) {
case 'h':
return usage(0);
break;
case 'd':
config.mode = ui_mode::cli;
config.action = cli_action::dump;
break;
case 'i':
config.mode = ui_mode::interactive;
break;
case 'o':
i++;
config.output_dir = argv[i];
break;
case 'f':
i++;
config.requested_format =
texture_dumper::get_fromat_from_string(argv[i]);
break;
default:
return arg_error();
}
else {
// set the input path
config.input_path = argv[i];
}
}
if (config.is_valid()) {
tinygltf::TinyGLTF loader;
tinygltf::Model model;
std::string error;
bool state;
switch (detectType(config.input_path)) {
case FileType::Ascii:
state = loader.LoadASCIIFromFile(&model, &error, config.input_path);
break;
case FileType::Binary:
state = loader.LoadBinaryFromFile(&model, &error, config.input_path);
break;
case FileType::Unknown:
default:
return arg_error();
break;
}
std::cerr << "state is " << state << '\n';
if (config.mode == ui_mode::interactive) {
// interactive usage now;
// TODO impl
} else {
switch (config.action) {
case cli_action::help:
return usage();
break;
case cli_action::dump: {
texture_dumper dumper(model);
if (config.requested_format !=
texture_dumper::texture_output_format::not_specified)
dumper.set_output_format(config.requested_format);
if (config.output_dir.empty())
dumper.dump_to_folder();
else
dumper.dump_to_folder(config.output_dir);
} break;
default:
return arg_error();
}
}
} else {
return arg_error();
}
return 0;
}
} // namespace gltfutil
int main(int argc, char* argv[]) {
if (argc == 1) return gltfutil::usage();
if (argc > 1) return gltfutil::parse_args(argc, argv);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
#include <iostream>
#include "stb_image_write.h"
#include "texture_dumper.h"
#include <tiny_gltf.h>
using namespace gltfutil;
using namespace tinygltf;
using std::cout;
texture_dumper::texture_dumper(const Model& input)
: model(input), configured_format(texture_output_format::png) {
cout << "Texture dumper\n";
}
void texture_dumper::dump_to_folder(const std::string& path) {
cout << "dumping to folder " << path << '\n';
cout << "model file has " << model.textures.size() << " textures.\n";
size_t index = 0;
for (const auto& texture : model.textures) {
index++;
const auto& image = model.images[texture.source];
cout << "image name is: \"" << image.name << "\"\n";
cout << "image size is: " << image.width << 'x' << image.height << '\n';
cout << "pixel channel count :" << image.component << '\n';
std::string name = image.name.empty() ? std::to_string(index) : image.name;
switch (configured_format) {
case texture_output_format::png:
name = path + "/" + name + ".png";
std::cout << "Image will be written to " << name << '\n';
stbi_write_png(name.c_str(), image.width, image.height, image.component,
image.image.data(), 0);
break;
case texture_output_format::bmp:
std::cout << "Image will be written to " << name << '\n';
name = path + "/" + name + ".bmp";
stbi_write_bmp(name.c_str(), image.width, image.height, image.component,
image.image.data());
break;
case texture_output_format::tga:
std::cout << "Image will be written to " << name << '\n';
name = path + "/" + name + ".tga";
stbi_write_tga(name.c_str(), image.width, image.height, image.component,
image.image.data());
break;
}
}
}
void texture_dumper::set_output_format(texture_output_format format) {
configured_format = format;
}
texture_dumper::texture_output_format texture_dumper::get_fromat_from_string(
const std::string& str) {
std::string type = str;
std::transform(str.begin(), str.end(), type.begin(), ::tolower);
if (type == "png") return texture_output_format::png;
if (type == "bmp") return texture_output_format::bmp;
if (type == "tga") return texture_output_format::tga;
return texture_output_format::not_specified;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <tiny_gltf.h>
namespace gltfutil {
class texture_dumper {
public:
enum class texture_output_format { png, bmp, tga, not_specified };
private:
const tinygltf::Model& model;
texture_output_format configured_format;
public:
texture_dumper(const tinygltf::Model& inputModel);
void dump_to_folder(const std::string& path = "./");
void set_output_format(texture_output_format format);
static texture_output_format get_fromat_from_string(const std::string& str);
};
} // namespace gltfutil

View File

@ -2,7 +2,7 @@ Simple OpenGL viewer for glTF geometry.
## Requirements ## Requirements
* premake4 : Requires recent `premake4` for macosx and linux, `premake5` for windows. * premake5 : Requires recent `premake5`(alpha12 or later) for macosx and linux. `premake5` for windows is included in `$tinygltf/tools/window` directory.
* GLEW * GLEW
* Ubuntu 16.04: sudo apt install libglew-dev * Ubuntu 16.04: sudo apt install libglew-dev
* glfw3 * glfw3
@ -19,7 +19,7 @@ Simple OpenGL viewer for glTF geometry.
### Windows(not tested well) ### Windows(not tested well)
Edit glew and glfw path in `premake4.lua`, then Edit glew and glfw path in `premake5.lua`, then
> premake5.exe vs2013 > premake5.exe vs2013

View File

@ -7,10 +7,10 @@ solution "glview"
kind "ConsoleApp" kind "ConsoleApp"
language "C++" language "C++"
cppdialect "C++11"
files { "glview.cc", "trackball.cc" } files { "glview.cc", "trackball.cc" }
includedirs { "./" } includedirs { "./" }
includedirs { "../../" } includedirs { "../../" }
flags "c++11"
configuration { "linux" } configuration { "linux" }
linkoptions { "`pkg-config --libs glfw3`" } linkoptions { "`pkg-config --libs glfw3`" }
@ -34,9 +34,10 @@ solution "glview"
configuration "Debug" configuration "Debug"
defines { "DEBUG" } defines { "DEBUG" }
flags { "Symbols", "ExtraWarnings"} symbols "On"
warnings "Extra"
configuration "Release" configuration "Release"
defines { "NDEBUG" } defines { "NDEBUG" }
flags { "Optimize", "ExtraWarnings"} optimize "On"
warnings "Extra"

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 ## Build
### Linux or macOS ### Linux or macOS
``` ```bash
$ premake5 gmake premake5 gmake
$ make make
``` ```
### Windows ### Windows
``` ```bash
$ premake5 vs2015 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. * picojson : BSD license.
* bt3gui : zlib license. * bt3gui : zlib license.
* glew : BSD/MIT license. * glew : BSD/MIT license.
* tinyobjloader : MIT license. * tinyobjloader : MIT license.
* glm : The Happy Bunny License (Modified MIT License). Copyright (c) 2005 - 2017 G-Truc Creation * 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, "commented_out_obj_filename": "cornellbox_suzanne.obj",
"width" : 512, "gltf_filename": "../../models/Cube/Cube.gltf",
"height" : 512, "scene_scale": 1.0,
"eye" : [0, 2.5, 15], "width": 512,
"up" : [0, 1, 0], "height": 512,
"look_at" : [0, 0, 0], "eye": [
"dummy" : 0 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 "gltf-loader.h"
#include <iostream> #include <iostream>
#include <memory> // c++11
#define TINYGLTF_IMPLEMENTATION #define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION #include <tiny_gltf.h>
#include "tiny_gltf.h"
namespace example { namespace example {
static std::string GetFilePathExtension(const std::string &FileName) { static std::string GetFilePathExtension(const std::string &FileName) {
if (FileName.find_last_of(".") != std::string::npos) if (FileName.find_last_of(".") != std::string::npos)
return FileName.substr(FileName.find_last_of(".") + 1); 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 /// 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,
// TODO(syoyo): Implement std::vector<Texture> *textures) {
// TODO(syoyo): Texture // TODO(syoyo): Texture
// TODO(syoyo): Material // TODO(syoyo): Material
tinygltf::Model model; tinygltf::Model model;
tinygltf::TinyGLTF loader; tinygltf::TinyGLTF loader;
std::string err; std::string err;
std::string ext = GetFilePathExtension(filename); const std::string ext = GetFilePathExtension(filename);
bool ret = false; bool ret = false;
if (ext.compare("glb") == 0) { if (ext.compare("glb") == 0) {
@ -46,8 +44,448 @@ bool LoadGLTF(const std::string &filename, float scale, std::vector<Mesh<float>
return false; return false;
} }
std::cerr << "LoadGLTF() function is not yet implemented!" << std::endl; std::cout << "loaded glTF file has:\n"
return false; << 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 meshes 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;
}
}
// Iterate through all texture declaration in glTF file
for (const auto &gltfTexture : model.textures) {
std::cout << "Found texture!";
Texture loadedTexture;
const auto &image = model.images[gltfTexture.source];
loadedTexture.components = image.component;
loadedTexture.width = image.width;
loadedTexture.height = image.height;
const auto size =
image.component * image.width * image.height * sizeof(unsigned char);
loadedTexture.image = new unsigned char[size];
memcpy(loadedTexture.image, image.image.data(), size);
textures->push_back(loadedTexture);
}
return ret;
}
} // namespace example

View File

@ -1,19 +1,166 @@
#ifndef EXAMPLE_GLTF_LOADER_H_ #ifndef EXAMPLE_GLTF_LOADER_H_
#define EXAMPLE_GLTF_LOADER_H_ #define EXAMPLE_GLTF_LOADER_H_
#include <vector> #include <stdexcept>
#include <string> #include <string>
#include <vector>
#include "mesh.h"
#include "material.h" #include "material.h"
#include "mesh.h"
namespace example { 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 /// 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

@ -53,12 +53,13 @@ THE SOFTWARE.
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <fstream>
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <map>
#include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream>
#include <map>
#include <atomic> // C++11 #include <atomic> // C++11
#include <chrono> // C++11 #include <chrono> // C++11
@ -72,22 +73,23 @@ THE SOFTWARE.
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4201) #pragma warning(disable : 4201)
#endif #endif
#include "glm/mat4x4.hpp"
#include "glm/gtc/quaternion.hpp"
#include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/quaternion.hpp"
#include "glm/gtc/type_ptr.hpp" #include "glm/gtc/type_ptr.hpp"
#include "glm/mat4x4.hpp"
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning(pop) #pragma warning(pop)
#endif #endif
#include "gltf-loader.h"
#include "nanosg.h" #include "nanosg.h"
#include "obj-loader.h"
#include "render-config.h" #include "render-config.h"
#include "render.h" #include "render.h"
#include "gltf-loader.h"
#include "trackball.h" #include "trackball.h"
#ifdef WIN32 #ifdef WIN32
@ -96,19 +98,17 @@ THE SOFTWARE.
#endif #endif
#endif #endif
#define SHOW_BUFFER_COLOR (0) b3gDefaultOpenGLWindow *window = 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;
int gWidth = 512; int gWidth = 512;
int gHeight = 512; int gHeight = 512;
int gMousePosX = -1, gMousePosY = -1; int gMousePosX = -1, gMousePosY = -1;
bool gMouseLeftDown = false; 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; int gShowBufferMode = SHOW_BUFFER_COLOR;
bool gTabPressed = false; bool gTabPressed = false;
bool gShiftPressed = false; bool gShiftPressed = false;
float gShowPositionScale = 1.0f; float gShowPositionScale = 1.0f;
@ -128,8 +128,7 @@ std::atomic<bool> gSceneDirty;
example::RenderConfig gRenderConfig; example::RenderConfig gRenderConfig;
std::mutex gMutex; std::mutex gMutex;
struct RenderLayer struct RenderLayer {
{
std::vector<float> displayRGBA; // Accumurated image. std::vector<float> displayRGBA; // Accumurated image.
std::vector<float> rgba; std::vector<float> rgba;
std::vector<float> auxRGBA; // Auxiliary buffer std::vector<float> auxRGBA; // Auxiliary buffer
@ -143,14 +142,12 @@ struct RenderLayer
RenderLayer gRenderLayer; RenderLayer gRenderLayer;
struct Camera struct Camera {
{
glm::mat4 view; glm::mat4 view;
glm::mat4 projection; glm::mat4 projection;
}; };
struct ManipConfig struct ManipConfig {
{
glm::vec3 snapTranslation; glm::vec3 snapTranslation;
glm::vec3 snapRotation; glm::vec3 snapRotation;
glm::vec3 snapScale; glm::vec3 snapScale;
@ -201,9 +198,12 @@ void RenderThread() {
// gRenderCancel may be set to true in main loop. // gRenderCancel may be set to true in main loop.
// Render() will repeatedly check this flag inside the rendering loop. // Render() will repeatedly check this flag inside the rendering loop.
bool ret = bool ret = example::Renderer::Render(
example::Renderer::Render(&gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0), &gRenderLayer.sampleCounts.at(0), &gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0),
gCurrQuat, gScene, gAsset, gRenderConfig, gRenderCancel); &gRenderLayer.sampleCounts.at(0), gCurrQuat, gScene, gAsset,
gRenderConfig, gRenderCancel,
gShowBufferMode // added mode passing
);
if (ret) { if (ret) {
std::lock_guard<std::mutex> guard(gMutex); std::lock_guard<std::mutex> guard(gMutex);
@ -219,16 +219,18 @@ void RenderThread() {
} }
} }
void InitRender(example::RenderConfig* rc) { void InitRender(example::RenderConfig *rc) {
rc->pass = 0; rc->pass = 0;
rc->max_passes = 128; rc->max_passes = 128;
gRenderLayer.sampleCounts.resize(rc->width * rc->height); 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); 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); gRenderLayer.rgba.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.rgba.begin(), gRenderLayer.rgba.end(), 0.0); std::fill(gRenderLayer.rgba.begin(), gRenderLayer.rgba.end(), 0.0);
@ -237,19 +239,23 @@ void InitRender(example::RenderConfig* rc) {
std::fill(gRenderLayer.auxRGBA.begin(), gRenderLayer.auxRGBA.end(), 0.0); std::fill(gRenderLayer.auxRGBA.begin(), gRenderLayer.auxRGBA.end(), 0.0);
gRenderLayer.normalRGBA.resize(rc->width * rc->height * 4); 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); 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); gRenderLayer.depthRGBA.resize(rc->width * rc->height * 4);
std::fill(gRenderLayer.depthRGBA.begin(), gRenderLayer.depthRGBA.end(), 0.0); std::fill(gRenderLayer.depthRGBA.begin(), gRenderLayer.depthRGBA.end(), 0.0);
gRenderLayer.texCoordRGBA.resize(rc->width * rc->height * 4); 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); 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->normalImage = &gRenderLayer.normalRGBA.at(0);
rc->positionImage = &gRenderLayer.positionRGBA.at(0); rc->positionImage = &gRenderLayer.positionRGBA.at(0);
@ -268,19 +274,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; GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLenum format = GL_RGBA; GLenum format = GL_RGBA;
if (components == 1) { if (components == 1) {
format = GL_LUMINANCE; format = GL_LUMINANCE;
@ -290,15 +295,15 @@ static int CreateDisplayTextureGL(const float *data, int width,
format = GL_RGB; format = GL_RGB;
} else if (components == 4) { } else if (components == 4) {
format = GL_RGBA; format = GL_RGBA;
} else { } else {
assert(0); // "Invalid components" assert(0); // "Invalid components"
} }
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, GL_FLOAT,
GL_FLOAT, data); data);
glBindTexture(GL_TEXTURE_2D, last_texture); glBindTexture(GL_TEXTURE_2D, last_texture);
return static_cast<int>(id); return static_cast<int>(id);
} }
@ -312,8 +317,10 @@ void keyboardCallback(int keycode, int state) {
// reset. // reset.
trackball(gCurrQuat, 0.0f, 0.0f, 0.0f, 0.0f); trackball(gCurrQuat, 0.0f, 0.0f, 0.0f, 0.0f);
// clear buffer. // clear buffer.
memset(gRenderLayer.rgba.data(), 0, sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4); memset(gRenderLayer.rgba.data(), 0,
memset(gRenderLayer.sampleCounts.data(), 0, sizeof(int) * gRenderConfig.width * gRenderConfig.height); sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4);
memset(gRenderLayer.sampleCounts.data(), 0,
sizeof(int) * gRenderConfig.width * gRenderConfig.height);
} else if (keycode == 9) { } else if (keycode == 9) {
gTabPressed = (state == 1); gTabPressed = (state == 1);
} else if (keycode == B3G_SHIFT) { } else if (keycode == B3G_SHIFT) {
@ -330,9 +337,10 @@ void keyboardCallback(int keycode, int state) {
} }
void mouseMoveCallback(float x, float y) { void mouseMoveCallback(float x, float y) {
if (gMouseLeftDown) { if (gMouseLeftDown) {
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) { if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
gSceneDirty = true;
// RequestRender();
} else { } else {
float w = static_cast<float>(gRenderConfig.width); float w = static_cast<float>(gRenderConfig.width);
float h = static_cast<float>(gRenderConfig.height); float h = static_cast<float>(gRenderConfig.height);
@ -371,31 +379,34 @@ void mouseButtonCallback(int button, int state, float x, float y) {
(void)y; (void)y;
ImGui_ImplBtGui_SetMouseButtonState(button, (state == 1)); ImGui_ImplBtGui_SetMouseButtonState(button, (state == 1));
ImGuiIO& io = ImGui::GetIO(); if (button == 0 && !state)
if (io.WantCaptureMouse || io.WantCaptureKeyboard) { gMouseLeftDown = false; // prevent sticky trackball after using gizmo
return;
}
// left button ImGuiIO &io = ImGui::GetIO();
if (button == 0) { if (io.WantCaptureMouse || io.WantCaptureKeyboard) {
if (state) { if (button == 0 && !state) {
gMouseLeftDown = true; if (ImGuizmo::IsUsing()) {
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
} else {
trackball(gPrevQuat, 0.0f, 0.0f, 0.0f, 0.0f);
}
} else {
gMouseLeftDown = false;
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
gSceneDirty = true; gSceneDirty = true;
RequestRender(); RequestRender();
} }
} }
} else {
// left button
if (button == 0) {
if (state) {
gMouseLeftDown = true;
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
} else {
trackball(gPrevQuat, 0.0f, 0.0f, 0.0f, 0.0f);
}
} else {
}
}
} }
} }
void resizeCallback(float width, float height) { void resizeCallback(float width, float height) {
//GLfloat h = (GLfloat)height / (GLfloat)width; // GLfloat h = (GLfloat)height / (GLfloat)width;
GLfloat xmax, znear, zfar; GLfloat xmax, znear, zfar;
znear = 1.0f; znear = 1.0f;
@ -406,7 +417,7 @@ void resizeCallback(float width, float height) {
gHeight = static_cast<int>(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 (ch == 0) { // red
if (v <= 0.5f) if (v <= 0.5f)
return 0.f; return 0.f;
@ -471,7 +482,7 @@ void UpdateDisplayTextureGL(GLint tex_id, int width, int height) {
for (size_t i = 0; i < buf.size(); i++) { for (size_t i = 0; i < buf.size(); i++) {
float v = (gRenderLayer.depthRGBA[i] - d_min) / d_diff; float v = (gRenderLayer.depthRGBA[i] - d_min) / d_diff;
if (gShowDepthPeseudoColor) { if (gShowDepthPeseudoColor) {
buf[i] = pesudoColor(v, i % 4); buf[i] = pseudoColor(v, i % 4);
} else { } else {
buf[i] = v; buf[i] = v;
} }
@ -491,31 +502,31 @@ void UpdateDisplayTextureGL(GLint tex_id, int width, int height) {
disp.resize(width * height * 4); disp.resize(width * height * 4);
{ {
for (size_t y = 0; y < height; y++) { 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); 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); glBindTexture(GL_TEXTURE_2D, 0);
//glRasterPos2i(-1, -1); // glRasterPos2i(-1, -1);
//glDrawPixels(width, height, GL_RGBA, GL_FLOAT, // glDrawPixels(width, height, GL_RGBA, GL_FLOAT,
// static_cast<const GLvoid*>(&buf.at(0))); // 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::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD); static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90)) if (ImGui::IsKeyPressed(90)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
mCurrentGizmoOperation = ImGuizmo::TRANSLATE; if (ImGui::IsKeyPressed(69)) mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(69)) if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE; mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) if (ImGui::RadioButton("Translate",
mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE; mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE)) if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
@ -524,14 +535,15 @@ void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& m
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE)) if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE; mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3]; 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("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3); ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 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)) if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL; mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine(); ImGui::SameLine();
@ -539,33 +551,32 @@ void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& m
mCurrentGizmoMode = ImGuizmo::WORLD; mCurrentGizmoMode = ImGuizmo::WORLD;
} }
static bool useSnap(false); static bool useSnap(false);
if (ImGui::IsKeyPressed(83)) if (ImGui::IsKeyPressed(83)) useSnap = !useSnap;
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap); ImGui::Checkbox("", &useSnap);
ImGui::SameLine(); ImGui::SameLine();
glm::vec3 snap; glm::vec3 snap;
switch (mCurrentGizmoOperation) switch (mCurrentGizmoOperation) {
{ case ImGuizmo::TRANSLATE:
case ImGuizmo::TRANSLATE: snap = config.snapTranslation;
snap = config.snapTranslation; ImGui::InputFloat3("Snap", &snap.x);
ImGui::InputFloat3("Snap", &snap.x); break;
break; case ImGuizmo::ROTATE:
case ImGuizmo::ROTATE: snap = config.snapRotation;
snap = config.snapRotation; ImGui::InputFloat("Angle Snap", &snap.x);
ImGui::InputFloat("Angle Snap", &snap.x); break;
break; case ImGuizmo::SCALE:
case ImGuizmo::SCALE: snap = config.snapScale;
snap = config.snapScale; ImGui::InputFloat("Scale Snap", &snap.x);
ImGui::InputFloat("Scale Snap", &snap.x); break;
break;
} }
ImGuiIO& io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); 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. // TODO(LTE): Use vertex array or use display list.
glBegin(GL_TRIANGLES); glBegin(GL_TRIANGLES);
@ -579,20 +590,17 @@ void DrawMesh(const example::Mesh<float> *mesh)
glNormal3f(mesh->facevarying_normals[9 * i + 0], glNormal3f(mesh->facevarying_normals[9 * i + 0],
mesh->facevarying_normals[9 * i + 1], mesh->facevarying_normals[9 * i + 1],
mesh->facevarying_normals[9 * i + 2]); mesh->facevarying_normals[9 * i + 2]);
glVertex3f(mesh->vertices[3 * f0 + 0], glVertex3f(mesh->vertices[3 * f0 + 0], mesh->vertices[3 * f0 + 1],
mesh->vertices[3 * f0 + 1],
mesh->vertices[3 * f0 + 2]); mesh->vertices[3 * f0 + 2]);
glNormal3f(mesh->facevarying_normals[9 * i + 3], glNormal3f(mesh->facevarying_normals[9 * i + 3],
mesh->facevarying_normals[9 * i + 4], mesh->facevarying_normals[9 * i + 4],
mesh->facevarying_normals[9 * i + 5]); mesh->facevarying_normals[9 * i + 5]);
glVertex3f(mesh->vertices[3 * f1 + 0], glVertex3f(mesh->vertices[3 * f1 + 0], mesh->vertices[3 * f1 + 1],
mesh->vertices[3 * f1 + 1],
mesh->vertices[3 * f1 + 2]); mesh->vertices[3 * f1 + 2]);
glNormal3f(mesh->facevarying_normals[9 * i + 6], glNormal3f(mesh->facevarying_normals[9 * i + 6],
mesh->facevarying_normals[9 * i + 7], mesh->facevarying_normals[9 * i + 7],
mesh->facevarying_normals[9 * i + 8]); mesh->facevarying_normals[9 * i + 8]);
glVertex3f(mesh->vertices[3 * f2 + 0], glVertex3f(mesh->vertices[3 * f2 + 0], mesh->vertices[3 * f2 + 1],
mesh->vertices[3 * f2 + 1],
mesh->vertices[3 * f2 + 2]); mesh->vertices[3 * f2 + 2]);
} }
@ -602,23 +610,19 @@ void DrawMesh(const example::Mesh<float> *mesh)
unsigned int f1 = mesh->faces[3 * i + 1]; unsigned int f1 = mesh->faces[3 * i + 1];
unsigned int f2 = mesh->faces[3 * i + 2]; unsigned int f2 = mesh->faces[3 * i + 2];
glVertex3f(mesh->vertices[3 * f0 + 0], glVertex3f(mesh->vertices[3 * f0 + 0], mesh->vertices[3 * f0 + 1],
mesh->vertices[3 * f0 + 1],
mesh->vertices[3 * f0 + 2]); mesh->vertices[3 * f0 + 2]);
glVertex3f(mesh->vertices[3 * f1 + 0], glVertex3f(mesh->vertices[3 * f1 + 0], mesh->vertices[3 * f1 + 1],
mesh->vertices[3 * f1 + 1],
mesh->vertices[3 * f1 + 2]); mesh->vertices[3 * f1 + 2]);
glVertex3f(mesh->vertices[3 * f2 + 0], glVertex3f(mesh->vertices[3 * f2 + 0], mesh->vertices[3 * f2 + 1],
mesh->vertices[3 * f2 + 1],
mesh->vertices[3 * f2 + 2]); 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(); glPushMatrix();
glMultMatrixf(node.GetLocalXformPtr()); glMultMatrixf(node.GetLocalXformPtr());
@ -634,8 +638,8 @@ void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node)
} }
// Draw scene with OpenGL // 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_DEPTH_TEST);
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
@ -649,11 +653,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}; 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_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_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); glMatrixMode(GL_PROJECTION);
glPushMatrix(); glPushMatrix();
@ -675,15 +680,12 @@ void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene, const C
glDisable(GL_LIGHT1); glDisable(GL_LIGHT1);
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
} }
void BuildSceneItems( void BuildSceneItems(std::vector<std::string> *display_names,
std::vector<std::string> *display_names, std::vector<std::string> *names,
std::vector<std::string> *names, const nanosg::Node<float, example::Mesh<float> > &node,
const nanosg::Node<float, example::Mesh<float> > &node, int indent) {
int indent)
{
if (node.GetName().empty()) { if (node.GetName().empty()) {
// Skip a node with empty name. // Skip a node with empty name.
return; return;
@ -699,14 +701,16 @@ void BuildSceneItems(
display_names->push_back(display_name); display_names->push_back(display_name);
names->push_back(node.GetName()); names->push_back(node.GetName());
for (size_t i = 0; i < node.GetChildren().size(); i++) { for (size_t i = 0; i < node.GetChildren().size(); i++) {
BuildSceneItems(display_names, names, node.GetChildren()[i], indent + 1); 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"; std::string config_filename = "config.json";
if (argc > 1) { if (argc > 1) {
@ -729,13 +733,47 @@ int main(int argc, char** argv) {
std::vector<example::Material> materials; std::vector<example::Material> materials;
std::vector<example::Texture> textures; std::vector<example::Texture> textures;
bool ret = LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale, &meshes, &materials, &textures); // tigra: set default material to 95% white diffuse
if (!ret) { default_material.diffuse[0] = 0.95f;
std::cerr << "Failed to load glTF [ " << gRenderConfig.gltf_filename << " ]" << std::endl; default_material.diffuse[1] = 0.95f;
return -1; 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;
}
}
if (textures.size() > 0) {
materials[0].diffuse_texid = 0;
} }
gAsset.materials = materials; gAsset.materials = materials;
gAsset.default_material = default_material;
gAsset.textures = textures; gAsset.textures = textures;
for (size_t n = 0; n < meshes.size(); n++) { for (size_t n = 0; n < meshes.size(); n++) {
@ -744,12 +782,20 @@ int main(int argc, char** argv) {
} }
for (size_t n = 0; n < gAsset.meshes.size(); n++) { for (size_t n = 0; n < gAsset.meshes.size(); n++) {
nanosg::Node<float, example::Mesh<float> > node(&gAsset.meshes[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.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); gNodes.push_back(node);
gScene.AddNode(node); gScene.AddNode(node);
} }
@ -761,11 +807,12 @@ int main(int argc, char** argv) {
float bmin[3], bmax[3]; float bmin[3], bmax[3];
gScene.GetBoundingBox(bmin, bmax); gScene.GetBoundingBox(bmin, bmax);
printf(" # of nodes : %d\n", int(gNodes.size())); printf(" # of nodes : %d\n", int(gNodes.size()));
printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1],
printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); bmin[2]);
printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1],
bmax[2]);
} }
std::vector<const char *> imgui_node_names; std::vector<const char *> imgui_node_names;
std::vector<std::string> display_node_names; std::vector<std::string> display_node_names;
std::vector<std::string> node_names; std::vector<std::string> node_names;
@ -773,13 +820,14 @@ int main(int argc, char** argv) {
{ {
for (size_t i = 0; i < gScene.GetNodes().size(); i++) { 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. // List of strings for imgui.
// Assume nodes in the scene does not change. // Assume nodes in the scene does not change.
for (size_t i = 0; i < display_node_names.size(); i++) { 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()); imgui_node_names.push_back(display_node_names[i].c_str());
} }
@ -788,13 +836,13 @@ int main(int argc, char** argv) {
nanosg::Node<float, example::Mesh<float> > *node; nanosg::Node<float, example::Mesh<float> > *node;
if (gScene.FindNode(node_names[i], &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; node_map[i] = node;
} }
} }
} }
window = new b3gDefaultOpenGLWindow; window = new b3gDefaultOpenGLWindow;
b3gWindowConstructionInfo ci; b3gWindowConstructionInfo ci;
#ifdef USE_OPENGL2 #ifdef USE_OPENGL2
@ -834,11 +882,12 @@ int main(int argc, char** argv) {
window->setResizeCallback(resizeCallback); window->setResizeCallback(resizeCallback);
checkErrors("resize"); checkErrors("resize");
ImGui::CreateContext();
ImGui_ImplBtGui_Init(window); ImGui_ImplBtGui_Init(window);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
io.Fonts->AddFontDefault(); io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f); // io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f);
glm::mat4 projection(1.0f); glm::mat4 projection(1.0f);
glm::mat4 view(1.0f); glm::mat4 view(1.0f);
@ -861,7 +910,7 @@ int main(int argc, char** argv) {
ImGuizmo::BeginFrame(); ImGuizmo::BeginFrame();
ImGuizmo::Enable(true); ImGuizmo::Enable(true);
//ImGuiIO &io = ImGui::GetIO(); // ImGuiIO &io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGui::Begin("UI"); ImGui::Begin("UI");
@ -897,7 +946,7 @@ int main(int argc, char** argv) {
ImGui::InputFloat("show pos scale", &gShowPositionScale); ImGui::InputFloat("show pos scale", &gShowPositionScale);
ImGui::InputFloat2("show depth range", gShowDepthRange); ImGui::InputFloat2("show depth range", gShowDepthRange);
ImGui::Checkbox("show depth pesudo color", &gShowDepthPeseudoColor); ImGui::Checkbox("show depth pseudo color", &gShowDepthPeseudoColor);
} }
ImGui::End(); ImGui::End();
@ -908,38 +957,47 @@ int main(int argc, char** argv) {
checkErrors("clear"); checkErrors("clear");
// fix max passes issue
if (gShowBufferMode_prv != gShowBufferMode) {
gRenderConfig.pass = 0;
gShowBufferMode_prv = gShowBufferMode;
}
// Render display window // Render display window
{ {
static GLint gl_texid = -1; static GLint gl_texid = -1;
if (gl_texid < 0) { 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. // Refresh texture until rendering finishes.
if (gRenderConfig.pass < gRenderConfig.max_passes) { if (gRenderConfig.pass < gRenderConfig.max_passes) {
// FIXME(LTE): Do not update GL texture frequently. // 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"); ImGui::Begin("Render");
ImTextureID tex_id = reinterpret_cast<void *>( ImTextureID tex_id =
static_cast<intptr_t>(gl_texid)); reinterpret_cast<void *>(static_cast<intptr_t>(gl_texid));
ImGui::Image(tex_id, ImVec2(256, 256), ImVec2(0, 0), 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(); ImGui::End();
} }
// scene graph tree // scene graph tree
glm::mat4 node_matrix; glm::mat4 node_matrix;
static int node_selected = 0; static int node_selected_index = 0;
{ {
ImGui::Begin("Scene"); ImGui::Begin("Scene");
ImGui::ListBox("Node list", &node_selected, imgui_node_names.data(), imgui_node_names.size(), 16); ImGui::ListBox("Node list", &node_selected_index, imgui_node_names.data(),
node_matrix = glm::make_mat4(node_map[node_selected]->GetLocalXformPtr()); imgui_node_names.size(), 16);
auto node_selected = node_map.at(node_selected_index);
node_matrix = glm::make_mat4(node_selected->GetLocalXformPtr());
ImGui::End(); ImGui::End();
} }
@ -964,11 +1022,14 @@ int main(int argc, char** argv) {
up[2] = gRenderConfig.up[2]; up[2] = gRenderConfig.up[2];
// NOTE(LTE): w, then (x,y,z) for glm::quat. // 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); glm::mat4 rm = glm::mat4_cast(rot);
view = glm::lookAt(eye, lookat, up) * glm::inverse(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.view = view;
camera.projection = projection; camera.projection = projection;
@ -978,10 +1039,10 @@ int main(int argc, char** argv) {
float mat[4][4]; float mat[4][4];
memcpy(mat, &node_matrix[0][0], sizeof(float) * 16); 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"); checkErrors("edit_transform");
ImGui::End(); ImGui::End();
} }

View File

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

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <limits>
namespace example { 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> template<typename T>
class Mesh { class Mesh {
public: public:
explicit Mesh(const size_t vertex_stride) :
stride(vertex_stride) {
}
std::string name; 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_normals; /// [xyz] * 3(triangle) * num_faces
std::vector<T> facevarying_tangents; /// [xyz] * 3(triangle) * num_faces std::vector<T> facevarying_tangents; /// [xyz] * 3(triangle) * num_faces
std::vector<T> facevarying_binormals; /// [xyz] * 3(triangle) * num_faces std::vector<T> facevarying_binormals; /// [xyz] * 3(triangle) * num_faces
@ -82,6 +87,7 @@ class Mesh {
std::vector<unsigned int> material_ids; /// index x num_faces std::vector<unsigned int> material_ids; /// index x num_faces
T pivot_xform[4][4]; T pivot_xform[4][4];
size_t stride; /// stride for vertex data.
// --- Required methods in Scene::Traversal. --- // --- Required methods in Scene::Traversal. ---

View File

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

View File

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

View File

@ -31,35 +31,36 @@ THE SOFTWARE.
#endif #endif
#endif #endif
#include <iostream>
#include <limits> #include <limits>
#include <vector> #include <vector>
#include <iostream>
#include "nanort.h" #include "nanort.h"
namespace nanosg { namespace nanosg {
template<class T> template <class T>
class PrimitiveInterface; class PrimitiveInterface;
template<class T> template <class T>
class PrimitiveInterface{ class PrimitiveInterface {
public: public:
void print(){ static_cast<T &>(this)->print(); } void print() { static_cast<T &>(this)->print(); }
}; };
class SpherePrimitive : PrimitiveInterface<SpherePrimitive> { class SpherePrimitive : PrimitiveInterface<SpherePrimitive> {
public: public:
void print(){ std::cout << "Sphere" << std::endl; } void print() { std::cout << "Sphere" << std::endl; }
}; };
// 4x4 matrix // 4x4 matrix
template <typename T> class Matrix { template <typename T>
public: class Matrix {
public:
Matrix(); Matrix();
~Matrix(); ~Matrix();
static void Print(T m[4][4]) { static void Print(const T m[4][4]) {
for (int i = 0; i < 4; i++) { 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]); 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[1] = tmp[1];
dst[2] = tmp[2]; dst[2] = tmp[2];
} }
}; };
//typedef Matrix<float> Matrixf; // typedef Matrix<float> Matrixf;
//typedef Matrix<double> Matrixd; // 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]) {
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) // create bounding vertex from (bmin, bmax)
T b[8][3]; T b[8][3];
@ -286,7 +284,6 @@ static void XformBoundingBox(T xbmin[3], // out
xbmax[2] = xb[0][2]; xbmax[2] = xb[0][2];
for (int i = 1; i < 8; i++) { for (int i = 1; i < 8; i++) {
xbmin[0] = std::min(xb[i][0], xbmin[0]); xbmin[0] = std::min(xb[i][0], xbmin[0]);
xbmin[1] = std::min(xb[i][1], xbmin[1]); xbmin[1] = std::min(xb[i][1], xbmin[1]);
xbmin[2] = std::min(xb[i][2], xbmin[2]); xbmin[2] = std::min(xb[i][2], xbmin[2]);
@ -297,48 +294,45 @@ static void XformBoundingBox(T xbmin[3], // out
} }
} }
template<typename T> template <typename T>
struct Intersection struct Intersection {
{
// required fields. // required fields.
T t; // hit distance T t; // hit distance
unsigned int prim_id; // primitive ID of the hit unsigned int prim_id; // primitive ID of the hit
float u; float u;
float v; float v;
unsigned int node_id; // node ID of the hit. unsigned int node_id; // node ID of the hit.
nanort::real3<T> P; // intersection point nanort::real3<T> P; // intersection point
nanort::real3<T> Ns; // shading normal nanort::real3<T> Ns; // shading normal
nanort::real3<T> Ng; // geometric normal nanort::real3<T> Ng; // geometric normal
}; };
/// ///
/// Renderable node /// Renderable node
/// ///
template<typename T, class M> template <typename T, class M>
class Node class Node {
{
public: public:
typedef Node<T, M> type; typedef Node<T, M> type;
explicit Node(const M *mesh) explicit Node(const M *mesh) : mesh_(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();
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(); lbmin_[0] = lbmin_[1] = lbmin_[2] = std::numeric_limits<T>::max();
lbmax_[0] = lbmax_[1] = lbmax_[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(local_xform_);
Matrix<T>::Identity(xform_); Matrix<T>::Identity(xform_);
Matrix<T>::Identity(inv_xform_); Matrix<T>::Identity(inv_xform_);
Matrix<T>::Identity(inv_xform33_); inv_xform33_[3][3] = static_cast<T>(0.0); Matrix<T>::Identity(inv_xform33_);
Matrix<T>::Identity(inv_transpose_xform33_); inv_transpose_xform33_[3][3] = static_cast<T>(0.0); 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) { void Copy(const type &rhs) {
Matrix<T>::Copy(local_xform_, rhs.local_xform_); Matrix<T>::Copy(local_xform_, rhs.local_xform_);
@ -369,53 +363,44 @@ class Node
children_ = rhs.children_; children_ = rhs.children_;
} }
Node(const type &rhs) { Node(const type &rhs) { Copy(rhs); }
Copy(rhs);
}
const type &operator=(const type &rhs) { const type &operator=(const type &rhs) {
Copy(rhs); Copy(rhs);
return (*this); return (*this);
} }
void SetName(const std::string &name) { void SetName(const std::string &name) { name_ = name; }
name_ = name;
}
const std::string &GetName() const { const std::string &GetName() const { return name_; }
return name_;
}
/// ///
/// Add child node. /// Add child node.
/// ///
void AddChild(const type &child) { void AddChild(const type &child) { children_.push_back(child); }
children_.push_back(child);
}
/// ///
/// Get chidren /// Get chidren
/// ///
const std::vector<type> &GetChildren() const { const std::vector<type> &GetChildren() const { return children_; }
return children_;
}
std::vector<type> &GetChildren() { std::vector<type> &GetChildren() { return children_; }
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)) {
///
/// 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. // Assume mesh is composed of triangle faces only.
nanort::TriangleMesh<float> triangle_mesh(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3); nanort::TriangleMesh<float> triangle_mesh(
nanort::TriangleSAHPred<float> triangle_pred(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3); 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. // Update local bbox.
if (ret) { if (ret) {
@ -433,7 +418,7 @@ class Node
Matrix<T>::Copy(inv_xform_, xform_); Matrix<T>::Copy(inv_xform_, xform_);
Matrix<T>::Inverse(inv_xform_); Matrix<T>::Inverse(inv_xform_);
// Clear translation, then inverse(xform) // Clear translation, then inverse(xform)
Matrix<T>::Copy(inv_xform33_, xform_); Matrix<T>::Copy(inv_xform33_, xform_);
inv_xform33_[3][0] = static_cast<T>(0.0); inv_xform33_[3][0] = static_cast<T>(0.0);
inv_xform33_[3][1] = 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++) { for (size_t i = 0; i < children_.size(); i++) {
children_[i].Update(xform_); children_[i].Update(xform_);
} }
} }
/// ///
/// Set local transformation. /// Set local transformation.
/// ///
void SetLocalXform(const T xform[4][4]) { void SetLocalXform(const T xform[4][4]) {
memcpy(local_xform_, xform, sizeof(float) * 16); memcpy(local_xform_, xform, sizeof(float) * 16);
} }
const T *GetLocalXformPtr() const { const T *GetLocalXformPtr() const { return &local_xform_[0][0]; }
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 { inline void GetLocalBoundingBox(T bmin[3], T bmax[3]) const {
return &xform_[0][0]; 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 { T local_xform_[4][4]; // Node's local transformation matrix.
return mesh_; 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
const nanort::BVHAccel<T> &GetAccel() const { // transforming direction vector)
return accel_; T inv_transpose_xform33_[4][4]; // inverse(transpose(xform)) with upper-left
} // 3x3 elements only(for transforming normal
// vector)
inline void GetWorldBoundingBox(T bmin[3], T bmax[3]) const {
bmin[0] = xbmin_[0];
bmin[1] = xbmin_[1];
bmin[2] = xbmin_[2];
bmax[0] = xbmax_[0];
bmax[1] = xbmax_[1];
bmax[2] = xbmax_[2];
}
inline void GetLocalBoundingBox(T bmin[3], T bmax[3]) const {
bmin[0] = lbmin_[0];
bmin[1] = lbmin_[1];
bmin[2] = lbmin_[2];
bmax[0] = lbmax_[0];
bmax[1] = lbmax_[1];
bmax[2] = lbmax_[2];
}
T local_xform_[4][4]; // Node's local transformation matrix.
T xform_[4][4]; // Parent xform x local_xform.
T inv_xform_[4][4]; // inverse(xform). world -> local
T inv_xform33_[4][4]; // inverse(xform0 with upper-left 3x3 elemets only(for transforming direction vector)
T inv_transpose_xform33_[4][4]; // inverse(transpose(xform)) with upper-left 3x3 elements only(for transforming normal vector)
private: private:
// bounding box(local space)
T lbmin_[3];
T lbmax_[3];
// bounding box(local space) // bounding box after xform(world space)
T lbmin_[3]; T xbmin_[3];
T lbmax_[3]; T xbmax_[3];
// bounding box after xform(world space)
T xbmin_[3];
T xbmax_[3];
nanort::BVHAccel<T> accel_; nanort::BVHAccel<T> accel_;
std::string name_; std::string name_;
@ -516,16 +495,16 @@ class Node
const M *mesh_; const M *mesh_;
std::vector<type> children_; std::vector<type> children_;
}; };
// ------------------------------------------------- // -------------------------------------------------
// Predefined SAH predicator for cube. // Predefined SAH predicator for cube.
template<typename T, class M> template <typename T, class M>
class NodeBBoxPred { class NodeBBoxPred {
public: 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 { void Set(int axis, float pos) const {
axis_ = axis; axis_ = axis;
@ -538,8 +517,8 @@ class NodeBBoxPred {
T bmin[3], bmax[3]; T bmin[3], bmax[3];
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax); (*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
T center = bmax[axis] - bmin[axis]; T center = bmax[axis] - bmin[axis];
return (center < pos); return (center < pos);
@ -551,15 +530,15 @@ class NodeBBoxPred {
const std::vector<Node<T, M> > *nodes_; const std::vector<Node<T, M> > *nodes_;
}; };
template<typename T, class M> template <typename T, class M>
class NodeBBoxGeometry { class NodeBBoxGeometry {
public: public:
NodeBBoxGeometry(const std::vector<Node<T, M> >* nodes) NodeBBoxGeometry(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
: nodes_(nodes) {}
/// Compute bounding box for `prim_index`th cube. /// Compute bounding box for `prim_index`th cube.
/// This function is called for each primitive in BVH build. /// 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]; T a[3], b[3];
(*nodes_)[prim_index].GetWorldBoundingBox(a, b); (*nodes_)[prim_index].GetWorldBoundingBox(a, b);
(*bmin)[0] = a[0]; (*bmin)[0] = a[0];
@ -570,10 +549,11 @@ class NodeBBoxGeometry {
(*bmax)[2] = b[2]; (*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_org_;
mutable nanort::real3<T> ray_dir_; mutable nanort::real3<T> ray_dir_;
mutable nanort::BVHTraceOptions trace_options_; mutable nanort::BVHTraceOptions trace_options_;
int _pad_;
}; };
class NodeBBoxIntersection { class NodeBBoxIntersection {
@ -587,14 +567,13 @@ class NodeBBoxIntersection {
unsigned int prim_id; unsigned int prim_id;
}; };
template<typename T, class M> template <typename T, class M>
class NodeBBoxIntersector { class NodeBBoxIntersector {
public: public:
NodeBBoxIntersector(const std::vector<Node<T, M> >* nodes) NodeBBoxIntersector(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
: nodes_(nodes) {}
bool Intersect(float* out_t_min, float *out_t_max, unsigned int prim_index) const {
bool Intersect(float *out_t_min, float *out_t_max,
unsigned int prim_index) const {
T bmin[3], bmax[3]; T bmin[3], bmax[3];
(*nodes_)[prim_index].GetWorldBoundingBox(bmin, bmax); (*nodes_)[prim_index].GetWorldBoundingBox(bmin, bmax);
@ -634,7 +613,7 @@ class NodeBBoxIntersector {
/// Prepare BVH traversal(e.g. compute inverse ray direction) /// Prepare BVH traversal(e.g. compute inverse ray direction)
/// This function is called only once in BVH traversal. /// 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_[0] = ray.org[0];
ray_org_[1] = ray.org[1]; ray_org_[1] = ray.org[1];
ray_org_[2] = ray.org[2]; 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_[1] = static_cast<T>(1.0) / ray.dir[1];
ray_inv_dir_[2] = static_cast<T>(1.0) / ray.dir[2]; 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_[0] = ray.dir[0] < static_cast<T>(0.0) ? 1 : 0;
ray_dir_sign_[1] = ray.dir[1] < 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) ? 1 : 0;
ray_dir_sign_[2] = ray.dir[2] < 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) ? 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_org_;
mutable nanort::real3<T> ray_dir_; mutable nanort::real3<T> ray_dir_;
mutable nanort::real3<T> ray_inv_dir_; mutable nanort::real3<T> ray_inv_dir_;
mutable int ray_dir_sign_[3]; mutable int ray_dir_sign_[3];
}; };
template<typename T, class M> template <typename T, class M>
class Scene class Scene {
{
public: public:
Scene() { Scene() {
bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits<T>::max(); bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits<T>::max();
bmax_[0] = bmax_[1] = bmax_[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. /// Add intersectable node to the scene.
/// ///
bool AddNode(const Node<T, M> &node) { bool AddNode(const Node<T, M> &node) {
nodes_.push_back(node); nodes_.push_back(node);
return true; return true;
} }
const std::vector<Node<T, M> > &GetNodes() const { const std::vector<Node<T, M> > &GetNodes() const { return nodes_; }
return nodes_;
}
bool FindNode(const std::string &name, Node<T, M> **found_node) { bool FindNode(const std::string &name, Node<T, M> **found_node) {
if (!found_node) { if (!found_node) {
@ -702,11 +678,15 @@ class Scene
return false; return false;
} }
/// ///
/// Commit the scene. Must be called before tracing rays into the scene. /// Commit the scene. Must be called before tracing rays into the scene.
/// ///
bool Commit() { 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. // Update nodes.
for (size_t i = 0; i < nodes_.size(); i++) { for (size_t i = 0; i < nodes_.size(); i++) {
@ -719,21 +699,26 @@ class Scene
// Build toplevel BVH. // Build toplevel BVH.
NodeBBoxGeometry<T, M> geom(&nodes_); NodeBBoxGeometry<T, M> geom(&nodes_);
NodeBBoxPred<T, M> pred(&nodes_); NodeBBoxPred<T, M> pred(&nodes_);
// FIXME(LTE): Limit one leaf contains one node bbox primitive. This would work, but would be inefficient. // FIXME(LTE): Limit one leaf contains one node bbox primitive. This would
// e.g. will miss some node when constructed BVH depth is larger than the value of BVHBuildOptions. // work, but would be inefficient.
// Implement more better and efficient BVH build and traverse for Toplevel BVH. // 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; nanort::BVHBuildOptions<T> build_options;
build_options.min_leaf_primitives = 1; 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(); nanort::BVHBuildStatistics stats = toplevel_accel_.GetStatistics();
(void)stats;
//toplevel_accel_.Debug(); // toplevel_accel_.Debug();
if (ret) { if (ret) {
toplevel_accel_.BoundingBox(bmin_, bmax_); toplevel_accel_.BoundingBox(bmin_, bmax_);
} else { } else {
// Set invalid bbox value. // Set invalid bbox value.
bmin_[0] = std::numeric_limits<T>::max(); bmin_[0] = std::numeric_limits<T>::max();
@ -766,51 +751,54 @@ class Scene
/// First find the intersection of nodes' bounding box using toplevel BVH. /// First find the intersection of nodes' bounding box using toplevel BVH.
/// Then, trace into the hit node to find the intersection of the primitive. /// Then, trace into the hit node to find the intersection of the primitive.
/// ///
template<class H> template <class H>
bool Traverse(nanort::Ray<T> &ray, H *isect, const bool cull_back_face = false) const { bool Traverse(nanort::Ray<T> &ray, H *isect,
const bool cull_back_face = false) const {
if (!toplevel_accel_.IsValid()) { if (!toplevel_accel_.IsValid()) {
return false; return false;
} }
const int kMaxIntersections = 64; const int kMaxIntersections = 64;
bool has_hit = false; bool has_hit = false;
NodeBBoxIntersector<T, M> isector(&nodes_); NodeBBoxIntersector<T, M> isector(&nodes_);
nanort::StackVector<nanort::NodeHit<T>, 128> node_hits; 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) { if (may_hit) {
T t_max = std::numeric_limits<T>::max();
T t_max = std::numeric_limits<T>::max(); T t_nearest = t_max;
T t_nearest = t_max;
nanort::BVHTraceOptions trace_options; nanort::BVHTraceOptions trace_options;
trace_options.cull_back_face = cull_back_face; trace_options.cull_back_face = cull_back_face;
// Find actual intersection point. // Find actual intersection point.
for (size_t i = 0; i < node_hits->size(); i++) { for (size_t i = 0; i < node_hits->size(); i++) {
// Early cull test.
// Early cull test.
if (t_nearest < node_hits[i].t_min) { 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; continue;
} }
assert(node_hits[i].node_id < nodes_.size()); assert(node_hits[i].node_id < nodes_.size());
const Node<T, M> &node = nodes_[node_hits[i].node_id]; 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 // TODO(LTE): Set ray tmin and tmax
nanort::Ray<T> local_ray; nanort::Ray<T> local_ray;
Matrix<T>::MultV(local_ray.org, node.inv_xform_, ray.org); Matrix<T>::MultV(local_ray.org, node.inv_xform_, ray.org);
Matrix<T>::MultV(local_ray.dir, node.inv_xform33_, ray.dir); 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; 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) { if (hit) {
// Calulcate hit distance in world coordiante. // Calulcate hit distance in world coordiante.
@ -828,6 +816,7 @@ class Scene
po[2] = world_P[2] - ray.org[2]; po[2] = world_P[2] - ray.org[2];
float t_world = vlength(po); float t_world = vlength(po);
// printf("tworld %f, tnear %f\n", t_world, t_nearest);
if (t_world < t_nearest) { if (t_world < t_nearest) {
t_nearest = t_world; t_nearest = t_world;
@ -839,36 +828,30 @@ class Scene
isect->v = local_isect.v; isect->v = local_isect.v;
// TODO(LTE): Implement // 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. // Convert position and normal into world coordinate.
isect->t = t_world; isect->t = t_world;
Matrix<T>::MultV(isect->P, node.xform_, local_P); Matrix<T>::MultV(isect->P, node.xform_, local_P);
Matrix<T>::MultV(isect->Ng, node.inv_transpose_xform33_, Matrix<T>::MultV(isect->Ng, node.inv_transpose_xform33_, Ng);
Ng); Matrix<T>::MultV(isect->Ns, node.inv_transpose_xform33_, Ns);
Matrix<T>::MultV(isect->Ns, node.inv_transpose_xform33_, }
Ns);
}
} }
}
} }
}
return has_hit; return has_hit;
} }
private: private:
/// ///
/// Find a node by name. /// 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) { if (root->GetName().compare(name) == 0) {
(*found_node) = root; (*found_node) = root;
return true; return true;
@ -882,19 +865,18 @@ class Scene
} }
return false; return false;
} }
// Scene bounding box. // Scene bounding box.
// Valid after calling `Commit()`. // Valid after calling `Commit()`.
T bmin_[3]; T bmin_[3];
T bmax_[3]; T bmax_[3];
// Toplevel BVH accel. // Toplevel BVH accel.
nanort::BVHAccel<T> toplevel_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 { newoption {
trigger = "asan", trigger = "asan",
description = "Enable Address Sanitizer(gcc5+ ang clang only)" description = "Enable Address Sanitizer(gcc5+ ang clang only)"
} }
sources = { sources = {
"stbi-impl.cc",
"main.cc", "main.cc",
"render.cc", "render.cc",
"render-config.cc", "render-config.cc",
"obj-loader.cc",
"gltf-loader.cc", "gltf-loader.cc",
"matrix.cc", "matrix.cc",
"../common/trackball.cc", "../common/trackball.cc",
@ -16,7 +23,7 @@ sources = {
"../common/imgui/ImGuizmo.cpp", "../common/imgui/ImGuizmo.cpp",
} }
solution "RaytraceSolution" solution "NanoSGSolution"
configurations { "Release", "Debug" } configurations { "Release", "Debug" }
if os.is("Windows") then if os.is("Windows") then
@ -53,7 +60,6 @@ solution "RaytraceSolution"
end end
if os.is("Windows") then if os.is("Windows") then
flags { "FatalCompileWarnings" }
warnings "Extra" -- /W4 warnings "Extra" -- /W4
defines { "NOMINMAX" } defines { "NOMINMAX" }

View File

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

View File

@ -28,7 +28,9 @@ typedef struct {
float *varycoordImage; float *varycoordImage;
// Scene input info // Scene input info
std::string obj_filename;
std::string gltf_filename; std::string gltf_filename;
std::string eson_filename;
float scene_scale; float scene_scale;
} RenderConfig; } 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, nanort::Ray<float> GenerateRay(const float3& origin, const float3& corner,
const float3& du, const float3& dv, float u, const float3& du, const float3& dv, float u,
float v) { float v) {
@ -217,9 +218,12 @@ nanort::Ray<float> GenerateRay(const float3& origin, const float3& corner,
ray.org[1] = origin[1]; ray.org[1] = origin[1];
ray.org[2] = origin[2]; ray.org[2] = origin[2];
ray.dir[0] = dir[0]; ray.dir[0] = dir[0];
ray.dir[1] = dir[1];
ray.dir[2] = dir[2];
return ray; return ray;
} }
#endif
void FetchTexture(const Texture &texture, float u, float v, float* col) { void FetchTexture(const Texture &texture, float u, float v, float* col) {
int tx = u * texture.width; 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 nanosg::Scene<float, example::Mesh<float>> &scene,
const example::Asset &asset, const example::Asset &asset,
const RenderConfig& config, const RenderConfig& config,
std::atomic<bool>& cancelFlag) { std::atomic<bool>& cancelFlag,
int &_showBufferMode
) {
//if (!gAccel.IsValid()) { //if (!gAccel.IsValid()) {
// return false; // return false;
//} //}
int width = config.width; int width = config.width;
int height = config.height; int height = config.height;
@ -300,6 +309,19 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
float u1 = pcg32_random(&rng); float u1 = pcg32_random(&rng);
float3 dir; 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 + dir = corner + (float(x) + u0) * u +
(float(config.height - y - 1) + u1) * v; (float(config.height - y - 1) + u1) * v;
dir = vnormalize(dir); 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<Material> &materials = asset.materials;
const std::vector<Texture> &textures = asset.textures; const std::vector<Texture> &textures = asset.textures;
const Mesh<float> &mesh = asset.meshes[isect.node_id]; const Mesh<float> &mesh = asset.meshes[isect.node_id];
//tigra: add default material
const Material &default_material = asset.default_material;
float3 p; float3 p;
p[0] = p[0] =
@ -410,26 +435,49 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
// Fetch texture // Fetch texture
unsigned int material_id = unsigned int material_id =
mesh.material_ids[isect.prim_id]; mesh.material_ids[isect.prim_id];
//printf("material_id=%d materials=%lld\n", material_id, materials.size());
float diffuse_col[3]; 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]; float specular_col[3];
int specular_texid = materials[material_id].specular_texid;
if (specular_texid >= 0) { //tigra: material_id is ok
FetchTexture(textures[specular_texid], UV[0], UV[1], specular_col); if(material_id<materials.size())
} else { {
specular_col[0] = materials[material_id].specular[0]; //printf("ok mat\n");
specular_col[1] = materials[material_id].specular[1];
specular_col[2] = materials[material_id].specular[2]; 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 // Simple shading
float NdotV = fabsf(vdot(N, dir)); float NdotV = fabsf(vdot(N, dir));

View File

@ -3,6 +3,15 @@
#include <atomic> // C++11 #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 "render-config.h"
#include "nanosg.h" #include "nanosg.h"
#include "mesh.h" #include "mesh.h"
@ -13,6 +22,9 @@ namespace example {
struct Asset { struct Asset {
std::vector<Mesh<float> > meshes; std::vector<Mesh<float> > meshes;
std::vector<Material> materials; std::vector<Material> materials;
//tigra: add default material
Material default_material;
std::vector<Texture> textures; std::vector<Texture> textures;
}; };
@ -23,7 +35,10 @@ class Renderer {
/// Returns false when the rendering was canceled. /// Returns false when the rendering was canceled.
static bool Render(float* rgba, float* aux_rgba, int *sample_counts, float quat[4], 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

View File

@ -0,0 +1,2 @@
all:
clang++ -std=c++11 -I../../ -g -O1 -o saver main.cc

1
examples/saver/README.md Normal file
View File

@ -0,0 +1 @@
# Simple serialization API sample.

33
examples/saver/main.cc Normal file
View File

@ -0,0 +1,33 @@
#include <fstream>
#include <iostream>
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "tiny_gltf.h"
int main(int argc, char *argv[])
{
if (argc != 3) {
std::cout << "Needs input.gltf output.gltf" << std::endl;
return EXIT_FAILURE;
}
tinygltf::Model model;
tinygltf::TinyGLTF loader;
std::string err;
std::string input_filename(argv[1]);
std::string output_filename(argv[2]);
// assume ascii glTF.
bool ret = loader.LoadASCIIFromFile(&model, &err, input_filename.c_str());
if (!ret) {
if (!err.empty()) {
std::cerr << err << std::endl;
}
return EXIT_FAILURE;
}
loader.WriteGltfSceneToFile(&model, output_filename);
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load Diff