From b83da89f02fb526b49bf0f12d7075021f9e92dfc Mon Sep 17 00:00:00 2001 From: yozhijk Date: Thu, 5 Jan 2017 18:34:05 +0100 Subject: [PATCH 01/24] Implement new scene interface --- App/AO/aorenderer.cpp | 11 +- App/AO/aorenderer.h | 4 +- App/CL/bxdf.cl | 19 +- App/CL/camera.cl | 23 +- App/CL/integrator_pt.cl | 18 +- App/CL/isect.cl | 50 ++ App/CL/light.cl | 1 + App/CL/path.cl | 11 +- App/CL/payload.cl | 241 +++-- App/CL/ray.cl | 64 ++ App/CL/sampling.cl | 36 +- App/CL/scene.cl | 26 + App/CL/texture.cl | 25 +- App/CL/volumetrics.cl | 31 - App/CLW/clwscene.h | 23 +- App/Core/renderer.h | 8 +- App/PT/ptrenderer.cpp | 16 +- App/PT/ptrenderer.h | 6 +- App/Scene/Collector/collector.cpp | 139 +++ App/Scene/Collector/collector.h | 103 +++ App/Scene/Loaders/scene_loader.cpp | 191 ++++ App/Scene/Loaders/scene_loader.h | 38 + App/Scene/camera.cpp | 188 ++++ App/Scene/camera.h | 180 ++++ App/Scene/iterator.h | 132 +++ App/Scene/light.h | 219 +++++ App/Scene/material.cpp | 328 +++++++ App/Scene/material.h | 192 ++++ App/Scene/scene.cpp | 1339 ---------------------------- App/Scene/scene.h | 269 ------ App/Scene/scene1.cpp | 135 +++ App/Scene/scene1.h | 103 +++ App/Scene/scene_tracker.cpp | 1017 +++++++++++++++++---- App/Scene/scene_tracker.h | 100 ++- App/Scene/shape.cpp | 160 ++++ App/Scene/shape.h | 130 +++ App/Scene/texture.h | 75 ++ App/main.cpp | 65 +- App/perspective_camera.cpp | 160 ---- App/perspective_camera.h | 167 ---- CLW/CLWBuffer.h | 2 +- 41 files changed, 3610 insertions(+), 2435 deletions(-) create mode 100644 App/CL/isect.cl create mode 100644 App/CL/ray.cl create mode 100644 App/Scene/Collector/collector.cpp create mode 100644 App/Scene/Collector/collector.h create mode 100644 App/Scene/Loaders/scene_loader.cpp create mode 100644 App/Scene/Loaders/scene_loader.h create mode 100644 App/Scene/camera.cpp create mode 100644 App/Scene/camera.h create mode 100644 App/Scene/iterator.h create mode 100644 App/Scene/light.h create mode 100644 App/Scene/material.cpp create mode 100644 App/Scene/material.h delete mode 100755 App/Scene/scene.cpp delete mode 100755 App/Scene/scene.h create mode 100644 App/Scene/scene1.cpp create mode 100644 App/Scene/scene1.h create mode 100644 App/Scene/shape.cpp create mode 100644 App/Scene/shape.h create mode 100644 App/Scene/texture.h delete mode 100755 App/perspective_camera.cpp delete mode 100755 App/perspective_camera.h diff --git a/App/AO/aorenderer.cpp b/App/AO/aorenderer.cpp index 595d3bd0..1d8c01c8 100755 --- a/App/AO/aorenderer.cpp +++ b/App/AO/aorenderer.cpp @@ -21,7 +21,7 @@ THE SOFTWARE. ********************************************************************/ #include "AO/aorenderer.h" #include "CLW/clwoutput.h" -#include "Scene/scene.h" +#include "Scene/scene1.h" #include #include @@ -149,15 +149,18 @@ namespace Baikal m_resetsampler = true; } - void AoRenderer::Preprocess(Scene const& scene) + void AoRenderer::Preprocess(Scene1 const& scene) { } // Render the scene into the output - void AoRenderer::Render(Scene const& scene) + void AoRenderer::Render(Scene1 const& scene) { auto api = m_scene_tracker.GetIntersectionApi(); - auto& clwscene = m_scene_tracker.CompileScene(scene); + + Collector mat_collector; + Collector tex_collector; + auto& clwscene = m_scene_tracker.CompileScene(scene, mat_collector, tex_collector); // Check output assert(m_output); diff --git a/App/AO/aorenderer.h b/App/AO/aorenderer.h index d9ba201b..a684638d 100755 --- a/App/AO/aorenderer.h +++ b/App/AO/aorenderer.h @@ -50,9 +50,9 @@ namespace Baikal // Clear output void Clear(RadeonRays::float3 const& val, Output& output) const override; // Do necessary precalculation and initialization - void Preprocess(Scene const& scene) override; + void Preprocess(Scene1 const& scene) override; // Render the scene into the output - void Render(Scene const& scene) override; + void Render(Scene1 const& scene) override; // Set output void SetOutput(Output* output) override; // Interop function diff --git a/App/CL/bxdf.cl b/App/CL/bxdf.cl index 37be438b..eea8a13c 100755 --- a/App/CL/bxdf.cl +++ b/App/CL/bxdf.cl @@ -30,24 +30,7 @@ THE SOFTWARE. #define DENOM_EPS 0.0f #define ROUGHNESS_EPS 0.0001f -enum Bxdf -{ - kZero, - kLambert, - kIdealReflect, - kIdealRefract, - kMicrofacetBlinn, - kMicrofacetBeckmann, - kMicrofacetGGX, - kLayered, - kFresnelBlend, - kMix, - kEmissive, - kPassthrough, - kTranslucent, - kMicrofacetRefractionGGX, - kMicrofacetRefractionBeckmann -}; + enum BxdfFlags { diff --git a/App/CL/camera.cl b/App/CL/camera.cl index f29b006d..daaa6903 100755 --- a/App/CL/camera.cl +++ b/App/CL/camera.cl @@ -29,28 +29,7 @@ THE SOFTWARE. #include <../App/CL/path.cl> -/// Camera descriptor -/// -typedef struct _Camera - { - // Camera coordinate frame - float3 forward; - float3 right; - float3 up; - float3 p; - - // Image plane width & height in current units - float2 dim; - - // Near and far Z - float2 zcap; - // Focal lenght - float focal_length; - // Camera aspect ratio - float aspect; - float focus_distance; - float aperture; - } Camera; + /// Ray generation kernel for perspective camera. diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index d522d342..32f6f9e7 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -20,6 +20,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ********************************************************************/ + +//#define SOBOL +#define MULTISCATTER + +#include <../App/CL/ray.cl> +#include <../App/CL/isect.cl> #include <../App/CL/utils.cl> #include <../App/CL/random.cl> #include <../App/CL/payload.cl> @@ -41,6 +47,7 @@ THE SOFTWARE. #define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE)) #define NON_BLACK(x) (length(x) > 0.f) + // This kernel only handles scattered paths. // It applies direct illumination and generates // path continuation if multiscattering is enabled. @@ -474,8 +481,7 @@ __kernel void ShadeSurface( { wo = lightwo; float ndotwo = fabs(dot(diffgeo.n, normalize(wo))); - radiance = le * Bxdf_Evaluate(&diffgeo, wi, normalize(wo), TEXTURE_ARGS) * throughput * - ndotwo * lightweight / lightpdf / selection_pdf; + radiance = le * Bxdf_Evaluate(&diffgeo, wi, normalize(wo), TEXTURE_ARGS) * throughput * ndotwo * lightweight / lightpdf / selection_pdf; } } @@ -584,8 +590,8 @@ __kernel void ShadeMiss( // Multiply by throughput int volidx = paths[pixelidx].volume; - if (volidx == -1) - output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)); + if (volidx == -1); + //output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)); else { output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * @@ -776,8 +782,8 @@ __kernel void ShadeBackground( // In case of a miss if (isects[globalid].shapeid < 0 && Path_IsAlive(path)) { - float3 t = Path_GetThroughput(path); - output[pixelidx].xyz += REASONABLE_RADIANCE(envmapmul * Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * t); + //float3 t = Path_GetThroughput(path); + //output[pixelidx].xyz += REASONABLE_RADIANCE(envmapmul * Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * t); } } } diff --git a/App/CL/isect.cl b/App/CL/isect.cl new file mode 100644 index 00000000..616f5926 --- /dev/null +++ b/App/CL/isect.cl @@ -0,0 +1,50 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ +#ifndef ISECT_CL +#define ISECT_CL + +/// Intersection data returned by RadeonRays +typedef struct _Intersection +{ + // id of a shape + int shapeid; + // Primitive index + int primid; + // Padding elements + int padding0; + int padding1; + + // uv - hit barycentrics, w - ray distance + float4 uvwt; +} Intersection; + +float Intersection_GetDistance(__global Intersection const* isect) +{ + return isect->uvwt.w; +} + +float2 Intersection_GetBarycentrics(__global Intersection const* isect) +{ + return isect->uvwt.xy; +} + +#endif diff --git a/App/CL/light.cl b/App/CL/light.cl index 37a93167..ebafe06d 100755 --- a/App/CL/light.cl +++ b/App/CL/light.cl @@ -26,6 +26,7 @@ THE SOFTWARE. #include <../App/CL/payload.cl> #include <../App/CL/random.cl> #include <../App/CL/texture.cl> +#include <../App/CL/scene.cl> bool IntersectTriangle(ray const* r, float3 v1, float3 v2, float3 v3, float* a, float* b) diff --git a/App/CL/path.cl b/App/CL/path.cl index 068a213a..f8d5ef5e 100755 --- a/App/CL/path.cl +++ b/App/CL/path.cl @@ -33,6 +33,14 @@ typedef struct _Path int extra1; } Path; +typedef enum _PathFlags +{ + kNone = 0x0, + kKilled = 0x1, + kScattered = 0x2, + kSpecularBounce = 0x4 +} PathFlags; + bool Path_IsScattered(__global Path const* path) { return path->flags & kScattered; @@ -69,7 +77,6 @@ void Path_SetSpecularFlag(__global Path* path) path->flags |= kSpecularBounce; } - void Path_Restart(__global Path* path) { path->flags = 0; @@ -103,4 +110,4 @@ void Path_AddContribution(__global Path* path, __global float3* output, int idx, -#endif \ No newline at end of file +#endif diff --git a/App/CL/payload.cl b/App/CL/payload.cl index 09838164..47548106 100755 --- a/App/CL/payload.cl +++ b/App/CL/payload.cl @@ -21,35 +21,29 @@ THE SOFTWARE. ********************************************************************/ #ifndef PAYLOAD_CL #define PAYLOAD_CL -//#define SOBOL -#define MULTISCATTER -/// Ray descriptor -typedef struct _ray -{ - /// xyz - origin, w - max range - float4 o; - /// xyz - direction, w - time - float4 d; - /// x - ray mask, y - activity flag - int2 extra; - float2 padding; -} ray; - -/// Intersection data returned by RadeonRays -typedef struct _Intersection -{ - // id of a shape - int shapeid; - // Primitive index - int primid; - // Padding elements - int padding0; - int padding1; - - // uv - hit barycentrics, w - ray distance - float4 uvwt; -} Intersection; +/// Camera descriptor +/// +typedef struct _Camera + { + // Camera coordinate frame + float3 forward; + float3 right; + float3 up; + float3 p; + + // Image plane width & height in current units + float2 dim; + + // Near and far Z + float2 zcap; + // Focal lenght + float focal_length; + // Camera aspect ratio + float aspect; + float focus_distance; + float aperture; + } Camera; // Shape description typedef struct _Shape @@ -73,27 +67,25 @@ typedef struct _Shape float4 m3; } Shape; -// Emissive object -typedef struct _Emissive -{ - // Shape index - int shapeidx; - // Polygon index - int primidx; - // Material index - int m; - // - int padding; -} Emissive; - - -typedef enum _PathFlags + +enum Bxdf { - kNone = 0x0, - kKilled = 0x1, - kScattered = 0x2, - kSpecularBounce = 0x4 -} PathFlags; + kZero, + kLambert, + kIdealReflect, + kIdealRefract, + kMicrofacetBlinn, + kMicrofacetBeckmann, + kMicrofacetGGX, + kLayered, + kFresnelBlend, + kMix, + kEmissive, + kPassthrough, + kTranslucent, + kMicrofacetRefractionGGX, + kMicrofacetRefractionBeckmann +}; // Material description typedef struct _Material @@ -131,9 +123,13 @@ typedef struct _Material float fresnel; }; - int type; + union + { + int type; + int num_materials; + }; + int twosided; - } Material; @@ -183,31 +179,62 @@ typedef struct _Light float3 intensity; } Light; -typedef struct _Scene +typedef enum + { + kEmpty, + kHomogeneous, + kHeterogeneous + } VolumeType; + +typedef enum + { + kUniform, + kRayleigh, + kMieMurky, + kMieHazy, + kHG // this one requires one extra coeff + } PhaseFunction; + +typedef struct _Volume + { + VolumeType type; + PhaseFunction phase_func; + + // Id of volume data if present + int data; + int extra; + + // Absorbtion + float3 sigma_a; + // Scattering + float3 sigma_s; + // Emission + float3 sigma_e; + } Volume; + +/// Supported formats +enum TextureFormat { - // Vertices - __global float3 const* vertices; - // Normals - __global float3 const* normals; - // UVs - __global float2 const* uvs; - // Indices - __global int const* indices; - // Shapes - __global Shape const* shapes; - // Material IDs - __global int const* materialids; - // Materials - __global Material const* materials; - // Emissive objects - __global Light const* lights; - // Envmap idx - int envmapidx; - // Envmap multiplier - float envmapmul; - // Number of emissive objects - int num_lights; -} Scene; + UNKNOWN, + RGBA8, + RGBA16, + RGBA32 +}; + +/// Texture description +typedef + struct _Texture + { + // Width, height and depth + int w; + int h; + int d; + // Offset in texture data array + int dataoffset; + // Format + int fmt; + int extra; + } Texture; // Hit data typedef struct _DifferentialGeometry @@ -230,76 +257,10 @@ typedef struct _DifferentialGeometry -typedef enum -{ - kPixelX = 0, - kPixelY = 1, - kLensX = 2, - kLensY = 3, - kPathBase = 4, - kBrdf = 0, - kLight = 1, - kLightU = 2, - kLightV = 3, - kBrdfU = 4, - kBrdfV = 5, - kIndirectU = 6, - kIndirectV = 7, - kRR = 8, - kVolume = 9, - kVolumeLight = 10, - kVolumeLightU = 11, - kVolumeLightV = 12, - kMaterial = 13, -#ifdef MULTISCATTER - kVolumeIndirectU = 14, - kVolumeIndirectV = 15, - kNumPerBounce = 16, -#else - kNumPerBounce = 14 -#endif -} SampleDim; - -int GetSampleDim(int pass, SampleDim dim) -{ - return kPathBase + pass * kNumPerBounce + dim; -} - -float Intersection_GetDistance(__global Intersection const* isect) -{ - return isect->uvwt.w; -} - -float2 Intersection_GetBarycentrics(__global Intersection const* isect) -{ - return isect->uvwt.xy; -} -void Ray_SetInactive(__global ray* r) -{ - r->extra.y = 0; -} -void Ray_SetExtra(__global ray* r, float2 extra) -{ - r->padding = extra; -} -float2 Ray_GetExtra(__global ray const* r) -{ - return r->padding; -} -void Ray_Init(__global ray* r, float3 o, float3 d, float maxt, float time, int mask) -{ - // TODO: Check if it generates MTBUF_XYZW write - r->o.xyz = o; - r->d.xyz = d; - r->o.w = maxt; - r->d.w = time; - r->extra.x = mask; - r->extra.y = 0xFFFFFFFF; -} diff --git a/App/CL/ray.cl b/App/CL/ray.cl new file mode 100644 index 00000000..8e62f046 --- /dev/null +++ b/App/CL/ray.cl @@ -0,0 +1,64 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ +#ifndef RAY_CL +#define RAY_CL + +/// Ray descriptor +typedef struct _ray +{ + /// xyz - origin, w - max range + float4 o; + /// xyz - direction, w - time + float4 d; + /// x - ray mask, y - activity flag + int2 extra; + /// Padding + float2 padding; +} ray; + +void Ray_SetInactive(__global ray* r) +{ + r->extra.y = 0; +} + +void Ray_SetExtra(__global ray* r, float2 extra) +{ + r->padding = extra; +} + +float2 Ray_GetExtra(__global ray const* r) +{ + return r->padding; +} + +void Ray_Init(__global ray* r, float3 o, float3 d, float maxt, float time, int mask) +{ + // TODO: Check if it generates MTBUF_XYZW write + r->o.xyz = o; + r->d.xyz = d; + r->o.w = maxt; + r->d.w = time; + r->extra.x = mask; + r->extra.y = 0xFFFFFFFF; +} + +#endif diff --git a/App/CL/sampling.cl b/App/CL/sampling.cl index 757ae8f1..c7ccb4ec 100755 --- a/App/CL/sampling.cl +++ b/App/CL/sampling.cl @@ -25,6 +25,40 @@ THE SOFTWARE. #include <../App/CL/utils.cl> #include <../App/CL/random.cl> +typedef enum + { + kPixelX = 0, + kPixelY = 1, + kLensX = 2, + kLensY = 3, + kPathBase = 4, + kBrdf = 0, + kLight = 1, + kLightU = 2, + kLightV = 3, + kBrdfU = 4, + kBrdfV = 5, + kIndirectU = 6, + kIndirectV = 7, + kRR = 8, + kVolume = 9, + kVolumeLight = 10, + kVolumeLightU = 11, + kVolumeLightV = 12, + kMaterial = 13, +#ifdef MULTISCATTER + kVolumeIndirectU = 14, + kVolumeIndirectV = 15, + kNumPerBounce = 16, +#else + kNumPerBounce = 14 +#endif + } SampleDim; + +int GetSampleDim(int pass, SampleDim dim) +{ + return kPathBase + pass * kNumPerBounce + dim; +} /// Sample hemisphere with cos weight float3 Sample_MapToHemisphere( @@ -172,4 +206,4 @@ float SobolSampler_Sample1D(uint index, uint dimension, uint scramble, __global } -#endif // SAMPLING_CL \ No newline at end of file +#endif // SAMPLING_CL diff --git a/App/CL/scene.cl b/App/CL/scene.cl index acdc7810..5041b563 100755 --- a/App/CL/scene.cl +++ b/App/CL/scene.cl @@ -25,6 +25,32 @@ THE SOFTWARE. #include "../App/CL/utils.cl" #include "../App/CL/payload.cl" +typedef struct _Scene + { + // Vertices + __global float3 const* vertices; + // Normals + __global float3 const* normals; + // UVs + __global float2 const* uvs; + // Indices + __global int const* indices; + // Shapes + __global Shape const* shapes; + // Material IDs + __global int const* materialids; + // Materials + __global Material const* materials; + // Emissive objects + __global Light const* lights; + // Envmap idx + int envmapidx; + // Envmap multiplier + float envmapmul; + // Number of emissive objects + int num_lights; + } Scene; + /// Fill DifferentialGeometry structure based on intersection info from RadeonRays void FillDifferentialGeometry(// Scene Scene const* scene, diff --git a/App/CL/texture.cl b/App/CL/texture.cl index 896aa50f..471ea58d 100755 --- a/App/CL/texture.cl +++ b/App/CL/texture.cl @@ -22,31 +22,10 @@ THE SOFTWARE. #ifndef TEXTURE_CL #define TEXTURE_CL + +#include <../App/CL/payload.cl> #include <../App/CL/utils.cl> -/// Supported formats -enum TextureFormat -{ - UNKNOWN, - RGBA8, - RGBA16, - RGBA32 -}; - -/// Texture description -typedef - struct _Texture - { - // Width, height and depth - int w; - int h; - int d; - // Offset in texture data array - int dataoffset; - // Format - int fmt; - int extra; - } Texture; /// To simplify a bit #define TEXTURE_ARG_LIST __global Texture const* textures, __global char const* texturedata diff --git a/App/CL/volumetrics.cl b/App/CL/volumetrics.cl index 4a7c54aa..0ed85c4a 100755 --- a/App/CL/volumetrics.cl +++ b/App/CL/volumetrics.cl @@ -27,38 +27,7 @@ THE SOFTWARE. #define FAKE_SHAPE_SENTINEL 0xFFFFFF -typedef enum -{ - kEmpty, - kHomogeneous, - kHeterogeneous -} VolumeType; -typedef enum -{ - kUniform, - kRayleigh, - kMieMurky, - kMieHazy, - kHG // this one requires one extra coeff -} PhaseFunction; - -typedef struct _Volume -{ - VolumeType type; - PhaseFunction phase_func; - - // Id of volume data if present - int data; - int extra; - - // Absorbtion - float3 sigma_a; - // Scattering - float3 sigma_s; - // Emission - float3 sigma_e; -} Volume; // The following functions are taken from PBRT diff --git a/App/CLW/clwscene.h b/App/CLW/clwscene.h index 700a01ea..6de6506a 100755 --- a/App/CLW/clwscene.h +++ b/App/CLW/clwscene.h @@ -2,11 +2,15 @@ #include "CLW.h" #include "math/float3.h" -#include "Scene/scene.h" +#include "Scene/scene1.h" #include "radeon_rays.h" +#include "Scene/Collector/collector.h" + namespace Baikal { + using namespace RadeonRays; + enum class CameraType { kDefault, @@ -17,21 +21,26 @@ namespace Baikal struct ClwScene { + #include "CL/payload.cl" + CLWBuffer vertices; CLWBuffer normals; CLWBuffer uvs; CLWBuffer indices; - CLWBuffer shapes; + CLWBuffer shapes; - CLWBuffer materials; - CLWBuffer lights; + CLWBuffer materials; + CLWBuffer lights; CLWBuffer materialids; - CLWBuffer volumes; - CLWBuffer textures; + CLWBuffer volumes; + CLWBuffer textures; CLWBuffer texturedata; - CLWBuffer camera; + CLWBuffer camera; + + std::unique_ptr material_bundle; + std::unique_ptr texture_bundle; int num_lights; int envmapidx; diff --git a/App/Core/renderer.h b/App/Core/renderer.h index 5c90269d..4cf1a9d9 100755 --- a/App/Core/renderer.h +++ b/App/Core/renderer.h @@ -35,7 +35,7 @@ THE SOFTWARE. namespace Baikal { class Output; - class Scene; + class Scene1; /** \brief Interface for the renderer. @@ -76,14 +76,14 @@ namespace Baikal \param scene The scene to process */ - virtual void Preprocess(Scene const& scene) = 0; + virtual void Preprocess(Scene1 const& scene) = 0; /** \brief Render single iteration. \param scene Scene to render */ - virtual void Render(Scene const& scene) = 0; + virtual void Render(Scene1 const& scene) = 0; /** \brief Set the output for rendering. @@ -111,6 +111,6 @@ namespace Baikal float shadow_rays_time_in_ms; }; - virtual void RunBenchmark(Scene const& scene, std::uint32_t num_passes, BenchmarkStats& stats) {} + virtual void RunBenchmark(Scene1 const& scene, std::uint32_t num_passes, BenchmarkStats& stats) {} }; } diff --git a/App/PT/ptrenderer.cpp b/App/PT/ptrenderer.cpp index 6faa3302..8ce9b61b 100755 --- a/App/PT/ptrenderer.cpp +++ b/App/PT/ptrenderer.cpp @@ -21,7 +21,8 @@ THE SOFTWARE. ********************************************************************/ #include "PT/ptrenderer.h" #include "CLW/clwoutput.h" -#include "Scene/scene.h" +#include "Scene/scene1.h" +#include "Scene/Collector/collector.h" #include #include @@ -88,6 +89,9 @@ namespace Baikal Buffer* fr_hits; Buffer* fr_intersections; Buffer* fr_hitcount; + + Collector mat_collector; + Collector tex_collector; RenderData() : fr_shadowrays(nullptr) @@ -165,15 +169,15 @@ namespace Baikal m_num_bounces = num_bounces; } - void PtRenderer::Preprocess(Scene const& scene) + void PtRenderer::Preprocess(Scene1 const& scene) { } // Render the scene into the output - void PtRenderer::Render(Scene const& scene) + void PtRenderer::Render(Scene1 const& scene) { auto api = m_scene_tracker.GetIntersectionApi(); - auto& clwscene = m_scene_tracker.CompileScene(scene); + auto& clwscene = m_scene_tracker.CompileScene(scene, m_render_data->mat_collector, m_render_data->tex_collector); // Check output assert(m_output); @@ -600,13 +604,13 @@ namespace Baikal } - void PtRenderer::RunBenchmark(Scene const& scene, std::uint32_t num_passes, BenchmarkStats& stats) + void PtRenderer::RunBenchmark(Scene1 const& scene, std::uint32_t num_passes, BenchmarkStats& stats) { stats.num_passes = num_passes; stats.resolution = int2(m_output->width(), m_output->height()); auto api = m_scene_tracker.GetIntersectionApi(); - auto& clwscene = m_scene_tracker.CompileScene(scene); + auto& clwscene = m_scene_tracker.CompileScene(scene, m_render_data->mat_collector, m_render_data->tex_collector); // Check output assert(m_output); diff --git a/App/PT/ptrenderer.h b/App/PT/ptrenderer.h index 057eb25a..b8feb4a5 100755 --- a/App/PT/ptrenderer.h +++ b/App/PT/ptrenderer.h @@ -50,9 +50,9 @@ namespace Baikal // Clear output void Clear(RadeonRays::float3 const& val, Output& output) const override; // Do necessary precalculation and initialization - void Preprocess(Scene const& scene) override; + void Preprocess(Scene1 const& scene) override; // Render the scene into the output - void Render(Scene const& scene) override; + void Render(Scene1 const& scene) override; // Set output void SetOutput(Output* output) override; // Set number of light bounces @@ -63,7 +63,7 @@ namespace Baikal CLWKernel GetAccumulateKernel(); // Run render benchmark - void RunBenchmark(Scene const& scene, std::uint32_t num_passes, BenchmarkStats& stats) override; + void RunBenchmark(Scene1 const& scene, std::uint32_t num_passes, BenchmarkStats& stats) override; protected: // Resize output-dependent buffers diff --git a/App/Scene/Collector/collector.cpp b/App/Scene/Collector/collector.cpp new file mode 100644 index 00000000..6e23c6ac --- /dev/null +++ b/App/Scene/Collector/collector.cpp @@ -0,0 +1,139 @@ +#include "collector.h" +#include "CLW/clwscene.h" +#include "Scene/iterator.h" +#include +#include + +namespace Baikal +{ + using ItemMap = std::map; + using ItemSet = std::set; + + class BundleImpl : public Bundle + { + public: + BundleImpl(ItemMap const& map, ItemSet const& set) + : m_map(map) + , m_set(set) + { + } + + ItemMap m_map; + ItemSet m_set; + }; + + struct Collector::CollectorImpl + { + ItemMap m_map; + ItemSet m_set; + }; + + Collector::Collector() + : m_impl (new CollectorImpl) + { + } + + Collector::~Collector() = default; + + void Collector::Clear() + { + m_impl->m_map.clear(); + m_impl->m_set.clear(); + } + + Iterator* Collector::CreateIterator() const + { + return new IteratorImpl(m_impl->m_set.cbegin(), m_impl->m_set.cend()); + } + + void Collector::Collect(Iterator* shape_iter, ExpandFunc expand_func) + { + for(;shape_iter->IsValid();shape_iter->Next()) + { + // Expand current item + auto cur_items = expand_func(shape_iter->Item()); + + // Insert items + m_impl->m_set.insert(cur_items.cbegin(), cur_items.cend()); + } + } + + void Collector::Commit() + { + m_impl->m_map.clear(); + + int idx = 0; + for (auto& i : m_impl->m_set) + { + m_impl->m_map[i] = idx++; + } + } + + void Collector::Finalize(FinalizeFunc finalize_func) + { + for (auto& i : m_impl->m_set) + { + finalize_func(i); + } + } + + bool Collector::NeedsUpdate(Bundle const* bundle, ChangedFunc changed_func) const + { + auto bundle_impl = static_cast(bundle); + + // Check if: + // 0) bundle and our map sizes match. + // 1) All the objects collector has are in the bundle. + // 2) They have not changed. + if (bundle_impl->m_set.size() != m_impl->m_set.size()) + { + return true; + } + + for (auto& i : m_impl->m_set) + { + if (changed_func(i)) + { + // Case 2: we have the object which is changed. + return true; + } + + auto iter = bundle_impl->m_set.find(i); + + if (iter == bundle_impl->m_set.cend()) + { + // Case 1: we have an object which is not serialized as a part of bundle. + return true; + } + + // Here we know that bundle_impl->m_map[i] == m_impl->m_map[i] + // since it is defined by set traversal order. + } + + return false; + } + + std::size_t Collector::GetNumItems() const + { + return m_impl->m_set.size(); + } + + Bundle* Collector::CreateBundle() const + { + return new BundleImpl { m_impl->m_map, m_impl->m_set }; + } + + std::uint32_t Collector::GetItemIndex(void const* item) const + { + auto iter = m_impl->m_map.find(item); + + if (iter == m_impl->m_map.cend()) + { + throw std::runtime_error("No such item in the collector"); + } + + return iter->second; + } + + +} diff --git a/App/Scene/Collector/collector.h b/App/Scene/Collector/collector.h new file mode 100644 index 00000000..8bbd917c --- /dev/null +++ b/App/Scene/Collector/collector.h @@ -0,0 +1,103 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file collector.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains collector related classes. + */ +#pragma once +#include +#include +#include + + +namespace Baikal +{ + class Material; + class Iterator; + + /** + \brief Serialized bundle. + + Bundle is a representation of a chunk of serialized memory keeping pointer to offset map. + */ + class Bundle + { + public: + virtual ~Bundle() = 0; + }; + + /** + \brief Collector class. + + Collector iterates over collection of objects collecting objects and their dependecies into random access bundle. + The engine uses collectors in order to resolve material-texture or shape-material dependecies for GPU serialization. + */ + class Collector + { + public: + + using ExpandFunc = std::function(void const*)>; + using ChangedFunc = std::function; + using FinalizeFunc = std::function; + + // Constructor + Collector(); + // Destructor + virtual ~Collector(); + + // Clear collector state (CreateIterator returns invalid iterator if the collector is empty) + virtual void Clear(); + // Create an iterator of objects + virtual Iterator* CreateIterator() const; + // Collect objects and their dependencies + virtual void Collect(Iterator* iter, ExpandFunc expand_func); + // Commit collected objects + virtual void Commit(); + // Given a budnle check if all collected objects are in the bundle and do not require update + virtual bool NeedsUpdate(Bundle const* bundle, ChangedFunc cahnged_func) const; + // Get number of objects in the collection + virtual std::size_t GetNumItems() const; + // Create serialised bundle (randomly accessible dump of objects) + virtual Bundle* CreateBundle() const; + // Get item index within a collection + virtual std::uint32_t GetItemIndex(void const* item) const; + // Finalization function + virtual void Finalize(FinalizeFunc finalize_func); + + // Forbidden stuff + Collector(Collector const&) = delete; + Collector& operator = (Collector const&) = delete; + + private: + struct CollectorImpl; + std::unique_ptr m_impl; + }; + + inline Bundle::~Bundle() + { + } +} + + diff --git a/App/Scene/Loaders/scene_loader.cpp b/App/Scene/Loaders/scene_loader.cpp new file mode 100644 index 00000000..de2498f8 --- /dev/null +++ b/App/Scene/Loaders/scene_loader.cpp @@ -0,0 +1,191 @@ +#include "scene_loader.h" +#include "../scene1.h" +#include "../shape.h" +#include "../material.h" +#include "../light.h" + +#include "tiny_obj_loader.h" + +namespace Baikal +{ + Scene1* LoadFromObj(std::string const& filename, std::string const& basepath) + { + using namespace tinyobj; + + // Loader data + std::vector objshapes; + std::vector objmaterials; + + // Try loading file + std::string res = LoadObj(objshapes, objmaterials, filename.c_str(), basepath.c_str()); + if (res != "") + { + throw std::runtime_error(res); + } + + // Allocate scene + Scene1* scene(new Scene1); + + // Texture map + std::map textures; + std::map matmap; + + // Enumerate and translate materials + /*for (int i = 0; i < (int)objmaterials.size(); ++i) + { + Material material; + + material.kx = float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2]); + material.ni = objmaterials[i].ior; + material.type = kLambert; + material.fresnel = 0.f; + + // Load diffuse texture if needed + if (!objmaterials[i].diffuse_texname.empty()) + { + auto iter = textures.find(objmaterials[i].diffuse_texname); + if (iter != textures.end()) + { + material.kxmapidx = iter->second; + } + else + { + Texture texture; + + // Load texture + LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); + + // Add texture desc + material.kxmapidx = (int)scene->textures_.size(); + scene->textures_.push_back(texture); + + // Save in the map + textures[objmaterials[i].diffuse_texname] = material.kxmapidx; + } + } + + // Load specular texture + if (!objmaterials[i].specular_texname.empty()) + { + auto iter = textures.find(objmaterials[i].specular_texname); + if (iter != textures.end()) + { + material.ksmapidx = iter->second; + } + else + { + Texture texture; + // Load texture + LoadTexture(basepath + "/" + objmaterials[i].specular_texname, texture, scene->texturedata_); + // Add texture desc + material.ksmapidx = (int)scene->textures_.size(); + scene->textures_.push_back(texture); + // Save in the map + textures[objmaterials[i].specular_texname] = material.ksmapidx; + } + } + + // Load normal map + + + + scene->materials_.push_back(material); + scene->material_names_.push_back(objmaterials[i].name); + + float3 spec = float3(0.5f, 0.5f, 0.5f);// float3(objmaterials[i].specular[0], objmaterials[i].specular[1], objmaterials[i].specular[2]); + if (spec.sqnorm() > 0.f) + { + Material specular; + specular.kx = spec; + specular.ni = 1.33f;//objmaterials[i].ior; + specular.ns = 0.05f;//1.f - objmaterials[i].shininess; + specular.type = kMicrofacetGGX; + specular.nmapidx = -1;// scene->materials_.back().nmapidx; + specular.fresnel = 1.f; + + if (!objmaterials[i].normal_texname.empty()) + { + auto iter = textures.find(objmaterials[i].normal_texname); + if (iter != textures.end()) + { + specular.nmapidx = iter->second; + } + else + { + Texture texture; + + // Load texture + LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); + + // Add texture desc + specular.nmapidx = (int)scene->textures_.size(); + scene->textures_.push_back(texture); + + // Save in the map + textures[objmaterials[i].normal_texname] = specular.nmapidx; + } + } + + scene->materials_.push_back(specular); + scene->material_names_.push_back(objmaterials[i].name); + + Material layered; + layered.ni = 1.9f;// objmaterials[i].ior; + layered.type = kFresnelBlend; + layered.brdftopidx = scene->materials_.size() - 1; + layered.brdfbaseidx = scene->materials_.size() - 2; + layered.fresnel = 1.f; + layered.twosided = 1; + + scene->materials_.push_back(layered); + scene->material_names_.push_back(objmaterials[i].name); + } + + matmap[i] = scene->materials_.size() - 1; + } + }*/ + + Material* fakemat = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + fakemat->SetTwoSided(false); + + // Enumerate all shapes in the scene + for (int s = 0; s < (int)objshapes.size(); ++s) + { + Mesh* mesh = new Mesh(); + + auto num_vertices = objshapes[s].mesh.positions.size() / 3; + mesh->SetVertices(&objshapes[s].mesh.positions[0], num_vertices); + + auto num_normals = objshapes[s].mesh.normals.size() / 3; + mesh->SetNormals(&objshapes[s].mesh.normals[0], num_normals); + + auto num_uvs = objshapes[s].mesh.texcoords.size() / 2; + + if (num_uvs) + { + mesh->SetUVs(&objshapes[s].mesh.texcoords[0], num_uvs); + } + else + { + std::vector zero(num_vertices); + mesh->SetUVs(&zero[0], num_vertices); + } + + auto num_indices = objshapes[s].mesh.indices.size(); + mesh->SetIndices(reinterpret_cast(&objshapes[s].mesh.indices[0]), num_indices); + + mesh->SetMaterial(fakemat); + scene->AttachShape(mesh); + } + + //ImageBasedLight* light = new ImageBasedLight(); + //g_scene->SetEnvironment(g_envmapname, "", g_envmapmul); + + PointLight* light = new PointLight(); + light->SetPosition(RadeonRays::float3(-0.3f, 1.5f, 2.f)); + light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); + + scene->AttachLight(light); + return scene; + } +} diff --git a/App/Scene/Loaders/scene_loader.h b/App/Scene/Loaders/scene_loader.h new file mode 100644 index 00000000..567c4fb5 --- /dev/null +++ b/App/Scene/Loaders/scene_loader.h @@ -0,0 +1,38 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file scene_loader.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains a class representing material + */ +#pragma once + +#include + +namespace Baikal +{ + class Scene1; + + Scene1* LoadFromObj(std::string const& filename, std::string const& basepath); +} diff --git a/App/Scene/camera.cpp b/App/Scene/camera.cpp new file mode 100644 index 00000000..38855700 --- /dev/null +++ b/App/Scene/camera.cpp @@ -0,0 +1,188 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ +#include "camera.h" + +#include +#include + +#include "math/quaternion.h" +#include "math/matrix.h" +#include "math/mathutils.h" + + +namespace Baikal +{ + using namespace RadeonRays; + + PerspectiveCamera::PerspectiveCamera(float3 const& eye, float3 const& at, float3 const& up) + : m_p(eye) + , m_aperture(0.f) + , m_focus_distance(0.f) + , m_focal_length(0.f) + , m_zcap(0.f, 0.f) + { + // Construct camera frame + m_forward = normalize(at - eye); + m_right = cross(m_forward, normalize(up)); + m_up = cross(m_right, m_forward); + } + + // Rotate camera around world Z axis, use for FPS camera + void PerspectiveCamera::Rotate(float angle) + { + Rotate(float3(0.f, 1.f, 0.f), angle); + } + + void PerspectiveCamera::Rotate(float3 v, float angle) + { + /// matrix should have basis vectors in rows + /// to be used for quaternion construction + /// would be good to add several options + /// to quaternion class + matrix cam_matrix = matrix( + m_up.x, m_up.y, m_up.z, 0.f, + m_right.x, m_right.y, m_right.z, 0.f, + m_forward.x, m_forward.y, m_forward.z, 0.f, + 0.f, 0.f, 0.f, 1.f); + + // Create camera orientation quaternion + quaternion q = normalize(quaternion(cam_matrix)); + + // Rotate camera frame around v + q = q * rotation_quaternion(v, -angle); + + // Uncomress back to lookat + q.to_matrix(cam_matrix); + + m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); + m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); + m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); + } + + // Tilt camera + void PerspectiveCamera::Tilt(float angle) + { + Rotate(m_right, angle); + } + + // Move along camera Z direction + void PerspectiveCamera::MoveForward(float distance) + { + m_p += distance * m_forward; + } + + // Move along camera X direction + void PerspectiveCamera::MoveRight(float distance) + { + m_p += distance * m_right; + } + + // Move along camera Y direction + void PerspectiveCamera::MoveUp(float distance) + { + m_p += distance * m_up; + } + + RadeonRays::float3 PerspectiveCamera::GetForwardVector() const + { + return m_forward; + } + + RadeonRays::float3 PerspectiveCamera::GetUpVector() const + { + return m_up; + } + + RadeonRays::float3 PerspectiveCamera::GetRightVector() const + { + return m_right; + } + + RadeonRays::float3 PerspectiveCamera::GetPosition() const + { + return m_p; + } + + float PerspectiveCamera::GetAspectRatio() const + { + return m_aspect; + } + + // Arcball rotation + void PerspectiveCamera::ArcballRotateHorizontally(float3 c, float angle) + { + // Build camera matrix + matrix cam_matrix = matrix( + m_up.x, m_up.y, m_up.z, 0, + m_right.x, m_right.y, m_right.z, 0, + m_forward.x, m_forward.y, m_forward.z, 0, + 0, 0, 0, 1); + + // Create camera orientation quaternion + quaternion q = normalize(quaternion(cam_matrix)); + + // Rotation of camera around the center of interest by a == rotation of the frame by -a and rotation of the position by a + q = q * rotation_quaternion(float3(0.f, 1.f, 0.f), -angle); + + // Rotate the frame + q.to_matrix(cam_matrix); + + m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); + m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); + m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); + + // Rotate the position + float3 dir = c - m_p; + dir = rotate_vector(dir, rotation_quaternion(float3(0.f, 1.f, 0.f), angle)); + + m_p = c - dir; + } + + void PerspectiveCamera::ArcballRotateVertically(float3 c, float angle) + { + // Build camera matrix + matrix cam_matrix = matrix( + m_up.x, m_up.y, m_up.z, 0, + m_right.x, m_right.y, m_right.z, 0, + m_forward.x, m_forward.y, m_forward.z, 0, + 0, 0, 0, 1); + + // Create camera orientation quaternion + quaternion q = normalize(quaternion(cam_matrix)); + + // Rotation of camera around the center of interest by a == rotation of the frame by -a and rotation of the position by a + q = q * rotation_quaternion(m_right, -angle); + + // Rotate the frame + q.to_matrix(cam_matrix); + + m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); + m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); + m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); + + // Rotate the position + float3 dir = c - m_p; + dir = rotate_vector(dir, rotation_quaternion(m_right, angle)); + + m_p = c - dir; + } +} diff --git a/App/Scene/camera.h b/App/Scene/camera.h new file mode 100644 index 00000000..19830287 --- /dev/null +++ b/App/Scene/camera.h @@ -0,0 +1,180 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file camera.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains declaration of camera types supported by the renderer. + */ +#pragma once + +#include "math/float3.h" +#include "math/float2.h" + +namespace Baikal +{ + class Camera + { + public: + Camera() = default; + virtual ~Camera() = default; + + // Forbidden stuff + Camera(Camera const&) = delete; + Camera& operator = (Camera const&) = delete; + }; + + class PerspectiveCamera : public Camera + { + public: + // Pass camera position, camera aim, camera up vector, depth limits, vertical field of view + // and image plane aspect ratio + PerspectiveCamera(RadeonRays::float3 const& eye, + RadeonRays::float3 const& at, + RadeonRays::float3 const& up); + + // Set camera focus distance in meters, + // this is essentially a distance from the lens to the focal plane. + // Altering this is similar to rotating the focus ring on real lens. + void SetFocusDistance(float distance); + float GetFocusDistance() const; + + // Set camera focal length in meters, + // this is essentially a distance between a camera sensor and a lens. + // Altering this is similar to rotating zoom ring on a lens. + void SetFocalLength(float length); + float GetFocalLength() const; + + // Set aperture value in meters. + // This is a radius of a lens. + void SetAperture(float aperture); + float GetAperture() const; + + // Set camera sensor size in meters. + // This distinguishes APC-S vs full-frame, etc + void SetSensorSize(RadeonRays::float2 const& size); + RadeonRays::float2 GetSensorSize() const; + + // Set camera depth range. + // Does not really make sence for physical camera + void SetDepthRange(RadeonRays::float2 const& range); + RadeonRays::float2 GetDepthRange() const; + + RadeonRays::float3 GetForwardVector() const; + RadeonRays::float3 GetUpVector() const; + RadeonRays::float3 GetRightVector() const; + RadeonRays::float3 GetPosition() const; + float GetAspectRatio() const; + + + // Rotate camera around world Z axis + void Rotate(float angle); + // Tilt camera + void Tilt(float angle); + // Move along camera Z direction + void MoveForward(float distance); + // Move along camera X direction + void MoveRight(float distance); + // Move along camera Y direction + void MoveUp(float distance); + + // + void ArcballRotateHorizontally(RadeonRays::float3 c, float angle); + // + void ArcballRotateVertically(RadeonRays::float3 c, float angle); + + + private: + // Rotate camera around world Z axis + void Rotate(RadeonRays::float3, float angle); + + // Camera coordinate frame + RadeonRays::float3 m_forward; + RadeonRays::float3 m_right; + RadeonRays::float3 m_up; + RadeonRays::float3 m_p; + + // Image plane width & hight in scene units + RadeonRays::float2 m_dim; + + // Near and far Z + RadeonRays::float2 m_zcap; + + float m_focal_length; + float m_aspect; + float m_focus_distance; + float m_aperture; + + friend std::ostream& operator << (std::ostream& o, PerspectiveCamera const& p); + }; + + inline void PerspectiveCamera::SetFocusDistance(float distance) + { + m_focus_distance = distance; + } + + inline void PerspectiveCamera::SetFocalLength(float length) + { + m_focal_length = length; + } + + inline void PerspectiveCamera::SetAperture(float aperture) + { + m_aperture = aperture; + } + + inline float PerspectiveCamera::GetFocusDistance() const + { + return m_focus_distance; + } + + inline float PerspectiveCamera::GetFocalLength() const + { + return m_focal_length; + } + + inline float PerspectiveCamera::GetAperture() const + { + return m_aperture; + } + + inline RadeonRays::float2 PerspectiveCamera::GetSensorSize() const + { + return m_dim; + } + + inline void PerspectiveCamera::SetSensorSize(RadeonRays::float2 const& size) + { + m_dim = size; + } + inline void PerspectiveCamera::SetDepthRange(RadeonRays::float2 const& range) + { + m_zcap = range; + } + + inline RadeonRays::float2 PerspectiveCamera::GetDepthRange() const + { + return m_zcap; + } + +} diff --git a/App/Scene/iterator.h b/App/Scene/iterator.h new file mode 100644 index 00000000..8c1e842e --- /dev/null +++ b/App/Scene/iterator.h @@ -0,0 +1,132 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ +#pragma once + +/** + \file iterator.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains declaration of Baikal object iterators. + */ +namespace Baikal +{ + /** + \brief Iterator base interface. + + \details Iterators are used to go over different Baikal scene graph objects, such as meshes or lights. + */ + class Iterator + { + public: + // Constructor + Iterator() = default; + // Destructor + virtual ~Iterator() = default; + + // Valid means it still has items to iterate over: you can call Next() + virtual bool IsValid() const = 0; + + // Goes to next object, iterator should be valid before this call + virtual void Next() = 0; + + // Retrieve underlying object + virtual void const* Item() const = 0; + + // Sets the iterator into its initial state (beginning of the sequence) + virtual void Reset() = 0; + + // Retrieve with uncoditional cast: caller is responsible of all the implications, no type check here + template T* ItemAs() const { return reinterpret_cast(Item()); } + + // TODO: support these later + Iterator(Iterator const&) = delete; + Iterator& operator = (Iterator const&) = delete; + }; + + + /** + \brief Represents empty sequence. + + \details Objects return empty iterator when there is nothing to iterate over. + */ + class EmptyIterator : public Iterator + { + public: + EmptyIterator() = default; + + ~EmptyIterator() = default; + + bool IsValid() const override { return false; } + + void Next() override {} + + void const* Item() const override { return nullptr; } + + void Reset() override {} + }; + + /** + \brief Represents wrapper on iterable entities supporting comparison and ++. + + \details Objects return this type of iterator if they are iteration over arrays or stl containers. + */ + template class IteratorImpl : public Iterator + { + public: + // Initialize with an existing iterators + IteratorImpl(UnderlyingIterator begin, UnderlyingIterator end) + : m_begin(begin) + , m_end(end) + , m_cur(begin) + { + } + + // Check if we reached end + bool IsValid() const override + { + return m_cur != m_end; + } + + // Advance by 1 + void Next() override + { + ++m_cur; + } + + // Get underlying item + void const* Item() const override + { + return *m_cur; + } + + // Set to starting iterator + void Reset() override + { + m_cur = m_begin; + } + + private: + UnderlyingIterator m_begin; + UnderlyingIterator m_end; + UnderlyingIterator m_cur; + }; +} diff --git a/App/Scene/light.h b/App/Scene/light.h new file mode 100644 index 00000000..66795c6c --- /dev/null +++ b/App/Scene/light.h @@ -0,0 +1,219 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file light.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains declaration of various light types supported by the renderer. + */ +#pragma once + +#include "math/float3.h" +#include "math/float2.h" +#include +#include + +#include "iterator.h" + +namespace Baikal +{ + class Texture; + + /** + \brief Light base interface. + + High-level interface all light classes need to implement. + */ + class Light + { + public: + // Constructor + Light() = default; + // Destructor + virtual ~Light() = 0; + + // Get total radiant power (integral) + //virtual RadeonRays::float3 GetRadiantPower() const = 0; + + // Set and get position + virtual RadeonRays::float3 GetPosition() const; + virtual void SetPosition(RadeonRays::float3 const& p); + + // Set and get direction + virtual RadeonRays::float3 GetDirection() const; + virtual void SetDirection(RadeonRays::float3 const& d); + + // Set and get direction + virtual RadeonRays::float3 GetEmittedRadiance() const; + virtual void SetEmittedRadiance(RadeonRays::float3 const& e); + + // Iterate all textures used by the light + virtual Iterator* CreateTextureIterator() const; + + // Forbidden stuff + Light(Light const&) = delete; + Light& operator = (Light const&) = delete; + + private: + RadeonRays::float3 m_p; + RadeonRays::float3 m_d; + RadeonRays::float3 m_e; + }; + + /** + \brief Simple point light source. + + Non-physical light emitting power from a single point in all directions. + */ + class PointLight: public Light + { + public: + }; + + /** + \brief Simple directional light source. + + Non-physical light emitting power from a single direction. + */ + class DirectionalLight: public Light + { + public: + }; + + /** + \brief Simple cone-shaped light source. + + Non-physical light emitting power from a single point in a cone of directions. + */ + class SpotLight: public Light + { + public: + // Get and set inner and outer falloff angles: they are set as cosines of angles between light direction + // and cone opening. + virtual void SetConeShape(RadeonRays::float2 angles); + virtual RadeonRays::float2 GetConeShape() const; + + private: + RadeonRays::float2 m_angles; + }; + + /** + \brief Image-based distant light source. + + Physical light emitting from all spherical directions. + */ + class ImageBasedLight: public Light + { + public: + // Get and set illuminant texture + virtual void SetTexture(Texture const* texture); + virtual Texture const* GetTexture() const; + + private: + Texture const* m_texture; + }; + + // Area light + class AreaLight: public Light + { + public: + // Get parent shape + virtual Shape const* GetShape() const; + // Get parent prim idx + virtual std::size_t GetPrimitiveIdx() const; + + private: + Shape const* m_shape; + std::size_t m_prim_idx; + }; + + inline Light::~Light() + { + + } + + inline RadeonRays::float3 Light::GetPosition() const + { + return m_p; + } + + inline void Light::SetPosition(RadeonRays::float3 const& p) + { + m_p = p; + } + + inline RadeonRays::float3 Light::GetDirection() const + { + return m_d; + } + + inline void Light::SetDirection(RadeonRays::float3 const& d) + { + m_d = d; + } + + inline Iterator* Light::CreateTextureIterator() const + { + return new EmptyIterator(); + } + + inline RadeonRays::float3 Light::GetEmittedRadiance() const + { + return m_e; + } + + inline void Light::SetEmittedRadiance(RadeonRays::float3 const& e) + { + m_e = e; + } + + inline void SpotLight::SetConeShape(RadeonRays::float2 angles) + { + m_angles = angles; + } + + inline RadeonRays::float2 SpotLight::GetConeShape() const + { + return m_angles; + } + + inline void ImageBasedLight::SetTexture(Texture const* texture) + { + m_texture = texture; + } + + inline Texture const* ImageBasedLight::GetTexture() const + { + return m_texture; + } + + inline std::size_t AreaLight::GetPrimitiveIdx() const + { + return m_prim_idx; + } + + inline Shape const* AreaLight::GetShape() const + { + return m_shape; + } +} diff --git a/App/Scene/material.cpp b/App/Scene/material.cpp new file mode 100644 index 00000000..af658da2 --- /dev/null +++ b/App/Scene/material.cpp @@ -0,0 +1,328 @@ +#include "material.h" +#include "iterator.h" + +#include + +namespace Baikal +{ + class Material::InputIterator : public Iterator + { + public: + + InputIterator(InputMap::const_iterator begin, InputMap::const_iterator end) + : m_begin(begin) + , m_end(end) + { + Reset(); + } + + // Check if we reached end + bool IsValid() const override + { + return m_cur != m_end; + } + + // Advance by 1 + void Next() override + { + ++m_cur; + } + + // Set to starting iterator + void Reset() override + { + m_cur = m_begin; + } + + // Get underlying item + void const* Item() const override + { + return &m_cur->second; + } + + private: + InputMap::const_iterator m_begin; + InputMap::const_iterator m_end; + InputMap::const_iterator m_cur; + }; + + template class Material::InputDependencyIterator : public Iterator + { + public: + InputDependencyIterator(T&& container) : + m_container(std::move(container)) + { + m_begin = m_container.cbegin(); + m_end = m_container.cend(); + Reset(); + } + + bool IsValid() const override + { + return m_cur != m_end; + } + + // Advance by 1 + void Next() override + { + ++m_cur; + } + + // Get underlying item + void const* Item() const override + { + return *m_cur; + } + + // Set to starting iterator + void Reset() override + { + m_cur = m_begin; + } + + private: + T m_container; + typename T::const_iterator m_begin; + typename T::const_iterator m_end; + typename T::const_iterator m_cur; + }; + + + Material::Material() + : m_dirty(true) + , m_twosided(false) + { + } + + void Material::RegisterInput(std::string const& name, std::string const& desc, std::set&& supported_types) + { + Input input = {{name, desc, std::move(supported_types)}}; + + assert(input.info.supported_types.size() > 0); + + input.value.type = *input.info.supported_types.begin(); + + switch (input.value.type) + { + case InputType::kFloat4: + input.value.float_value = RadeonRays::float4(); + break; + case InputType::kTexture: + input.value.tex_value = nullptr; + break; + case InputType::kMaterial: + input.value.mat_value = nullptr; + break; + default: + break; + } + + m_inputs.emplace(std::make_pair(name, input)); + } + + void Material::ClearInputs() + { + m_inputs.clear(); + } + + + // Iterator of dependent materials (plugged as inputs) + Iterator* Material::CreateMaterialIterator() const + { + std::set materials; + + std::for_each(m_inputs.cbegin(), m_inputs.cend(), + [&materials](std::pair const& map_entry) + { + if (map_entry.second.value.type == InputType::kMaterial && + map_entry.second.value.mat_value != nullptr) + { + materials.insert(map_entry.second.value.mat_value); + } + } + ); + + return new InputDependencyIterator>(std::move(materials)); + + } + + // Iterator of textures (plugged as inputs) + Iterator* Material::CreateTextureIterator() const + { + std::set textures; + + std::for_each(m_inputs.cbegin(), m_inputs.cend(), + [&textures](std::pair const& map_entry) + { + if (map_entry.second.value.type == InputType::kTexture && + map_entry.second.value.tex_value != nullptr) + { + textures.insert(map_entry.second.value.tex_value); + } + } + ); + + return new InputDependencyIterator>(std::move(textures)); + } + + // Iterator of inputs + Iterator* Material::CreateInputIterator() const + { + return new InputIterator(m_inputs.cbegin(), m_inputs.cend()); + } + + // Set input value + // If specific data type is not supported throws std::runtime_error + void Material::SetInputValue(std::string const& name, RadeonRays::float4 const& value) + { + auto input_iter = m_inputs.find(name); + + if (input_iter != m_inputs.cend()) + { + auto& input = input_iter->second; + + if (input.info.supported_types.find(InputType::kFloat4) != input.info.supported_types.cend()) + { + input.value.type = InputType::kFloat4; + input.value.float_value = value; + SetDirty(true); + } + else + { + throw std::runtime_error("Input type not supported"); + } + } + else + { + throw std::runtime_error("No such input"); + } + } + + void Material::SetInputValue(std::string const& name, Texture const* texture) + { + auto input_iter = m_inputs.find(name); + + if (input_iter != m_inputs.cend()) + { + auto& input = input_iter->second; + + if (input.info.supported_types.find(InputType::kTexture) != input.info.supported_types.cend()) + { + input.value.type = InputType::kTexture; + input.value.tex_value = texture; + SetDirty(true); + } + else + { + throw std::runtime_error("Input type not supported"); + } + } + else + { + throw std::runtime_error("No such input"); + } + } + + void Material::SetInputValue(std::string const& name, Material const* material) + { + auto input_iter = m_inputs.find(name); + + if (input_iter != m_inputs.cend()) + { + auto& input = input_iter->second; + + if (input.info.supported_types.find(InputType::kMaterial) != input.info.supported_types.cend()) + { + input.value.type = InputType::kMaterial; + input.value.mat_value = material; + SetDirty(true); + } + else + { + throw std::runtime_error("Input type not supported"); + } + } + else + { + throw std::runtime_error("No such input"); + } + } + + Material::InputValue Material::GetInputValue(std::string const& name) const + { + auto input_iter = m_inputs.find(name); + + if (input_iter != m_inputs.cend()) + { + auto& input = input_iter->second; + + return input.value; + } + else + { + throw std::runtime_error("No such input"); + } + } + + bool Material::IsTwoSided() const + { + return m_twosided; + } + + void Material::SetTwoSided(bool twosided) + { + m_twosided = twosided; + SetDirty(true); + } + + bool Material::IsDirty() const + { + return m_dirty; + } + + void Material::SetDirty(bool dirty) const + { + m_dirty = dirty; + } + + SingleBxdf::SingleBxdf(BxdfType type) + : m_type(type) + { + RegisterInput("albedo", "Diffuse color", {InputType::kFloat4, InputType::kTexture}); + RegisterInput("normal", "Normal map", {InputType::kTexture}); + RegisterInput("ior", "Index of refraction", {InputType::kFloat4}); + RegisterInput("fresnel", "Fresnel flag", {InputType::kFloat4}); + + SetInputValue("albedo", RadeonRays::float4(0.7f, 0.7f, 0.7f, 1.f)); + } + + SingleBxdf::BxdfType SingleBxdf::GetBxdfType() const + { + return m_type; + } + + void SingleBxdf::SetBxdfType(BxdfType type) + { + m_type = type; + SetDirty(true); + } + + MultiBxdf::MultiBxdf(Type type) + : m_type(type) + { + RegisterInput("base_material", "Base material", {InputType::kMaterial}); + RegisterInput("top_material", "Top material", {InputType::kMaterial}); + RegisterInput("ior", "Index of refraction", {InputType::kFloat4}); + } + + MultiBxdf::Type MultiBxdf::GetType() const + { + return m_type; + } + + void MultiBxdf::SetType(Type type) + { + m_type = type; + SetDirty(true); + } +} diff --git a/App/Scene/material.h b/App/Scene/material.h new file mode 100644 index 00000000..78966e4b --- /dev/null +++ b/App/Scene/material.h @@ -0,0 +1,192 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file material.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains a class representing material + */ +#pragma once + +#include +#include +#include +#include + +#include "math/float3.h" + +namespace Baikal +{ + class Iterator; + class Texture; + + /** + \brief High level material interface + + \details Base class for all CPU side material supported by the renderer. + */ + class Material + { + public: + // Material input type + enum class InputType + { + kFloat4, + kTexture, + kMaterial + }; + + // Input description + struct InputInfo + { + // Short name + std::string name; + // Desctiption + std::string desc; + // Set of supported types + std::set supported_types; + }; + + // Input value description + struct InputValue + { + // Current type + InputType type; + + // Possible values (use based on type) + union + { + RadeonRays::float4 float_value; + Texture const* tex_value; + Material const* mat_value; + }; + }; + + // Full input state + struct Input + { + InputInfo info; + InputValue value; + }; + + // Constructor + Material(); + // Destructor + virtual ~Material() = 0; + + // Iterator of dependent materials (plugged as inputs) + virtual Iterator* CreateMaterialIterator() const; + // Iterator of textures (plugged as inputs) + virtual Iterator* CreateTextureIterator() const; + // Iterator of inputs + virtual Iterator* CreateInputIterator() const; + + // Set input value + // If specific data type is not supported throws std::runtime_error + virtual void SetInputValue(std::string const& name, RadeonRays::float4 const& value); + virtual void SetInputValue(std::string const& name, Texture const* texture); + virtual void SetInputValue(std::string const& name, Material const* material); + + virtual InputValue GetInputValue(std::string const& name) const; + + // Check if material is two-sided (normal direction does not matter and can be reversed) + virtual bool IsTwoSided() const; + // Set two-sidedness + virtual void SetTwoSided(bool twosided); + + // Check if material state has changed + virtual bool IsDirty() const; + // Set dirty state + virtual void SetDirty(bool dirty) const; + + protected: + // Register specific input + void RegisterInput(std::string const& name, std::string const& desc, std::set&& supported_types); + // Wipe out all the inputs + void ClearInputs(); + + private: + + class InputIterator; + template class InputDependencyIterator; + + using InputMap = std::map; + // Input map + InputMap m_inputs; + // Dirty flag + mutable bool m_dirty; + // Sidedness flag + bool m_twosided; + }; + + inline Material::~Material() + { + } + + class SingleBxdf : public Material + { + public: + enum class BxdfType + { + kZero, + kLambert, + kIdealReflect, + kIdealRefract, + kMicrofacetBlinn, + kMicrofacetBeckmann, + kMicrofacetGGX, + kEmissive, + kPassthrough, + kTranslucent, + kMicrofacetRefractionGGX, + kMicrofacetRefractionBeckmann + }; + + SingleBxdf(BxdfType type); + + virtual BxdfType GetBxdfType() const; + virtual void SetBxdfType(BxdfType type); + + private: + BxdfType m_type; + }; + + class MultiBxdf : public Material + { + public: + enum class Type + { + kLayered, + kFresnelBlend, + kMix + }; + + MultiBxdf(Type type); + + virtual Type GetType() const; + virtual void SetType(Type type); + + private: + Type m_type; + }; +} diff --git a/App/Scene/scene.cpp b/App/Scene/scene.cpp deleted file mode 100755 index 92b0446d..00000000 --- a/App/Scene/scene.cpp +++ /dev/null @@ -1,1339 +0,0 @@ -/********************************************************************** -Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -********************************************************************/ -#include "Scene/scene.h" -#include "tiny_obj_loader.h" -#include "sh.h" -#include "shproject.h" -#include "OpenImageIO/imageio.h" -#include "Light/Ibl.h" - -#include -#include - -namespace Baikal -{ - -using namespace RadeonRays; - -static int GetTextureForemat(OIIO_NAMESPACE::ImageSpec const& spec) -{ - OIIO_NAMESPACE_USING - - if (spec.format.basetype == TypeDesc::UINT8) - return Scene::RGBA8; - if (spec.format.basetype == TypeDesc::HALF) - return Scene::RGBA16; - else - return Scene::RGBA32; -} - - -static void LoadTexture(std::string const& filename, Scene::Texture& texture, std::vector >& data) -{ - OIIO_NAMESPACE_USING - - ImageInput* input = ImageInput::open(filename); - - if (!input) - { - throw std::runtime_error("Can't load " + filename + " image"); - } - - ImageSpec const& spec = input->spec(); - - texture.w = spec.width; - texture.h = spec.height; - texture.d = spec.depth; - texture.fmt = GetTextureForemat(spec); - - // Save old size for reading offset - texture.dataoffset = (int)data.size(); - - if (texture.fmt == Scene::RGBA8) - { - texture.size = spec.width * spec.height * spec.depth * 4; - - // Resize storage - std::unique_ptr texturedata(new char[spec.width * spec.height * spec.depth * 4]); - - // Read data to storage - input->read_image(TypeDesc::UINT8, texturedata.get(), sizeof(char) * 4); - - // Close handle - input->close(); - - // Add to texture pool - data.push_back(std::move(texturedata)); - } - else if (texture.fmt == Scene::RGBA16) - { - texture.size = spec.width * spec.height * spec.depth * sizeof(float) * 2; - - // Resize storage - std::unique_ptr texturedata(new char[spec.width * spec.height * spec.depth * sizeof(float) * 2]); - - // Read data to storage - input->read_image(TypeDesc::HALF, texturedata.get(), sizeof(float) * 2); - - // Close handle - input->close(); - - // Add to texture pool - data.push_back(std::move(texturedata)); - } - else - { - texture.size = spec.width * spec.height * spec.depth * sizeof(float3); - - // Resize storage - std::unique_ptr texturedata(new char[spec.width * spec.height * spec.depth * sizeof(float3)]); - - // Read data to storage - input->read_image(TypeDesc::FLOAT, texturedata.get(), sizeof(float3)); - - // Close handle - input->close(); - - // Add to texture pool - data.push_back(std::move(texturedata)); - } - - // Cleanup - delete input; -} - -Scene* Scene::LoadFromObj(std::string const& filename, std::string const& basepath) -{ - using namespace tinyobj; - - // Loader data - std::vector objshapes; - std::vector objmaterials; - - // Try loading file - std::string res = LoadObj(objshapes, objmaterials, filename.c_str(), basepath.c_str()); - if (res != "") - { - throw std::runtime_error(res); - } - - // Allocate scene - Scene* scene(new Scene); - - // Texture map - std::map textures; - std::map matmap; - - // Enumerate and translate materials - for (int i = 0; i < (int)objmaterials.size(); ++i) - { - if (objmaterials[i].name == "carpaint" || objmaterials[i].name == "inside1" || objmaterials[i].name == "CarShellNew") - { - Material diffuse; - diffuse.kx = float3(0.787f, 0.081f, 0.027f); - diffuse.ni = 1.5f; - diffuse.ns = 0.3f; - diffuse.type = kMicrofacetBeckmann; - - Material specular; - specular.kx = float3(0.99f, 0.99f, 0.99f); - specular.ni = 1.5f; - specular.ns = 0.01f; - specular.type = kMicrofacetBeckmann; - - scene->materials_.push_back(diffuse); - scene->material_names_.push_back(objmaterials[i].name); - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.5f; - layered.type = kFresnelBlend; - layered.brdfbaseidx = scene->materials_.size() - 2; - layered.brdftopidx = scene->materials_.size() - 1; - - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - - - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "wire_088144225") - { - - Material diffuse; - diffuse.kx = float3(0.7f, 0.7f, 0.7f); - diffuse.ni = 1.6f; - diffuse.type = kLambert; - - Material specular; - specular.kx = float3(0.9f, 0.9f, 0.9f); - specular.ni = 1.6f; - specular.ns = 0.005f; - specular.type = kMicrofacetGGX; - - scene->materials_.push_back(diffuse); - scene->material_names_.push_back(objmaterials[i].name); - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.6f; - layered.type = kFresnelBlend; - layered.brdfbaseidx = scene->materials_.size() - 2; - layered.brdftopidx = scene->materials_.size() - 1; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "rubber" || objmaterials[i].name == "Rubber_Dark") - { - Material specular; - specular.kx = float3(0.4f, 0.4f, 0.4f); - specular.ni = 2.1f; - specular.ns = 0.1f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "sss") - { - /*Material refract; - refract.type = kIdealRefract; - refract.kx = float3(0.1f, 0.5f, 0.1f); - refract.ni = 1.3f; - refract.fresnel = 1.f; - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name);*/ - - Material specular; - specular.kx = float3(0.7f, 0.7f, 0.7f); - specular.ni = 1.3f; - specular.ns = 0.1f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - /*Material layered; - layered.ni = 1.3f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name);*/ - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "glass" || objmaterials[i].name == "water") - { - - Material refract; - refract.kx = float3(1.f, 1.f, 1.f); - refract.ni = 1.33f; - refract.type = kIdealRefract; - refract.fresnel = 1.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - refract.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - refract.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = refract.nmapidx; - } - } - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name); - - Material specular; - specular.kx = float3(1.f, 1.f, 1.f); - specular.ni = 1.33f; - specular.ns = 0.001f; - specular.type = kIdealReflect; - specular.fresnel = 1.f; - specular.nmapidx = refract.nmapidx; - - - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.33f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "phong4SG" || objmaterials[i].name == "Material.007" || objmaterials[i].name == "glass_architectural") - { - - Material refract; - refract.kx = float3(0.83f, 1.f, 0.92f); - refract.ns = 0.2f; - refract.ni = 1.33f; - refract.type = kMicrofacetRefractionGGX; - refract.fresnel = 0.f; - - auto iter = textures.find("checker.png"); - if (iter != textures.end()) - { - refract.nsmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/checker.png", texture, scene->texturedata_); - - // Add texture desc - refract.nsmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures["checker.png"] = refract.nsmapidx; - } - - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name); - - Material specular; - specular.kx = float3(0.83f, 1.f, 0.92f); - specular.ni = 1.33f; - specular.ns = 0.2f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - specular.nsmapidx = refract.nsmapidx; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.33f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - //layered.twosided = 1; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - - continue; - } - else if (objmaterials[i].name == "Test_Material.003") - { - Material specular; - specular.kx = float3(1.f, 1.f, 1.f); - specular.ni = 1.33f; - specular.ns = 0.001f; - specular.type = kIdealReflect; - specular.fresnel = 1.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material refract; - refract.kx = float3(1.f, 1.f, 1.f); - refract.ni = 1.33f; - refract.type = kIdealRefract; - refract.fresnel = 1.f; - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name); - - Material diffuse; - diffuse.kx = float3(0.f, 0.7f, 0.f); - diffuse.ni = 1.33f; - diffuse.ns = 0.001f; - diffuse.type = kLambert; - diffuse.fresnel = 1.f; - - scene->materials_.push_back(diffuse); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 2.33f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - - layered.ni = 1.33f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 4; - layered.brdfbaseidx = scene->materials_.size() - 1; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "red_glass" || objmaterials[i].name == "Tailight") - { - - Material refract; - refract.kx = float3(1.f, 0.3f, 0.3f); - refract.ni = 1.33f; - refract.type = kIdealRefract; - refract.fresnel = 1.f; - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - refract.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - refract.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = refract.kxmapidx; - } - } - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name); - - Material specular; - specular.kx = float3(1.f, 0.3f, 0.3f); - specular.ni = 1.33f; - specular.ns = 0.001f; - specular.type = kIdealReflect; - specular.fresnel = 1.f; - specular.kxmapidx = refract.kxmapidx; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.33f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "wire_113135006") - { - - Material refract; - refract.kx = float3(1.f, 1.f, 1.f); - refract.ni = 2.66f; - refract.type = kIdealRefract; - refract.fresnel = 0.f; - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name); - - Material specular; - specular.kx = float3(1.f, 1.f, 1.f); - specular.ni = 2.66f; - specular.ns = 0.001f; - specular.type = kIdealReflect; - specular.fresnel = 1.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 2.66f; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "glasstranslucent" || objmaterials[i].name == "glass1" || objmaterials[i].name == "Glass" || objmaterials[i].name == "HeadlightGlass") - { - - Material refract; - refract.kx = float3(0.8f, 0.8f, 0.8f); - refract.ni = 1.f; - refract.type = kIdealRefract; - - scene->materials_.push_back(refract); - scene->material_names_.push_back(objmaterials[i].name); - - Material specular; - specular.kx = float3(0.8f, 0.8f, 0.8f); - specular.ni = 1.2f; - specular.type = kIdealReflect; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.2f; - layered.type = kFresnelBlend; - layered.twosided = 1; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "metal" || objmaterials[i].name == "Base_Motor_mat" || objmaterials[i].name == "Base_Motor_mat1" - || objmaterials[i].name == "Base_Motor_mat2" || objmaterials[i].name == "Base_Motor_mat3" || objmaterials[i].name == "ChromeMatte") - { - Material specular; - specular.kx = float3(0.9f, 0.9f, 0.9f); - specular.ni = 5.5f; - specular.ns = 0.15f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - specular.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - specular.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = specular.kxmapidx; - } - } - - - //auto iter = textures.find("rm.jpg"); - //if (iter != textures.end()) - //{ - // specular.kxmapidx = iter->second; - //} - //else - //{ - // Texture texture; - - // // Load texture - // LoadTexture(basepath + "/rm.jpg", texture, scene->texturedata_); - - // // Add texture desc - // specular.nsmapidx= (int)scene->textures_.size(); - // scene->textures_.push_back(texture); - - // // Save in the map - // textures["rm.jpg"] = specular.nsmapidx; - //} - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "plastic1" || objmaterials[i].name == "inside1" ) - { - Material specular; - specular.kx = float3(0.6f, 0.6f, 0.6f); - specular.ni = 1.4f; - specular.ns = 0.05f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "mirror") - { - Material specular; - specular.kx = float3(1.f, 1.f, 1.f); - specular.ni = 1.4f; - specular.ns = 0.05f; - specular.type = kIdealReflect; - specular.fresnel = 0.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "fabric") - { - Material specular; - specular.kx = float3(0.6f, 0.6f, 0.6f); - specular.ni = 1.3f; - specular.ns = 0.3f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - specular.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - specular.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = specular.kxmapidx; - } - } - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "leather") - { - Material specular; - specular.kx = float3(0.7f, 0.7f, 0.7f); - specular.ni = 1.1f; - specular.ns = 0.03f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - specular.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - specular.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = specular.kxmapidx; - } - } - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "pillow") - { - Material specular; - specular.kx = float3(0.95f, 0.95f, 0.95f); - specular.ni = 1.3f; - specular.ns = 0.3f; - specular.type = kLambert; - specular.fresnel = 0.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - specular.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - specular.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = specular.kxmapidx; - } - } - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "chrome" || objmaterials[i].name == "wire_225143087" || objmaterials[i].name == "HeadlightChrome" || objmaterials[i].name == "Chrome") - { - Material specular; - specular.kx = float3(0.99f, 0.99f, 0.99f); - specular.ni = 10.5f; - specular.ns = 0.025f; - specular.type = kMicrofacetGGX; - specular.fresnel = 1.f; - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "brakes") - { - Material diffuse; - diffuse.kx = float3(0.f, 0.f, 0.f); - diffuse.ni = 1.3f; - diffuse.type = kLambert; - - Material specular; - specular.kx = float3(0.9f, 0.9f, 0.9f); - specular.ni = 1.3f; - specular.ns = 0.005f; - specular.type = kMicrofacetGGX; - - scene->materials_.push_back(diffuse); - scene->material_names_.push_back(objmaterials[i].name); - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.3f; - layered.type = kFresnelBlend; - layered.brdfbaseidx = scene->materials_.size() - 2; - layered.brdftopidx = scene->materials_.size() - 1; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "asphalt" || objmaterials[i].name == "Floor") - { - Material specular; - specular.kx = 0.5f * float3(0.073f, 0.085f, 0.120f); - specular.ni = 1.3f; - specular.ns = 0.25f; - specular.type = kMicrofacetGGX; - specular.fresnel = 0.f; - specular.twosided = 1; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "light" || objmaterials[i].name == "Emit1" || objmaterials[i].name == "Light3" || objmaterials[i].name == "dayLight_portal") - { - Material emissive; - emissive.kx = 10.f * float3(0.8f, 0.8f, 0.8f); - emissive.type = kEmissive; - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - emissive.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - emissive.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = emissive.kxmapidx; - } - } - - - scene->materials_.push_back(emissive); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } // - else if (objmaterials[i].name == "wire_138008110") - { - Material emissive; - emissive.kx = 5.f * float3(0.8f, 0.8f, 0.7f); - emissive.type = kEmissive; - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - emissive.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - emissive.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = emissive.nmapidx; - } - } - - - scene->materials_.push_back(emissive); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } // wire_138008110 - else if (objmaterials[i].name == "HeadLightAngelEye") - { - Material emissive; - emissive.kx = 50.f * float3(0.53f, 0.7f, 0.95f); - emissive.type = kLambert; - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - emissive.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - emissive.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = emissive.nmapidx; - } - } - - - scene->materials_.push_back(emissive); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else if (objmaterials[i].name == "HeadLightGlass") - { - Material emissive; - emissive.kx = 50.f * float3(0.64f, 0.723f, 0.8f); - emissive.type = kLambert; - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - emissive.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - emissive.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = emissive.nmapidx; - } - } - - - scene->materials_.push_back(emissive); - scene->material_names_.push_back(objmaterials[i].name); - matmap[i] = scene->materials_.size() - 1; - continue; - } - else - { - Material material; - - material.kx = float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2]); - material.ni = objmaterials[i].ior; - material.type = kLambert; - material.fresnel = 0.f; - - // Load diffuse texture if needed - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - material.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - material.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = material.kxmapidx; - } - } - - // Load specular texture - /*if (!objmaterials[i].specular_texname.empty()) - { - auto iter = textures.find(objmaterials[i].specular_texname); - if (iter != textures.end()) - { - material.ksmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].specular_texname, texture, scene->texturedata_); - - // Add texture desc - material.ksmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].specular_texname] = material.ksmapidx; - } - }*/ - - // Load normal map - - - - scene->materials_.push_back(material); - scene->material_names_.push_back(objmaterials[i].name); - - float3 spec = float3(0.5f, 0.5f, 0.5f);// float3(objmaterials[i].specular[0], objmaterials[i].specular[1], objmaterials[i].specular[2]); - if (spec.sqnorm() > 0.f) - { - Material specular; - specular.kx = spec; - specular.ni = 1.33f;//objmaterials[i].ior; - specular.ns = 0.05f;//1.f - objmaterials[i].shininess; - specular.type = kMicrofacetGGX; - specular.nmapidx = -1;// scene->materials_.back().nmapidx; - specular.fresnel = 1.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.9f;// objmaterials[i].ior; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - layered.fresnel = 1.f; - layered.twosided = 1; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - } - - matmap[i] = scene->materials_.size() - 1; - } - } - - // Enumerate all shapes in the scene - for (int s = 0; s < (int)objshapes.size(); ++s) - { - // Prepare shape - Shape shape; - shape.startidx = (int)scene->indices_.size(); - shape.numprims = (int)objshapes[s].mesh.indices.size() / 3; - shape.startvtx = (int)scene->vertices_.size(); - shape.numvertices = (int)objshapes[s].mesh.positions.size() / 3; - shape.m = matrix(); - shape.linearvelocity = float3(0.0f, 0.f, 0.f); - shape.angularvelocity = quaternion(0.f, 0.f, 0.f, 1.f); - // Save last index to add to this shape indices - // int baseidx = (int)scene->vertices_.size(); - - int pos_count = (int)objshapes[s].mesh.positions.size() / 3; - // Enumerate and copy vertex data - for (int i = 0; i < pos_count; ++i) - { - scene->vertices_.push_back(float3(objshapes[s].mesh.positions[3 * i], objshapes[s].mesh.positions[3 * i + 1], objshapes[s].mesh.positions[3 * i + 2])); - } - - for (int i = 0; i < (int)objshapes[s].mesh.normals.size() / 3; ++i) - { - scene->normals_.push_back(float3(objshapes[s].mesh.normals[3 * i], objshapes[s].mesh.normals[3 * i + 1], objshapes[s].mesh.normals[3 * i + 2])); - } - - //check UV - int texcoords_count = objshapes[s].mesh.texcoords.size() / 2; - if (texcoords_count == pos_count) - { - for (int i = 0; i < texcoords_count; ++i) - { - float2 uv = float2(objshapes[s].mesh.texcoords[2 * i], objshapes[s].mesh.texcoords[2 * i + 1]); - scene->uvs_.push_back(uv); - } - } - else - { - for (int i = 0; i < pos_count; ++i) - { - scene->uvs_.push_back(float2(0, 0)); - } - } - - // Enumerate and copy indices (accounting for base index) and material indices - for (int i = 0; i < (int)objshapes[s].mesh.indices.size() / 3; ++i) - { - scene->indices_.push_back(objshapes[s].mesh.indices[3 * i]); - scene->indices_.push_back(objshapes[s].mesh.indices[3 * i + 1]); - scene->indices_.push_back(objshapes[s].mesh.indices[3 * i + 2]); - - int matidx = matmap[objshapes[s].mesh.material_ids[i]]; - scene->materialids_.push_back(matidx); - - if (scene->materials_[matidx].type == kEmissive) - { - Light light; - light.type = kArea; - light.shapeidx = s; - light.primidx = i; - light.matidx = matidx; - scene->lights_.push_back(light); - } - } - - scene->shapes_.push_back(shape); - } - - // Check if there is no UVs - if (scene->uvs_.empty()) - { - scene->uvs_.resize(scene->normals_.size()); - std::fill(scene->uvs_.begin(), scene->uvs_.end(), float2(0, 0)); - } - - scene->envidx_ = -1; - - return scene; -} - -void Scene::AddDirectionalLight(RadeonRays::float3 const& d, RadeonRays::float3 const& e) -{ - Light light; - light.type = kDirectional; - light.d = normalize(d); - light.intensity = e; - lights_.push_back(light); -} - -void Scene::AddPointLight(RadeonRays::float3 const& p, RadeonRays::float3 const& e) -{ - Light light; - light.type = kPoint; - light.p = p; - light.intensity = e; - lights_.push_back(light); -} - -void Scene::AddSpotLight(RadeonRays::float3 const& p, RadeonRays::float3 const& d, RadeonRays::float3 const& e, float ia, float oa) -{ - Light light; - light.type = kSpot; - light.p = p; - light.d = normalize(d); - light.intensity = e; - light.ia = ia; - light.oa = oa; - lights_.push_back(light); -} - -void Scene::SetEnvironment(std::string const& filename, std::string const& basepath, float envmapmul) -{ - // Save multiplier - envmapmul_ = envmapmul; - - Texture texture; - - // Load texture - if (!basepath.empty()) - { - LoadTexture(basepath + "/" + filename, texture, texturedata_); - } - else - { - LoadTexture(filename, texture, texturedata_); - } - - // - //Ibl* ibl = new Ibl((float3*)(texturedata_[texture.dataoffset].get()), texture.w, texture.h); - //ibl->Simulate("pdf.png"); - - - // Save index - envidx_ = (int)textures_.size(); - - // Add texture desc - textures_.push_back(texture); -} - -void Scene::SetBackground(std::string const& filename, std::string const& basepath) -{ - Texture texture; - - // Load texture - if (!basepath.empty()) - { - LoadTexture(basepath + "/" + filename, texture, texturedata_); - } - else - { - LoadTexture(filename, texture, texturedata_); - } - - // Save index - bgimgidx_ = (int)textures_.size(); - - // Add texture desc - textures_.push_back(texture); -} -} diff --git a/App/Scene/scene.h b/App/Scene/scene.h deleted file mode 100755 index 0bbc3732..00000000 --- a/App/Scene/scene.h +++ /dev/null @@ -1,269 +0,0 @@ -/********************************************************************** -Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -********************************************************************/ -#pragma once - -#include "math/mathutils.h" -#include "perspective_camera.h" -#include -#include -#include - -namespace Baikal -{ - class Scene - { - public: - // Load the scene from OBJ file - static Scene* LoadFromObj(std::string const& filename, std::string const& basepath = ""); - - Scene() : dirty_(kCamera) - { - } - - void SetEnvironment(std::string const& filename, std::string const& basepath = "", float envmapmul = 1.f); - - void SetBackground(std::string const& filename, std::string const& basepath = ""); - - void AddDirectionalLight(RadeonRays::float3 const& d, RadeonRays::float3 const& e); - - void AddPointLight(RadeonRays::float3 const& p, RadeonRays::float3 const& e); - - void AddSpotLight(RadeonRays::float3 const& p, RadeonRays::float3 const& d, RadeonRays::float3 const& e, float ia, float oa); - - enum DirtyFlags - { - kNone = 0x0, - kCamera = 0x1, - kGeometry = 0x2, - kGeometryTransform = 0x4, - kMaterials = 0x8, - kMaterialInputs = 0x16, - kTextures = 0x32, - kEnvironmentLight = 0x64, - }; - - enum Bxdf - { - kZero, - kLambert, - kIdealReflect, - kIdealRefract, - kMicrofacetBlinn, - kMicrofacetBeckmann, - kMicrofacetGGX, - kLayered, - kFresnelBlend, - kMix, - kEmissive, - kPassthrough, - kTranslucent, - kMicrofacetRefractionGGX, - kMicrofacetRefractionBeckmann - }; - - // Material description - struct Material - { - RadeonRays::float3 kx; - float ni; - float ns; - - union - { - // Color map index - int kxmapidx; - // - int brdftopidx; - }; - - union - { - // Normal map index - int nmapidx; - // - int brdfbaseidx; - }; - - union - { - // Parameter map idx - int nsmapidx; - }; - - union - { - float fresnel; - }; - - // BXDF type - int type; - int twosided; - - Material() : - kx(0.7f, 0.7f, 0.7f, 1.f) - , ni(25.f) - , ns(0.05f) - , kxmapidx(-1) - , nmapidx(-1) - , nsmapidx(-1) - , fresnel(0) - , type (kLambert) - , twosided(0) - { - } - }; - - // Texture format - enum TextureFormat - { - UNKNOWN, - RGBA8, - RGBA16, - RGBA32 - }; - - // Texture description - struct Texture - { - // Texture width, height and depth - int w; - int h; - int d; - int dataoffset; - int fmt; - int size; - }; - - // Shape description - struct Shape - { - // Shape starting index in indices_ array - int startidx; - // Number of primitives in the shape - int numprims; - // Start vertex - int startvtx; - // Number of vertices - int numvertices; - // Linear velocity - RadeonRays::float3 linearvelocity; - // Angular velocity - RadeonRays::quaternion angularvelocity; - // Transform - RadeonRays::matrix m; - }; - - enum LightType - { - kPoint = 0x1, - kDirectional, - kSpot, - kArea, - kIbl - }; - - struct Light - { - int type; - - union - { - // Area light - struct - { - int shapeidx; - int primidx; - int matidx; - }; - - // IBL - struct - { - int tex; - int texdiffuse; - float multiplier; - }; - - // Spot - struct - { - float ia; - float oa; - float f; - }; - }; - - RadeonRays::float3 p; - RadeonRays::float3 d; - RadeonRays::float3 intensity; - }; - - struct Volume - { - int type; - int phase_func; - int data; - int extra; - - RadeonRays::float3 sigma_a; - RadeonRays::float3 sigma_s; - RadeonRays::float3 sigma_e; - }; - - void clear_dirty() const { dirty_ = kNone; } - std::uint32_t dirty() const { return dirty_; } - void set_dirty(int dirty) const { dirty_ = dirty_ | dirty; } - - // Scene data - // Vertices - std::vector vertices_; - std::vector normals_; - std::vector uvs_; - // Primitive indices - std::vector indices_; - // Shapes: index which points to indices array - std::vector shapes_; - // Emissive primitive indices - std::vector lights_; - // Material indices per primitive - std::vector materialids_; - // Material descriptions - std::vector materials_; - std::vector material_names_; - // Textures - std::vector textures_; - // Texture data - std::vector> texturedata_; - // Camera - std::unique_ptr camera_; - // Environment texture index - int envidx_; - // Environment low-res texture index - int envlowresidx_; - // Multiplier for envmap power - float envmapmul_; - // Background image - int bgimgidx_; - // Dirty flags - mutable int dirty_; - }; - } diff --git a/App/Scene/scene1.cpp b/App/Scene/scene1.cpp new file mode 100644 index 00000000..bc376c1f --- /dev/null +++ b/App/Scene/scene1.cpp @@ -0,0 +1,135 @@ +#include "scene1.h" +#include "light.h" +#include "camera.h" +#include "iterator.h" + +#include +#include +#include + +namespace Baikal +{ + using ShapeList = std::vector; + using LightList = std::vector; + + struct Scene1::SceneImpl + { + ShapeList m_shapes; + LightList m_lights; + + Camera const* m_camera; + + DirtyFlags m_dirty_flags; + }; + + Scene1::Scene1() + : m_impl(new SceneImpl) + { + m_impl->m_camera = nullptr; + ClearDirtyFlags(); + } + + Scene1::~Scene1() + { + } + + Scene1::DirtyFlags Scene1::GetDirtyFlags() const + { + return m_impl->m_dirty_flags; + } + + void Scene1::ClearDirtyFlags() const + { + m_impl->m_dirty_flags = 0; + } + + void Scene1::SetDirtyFlag(DirtyFlags flag) const + { + m_impl->m_dirty_flags = m_impl->m_dirty_flags | flag; + } + + void Scene1::SetCamera(Camera const* camera) + { + m_impl->m_camera = camera; + SetDirtyFlag(kCamera); + } + + Camera const* Scene1::GetCamera() const + { + return m_impl->m_camera; + } + + void Scene1::AttachLight(Light const* light) + { + assert(light); + + LightList::const_iterator citer = std::find(m_impl->m_lights.cbegin(), m_impl->m_lights.cend(), light); + + if (citer == m_impl->m_lights.cend()) + { + m_impl->m_lights.push_back(light); + + SetDirtyFlag(kLights); + } + } + + void Scene1::DetachLight(Light const* light) + { + LightList::const_iterator citer = std::find(m_impl->m_lights.cbegin(), m_impl->m_lights.cend(), light); + + if (citer != m_impl->m_lights.cend()) + { + m_impl->m_lights.erase(citer); + + SetDirtyFlag(kLights); + } + } + + std::size_t Scene1::GetNumLights() const + { + return m_impl->m_lights.size(); + } + + Iterator* Scene1::CreateShapeIterator() const + { + return new IteratorImpl(m_impl->m_shapes.begin(), m_impl->m_shapes.end()); + } + + void Scene1::AttachShape(Shape const* shape) + { + assert(shape); + + ShapeList::const_iterator citer = std::find(m_impl->m_shapes.cbegin(), m_impl->m_shapes.cend(), shape); + + if (citer == m_impl->m_shapes.cend()) + { + m_impl->m_shapes.push_back(shape); + + SetDirtyFlag(kShapes); + } + } + + void Scene1::DetachShape(Shape const* shape) + { + assert(shape); + + ShapeList::const_iterator citer = std::find(m_impl->m_shapes.cbegin(), m_impl->m_shapes.cend(), shape); + + if (citer != m_impl->m_shapes.cend()) + { + m_impl->m_shapes.erase(citer); + + SetDirtyFlag(kShapes); + } + } + + std::size_t Scene1::GetNumShapes() const + { + return m_impl->m_shapes.size(); + } + + Iterator* Scene1::CreateLightIterator() const + { + return new IteratorImpl(m_impl->m_lights.begin(), m_impl->m_lights.end()); + } +} diff --git a/App/Scene/scene1.h b/App/Scene/scene1.h new file mode 100644 index 00000000..39c4fe07 --- /dev/null +++ b/App/Scene/scene1.h @@ -0,0 +1,103 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file scene.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains declaration of Baikal::Scene1 class, core interface representing representing the scene. + */ +#pragma once + +#include + +namespace Baikal +{ + class Light; + class Shape; + class Volume; + class Camera; + class Iterator; + + /** + \brief Scene class. + + Scene represents a collection of objects such as ligths, meshes, volumes, etc. It also has a functionality + to add, remove and change these objects. + */ + class Scene1 + { + public: + + using DirtyFlags = std::uint32_t; + + enum + { + kNone, + kLights, + kShapes, + kShapeTransforms, + kCamera + }; + + // Constructor + Scene1(); + // Destructor + ~Scene1(); + + // Add or remove lights + void AttachLight(Light const* light); + void DetachLight(Light const* light); + + // TODO: make it iterable + std::size_t GetNumLights() const; + Iterator* CreateLightIterator() const; + + // Add or remove shapes + void AttachShape(Shape const* shape); + void DetachShape(Shape const* shape); + + // TODO: make it iterable + std::size_t GetNumShapes() const; + Iterator* CreateShapeIterator() const; + + // Set camera + void SetCamera(Camera const* camera); + Camera const* GetCamera() const; + + // Get state change since last clear + DirtyFlags GetDirtyFlags() const; + // Set specified flag in dirty state + void SetDirtyFlag(DirtyFlags flag) const; + // Clear all flags + void ClearDirtyFlags() const; + + // Forbidden stuff + Scene1(Scene1 const&) = delete; + Scene1& operator = (Scene1 const&) = delete; + + private: + struct SceneImpl; + std::unique_ptr m_impl; + }; + +} diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index 4e0f6437..a064b5ba 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -1,9 +1,17 @@ #include "Scene/scene_tracker.h" -#include "Scene/scene.h" +#include "Scene/scene1.h" +#include "Scene/camera.h" +#include "Scene/light.h" +#include "Scene/shape.h" +#include "Scene/material.h" +#include "Scene/texture.h" #include "CLW/clwscene.h" -#include "perspective_camera.h" +#include "Scene/Collector/collector.h" +#include "iterator.h" #include +#include +#include using namespace RadeonRays; @@ -12,7 +20,6 @@ namespace Baikal { SceneTracker::SceneTracker(CLWContext context, int devidx) : m_context(context) - , m_vidmem_usage(0) { // Get raw CL data out of CLW context cl_device_id id = m_context.GetDevice(devidx).GetID(); @@ -34,106 +41,413 @@ namespace Baikal SceneTracker::~SceneTracker() { - //Flush(); // Delete API IntersectionApi::Delete(m_api); } - ClwScene& SceneTracker::CompileScene(Scene const& scene) const + ClwScene& SceneTracker::CompileScene(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector) const { + // The overall approach is: + // 1) Check if materials have changed, update collector if yes + // 2) Check if textures have changed, update collector if yes + // Note, that material are collected from shapes (potentially recursively). + // Textures are collected from materials and lights. + // As soon as we updated both collectors we have established + // correct pointer to buffer index mapping for both materials and textures. + // As soon as we have this mapping we are analyzing dirty flags and + // updating necessary parts. + + // We need to make sure collectors are empty before proceeding + mat_collector.Clear(); + tex_collector.Clear(); + + // Create shape and light iterators + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + std::unique_ptr light_iter(scene.CreateLightIterator()); + + // Collect materials from shapes first + mat_collector.Collect(shape_iter.get(), + // This function adds all materials to resulting map + // recursively via Material dependency API + [](void const* item) -> std::set + { + // Resulting material set + std::set mats; + // Material stack + std::stack material_stack; + + // Get material from current shape + auto shape = reinterpret_cast(item); + auto material = shape->GetMaterial(); + + // Push to stack as an initializer + material_stack.push(material); + + // Drain the stack + while (!material_stack.empty()) + { + // Get current material + Material const* m = material_stack.top(); + material_stack.pop(); + + // Emplace into the set + mats.emplace(m); + + // Create dependency iterator + std::unique_ptr mat_iter(m->CreateMaterialIterator()); + + // Push all dependencies into the stack + for (;mat_iter->IsValid(); mat_iter->Next()) + { + material_stack.push(mat_iter->ItemAs()); + } + } + + // Return resulting set + return mats; + }); + + // Commit stuff (we can iterate over it after commit has happened) + mat_collector.Commit(); + + // Now we need to collect textures from our materials + // Create material iterator + std::unique_ptr mat_iter(mat_collector.CreateIterator()); + + // Collect textures from materials + tex_collector.Collect(mat_iter.get(), + [](void const* item) -> std::set + { + // Texture set + std::set textures; + + auto material = reinterpret_cast(item); + + // Create texture dependency iterator + std::unique_ptr tex_iter(material->CreateTextureIterator()); + + // Emplace all dependent textures + for (;tex_iter->IsValid(); tex_iter->Next()) + { + textures.emplace(tex_iter->ItemAs()); + } + + // Return resulting set + return textures; + }); + + + // Collect textures from lights + tex_collector.Collect(light_iter.get(), + [](void const* item) -> std::set + { + // Resulting set + std::set textures; + + auto light = reinterpret_cast(item); + + // Create texture dependency iterator + std::unique_ptr tex_iter(light->CreateTextureIterator()); + + // Emplace all dependent textures + for (;tex_iter->IsValid(); tex_iter->Next()) + { + textures.emplace(tex_iter->ItemAs()); + } + + // Return resulting set + return textures; + }); + + // Commit textures + tex_collector.Commit(); + + // Try to find scene in cache first auto iter = m_scene_cache.find(&scene); if (iter == m_scene_cache.cend()) { + // If not found create scene entry in cache auto res = m_scene_cache.emplace(std::make_pair(&scene, ClwScene())); - RecompileFull(scene, res.first->second); + // Recompile all the stuff into cached scene + RecompileFull(scene, mat_collector, tex_collector, res.first->second); + // Load intersector data ReloadIntersector(scene, res.first->second); + // Set scene as current m_current_scene = &scene; - + + scene.ClearDirtyFlags(); + + mat_collector.Finalize([](void const* item) + { + auto material = reinterpret_cast(item); + material->SetDirty(false); + }); + + // Return the scene return res.first->second; } else { + // Exctract cached scene entry auto& out = iter->second; + auto dirty = scene.GetDirtyFlags(); - if (scene.dirty() & Scene::DirtyFlags::kCamera) + // Update camera if needed + if (dirty & Scene1::kCamera) { - UpdateCamera(scene, out); + UpdateCamera(scene, mat_collector, tex_collector, out); } - - if (scene.dirty() & Scene::DirtyFlags::kGeometry) + + // Update lights if needed + if (dirty & Scene1::kLights) { - UpdateGeometry(scene, out); + UpdateLights(scene, mat_collector, tex_collector, out); } - else if (scene.dirty() & Scene::DirtyFlags::kGeometryTransform) + + // Update shapes if needed + if (dirty & Scene1::kShapes) { - // TODO: this is not yet supported in the renderer + UpdateShapes(scene, mat_collector, tex_collector, out); } - - if (scene.dirty() & Scene::DirtyFlags::kMaterials) + else if (dirty & Scene1::kShapeTransforms) { - UpdateMaterials(scene, out); + // TODO: this is not yet supported in the renderer } - else if (scene.dirty() & Scene::DirtyFlags::kMaterialInputs) + + // If materials need an update, do it. + // We are passing material dirty state detection function in there. + if (!out.material_bundle || + mat_collector.NeedsUpdate(out.material_bundle.get(), + [](void const* material)->bool + { + auto mat = reinterpret_cast(material); + return mat->IsDirty(); + } + )) { - UpdateMaterialInputs(scene, out); + UpdateMaterials(scene, mat_collector, tex_collector, out); } + // If textures need an update, do it. + if (tex_collector.GetNumItems() > 0 && ( + !out.texture_bundle || + tex_collector.NeedsUpdate(out.texture_bundle.get(), [](void const*){return false;}))) + { + UpdateTextures(scene, mat_collector, tex_collector, out); + } + + // Set current scene if (m_current_scene != &scene) { + // If we are changing current scene need + // to reload the intersector ReloadIntersector(scene, out); m_current_scene = &scene; } - scene.clear_dirty(); - + // Make sure to clear dirty flags + scene.ClearDirtyFlags(); + + // Clear material dirty flags + mat_collector.Finalize([](void const* item) + { + auto material = reinterpret_cast(item); + material->SetDirty(false); + }); + + // Return the scene return out; } } - void SceneTracker::UpdateCamera(Scene const& scene, ClwScene& out) const + void SceneTracker::UpdateCamera(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const { - // Update camere type - out.camera_type = scene.camera_->GetAperture() > 0.f ? CameraType::kPhysical : CameraType::kDefault; + // TODO: support different camera types here + auto camera = static_cast(scene.GetCamera()); + + // TODO: remove this + out.camera_type = camera->GetAperture() > 0.f ? CameraType::kPhysical : CameraType::kDefault; // Update camera data - m_context.WriteBuffer(0, out.camera, scene.camera_.get(), 1); + ClwScene::Camera* data = nullptr; + + // Map GPU camera buffer + m_context.MapBuffer(0, out.camera, CL_MAP_WRITE, &data).Wait(); + + // Copy camera parameters + data->forward = camera->GetForwardVector(); + data->up = camera->GetUpVector(); + data->right = camera->GetRightVector(); + data->p = camera->GetPosition(); + data->aperture = camera->GetAperture(); + data->aspect=camera->GetAspectRatio(); + data->dim = camera->GetSensorSize(); + data->focal_length = camera->GetFocalLength(); + data->focus_distance = camera->GetFocusDistance(); + data->zcap = camera->GetDepthRange(); + + // Unmap camera buffer + m_context.UnmapBuffer(0, out.camera, data); + } - void SceneTracker::UpdateGeometry(Scene const& scene, ClwScene& out) const + void SceneTracker::UpdateShapes(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const { - out.vertices = m_context.CreateBuffer(scene.vertices_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.vertices_[0]); - - out.normals = m_context.CreateBuffer(scene.normals_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.normals_[0]); - - out.uvs = m_context.CreateBuffer(scene.uvs_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.uvs_[0]); - - out.indices = m_context.CreateBuffer(scene.indices_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.indices_[0]); - - out.shapes = m_context.CreateBuffer(scene.shapes_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.shapes_[0]); - } - - void SceneTracker::UpdateMaterials(Scene const& scene, ClwScene& out) const - { - // Material IDs - out.materialids = m_context.CreateBuffer(scene.materialids_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.materialids_[0]); - - // Material descriptions - out.materials = m_context.CreateBuffer(scene.materials_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.materials_[0]); + std::size_t num_vertices = 0; + std::size_t num_normals = 0; + std::size_t num_uvs = 0; + std::size_t num_indices = 0; + + std::size_t num_vertices_written = 0; + std::size_t num_normals_written = 0; + std::size_t num_uvs_written = 0; + std::size_t num_indices_written = 0; + std::size_t num_matids_written = 0; + std::size_t num_shapes_written = 0; + + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + + // Calculate array sizes upfront + for (;shape_iter->IsValid(); shape_iter->Next()) + { + auto mesh = shape_iter->ItemAs(); + + num_vertices += mesh->GetNumVertices(); + num_normals += mesh->GetNumNormals(); + num_uvs += mesh->GetNumUVs(); + num_indices += mesh->GetNumIndices(); + } + + // Create CL arrays + out.vertices = m_context.CreateBuffer(num_vertices, CL_MEM_READ_ONLY); + out.normals = m_context.CreateBuffer(num_normals, CL_MEM_READ_ONLY); + out.uvs = m_context.CreateBuffer(num_uvs, CL_MEM_READ_ONLY); + out.indices = m_context.CreateBuffer(num_indices, CL_MEM_READ_ONLY); + out.shapes = m_context.CreateBuffer(scene.GetNumShapes(), CL_MEM_READ_ONLY); + out.materialids = m_context.CreateBuffer(num_indices / 3, CL_MEM_READ_ONLY); + + float3* vertices = nullptr; + float3* normals = nullptr; + float2* uvs = nullptr; + int* indices = nullptr; + int* matids = nullptr; + ClwScene::Shape* shapes = nullptr; + + // Map arrays and prepare to write data + m_context.MapBuffer(0, out.vertices, CL_MAP_WRITE, &vertices); + m_context.MapBuffer(0, out.normals, CL_MAP_WRITE, &normals); + m_context.MapBuffer(0, out.uvs, CL_MAP_WRITE, &uvs); + m_context.MapBuffer(0, out.indices, CL_MAP_WRITE, &indices); + m_context.MapBuffer(0, out.materialids, CL_MAP_WRITE, &matids); + m_context.MapBuffer(0, out.shapes, CL_MAP_WRITE, &shapes).Wait(); + + shape_iter->Reset(); + for (;shape_iter->IsValid(); shape_iter->Next()) + { + // TODO: support instances here + auto mesh = shape_iter->ItemAs(); + + // Get pointers data + auto mesh_vertex_array = mesh->GetVertices(); + auto mesh_num_vertices = mesh->GetNumVertices(); + + auto mesh_normal_array = mesh->GetNormals(); + auto mesh_num_normals = mesh->GetNumNormals(); + + auto mesh_uv_array = mesh->GetUVs(); + auto mesh_num_uvs = mesh->GetNumUVs(); + + auto mesh_index_array = mesh->GetIndices(); + auto mesh_num_indices = mesh->GetNumIndices(); + + // Prepare shape descriptor + ClwScene::Shape shape; + shape.numprims = static_cast(mesh_num_indices / 3); + shape.startvtx = static_cast(num_vertices_written); + shape.numvertices = static_cast(mesh_num_vertices); + shape.startidx = static_cast(num_indices_written); + shape.m0 = float4(1.0f, 0.f, 0.f, 0.f); + shape.m1 = float4(0.0f, 1.f, 0.f, 0.f); + shape.m2 = float4(0.0f, 0.f, 1.f, 0.f); + shape.m3 = float4(0.0f, 0.f, 0.f, 1.f); + shape.linearvelocity = float3(0.0f, 0.f, 0.f); + shape.angularvelocity = float3(0.f, 0.f, 0.f, 1.f); + + std::copy(mesh_vertex_array, mesh_vertex_array + mesh_num_vertices, vertices + num_vertices_written); + num_vertices_written += mesh_num_vertices; + + std::copy(mesh_normal_array, mesh_normal_array + mesh_num_normals, normals + num_normals_written); + num_normals_written += mesh_num_normals; + + std::copy(mesh_uv_array, mesh_uv_array + mesh_num_uvs, uvs + num_uvs_written); + num_uvs_written += mesh_num_uvs; + + std::copy(mesh_index_array, mesh_index_array + mesh_num_indices, indices + num_indices_written); + num_indices_written += mesh_num_indices; + + shapes[num_shapes_written] = shape; + ++num_shapes_written; + + std::fill(matids + num_matids_written, matids + num_matids_written + mesh_num_indices / 3, + mat_collector.GetItemIndex(mesh->GetMaterial())); + + num_matids_written += mesh_num_indices / 3; + } + + m_context.UnmapBuffer(0, out.vertices, vertices); + m_context.UnmapBuffer(0, out.normals, normals); + m_context.UnmapBuffer(0, out.uvs, uvs); + m_context.UnmapBuffer(0, out.indices, indices); + m_context.UnmapBuffer(0, out.materialids, matids); + m_context.UnmapBuffer(0, out.shapes, shapes); } - void SceneTracker::UpdateMaterialInputs(Scene const& scene, ClwScene& out) const + void SceneTracker::UpdateMaterials(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const { - m_context.WriteBuffer(0, out.materials, &scene.materials_[0], out.materials.GetElementCount()); + // Get new buffer size + std::size_t mat_buffer_size = mat_collector.GetNumItems(); + + // Recreate material buffer if it needs resize + if (mat_buffer_size > out.materials.GetElementCount()) + { + // Create material buffer + out.materials = m_context.CreateBuffer(mat_buffer_size, CL_MEM_READ_ONLY); + } + + ClwScene::Material* materials = nullptr; + std::size_t num_materials_written = 0; + + // Map GPU materials buffer + m_context.MapBuffer(0, out.materials, CL_MAP_WRITE, &materials).Wait(); + + // Serialize + { + // Update material bundle first to be able to track differences + out.material_bundle.reset(mat_collector.CreateBundle()); + + // Create material iterator + std::unique_ptr mat_iter(mat_collector.CreateIterator()); + + // Iterate and serialize + for (;mat_iter->IsValid(); mat_iter->Next()) + { + WriteMaterial(mat_iter->ItemAs(), mat_collector, tex_collector, materials + num_materials_written); + ++num_materials_written; + } + } + + // Unmap material buffer + m_context.UnmapBuffer(0, out.materials, materials); } - void SceneTracker::RecompileFull(Scene const& scene, ClwScene& out) const + void SceneTracker::RecompileFull(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const { - // This usually unnecessary, but just in case we reuse out parameter here + // This is usually unnecessary, but just in case we reuse "out" parameter here for (auto& s : out.isect_shapes) { m_api->DeleteShape(s); @@ -141,104 +455,70 @@ namespace Baikal out.isect_shapes.clear(); - m_vidmem_usage = 0; - - // Create static buffers - out.camera = m_context.CreateBuffer(1, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, scene.camera_.get()); - - // Vertex, normal and uv data - out.vertices = m_context.CreateBuffer(scene.vertices_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.vertices_[0]); - m_vidmem_usage += scene.vertices_.size() * sizeof(float3); - - out.normals = m_context.CreateBuffer(scene.normals_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.normals_[0]); - m_vidmem_usage += scene.normals_.size() * sizeof(float3); - - out.uvs = m_context.CreateBuffer(scene.uvs_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.uvs_[0]); - m_vidmem_usage += scene.uvs_.size() * sizeof(float2); - - // Index data - out.indices = m_context.CreateBuffer(scene.indices_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.indices_[0]); - m_vidmem_usage += scene.indices_.size() * sizeof(int); - - // Shapes - out.shapes = m_context.CreateBuffer(scene.shapes_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.shapes_[0]); - m_vidmem_usage += scene.shapes_.size() * sizeof(Scene::Shape); - - // Material IDs - out.materialids = m_context.CreateBuffer(scene.materialids_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.materialids_[0]); - m_vidmem_usage += scene.materialids_.size() * sizeof(int); - - // Material descriptions - out.materials = m_context.CreateBuffer(scene.materials_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.materials_[0]); - m_vidmem_usage += scene.materials_.size() * sizeof(Scene::Material); - - // Bake textures - BakeTextures(scene, out); - - // Emissives - if (scene.lights_.size() > 0) - { - out.lights = m_context.CreateBuffer(scene.lights_.size(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (void*)&scene.lights_[0]); - out.num_lights = scene.lights_.size(); - m_vidmem_usage += scene.lights_.size() * sizeof(Scene::Light); - } - else - { - out.lights = m_context.CreateBuffer(1, CL_MEM_READ_ONLY); - out.num_lights = 0; - m_vidmem_usage += sizeof(Scene::Light); - } - + // Create camera buffer + out.camera = m_context.CreateBuffer(1, CL_MEM_READ_ONLY); + + UpdateCamera(scene, mat_collector, tex_collector, out); + + UpdateLights(scene, mat_collector, tex_collector, out); + + UpdateShapes(scene, mat_collector, tex_collector, out); + + UpdateMaterials(scene, mat_collector, tex_collector, out); + + UpdateTextures(scene, mat_collector, tex_collector, out); + + //Volume vol = {1, 0, 0, 0, {0.9f, 0.6f, 0.9f}, {5.1f, 1.8f, 5.1f}, {0.0f, 0.0f, 0.0f}}; - Scene::Volume vol = { 1, 0, 0, 0,{ 1.2f, 0.4f, 1.2f },{ 5.1f, 4.8f, 5.1f },{ 0.0f, 0.0f, 0.0f } }; + + // Temporary code + ClwScene::Volume vol;// = { 1, 0, 0, 0, {1.2f, 0.4f, 1.2f },{ 5.1f, 4.8f, 5.1f },{ 0.0f, 0.0f, 0.0f } }; - out.volumes = m_context.CreateBuffer(1, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &vol); - - out.envmapmul = scene.envmapmul_; - out.envmapidx = scene.envidx_; - - std::cout << "Vidmem usage (data): " << m_vidmem_usage / (1024 * 1024) << "Mb\n"; - std::cout << "Polygon count " << scene.indices_.size() / 3 << "\n"; - - std::cout << "Number of objects: " << scene.shapes_.size() << "\n"; - std::cout << "Number of textures: " << scene.textures_.size() << "\n"; - std::cout << "Number of lights: " << scene.lights_.size() << "\n"; + out.volumes = m_context.CreateBuffer(1, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &vol); + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + // Enumerate all shapes in the scene - for (int i = 0; i < (int)scene.shapes_.size(); ++i) + // and submit to RadeonRays API + int id = 1; + for (;shape_iter->IsValid(); shape_iter->Next()) { - Shape* shape = nullptr; + RadeonRays::Shape* shape = nullptr; + + auto mesh = shape_iter->ItemAs(); shape = m_api->CreateMesh( // Vertices starting from the first one - (float*)&scene.vertices_[scene.shapes_[i].startvtx], + (float*)mesh->GetVertices(), // Number of vertices - scene.shapes_[i].numvertices, + static_cast(mesh->GetNumVertices()), // Stride sizeof(float3), // TODO: make API signature const - const_cast(&scene.indices_[scene.shapes_[i].startidx]), + reinterpret_cast(mesh->GetIndices()), // Index stride 0, // All triangles nullptr, // Number of primitives - (int)scene.shapes_[i].numprims + static_cast(mesh->GetNumIndices() / 3) ); + + // TODO: support this - shape->SetLinearVelocity(scene.shapes_[i].linearvelocity); + //shape->SetLinearVelocity(scene.shapes_[i].linearvelocity); - shape->SetAngularVelocity(scene.shapes_[i].angularvelocity); + //shape->SetAngularVelocity(scene.shapes_[i].angularvelocity); - shape->SetTransform(scene.shapes_[i].m, inverse(scene.shapes_[i].m)); + //shape->SetTransform(scene.shapes_[i].m, inverse(scene.shapes_[i].m)); - shape->SetId(i + 1); + shape->SetId(id++); out.isect_shapes.push_back(shape); } } - void SceneTracker::ReloadIntersector(Scene const& scene, ClwScene& inout) const + void SceneTracker::ReloadIntersector(Scene1 const& scene, ClwScene& inout) const { m_api->DetachAll(); @@ -247,86 +527,455 @@ namespace Baikal m_api->AttachShape(s); } - // Commit to intersector - auto startime = std::chrono::high_resolution_clock::now(); - m_api->Commit(); + } + + void SceneTracker::UpdateTextures(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const + { + // Get new buffer size + std::size_t tex_buffer_size = tex_collector.GetNumItems(); + std::size_t tex_data_buffer_size = 0; + + if (tex_buffer_size == 0) + { + out.textures = m_context.CreateBuffer(1, CL_MEM_READ_ONLY); + out.texturedata = m_context.CreateBuffer(1, CL_MEM_READ_ONLY); + return; + } + + // Recreate material buffer if it needs resize + if (tex_buffer_size > out.textures.GetElementCount()) + { + // Create material buffer + out.textures = m_context.CreateBuffer(tex_buffer_size, CL_MEM_READ_ONLY); + } + + ClwScene::Texture* textures = nullptr; + std::size_t num_textures_written = 0; + + // Map GPU materials buffer + m_context.MapBuffer(0, out.textures, CL_MAP_WRITE, &textures).Wait(); + + // Update material bundle first to be able to track differences + out.texture_bundle.reset(tex_collector.CreateBundle()); + + // Create material iterator + std::unique_ptr tex_iter(tex_collector.CreateIterator()); + + // Iterate and serialize + for (;tex_iter->IsValid(); tex_iter->Next()) + { + auto tex = tex_iter->ItemAs(); + + WriteTexture(tex, tex_data_buffer_size, textures + num_textures_written); + + ++num_textures_written; + + tex_data_buffer_size += tex->GetSizeInBytes(); + } + + // Unmap material buffer + m_context.UnmapBuffer(0, out.textures, textures); + + // Recreate material buffer if it needs resize + if (tex_data_buffer_size < out.texturedata.GetElementCount()) + { + // Create material buffer + out.texturedata = m_context.CreateBuffer(tex_data_buffer_size, CL_MEM_READ_ONLY); + } + + char* data = nullptr; + std::size_t num_bytes_written = 0; + + tex_iter->Reset(); + + // Map GPU materials buffer + m_context.MapBuffer(0, out.texturedata, CL_MAP_WRITE, &data).Wait(); + + // Write texture data for all textures + for (;tex_iter->IsValid(); tex_iter->Next()) + { + auto tex = tex_iter->ItemAs(); + + WriteTextureData(tex, data + num_bytes_written); + + num_bytes_written += tex->GetSizeInBytes(); + } - auto delta = std::chrono::high_resolution_clock::now() - startime; + // Unmap material buffer + m_context.UnmapBuffer(0, out.textures, textures); - std::cout << "Commited in " << std::chrono::duration_cast(delta).count() / 1000.f << "s\n"; } - - void SceneTracker::BakeTextures(Scene const& scene, ClwScene& out) const + + // Convert Material:: types to ClwScene:: types + static ClwScene::Bxdf GetMaterialType(Material const* material) { - if (scene.textures_.size() > 0) + // Distinguish between single bxdf materials and compound ones + if (auto bxdf = dynamic_cast(material)) { - // Evaluate data size - size_t datasize = 0; - for (auto iter = scene.textures_.cbegin(); iter != scene.textures_.cend(); ++iter) + switch (bxdf->GetBxdfType()) { - datasize += iter->size; + case SingleBxdf::BxdfType::kZero: return ClwScene::Bxdf::kZero; + case SingleBxdf::BxdfType::kLambert: return ClwScene::Bxdf::kLambert; + case SingleBxdf::BxdfType::kEmissive: return ClwScene::Bxdf::kEmissive; + case SingleBxdf::BxdfType::kPassthrough: return ClwScene::Bxdf::kPassthrough; + case SingleBxdf::BxdfType::kTranslucent: return ClwScene::Bxdf::kTranslucent; + case SingleBxdf::BxdfType::kIdealReflect: return ClwScene::Bxdf::kIdealReflect; + case SingleBxdf::BxdfType::kIdealRefract: return ClwScene::Bxdf::kIdealRefract; + case SingleBxdf::BxdfType::kMicrofacetGGX: return ClwScene::Bxdf::kMicrofacetGGX; + case SingleBxdf::BxdfType::kMicrofacetBeckmann: return ClwScene::Bxdf::kMicrofacetBeckmann; + case SingleBxdf::BxdfType::kMicrofacetBlinn: return ClwScene::Bxdf::kMicrofacetBlinn; + case SingleBxdf::BxdfType::kMicrofacetRefractionGGX: return ClwScene::Bxdf::kMicrofacetRefractionGGX; + case SingleBxdf::BxdfType::kMicrofacetRefractionBeckmann: return ClwScene::Bxdf::kMicrofacetRefractionBeckmann; } - - // Texture descriptors - out.textures = m_context.CreateBuffer(scene.textures_.size(), CL_MEM_READ_ONLY); - m_vidmem_usage += scene.textures_.size() * sizeof(Scene::Texture); - - // Texture data - out.texturedata = m_context.CreateBuffer(datasize, CL_MEM_READ_ONLY); - - // Map both buffers - Scene::Texture* mappeddesc = nullptr; - char* mappeddata = nullptr; - Scene::Texture* mappeddesc_orig = nullptr; - char* mappeddata_orig = nullptr; - - m_context.MapBuffer(0, out.textures, CL_MAP_WRITE, &mappeddesc).Wait(); - m_context.MapBuffer(0, out.texturedata, CL_MAP_WRITE, &mappeddata).Wait(); - - // Save them for unmap - mappeddesc_orig = mappeddesc; - mappeddata_orig = mappeddata; - - // Write data into both buffers - int current_offset = 0; - for (auto iter = scene.textures_.cbegin(); iter != scene.textures_.cend(); ++iter) + } + else if (auto mat = dynamic_cast(material)) + { + switch (mat->GetType()) { - // Copy texture desc - Scene::Texture texture = *iter; - - // Write data into the buffer - memcpy(mappeddata, scene.texturedata_[texture.dataoffset].get(), texture.size); - m_vidmem_usage += texture.size; - - // Adjust offset in the texture - texture.dataoffset = current_offset; - - // Copy desc into the buffer - *mappeddesc = texture; - - // Adjust offset - current_offset += texture.size; - - // Adjust data pointer - mappeddata += texture.size; - - // Adjust descriptor pointer - ++mappeddesc; + case MultiBxdf::Type::kMix: return ClwScene::Bxdf::kMix; + case MultiBxdf::Type::kLayered: return ClwScene::Bxdf::kLayered; + case MultiBxdf::Type::kFresnelBlend: return ClwScene::Bxdf::kFresnelBlend; + } + } + else + { + return ClwScene::Bxdf::kZero; + } + } + + void SceneTracker::WriteMaterial(Material const* material, Collector& mat_collector, Collector& tex_collector, void* data) const + { + auto clw_material = reinterpret_cast(data); + + // Convert material type and sidedness + auto type = GetMaterialType(material); + + clw_material->type = type; + clw_material->twosided = material->IsTwoSided() ? 1 : 0; + + switch (type) + { + case ClwScene::Bxdf::kZero: + clw_material->kx = RadeonRays::float4(); + break; + + // We need to convert roughness for the following materials + case ClwScene::Bxdf::kMicrofacetBlinn: + case ClwScene::Bxdf::kMicrofacetGGX: + case ClwScene::Bxdf::kMicrofacetBeckmann: + case ClwScene::Bxdf::kMicrofacetRefractionGGX: + case ClwScene::Bxdf::kMicrofacetRefractionBeckmann: + { + Material::InputValue value = material->GetInputValue("roughness"); + + if (value.type == Material::InputType::kFloat4) + { + clw_material->ns = value.float_value.x; + clw_material->nsmapidx = -1; + } + else if (value.type == Material::InputType::kTexture) + { + clw_material->nsmapidx = value.tex_value ? tex_collector.GetItemIndex(value.tex_value) : -1; + } + else + { + // TODO: should not happen + assert(false); + } + + // Intentionally missing break here } - m_context.UnmapBuffer(0, out.textures, mappeddesc_orig).Wait(); - m_context.UnmapBuffer(0, out.texturedata, mappeddata_orig).Wait(); + // For the rest we need to conver albedo, normal map, fresnel factor, ior + case ClwScene::Bxdf::kLambert: + case ClwScene::Bxdf::kEmissive: + case ClwScene::Bxdf::kPassthrough: + case ClwScene::Bxdf::kTranslucent: + case ClwScene::Bxdf::kIdealRefract: + case ClwScene::Bxdf::kIdealReflect: + { + Material::InputValue value = material->GetInputValue("albedo"); + + if (value.type == Material::InputType::kFloat4) + { + clw_material->kx = value.float_value; + clw_material->kxmapidx = -1; + } + else if (value.type == Material::InputType::kTexture) + { + clw_material->kxmapidx = value.tex_value ? tex_collector.GetItemIndex(value.tex_value) : -1; + } + else + { + // TODO: should not happen + assert(false); + } + + value = material->GetInputValue("normal"); + + if (value.type == Material::InputType::kTexture) + { + clw_material->nmapidx = value.tex_value ? tex_collector.GetItemIndex(value.tex_value) : -1; + } + else + { + clw_material->nmapidx = -1; + } + + value = material->GetInputValue("fresnel"); + + if (value.type == Material::InputType::kFloat4) + { + clw_material->fresnel = value.float_value.x > 0 ? 1.f : 0.f; + } + else + { + clw_material->fresnel = 0.f; + } + + value = material->GetInputValue("ior"); + + if (value.type == Material::InputType::kFloat4) + { + clw_material->ni = value.float_value.x; + } + else + { + clw_material->ni = 1.f; + } + + break; + } + + // For compound materials we need to convert dependencies + // and weights. + case ClwScene::Bxdf::kMix: + case ClwScene::Bxdf::kFresnelBlend: + { + Material::InputValue value0 = material->GetInputValue("base_material"); + Material::InputValue value1 = material->GetInputValue("top_material"); + + if (value0.type == Material::InputType::kMaterial && + value1.type == Material::InputType::kMaterial) + { + clw_material->brdfbaseidx = mat_collector.GetItemIndex(value0.mat_value); + clw_material->brdftopidx = mat_collector.GetItemIndex(value1.mat_value); + } + else + { + // Should not happen + assert(false); + } + + if (type == ClwScene::Bxdf::kMix) + { + clw_material->fresnel = 0.f; + + Material::InputValue value = material->GetInputValue("weight"); + + if (value.type == Material::InputType::kTexture) + { + clw_material->nsmapidx = tex_collector.GetItemIndex(value.tex_value); + } + else + { + clw_material->nsmapidx = -1; + clw_material->ns = value.float_value.x; + } + } + else + { + clw_material->fresnel = 1.f; + + Material::InputValue value = material->GetInputValue("ior"); + + if (value.type == Material::InputType::kFloat4) + { + clw_material->ni = value.float_value.x; + } + else + { + // Should not happen + assert(false); + } + } + } + + default: + break; + } + + material->SetDirty(false); + } + + // Convert Light:: types to ClwScene:: types + static int GetLightType(Light const* light) + { + + if (dynamic_cast(light)) + { + return ClwScene::kPoint; + } + else if (dynamic_cast(light)) + { + return ClwScene::kDirectional; + } + else if (dynamic_cast(light)) + { + return ClwScene::kSpot; + } + else if (dynamic_cast(light)) + { + return ClwScene::kIbl; } else { - // Create stub - out.textures = m_context.CreateBuffer(1, CL_MEM_READ_ONLY); - m_vidmem_usage += sizeof(Scene::Texture); + return ClwScene::LightType::kArea; + } + } + + void SceneTracker::WriteLight(Scene1 const& scene, Light const* light, Collector& tex_collector, void* data) const + { + auto clw_light = reinterpret_cast(data); + + auto type = GetLightType(light); + + clw_light->type = type; + + switch (type) + { + case ClwScene::kPoint: + { + clw_light->p = light->GetPosition(); + clw_light->intensity = light->GetEmittedRadiance(); + break; + } + + case ClwScene::kDirectional: + { + clw_light->d = light->GetDirection(); + clw_light->intensity = light->GetEmittedRadiance(); + break; + } + + case ClwScene::kSpot: + { + clw_light->p = light->GetPosition(); + clw_light->d = light->GetDirection(); + clw_light->intensity = light->GetEmittedRadiance(); + + auto cone_shape = static_cast(light)->GetConeShape(); + clw_light->ia = cone_shape.x; + clw_light->oa = cone_shape.y; + break; + } + + case ClwScene::kIbl: + { + // TODO: support this + clw_light->multiplier = 1.f; + auto tex = static_cast(light)->GetTexture(); + clw_light->tex = tex_collector.GetItemIndex(tex); + clw_light->texdiffuse = clw_light->tex; + break; + } + + case ClwScene::kArea: + { + // TODO: optimize this linear search + auto shape = static_cast(light)->GetShape(); + + std::size_t idx = -1; + + for (auto iter = scene.CreateShapeIterator(); iter->IsValid(); iter->Next(), ++idx) + { + if (iter->ItemAs() == shape) + break; + } + + clw_light->shapeidx = static_cast(idx); + clw_light->primidx = static_cast(static_cast(light)->GetPrimitiveIdx()); + break; + } + + + default: + assert(false); + break; + } + } + + void SceneTracker::UpdateLights(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const + { + std::size_t num_lights_written = 0; + + auto num_lights = scene.GetNumLights(); + + // Create light buffer if needed + if (num_lights > out.lights.GetElementCount()) + { + out.lights = m_context.CreateBuffer(num_lights, CL_MEM_READ_ONLY); + } + + ClwScene::Light* lights = nullptr; + + m_context.MapBuffer(0, out.lights, CL_MAP_WRITE, &lights).Wait(); + + std::unique_ptr light_iter(scene.CreateLightIterator()); + + // Serialize + { + for (;light_iter->IsValid(); light_iter->Next()) + { + WriteLight(scene, light_iter->ItemAs(), tex_collector, lights + num_lights_written); + ++num_lights_written; + + // TODO: temporary code + // Find and update IBL idx + auto ibl = dynamic_cast(light_iter->ItemAs()); + if (ibl) + { + out.envmapmul = 1.f; + out.envmapidx = tex_collector.GetItemIndex(ibl->GetTexture()); + } + } + } - // Texture data - out.texturedata = m_context.CreateBuffer(1, CL_MEM_READ_ONLY); - m_vidmem_usage += 1; + m_context.UnmapBuffer(0, out.lights, lights); + + out.num_lights = num_lights_written; + } + + + // Convert texture format into ClwScene:: types + static ClwScene::TextureFormat GetTextureFormat(Texture const* texture) + { + switch (texture->GetFormat()) + { + case Texture::Format::kRgba8: return ClwScene::TextureFormat::RGBA8; + case Texture::Format::kRgba16: return ClwScene::TextureFormat::RGBA16; + case Texture::Format::kRgba32: return ClwScene::TextureFormat::RGBA32; + default: return ClwScene::TextureFormat::RGBA8; } } + + void SceneTracker::WriteTexture(Texture const* texture, std::size_t data_offset, void* data) const + { + auto clw_texture = reinterpret_cast(data); + + auto dim = texture->GetSize(); + + clw_texture->w = dim.x; + clw_texture->h = dim.y; + clw_texture->fmt = GetTextureFormat(texture); + clw_texture->dataoffset = static_cast(data_offset); + } + + void SceneTracker::WriteTextureData(Texture const* texture, void* data) const + { + auto begin = texture->GetData(); + auto end = begin + texture->GetSizeInBytes(); + std::copy(begin, end, static_cast(data)); + } } diff --git a/App/Scene/scene_tracker.h b/App/Scene/scene_tracker.h index 424bb9d8..9f20c73c 100755 --- a/App/Scene/scene_tracker.h +++ b/App/Scene/scene_tracker.h @@ -1,3 +1,31 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file scene_tracker.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains SceneTracker class implementation. + */ #pragma once #include @@ -7,32 +35,74 @@ namespace Baikal { - class Scene; + class Scene1; struct ClwScene; + class Collector; + class Bundle; + class Material; + class Light; + class Texture; + + /** + \brief Tracks changes of a scene and serialized data into GPU memory when needed. + + SceneTracker class is intended to keep track of CPU side scene changes and update all + necessary GPU buffers. It essentially establishes a mapping between Scene class and + corresponding ClwScene class. It also pre-caches ClwScenes and speeds up loading for + already compiled scenes. + */ class SceneTracker { public: + // Constructor SceneTracker(CLWContext context, int devidx); - + // Destructor virtual ~SceneTracker(); - virtual ClwScene& CompileScene(Scene const& scene) const; + /** + \brief Given a scene this method produces (or loads from cache) corresponding GPU representation. + + \param scene CPU scene. + \param mat_collector Material collector helping to track materials. + \param tex_collector Texture collector helping to track textures. + */ + virtual ClwScene& CompileScene(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector) const; + /** + \brief Get underlying intersection API. + */ RadeonRays::IntersectionApi* GetIntersectionApi() { return m_api; } protected: - virtual void RecompileFull(Scene const& scene, ClwScene& out) const; - virtual void BakeTextures(Scene const& scene, ClwScene& out) const; - virtual void ReloadIntersector(Scene const& scene, ClwScene& inout) const; + // Recompile the scene from scratch, i.e. not loading from cache. + // All the buffers are recreated and reloaded. + void RecompileFull(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const; + // Clear intersector and load meshes into it. + void ReloadIntersector(Scene1 const& scene, ClwScene& inout) const; private: - void UpdateCamera(Scene const& scene, ClwScene& out) const; - void UpdateGeometry(Scene const& scene, ClwScene& out) const; - void UpdateMaterials(Scene const& scene, ClwScene& out) const; - void UpdateMaterialInputs(Scene const& scene, ClwScene& out) const; - - + // Update camera data only. + void UpdateCamera(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const; + // Update shape data only. + void UpdateShapes(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const; + // Update lights data only. + void UpdateLights(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const; + // Update material data. + void UpdateMaterials(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const; + // Update texture data only. + void UpdateTextures(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const; + // Write out single material at data pointer. + // Collectors are required to convert texture and material pointers into indices. + void WriteMaterial(Material const* material, Collector& mat_collector, Collector& tex_collector, void* data) const; + // Write out single light at data pointer. + // Collector is required to convert texture pointers into indices. + void WriteLight(Scene1 const& scene, Light const* light, Collector& tex_collector, void* data) const; + // Write out single texture header at data pointer. + // Header requires texture data offset, so it is passed in. + void WriteTexture(Texture const* texture, std::size_t data_offset, void* data) const; + // Write out texture data at data pointer. + void WriteTextureData(Texture const* texture, void* data) const; private: // Context @@ -40,9 +110,9 @@ namespace Baikal // Intersection API RadeonRays::IntersectionApi* m_api; // Current scene - mutable Scene const* m_current_scene; + mutable Scene1 const* m_current_scene; - mutable std::map m_scene_cache; - mutable std::size_t m_vidmem_usage; + // Scene cache map (CPU scene -> GPU scene mapping) + mutable std::map m_scene_cache; }; } diff --git a/App/Scene/shape.cpp b/App/Scene/shape.cpp new file mode 100644 index 00000000..eebf692c --- /dev/null +++ b/App/Scene/shape.cpp @@ -0,0 +1,160 @@ +#include "shape.h" +#include + +namespace Baikal +{ + Mesh::Mesh() + : m_num_indices(0) + , m_num_vertices(0) + , m_num_normals(0) + , m_num_uvs(0) + { + } + + void Mesh::SetIndices(std::uint32_t const* indices, std::size_t num_indices) + { + assert(indices); + assert(num_indices != 0); + + // Resize internal array and copy data + m_indices.reset(new std::uint32_t[num_indices]); + + std::copy(indices, indices + num_indices, m_indices.get()); + + m_num_indices = num_indices; + } + + std::size_t Mesh::GetNumIndices() const + { + return m_num_indices; + + } + std::uint32_t const* Mesh::GetIndices() const + { + return m_indices.get(); + } + + void Mesh::SetVertices(RadeonRays::float3 const* vertices, std::size_t num_vertices) + { + assert(vertices); + assert(num_vertices != 0); + + // Resize internal array and copy data + m_vertices.reset(new RadeonRays::float3[num_vertices]); + + std::copy(vertices, vertices + num_vertices, m_vertices.get()); + + m_num_vertices = num_vertices; + } + + void Mesh::SetVertices(float const* vertices, std::size_t num_vertices) + { + assert(vertices); + assert(num_vertices != 0); + + // Resize internal array and copy data + m_vertices.reset(new RadeonRays::float3[num_vertices]); + + for (std::size_t i = 0; i < num_vertices; ++i) + { + m_vertices[i].x = vertices[3 * i]; + m_vertices[i].y = vertices[3 * i + 1]; + m_vertices[i].z = vertices[3 * i + 2]; + m_vertices[i].w = 1; + } + + m_num_vertices = num_vertices; + } + + std::size_t Mesh::GetNumVertices() const + { + return m_num_vertices; + } + + RadeonRays::float3 const* Mesh::GetVertices() const + { + return m_vertices.get(); + } + + void Mesh::SetNormals(RadeonRays::float3 const* normals, std::size_t num_normals) + { + assert(normals); + assert(num_normals != 0); + + // Resize internal array and copy data + m_normals.reset(new RadeonRays::float3[num_normals]); + + std::copy(normals, normals + num_normals, m_normals.get()); + + m_num_normals = num_normals; + } + + void Mesh::SetNormals(float const* normals, std::size_t num_normals) + { + assert(normals); + assert(num_normals != 0); + + // Resize internal array and copy data + m_normals.reset(new RadeonRays::float3[num_normals]); + + for (std::size_t i = 0; i < num_normals; ++i) + { + m_normals[i].x = normals[3 * i]; + m_normals[i].y = normals[3 * i + 1]; + m_normals[i].z = normals[3 * i + 2]; + m_normals[i].w = 0; + } + + m_num_normals = num_normals; + } + + std::size_t Mesh::GetNumNormals() const + { + return m_num_normals; + } + + RadeonRays::float3 const* Mesh::GetNormals() const + { + return m_normals.get(); + } + + void Mesh::SetUVs(RadeonRays::float2 const* uvs, std::size_t num_uvs) + { + assert(uvs); + assert(num_uvs != 0); + + // Resize internal array and copy data + m_uvs.reset(new RadeonRays::float2[num_uvs]); + + std::copy(uvs, uvs + num_uvs, m_uvs.get()); + + m_num_uvs = num_uvs; + } + + void Mesh::SetUVs(float const* uvs, std::size_t num_uvs) + { + assert(uvs); + assert(num_uvs != 0); + + // Resize internal array and copy data + m_uvs.reset(new RadeonRays::float2[num_uvs]); + + for (std::size_t i = 0; i < num_uvs; ++i) + { + m_uvs[i].x = uvs[2 * i]; + m_uvs[i].y = uvs[2 * i + 1]; + } + + m_num_uvs = num_uvs; + } + + std::size_t Mesh::GetNumUVs() const + { + return m_num_uvs; + } + + RadeonRays::float2 const* Mesh::GetUVs() const + { + return m_uvs.get(); + } +} diff --git a/App/Scene/shape.h b/App/Scene/shape.h new file mode 100644 index 00000000..4c2eabd4 --- /dev/null +++ b/App/Scene/shape.h @@ -0,0 +1,130 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file shape.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains declaration of various shape types supported by the renderer. + */ +#pragma once + +#include "math/float3.h" +#include "math/float2.h" +#include +#include + +namespace Baikal +{ + class Material; + + /** + \brief Shape base interface. + + High-level interface all shape classes need to implement. + */ + class Shape + { + public: + // Constructor + Shape(); + // Destructor + virtual ~Shape() = 0; + + // Get and set material + virtual void SetMaterial(Material const* material); + virtual Material const* GetMaterial() const; + + // Forbidden stuff + Shape(Shape const&) = delete; + Shape& operator = (Shape const&) = delete; + + private: + Material const* m_material; + }; + + /** + \brief Triangle mesh class. + + Triangle mesh is a collection of indexed triangle. + */ + class Mesh : public Shape + { + public: + Mesh(); + + // Set and get index array + virtual void SetIndices(std::uint32_t const* indices, std::size_t num_indices); + virtual std::size_t GetNumIndices() const; + virtual std::uint32_t const* GetIndices() const; + + // Set and get vertex array + virtual void SetVertices(RadeonRays::float3 const* vertices, std::size_t num_vertices); + virtual void SetVertices(float const* vertices, std::size_t num_vertices); + virtual std::size_t GetNumVertices() const; + virtual RadeonRays::float3 const* GetVertices() const; + + // Set and get normal array + virtual void SetNormals(RadeonRays::float3 const* normals, std::size_t num_normals); + virtual void SetNormals(float const* normals, std::size_t num_normals); + virtual std::size_t GetNumNormals() const; + virtual RadeonRays::float3 const* GetNormals() const; + + // Set and get UV array + virtual void SetUVs(RadeonRays::float2 const* uvs, std::size_t num_uvs); + virtual void SetUVs(float const* uvs, std::size_t num_uvs); + virtual std::size_t GetNumUVs() const; + virtual RadeonRays::float2 const* GetUVs() const; + + private: + std::unique_ptr m_vertices; + std::unique_ptr m_normals; + std::unique_ptr m_uvs; + std::unique_ptr m_indices; + + std::size_t m_num_vertices; + std::size_t m_num_normals; + std::size_t m_num_uvs; + std::size_t m_num_indices; + }; + + inline Shape::~Shape() + { + + } + + inline Shape::Shape() : m_material(nullptr) + { + + } + + inline void Shape::SetMaterial(Material const* material) + { + m_material = material; + } + + inline Material const* Shape::GetMaterial() const + { + return m_material; + } +} + diff --git a/App/Scene/texture.h b/App/Scene/texture.h new file mode 100644 index 00000000..ed2929c4 --- /dev/null +++ b/App/Scene/texture.h @@ -0,0 +1,75 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file texture.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains declaration of a texture class. + */ +#pragma once + +#include "math/float3.h" +#include "math/float2.h" +#include "math/int2.h" +#include +#include + +namespace Baikal +{ + class Material; + + /** + \brief Texture class. + + Texture is used to host CPU memory for image data. + */ + class Texture + { + public: + enum class Format + { + kRgba8, + kRgba16, + kRgba32 + }; + + Texture() = default; + virtual ~Texture() = 0; + + virtual RadeonRays::int2 GetSize() const = 0; + virtual char const* GetData() const = 0; + virtual Format GetFormat() const = 0; + virtual std::size_t GetSizeInBytes() const = 0; + + Texture(Texture const&) = delete; + Texture& operator = (Texture const&) = delete; + + private: + + }; + + inline Texture::~Texture() + { + + } +} diff --git a/App/main.cpp b/App/main.cpp index f0bcf71f..da60f0ae 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -63,13 +63,17 @@ THE SOFTWARE. #include "math/mathutils.h" #include "tiny_obj_loader.h" -#include "perspective_camera.h" +#include "Scene/camera.h" #include "shader_manager.h" -#include "Scene/scene.h" +#include "Scene/scene1.h" #include "PT/ptrenderer.h" #include "AO/aorenderer.h" #include "CLW/clwoutput.h" #include "config_manager.h" +#include "Scene/scene1.h" +#include "Scene/Loaders/scene_loader.h" + +Baikal::Scene1 scene; using namespace RadeonRays; @@ -82,6 +86,7 @@ char const* g_modelname = "orig.objm"; char const* g_envmapname = "../Resources/Textures/studio015.hdr"; std::unique_ptr g_shader_manager; +std::unique_ptr g_camera; GLuint g_vertex_buffer; GLuint g_index_buffer; @@ -144,7 +149,7 @@ std::vector g_renderthreads; int g_primary = -1; -std::unique_ptr g_scene; +std::unique_ptr g_scene; static bool g_is_left_pressed = false; @@ -340,34 +345,32 @@ void InitData() basepath += "/"; std::string filename = basepath + g_modelname; - g_scene.reset(Baikal::Scene::LoadFromObj(filename, basepath)); + g_scene.reset(Baikal::LoadFromObj(filename, basepath)); - g_scene->camera_.reset(new PerspectiveCamera( - g_camera_pos - , g_camera_at - , g_camera_up)); + g_camera.reset(new Baikal::PerspectiveCamera( + g_camera_pos + , g_camera_at + , g_camera_up)); + + g_scene->SetCamera(g_camera.get()); // Adjust sensor size based on current aspect ratio float aspect = (float)g_window_width / g_window_height; g_camera_sensor_size.y = g_camera_sensor_size.x / aspect; - g_scene->camera_->SetSensorSize(g_camera_sensor_size); - g_scene->camera_->SetDepthRange(g_camera_zcap); - g_scene->camera_->SetFocalLength(g_camera_focal_length); - g_scene->camera_->SetFocusDistance(g_camera_focus_distance); - g_scene->camera_->SetAperture(g_camera_aperture); + g_camera->SetSensorSize(g_camera_sensor_size); + g_camera->SetDepthRange(g_camera_zcap); + g_camera->SetFocalLength(g_camera_focal_length); + g_camera->SetFocusDistance(g_camera_focus_distance); + g_camera->SetAperture(g_camera_aperture); - std::cout << "Camera type: " << (g_scene->camera_->GetAperture() > 0.f ? "Physical" : "Pinhole") << "\n"; - std::cout << "Lens focal length: " << g_scene->camera_->GetFocalLength() * 1000.f << "mm\n"; - std::cout << "Lens focus distance: " << g_scene->camera_->GetFocusDistance() << "m\n"; - std::cout << "F-Stop: " << 1.f / (g_scene->camera_->GetAperture() * 10.f) << "\n"; + std::cout << "Camera type: " << (g_camera->GetAperture() > 0.f ? "Physical" : "Pinhole") << "\n"; + std::cout << "Lens focal length: " << g_camera->GetFocalLength() * 1000.f << "mm\n"; + std::cout << "Lens focus distance: " << g_camera->GetFocusDistance() << "m\n"; + std::cout << "F-Stop: " << 1.f / (g_camera->GetAperture() * 10.f) << "\n"; std::cout << "Sensor size: " << g_camera_sensor_size.x * 1000.f << "x" << g_camera_sensor_size.y * 1000.f << "mm\n"; - g_scene->SetEnvironment(g_envmapname, "", g_envmapmul); - g_scene->AddDirectionalLight(RadeonRays::float3(-0.3f, -1.f, -0.4f), 2.f * RadeonRays::float3(1.f, 1.f, 1.f)); - g_scene->AddPointLight(RadeonRays::float3(-0.5f, 1.7f, 0.0f), RadeonRays::float3(1.f, 0.9f, 0.6f)); - g_scene->AddSpotLight(RadeonRays::float3(0.5f, 1.5f, 0.0f), RadeonRays::float3(-0.5f, -1.0f, 0.1f), RadeonRays::float3(1.f, 0.9f, 0.6f), - std::cos(M_PI_4/2), std::cos(M_PI_4)); + #pragma omp parallel for for (int i = 0; i < g_cfgs.size(); ++i) { @@ -535,7 +538,7 @@ void Update() if (std::abs(camroty) > 0.001f) { //g_scene->camera_->Tilt(camroty); - g_scene->camera_->ArcballRotateVertically(float3(0, 0, 0), camroty); + g_camera->ArcballRotateVertically(float3(0, 0, 0), camroty); update = true; } @@ -543,50 +546,50 @@ void Update() { //g_scene->camera_->Rotate(camrotx); - g_scene->camera_->ArcballRotateHorizontally(float3(0, 0, 0), camrotx); + g_camera->ArcballRotateHorizontally(float3(0, 0, 0), camrotx); update = true; } const float kMovementSpeed = g_cspeed; if (g_is_fwd_pressed) { - g_scene->camera_->MoveForward((float)dt.count() * kMovementSpeed); + g_camera->MoveForward((float)dt.count() * kMovementSpeed); update = true; } if (g_is_back_pressed) { - g_scene->camera_->MoveForward(-(float)dt.count() * kMovementSpeed); + g_camera->MoveForward(-(float)dt.count() * kMovementSpeed); update = true; } if (g_is_right_pressed) { - g_scene->camera_->MoveRight((float)dt.count() * kMovementSpeed); + g_camera->MoveRight((float)dt.count() * kMovementSpeed); update = true; } if (g_is_left_pressed) { - g_scene->camera_->MoveRight(-(float)dt.count() * kMovementSpeed); + g_camera->MoveRight(-(float)dt.count() * kMovementSpeed); update = true; } if (g_is_home_pressed) { - g_scene->camera_->MoveUp((float)dt.count() * kMovementSpeed); + g_camera->MoveUp((float)dt.count() * kMovementSpeed); update = true; } if (g_is_end_pressed) { - g_scene->camera_->MoveUp(-(float)dt.count() * kMovementSpeed); + g_camera->MoveUp(-(float)dt.count() * kMovementSpeed); update = true; } if (update) { - g_scene->set_dirty(Baikal::Scene::kCamera); + g_scene->SetDirtyFlag(Baikal::Scene1::kCamera); if (g_num_samples > -1) { diff --git a/App/perspective_camera.cpp b/App/perspective_camera.cpp deleted file mode 100755 index e8f61d82..00000000 --- a/App/perspective_camera.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/********************************************************************** -Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -********************************************************************/ -#include "perspective_camera.h" - -#include -#include - -#include "math/quaternion.h" -#include "math/matrix.h" -#include "math/mathutils.h" - - -using namespace RadeonRays; - -PerspectiveCamera::PerspectiveCamera(float3 const& eye, float3 const& at, float3 const& up) - : m_p(eye) - , m_aperture(0.f) - , m_focus_distance(0.f) - , m_focal_length(0.f) - , m_zcap(0.f, 0.f) -{ - // Construct camera frame - m_forward = normalize(at - eye); - m_right = cross(m_forward, normalize(up)); - m_up = cross(m_right, m_forward); -} - -// Rotate camera around world Z axis, use for FPS camera -void PerspectiveCamera::Rotate(float angle) -{ - Rotate(float3(0.f, 1.f, 0.f), angle); -} - -void PerspectiveCamera::Rotate(float3 v, float angle) -{ - /// matrix should have basis vectors in rows - /// to be used for quaternion construction - /// would be good to add several options - /// to quaternion class - matrix cam_matrix = matrix( - m_up.x, m_up.y, m_up.z, 0.f, - m_right.x, m_right.y, m_right.z, 0.f, - m_forward.x, m_forward.y, m_forward.z, 0.f, - 0.f, 0.f, 0.f, 1.f); - - // Create camera orientation quaternion - quaternion q = normalize(quaternion(cam_matrix)); - - // Rotate camera frame around v - q = q * rotation_quaternion(v, -angle); - - // Uncomress back to lookat - q.to_matrix(cam_matrix); - - m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); - m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); - m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); -} - -// Tilt camera -void PerspectiveCamera::Tilt(float angle) -{ - Rotate(m_right, angle); -} - -// Move along camera Z direction -void PerspectiveCamera::MoveForward(float distance) -{ - m_p += distance * m_forward; -} - -// Move along camera X direction -void PerspectiveCamera::MoveRight(float distance) -{ - m_p += distance * m_right; -} - -// Move along camera Y direction -void PerspectiveCamera::MoveUp(float distance) -{ - m_p += distance * m_up; -} - -// Arcball rotation -void PerspectiveCamera::ArcballRotateHorizontally(float3 c, float angle) -{ - // Build camera matrix - matrix cam_matrix = matrix( - m_up.x, m_up.y, m_up.z, 0, - m_right.x, m_right.y, m_right.z, 0, - m_forward.x, m_forward.y, m_forward.z, 0, - 0, 0, 0, 1); - - // Create camera orientation quaternion - quaternion q = normalize(quaternion(cam_matrix)); - - // Rotation of camera around the center of interest by a == rotation of the frame by -a and rotation of the position by a - q = q * rotation_quaternion(float3(0.f, 1.f, 0.f), -angle); - - // Rotate the frame - q.to_matrix(cam_matrix); - - m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); - m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); - m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); - - // Rotate the position - float3 dir = c - m_p; - dir = rotate_vector(dir, rotation_quaternion(float3(0.f, 1.f, 0.f), angle)); - - m_p = c - dir; -} - -void PerspectiveCamera::ArcballRotateVertically(float3 c, float angle) -{ - // Build camera matrix - matrix cam_matrix = matrix( - m_up.x, m_up.y, m_up.z, 0, - m_right.x, m_right.y, m_right.z, 0, - m_forward.x, m_forward.y, m_forward.z, 0, - 0, 0, 0, 1); - - // Create camera orientation quaternion - quaternion q = normalize(quaternion(cam_matrix)); - - // Rotation of camera around the center of interest by a == rotation of the frame by -a and rotation of the position by a - q = q * rotation_quaternion(m_right, -angle); - - // Rotate the frame - q.to_matrix(cam_matrix); - - m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); - m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); - m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); - - // Rotate the position - float3 dir = c - m_p; - dir = rotate_vector(dir, rotation_quaternion(m_right, angle)); - - m_p = c - dir; -} \ No newline at end of file diff --git a/App/perspective_camera.h b/App/perspective_camera.h deleted file mode 100755 index 24256c92..00000000 --- a/App/perspective_camera.h +++ /dev/null @@ -1,167 +0,0 @@ -/********************************************************************** -Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -********************************************************************/ -#ifndef PERSPECTIVE_CAMERA_H -#define PERSPECTIVE_CAMERA_H - -#include "math/float3.h" -#include "math/float2.h" -#include "math/ray.h" - -#include - - -class PerspectiveCamera -{ -public: - // Pass camera position, camera aim, camera up vector, depth limits, vertical field of view - // and image plane aspect ratio - PerspectiveCamera(RadeonRays::float3 const& eye, - RadeonRays::float3 const& at, - RadeonRays::float3 const& up); - - // Set camera focus distance in meters, - // this is essentially a distance from the lens to the focal plane. - // Altering this is similar to rotating the focus ring on real lens. - void SetFocusDistance(float distance); - float GetFocusDistance() const; - - // Set camera focal length in meters, - // this is essentially a distance between a camera sensor and a lens. - // Altering this is similar to rotating zoom ring on a lens. - void SetFocalLength(float length); - float GetFocalLength() const; - - // Set aperture value in meters. - // This is a radius of a lens. - void SetAperture(float aperture); - float GetAperture() const; - - // Set camera sensor size in meters. - // This distinguishes APC-S vs full-frame, etc - void SetSensorSize(RadeonRays::float2 const& size); - RadeonRays::float2 GetSensorSize() const; - - // Set camera depth range. - // Does not really make sence for physical camera - void SetDepthRange(RadeonRays::float2 const& range); - RadeonRays::float2 GetDepthRange() const; - - - // Rotate camera around world Z axis - void Rotate(float angle); - // Tilt camera - void Tilt(float angle); - // Move along camera Z direction - void MoveForward(float distance); - // Move along camera X direction - void MoveRight(float distance); - // Move along camera Y direction - void MoveUp(float distance); - - // - void ArcballRotateHorizontally(RadeonRays::float3 c, float angle); - // - void ArcballRotateVertically(RadeonRays::float3 c, float angle); - - -private: - // Rotate camera around world Z axis - void Rotate(RadeonRays::float3, float angle); - - // Camera coordinate frame - RadeonRays::float3 m_forward; - RadeonRays::float3 m_right; - RadeonRays::float3 m_up; - RadeonRays::float3 m_p; - - // Image plane width & hight in scene units - RadeonRays::float2 m_dim; - - // Near and far Z - RadeonRays::float2 m_zcap; - - float m_focal_length; - float m_aspect; - float m_focus_distance; - float m_aperture; - - friend std::ostream& operator << (std::ostream& o, PerspectiveCamera const& p); -}; - -inline std::ostream& operator << (std::ostream& o, PerspectiveCamera const& p) -{ - o << "Position: " << p.m_p.x << " " << p.m_p.y << " " << p.m_p.z << "\n"; - o << "At: " << p.m_p.x + p.m_forward.x << " " << p.m_p.y + p.m_forward.y << " " << p.m_p.z + p.m_forward.z << "\n"; - return o; -} - -inline void PerspectiveCamera::SetFocusDistance(float distance) -{ - m_focus_distance = distance; -} - -inline void PerspectiveCamera::SetFocalLength(float length) -{ - m_focal_length = length; -} - -inline void PerspectiveCamera::SetAperture(float aperture) -{ - m_aperture = aperture; -} - -inline float PerspectiveCamera::GetFocusDistance() const -{ - return m_focus_distance; -} - -inline float PerspectiveCamera::GetFocalLength() const -{ - return m_focal_length; -} - -inline float PerspectiveCamera::GetAperture() const -{ - return m_aperture; -} - -inline RadeonRays::float2 PerspectiveCamera::GetSensorSize() const -{ - return m_dim; -} - -inline void PerspectiveCamera::SetSensorSize(RadeonRays::float2 const& size) -{ - m_dim = size; -} -inline void PerspectiveCamera::SetDepthRange(RadeonRays::float2 const& range) -{ - m_zcap = range; -} - -inline RadeonRays::float2 PerspectiveCamera::GetDepthRange() const -{ - return m_zcap; -} - - -#endif // PERSPECTIVE_CAMERA_H \ No newline at end of file diff --git a/CLW/CLWBuffer.h b/CLW/CLWBuffer.h index 4d8fc31f..f36ee859 100644 --- a/CLW/CLWBuffer.h +++ b/CLW/CLWBuffer.h @@ -48,7 +48,7 @@ template class CLWBuffer : public ReferenceCounter Create(cl_context context, cl_mem_flags flags, size_t elementCount, void* data); static CLWBuffer CreateFromClBuffer(cl_mem buffer); - CLWBuffer(){} + CLWBuffer() : elementCount_(0){} virtual ~CLWBuffer(); size_t GetElementCount() const { return elementCount_; } From 066b4778a11510b8fdf27c3825eab264ab90b10d Mon Sep 17 00:00:00 2001 From: yozhijk Date: Thu, 5 Jan 2017 22:05:25 +0100 Subject: [PATCH 02/24] Improve material conversion --- App/Scene/Loaders/scene_loader.cpp | 128 ++--------------------------- App/Scene/light.h | 62 ++++++++++++-- App/Scene/scene1.cpp | 11 ++- App/Scene/scene1.h | 10 ++- App/Scene/scene_tracker.cpp | 6 +- App/Scene/scene_tracker.h | 13 +-- Resources/CornellBox/orig.objm | 15 +++- 7 files changed, 99 insertions(+), 146 deletions(-) diff --git a/App/Scene/Loaders/scene_loader.cpp b/App/Scene/Loaders/scene_loader.cpp index de2498f8..6bb89e88 100644 --- a/App/Scene/Loaders/scene_loader.cpp +++ b/App/Scene/Loaders/scene_loader.cpp @@ -26,127 +26,14 @@ namespace Baikal // Allocate scene Scene1* scene(new Scene1); - // Texture map - std::map textures; - std::map matmap; - // Enumerate and translate materials - /*for (int i = 0; i < (int)objmaterials.size(); ++i) + std::vector materials(objmaterials.size()); + for (int i = 0; i < (int)objmaterials.size(); ++i) { - Material material; - - material.kx = float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2]); - material.ni = objmaterials[i].ior; - material.type = kLambert; - material.fresnel = 0.f; - - // Load diffuse texture if needed - if (!objmaterials[i].diffuse_texname.empty()) - { - auto iter = textures.find(objmaterials[i].diffuse_texname); - if (iter != textures.end()) - { - material.kxmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].diffuse_texname, texture, scene->texturedata_); - - // Add texture desc - material.kxmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].diffuse_texname] = material.kxmapidx; - } - } - - // Load specular texture - if (!objmaterials[i].specular_texname.empty()) - { - auto iter = textures.find(objmaterials[i].specular_texname); - if (iter != textures.end()) - { - material.ksmapidx = iter->second; - } - else - { - Texture texture; - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].specular_texname, texture, scene->texturedata_); - // Add texture desc - material.ksmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - // Save in the map - textures[objmaterials[i].specular_texname] = material.ksmapidx; - } - } - - // Load normal map - - - - scene->materials_.push_back(material); - scene->material_names_.push_back(objmaterials[i].name); - - float3 spec = float3(0.5f, 0.5f, 0.5f);// float3(objmaterials[i].specular[0], objmaterials[i].specular[1], objmaterials[i].specular[2]); - if (spec.sqnorm() > 0.f) - { - Material specular; - specular.kx = spec; - specular.ni = 1.33f;//objmaterials[i].ior; - specular.ns = 0.05f;//1.f - objmaterials[i].shininess; - specular.type = kMicrofacetGGX; - specular.nmapidx = -1;// scene->materials_.back().nmapidx; - specular.fresnel = 1.f; - - if (!objmaterials[i].normal_texname.empty()) - { - auto iter = textures.find(objmaterials[i].normal_texname); - if (iter != textures.end()) - { - specular.nmapidx = iter->second; - } - else - { - Texture texture; - - // Load texture - LoadTexture(basepath + "/" + objmaterials[i].normal_texname, texture, scene->texturedata_); - - // Add texture desc - specular.nmapidx = (int)scene->textures_.size(); - scene->textures_.push_back(texture); - - // Save in the map - textures[objmaterials[i].normal_texname] = specular.nmapidx; - } - } - - scene->materials_.push_back(specular); - scene->material_names_.push_back(objmaterials[i].name); - - Material layered; - layered.ni = 1.9f;// objmaterials[i].ior; - layered.type = kFresnelBlend; - layered.brdftopidx = scene->materials_.size() - 1; - layered.brdfbaseidx = scene->materials_.size() - 2; - layered.fresnel = 1.f; - layered.twosided = 1; - - scene->materials_.push_back(layered); - scene->material_names_.push_back(objmaterials[i].name); - } - - matmap[i] = scene->materials_.size() - 1; - } - }*/ - - Material* fakemat = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - fakemat->SetTwoSided(false); + materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); + materials[i]->SetTwoSided(false); + } // Enumerate all shapes in the scene for (int s = 0; s < (int)objshapes.size(); ++s) @@ -174,7 +61,8 @@ namespace Baikal auto num_indices = objshapes[s].mesh.indices.size(); mesh->SetIndices(reinterpret_cast(&objshapes[s].mesh.indices[0]), num_indices); - mesh->SetMaterial(fakemat); + auto idx = objshapes[s].mesh.material_ids[0]; + mesh->SetMaterial(materials[idx]); scene->AttachShape(mesh); } diff --git a/App/Scene/light.h b/App/Scene/light.h index 66795c6c..820e5801 100644 --- a/App/Scene/light.h +++ b/App/Scene/light.h @@ -48,7 +48,7 @@ namespace Baikal { public: // Constructor - Light() = default; + Light(); // Destructor virtual ~Light() = 0; @@ -63,11 +63,11 @@ namespace Baikal virtual RadeonRays::float3 GetDirection() const; virtual void SetDirection(RadeonRays::float3 const& d); - // Set and get direction + // Set and get emitted radiance (differential) virtual RadeonRays::float3 GetEmittedRadiance() const; virtual void SetEmittedRadiance(RadeonRays::float3 const& e); - // Iterate all textures used by the light + // Iterator for all the textures used by the light virtual Iterator* CreateTextureIterator() const; // Forbidden stuff @@ -75,11 +75,24 @@ namespace Baikal Light& operator = (Light const&) = delete; private: + // Position RadeonRays::float3 m_p; + // Direction RadeonRays::float3 m_d; + // Emmited radiance RadeonRays::float3 m_e; }; + inline Light::Light() + : m_d(0.f, -1.f, 0.f) + , m_e(1.f, 1.f, 1.f) + { + } + + inline Light::~Light() + { + } + /** \brief Simple point light source. @@ -108,15 +121,22 @@ namespace Baikal class SpotLight: public Light { public: + SpotLight(); // Get and set inner and outer falloff angles: they are set as cosines of angles between light direction // and cone opening. virtual void SetConeShape(RadeonRays::float2 angles); virtual RadeonRays::float2 GetConeShape() const; private: + // Opening angles (x - inner, y - outer) RadeonRays::float2 m_angles; }; + inline SpotLight::SpotLight() + : m_angles(0.25f, 0.5f) + { + } + /** \brief Image-based distant light source. @@ -125,33 +145,46 @@ namespace Baikal class ImageBasedLight: public Light { public: + ImageBasedLight(); // Get and set illuminant texture virtual void SetTexture(Texture const* texture); virtual Texture const* GetTexture() const; + // Get and set multiplier. + // Multiplier is used to adjust emissive power. + virtual float GetMultiplier() const; + virtual void SetMultiplier(float m); + private: + // Illuminant texture Texture const* m_texture; + // Emissive multiplier + float m_multiplier; }; // Area light class AreaLight: public Light { public: + AreaLight(Shape const* shape, std::size_t idx); // Get parent shape virtual Shape const* GetShape() const; // Get parent prim idx virtual std::size_t GetPrimitiveIdx() const; private: + // Parent shape Shape const* m_shape; + // Parent primitive index std::size_t m_prim_idx; }; - inline Light::~Light() + inline AreaLight::AreaLight(Shape const* shape, std::size_t idx) + : m_shape(shape) + , m_prim_idx(idx) { - } - + inline RadeonRays::float3 Light::GetPosition() const { return m_p; @@ -216,4 +249,21 @@ namespace Baikal { return m_shape; } + + inline ImageBasedLight::ImageBasedLight() + : m_texture(nullptr) + , m_multiplier(1.f) + { + } + + inline float ImageBasedLight::GetMultiplier() const + { + return m_multiplier; + } + + inline void ImageBasedLight::SetMultiplier(float m) + { + m_multiplier = m; + } + } diff --git a/App/Scene/scene1.cpp b/App/Scene/scene1.cpp index bc376c1f..6a1cc9b8 100644 --- a/App/Scene/scene1.cpp +++ b/App/Scene/scene1.cpp @@ -9,14 +9,15 @@ namespace Baikal { + // Data structures for shapes and lights using ShapeList = std::vector; using LightList = std::vector; + // Internal data struct Scene1::SceneImpl { ShapeList m_shapes; LightList m_lights; - Camera const* m_camera; DirtyFlags m_dirty_flags; @@ -63,8 +64,10 @@ namespace Baikal { assert(light); + // Check if the light is already in the scene LightList::const_iterator citer = std::find(m_impl->m_lights.cbegin(), m_impl->m_lights.cend(), light); + // And insert only if not if (citer == m_impl->m_lights.cend()) { m_impl->m_lights.push_back(light); @@ -75,8 +78,10 @@ namespace Baikal void Scene1::DetachLight(Light const* light) { + // Check if the light is in the scene LightList::const_iterator citer = std::find(m_impl->m_lights.cbegin(), m_impl->m_lights.cend(), light); + // And remove it if yes if (citer != m_impl->m_lights.cend()) { m_impl->m_lights.erase(citer); @@ -99,8 +104,10 @@ namespace Baikal { assert(shape); + // Check if the shape is already in the scene ShapeList::const_iterator citer = std::find(m_impl->m_shapes.cbegin(), m_impl->m_shapes.cend(), shape); + // And attach it if not if (citer == m_impl->m_shapes.cend()) { m_impl->m_shapes.push_back(shape); @@ -113,8 +120,10 @@ namespace Baikal { assert(shape); + // Check if the shape is in the scene ShapeList::const_iterator citer = std::find(m_impl->m_shapes.cbegin(), m_impl->m_shapes.cend(), shape); + // And detach if yes if (citer != m_impl->m_shapes.cend()) { m_impl->m_shapes.erase(citer); diff --git a/App/Scene/scene1.h b/App/Scene/scene1.h index 39c4fe07..e838ef2d 100644 --- a/App/Scene/scene1.h +++ b/App/Scene/scene1.h @@ -48,8 +48,8 @@ namespace Baikal { public: + // Dirty flags are used to perform partial buffer updates to save traffic using DirtyFlags = std::uint32_t; - enum { kNone, @@ -68,19 +68,21 @@ namespace Baikal void AttachLight(Light const* light); void DetachLight(Light const* light); - // TODO: make it iterable + // Get the number of lights in the scene std::size_t GetNumLights() const; + // Get light iterator Iterator* CreateLightIterator() const; // Add or remove shapes void AttachShape(Shape const* shape); void DetachShape(Shape const* shape); - // TODO: make it iterable + // Get number of shapes in the scene std::size_t GetNumShapes() const; + // Get shape iterator Iterator* CreateShapeIterator() const; - // Set camera + // Set and get camera void SetCamera(Camera const* camera); Camera const* GetCamera() const; diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index a064b5ba..cf6bbbac 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -393,8 +393,8 @@ namespace Baikal shapes[num_shapes_written] = shape; ++num_shapes_written; - std::fill(matids + num_matids_written, matids + num_matids_written + mesh_num_indices / 3, - mat_collector.GetItemIndex(mesh->GetMaterial())); + auto matidx = mat_collector.GetItemIndex(mesh->GetMaterial()); + std::fill(matids + num_matids_written, matids + num_matids_written + mesh_num_indices / 3, matidx); num_matids_written += mesh_num_indices / 3; } @@ -874,7 +874,7 @@ namespace Baikal case ClwScene::kIbl: { // TODO: support this - clw_light->multiplier = 1.f; + clw_light->multiplier = static_cast(light)->GetMultiplier(); auto tex = static_cast(light)->GetTexture(); clw_light->tex = tex_collector.GetItemIndex(tex); clw_light->texdiffuse = clw_light->tex; diff --git a/App/Scene/scene_tracker.h b/App/Scene/scene_tracker.h index 9f20c73c..7d58e389 100755 --- a/App/Scene/scene_tracker.h +++ b/App/Scene/scene_tracker.h @@ -60,18 +60,11 @@ namespace Baikal // Destructor virtual ~SceneTracker(); - /** - \brief Given a scene this method produces (or loads from cache) corresponding GPU representation. - - \param scene CPU scene. - \param mat_collector Material collector helping to track materials. - \param tex_collector Texture collector helping to track textures. - */ + // Given a scene this method produces (or loads from cache) corresponding GPU representation. virtual ClwScene& CompileScene(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector) const; - /** - \brief Get underlying intersection API. - */ + + // Get underlying intersection API. RadeonRays::IntersectionApi* GetIntersectionApi() { return m_api; } protected: diff --git a/Resources/CornellBox/orig.objm b/Resources/CornellBox/orig.objm index bb4ef55d..e0cf6176 100644 --- a/Resources/CornellBox/orig.objm +++ b/Resources/CornellBox/orig.objm @@ -1,6 +1,7 @@ # Blender v2.69 (sub 0) OBJ File: '' # www.blender.org mtllib orig.mtl + o light v -0.240000 1.980000 0.160000 v -0.240000 1.980000 -0.220000 @@ -10,7 +11,8 @@ vn 0.000000 -1.000000 0.000000 usemtl light s off f 1//1 2//1 3//1 4//1 -o leftWall + +o shortBox v -1.010000 -0.000000 0.990000 v -0.990000 0.000000 -1.040000 v -1.020000 1.990000 -1.040000 @@ -41,6 +43,7 @@ vn -0.296399 -0.000000 0.955064 vn 0.285805 0.000000 -0.958288 vn 0.959629 -0.000000 0.281270 vn 0.999937 0.010050 0.004926 + usemtl shortBox s off f 9//2 10//2 11//2 12//2 @@ -48,8 +51,11 @@ f 13//3 14//3 15//3 16//3 f 17//4 18//4 19//4 20//4 f 25//5 26//5 27//5 28//5 f 21//6 22//6 23//6 24//6 + +o leftWall usemtl leftWall f 5//7 6//7 7//7 8//7 + o backWall v -0.990000 0.000000 -1.040000 v 1.000000 0.000000 -1.040000 @@ -59,6 +65,7 @@ vn 0.000000 -0.000000 1.000000 usemtl backWall s off f 29//8 30//8 31//8 32//8 + o rightWall v 1.000000 0.000000 -1.040000 v 1.000000 -0.000000 0.990000 @@ -68,6 +75,7 @@ vn -1.000000 0.000000 0.000000 usemtl rightWall s off f 33//9 34//9 35//9 36//9 + o ceiling v -1.020000 1.990000 0.990000 v -1.020000 1.990000 -1.040000 @@ -77,7 +85,8 @@ vn 0.000000 -1.000000 -0.000000 usemtl ceiling s off f 37//10 38//10 39//10 40//10 -o shortBox + +o tallBox v -0.530000 1.200000 0.090000 v 0.040000 1.200000 -0.090000 v -0.140000 1.200000 -0.670000 @@ -110,6 +119,8 @@ f 45//12 46//12 47//12 48//12 f 49//13 50//13 51//13 52//13 f 53//14 54//14 55//14 56//14 f 57//15 58//15 59//15 60//15 + + o floor v -1.010000 -0.000000 0.990000 v 1.000000 -0.000000 0.990000 From 98c13fe69e19e04a73fe8e6a474a86c8532f97df Mon Sep 17 00:00:00 2001 From: yozhijk Date: Fri, 6 Jan 2017 19:03:51 +0100 Subject: [PATCH 03/24] Implement texture loading --- App/CL/integrator_pt.cl | 4 +- App/Scene/Loaders/image_io.cpp | 92 ++++++++++++++++++++++++++++++ App/Scene/Loaders/image_io.h | 50 ++++++++++++++++ App/Scene/Loaders/scene_loader.cpp | 28 +++++++-- App/Scene/iterator.h | 41 +++++++++++++ App/Scene/light.h | 19 +++++- App/Scene/material.cpp | 46 +-------------- App/Scene/material.h | 1 - App/Scene/scene1.h | 2 + App/Scene/scene_tracker.cpp | 4 +- App/Scene/texture.h | 65 ++++++++++++++++++--- 11 files changed, 290 insertions(+), 62 deletions(-) create mode 100644 App/Scene/Loaders/image_io.cpp create mode 100644 App/Scene/Loaders/image_io.h diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index 32f6f9e7..8989ee1e 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -590,8 +590,8 @@ __kernel void ShadeMiss( // Multiply by throughput int volidx = paths[pixelidx].volume; - if (volidx == -1); - //output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)); + if (volidx == -1) + output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)); else { output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * diff --git a/App/Scene/Loaders/image_io.cpp b/App/Scene/Loaders/image_io.cpp new file mode 100644 index 00000000..77b9b59e --- /dev/null +++ b/App/Scene/Loaders/image_io.cpp @@ -0,0 +1,92 @@ +#include "image_io.h" +#include "../texture.h" + +#include "OpenImageIO/imageio.h" + +namespace Baikal +{ + class Oiio : public ImageIo + { + public: + Texture* LoadImage(std::string const& filename) const override; + }; + + static Texture::Format GetTextureForemat(OIIO_NAMESPACE::ImageSpec const& spec) + { + OIIO_NAMESPACE_USING + + if (spec.format.basetype == TypeDesc::UINT8) + return Texture::Format::kRgba8; + if (spec.format.basetype == TypeDesc::HALF) + return Texture::Format::kRgba16; + else + return Texture::Format::kRgba32; + } + + Texture* Oiio::LoadImage(const std::string &filename) const + { + OIIO_NAMESPACE_USING + + ImageInput* input = ImageInput::open(filename); + + if (!input) + { + throw std::runtime_error("Can't load " + filename + " image"); + } + + ImageSpec const& spec = input->spec(); + + auto fmt = GetTextureForemat(spec); + char* texturedata = nullptr; + + if (fmt == Texture::Format::kRgba8) + { + auto size = spec.width * spec.height * spec.depth * 4; + + texturedata = new char[size]; + + // Read data to storage + input->read_image(TypeDesc::UINT8, texturedata, sizeof(char) * 4); + + // Close handle + input->close(); + } + else if (fmt == Texture::Format::kRgba16) + { + auto size = spec.width * spec.height * spec.depth * sizeof(float) * 2; + + // Resize storage + texturedata = new char[size]; + + // Read data to storage + input->read_image(TypeDesc::HALF, texturedata, sizeof(float) * 2); + + // Close handle + input->close(); + } + else + { + auto size = spec.width * spec.height * spec.depth * sizeof(RadeonRays::float3); + + // Resize storage + texturedata = new char[size]; + + // Read data to storage + input->read_image(TypeDesc::FLOAT, texturedata, sizeof(RadeonRays::float3)); + + // Close handle + input->close(); + } + + // Cleanup + delete input; + + // Return new texture + return new Texture(texturedata, RadeonRays::int2(spec.width, spec.height), fmt); + } + + ImageIo* CreateImageIo() + { + return new Oiio(); + } +} diff --git a/App/Scene/Loaders/image_io.h b/App/Scene/Loaders/image_io.h new file mode 100644 index 00000000..4417deee --- /dev/null +++ b/App/Scene/Loaders/image_io.h @@ -0,0 +1,50 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file image_io.h + \author Dmitry Kozlov + \version 1.0 + \brief + */ +#pragma once + +#include + +namespace Baikal +{ + class Texture; + + class ImageIo + { + public: + ImageIo() = default; + ~ImageIo() = default; + + virtual Texture* LoadImage(std::string const& filename) const = 0; + + ImageIo(ImageIo const&) = delete; + ImageIo& operator = (ImageIo const&) = delete; + }; + + ImageIo* CreateImageIo(); +} diff --git a/App/Scene/Loaders/scene_loader.cpp b/App/Scene/Loaders/scene_loader.cpp index 6bb89e88..c4fe12a7 100644 --- a/App/Scene/Loaders/scene_loader.cpp +++ b/App/Scene/Loaders/scene_loader.cpp @@ -1,9 +1,11 @@ #include "scene_loader.h" +#include "image_io.h" #include "../scene1.h" #include "../shape.h" #include "../material.h" #include "../light.h" + #include "tiny_obj_loader.h" namespace Baikal @@ -12,6 +14,8 @@ namespace Baikal { using namespace tinyobj; + auto image_io(CreateImageIo()); + // Loader data std::vector objshapes; std::vector objmaterials; @@ -31,7 +35,17 @@ namespace Baikal for (int i = 0; i < (int)objmaterials.size(); ++i) { materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); + + if (!objmaterials[i].diffuse_texname.empty()) + { + auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); + materials[i]->SetInputValue("albedo", texture); + } + else + { + materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); + } + materials[i]->SetTwoSided(false); } @@ -66,14 +80,18 @@ namespace Baikal scene->AttachShape(mesh); } - //ImageBasedLight* light = new ImageBasedLight(); - //g_scene->SetEnvironment(g_envmapname, "", g_envmapmul); + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); - PointLight* light = new PointLight(); - light->SetPosition(RadeonRays::float3(-0.3f, 1.5f, 2.f)); + DirectionalLight* light = new DirectionalLight(); + light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); scene->AttachLight(light); + scene->AttachLight(ibl); + return scene; } } diff --git a/App/Scene/iterator.h b/App/Scene/iterator.h index 8c1e842e..5168154f 100644 --- a/App/Scene/iterator.h +++ b/App/Scene/iterator.h @@ -129,4 +129,45 @@ namespace Baikal UnderlyingIterator m_end; UnderlyingIterator m_cur; }; + + template class ContainerIterator : public Iterator + { + public: + ContainerIterator(T&& container) : + m_container(std::move(container)) + { + m_begin = m_container.cbegin(); + m_end = m_container.cend(); + Reset(); + } + + bool IsValid() const override + { + return m_cur != m_end; + } + + // Advance by 1 + void Next() override + { + ++m_cur; + } + + // Get underlying item + void const* Item() const override + { + return *m_cur; + } + + // Set to starting iterator + void Reset() override + { + m_cur = m_begin; + } + + private: + T m_container; + typename T::const_iterator m_begin; + typename T::const_iterator m_end; + typename T::const_iterator m_cur; + }; } diff --git a/App/Scene/light.h b/App/Scene/light.h index 820e5801..dc2329b6 100644 --- a/App/Scene/light.h +++ b/App/Scene/light.h @@ -32,6 +32,7 @@ #include "math/float2.h" #include #include +#include #include "iterator.h" @@ -155,6 +156,9 @@ namespace Baikal virtual float GetMultiplier() const; virtual void SetMultiplier(float m); + // Iterator for all the textures used by the light + Iterator* CreateTextureIterator() const override; + private: // Illuminant texture Texture const* m_texture; @@ -202,7 +206,7 @@ namespace Baikal inline void Light::SetDirection(RadeonRays::float3 const& d) { - m_d = d; + m_d = normalize(d); } inline Iterator* Light::CreateTextureIterator() const @@ -265,5 +269,16 @@ namespace Baikal { m_multiplier = m; } - + + inline Iterator* ImageBasedLight::CreateTextureIterator() const + { + std::set result; + + if (m_texture) + { + result.insert(m_texture); + } + + return new ContainerIterator>(std::move(result)); + } } diff --git a/App/Scene/material.cpp b/App/Scene/material.cpp index af658da2..a3a0b4e0 100644 --- a/App/Scene/material.cpp +++ b/App/Scene/material.cpp @@ -46,46 +46,7 @@ namespace Baikal InputMap::const_iterator m_cur; }; - template class Material::InputDependencyIterator : public Iterator - { - public: - InputDependencyIterator(T&& container) : - m_container(std::move(container)) - { - m_begin = m_container.cbegin(); - m_end = m_container.cend(); - Reset(); - } - - bool IsValid() const override - { - return m_cur != m_end; - } - - // Advance by 1 - void Next() override - { - ++m_cur; - } - - // Get underlying item - void const* Item() const override - { - return *m_cur; - } - - // Set to starting iterator - void Reset() override - { - m_cur = m_begin; - } - - private: - T m_container; - typename T::const_iterator m_begin; - typename T::const_iterator m_end; - typename T::const_iterator m_cur; - }; + Material::Material() @@ -142,8 +103,7 @@ namespace Baikal } ); - return new InputDependencyIterator>(std::move(materials)); - + return new ContainerIterator>(std::move(materials)); } // Iterator of textures (plugged as inputs) @@ -162,7 +122,7 @@ namespace Baikal } ); - return new InputDependencyIterator>(std::move(textures)); + return new ContainerIterator>(std::move(textures)); } // Iterator of inputs diff --git a/App/Scene/material.h b/App/Scene/material.h index 78966e4b..f062513c 100644 --- a/App/Scene/material.h +++ b/App/Scene/material.h @@ -128,7 +128,6 @@ namespace Baikal private: class InputIterator; - template class InputDependencyIterator; using InputMap = std::map; // Input map diff --git a/App/Scene/scene1.h b/App/Scene/scene1.h index e838ef2d..9a12aeac 100644 --- a/App/Scene/scene1.h +++ b/App/Scene/scene1.h @@ -93,6 +93,8 @@ namespace Baikal // Clear all flags void ClearDirtyFlags() const; + //TODO: add scene validation method + // Forbidden stuff Scene1(Scene1 const&) = delete; Scene1& operator = (Scene1 const&) = delete; diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index cf6bbbac..194d7f84 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -578,7 +578,7 @@ namespace Baikal m_context.UnmapBuffer(0, out.textures, textures); // Recreate material buffer if it needs resize - if (tex_data_buffer_size < out.texturedata.GetElementCount()) + if (tex_data_buffer_size > out.texturedata.GetElementCount()) { // Create material buffer out.texturedata = m_context.CreateBuffer(tex_data_buffer_size, CL_MEM_READ_ONLY); @@ -936,7 +936,7 @@ namespace Baikal auto ibl = dynamic_cast(light_iter->ItemAs()); if (ibl) { - out.envmapmul = 1.f; + out.envmapmul = ibl->GetMultiplier(); out.envmapidx = tex_collector.GetItemIndex(ibl->GetTexture()); } } diff --git a/App/Scene/texture.h b/App/Scene/texture.h index ed2929c4..4eb2c3a4 100644 --- a/App/Scene/texture.h +++ b/App/Scene/texture.h @@ -53,23 +53,74 @@ namespace Baikal kRgba32 }; - Texture() = default; - virtual ~Texture() = 0; + // Constructor + // Note, that texture takes ownership of data array + Texture(char const* data, RadeonRays::int2 size, Format format); + // Destructor (the data is destroyed as well) + virtual ~Texture(); - virtual RadeonRays::int2 GetSize() const = 0; - virtual char const* GetData() const = 0; - virtual Format GetFormat() const = 0; - virtual std::size_t GetSizeInBytes() const = 0; + // Get texture dimensions + virtual RadeonRays::int2 GetSize() const; + // Get texture raw data + virtual char const* GetData() const; + // Get texture format + virtual Format GetFormat() const; + // Get data size in bytes + virtual std::size_t GetSizeInBytes() const; Texture(Texture const&) = delete; Texture& operator = (Texture const&) = delete; private: - + std::unique_ptr m_data; + RadeonRays::int2 m_size; + Format m_format; }; + inline Texture::Texture(char const* data, RadeonRays::int2 size, Format format) + : m_data(data) + , m_size(size) + , m_format(format) + { + } + inline Texture::~Texture() { + } + + inline RadeonRays::int2 Texture::GetSize() const + { + return m_size; + } + + inline char const* Texture::GetData() const + { + return m_data.get(); + } + + inline Texture::Format Texture::GetFormat() const + { + return m_format; + } + + inline std::size_t Texture::GetSizeInBytes() const + { + std::uint32_t component_size = 1; + + switch (m_format) { + case Format::kRgba8: + component_size = 1; + break; + case Format::kRgba16: + component_size = 2; + break; + case Format::kRgba32: + component_size = 4; + break; + default: + break; + } + return 4 * component_size * m_size.x * m_size.y; } } From cc6f0c40a75bc055307c5451f5306fa75deed8ba Mon Sep 17 00:00:00 2001 From: yozhijk Date: Fri, 6 Jan 2017 20:19:37 +0100 Subject: [PATCH 04/24] Add autorelease functionality --- App/Scene/Loaders/scene_loader.cpp | 12 ++++++++ App/Scene/camera.h | 8 ++---- App/Scene/light.h | 8 ++---- App/Scene/material.h | 4 ++- App/Scene/scene1.cpp | 43 ++++++++++++++++++++++++++++ App/Scene/scene1.h | 8 +++++- App/Scene/scene_object.h | 46 ++++++++++++++++++++++++++++++ App/Scene/shape.h | 4 ++- App/Scene/texture.h | 9 +++--- 9 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 App/Scene/scene_object.h diff --git a/App/Scene/Loaders/scene_loader.cpp b/App/Scene/Loaders/scene_loader.cpp index c4fe12a7..b43c8199 100644 --- a/App/Scene/Loaders/scene_loader.cpp +++ b/App/Scene/Loaders/scene_loader.cpp @@ -4,6 +4,8 @@ #include "../shape.h" #include "../material.h" #include "../light.h" +#include "../texture.h" +#include "../scene_object.h" #include "tiny_obj_loader.h" @@ -40,6 +42,7 @@ namespace Baikal { auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); materials[i]->SetInputValue("albedo", texture); + scene->AttachAutoreleaseObject(texture); } else { @@ -47,6 +50,8 @@ namespace Baikal } materials[i]->SetTwoSided(false); + + scene->AttachAutoreleaseObject(materials[i]); } // Enumerate all shapes in the scene @@ -78,16 +83,23 @@ namespace Baikal auto idx = objshapes[s].mesh.material_ids[0]; mesh->SetMaterial(materials[idx]); scene->AttachShape(mesh); + + scene->AttachAutoreleaseObject(mesh); } Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + ImageBasedLight* ibl = new ImageBasedLight(); ibl->SetTexture(ibl_texture); ibl->SetMultiplier(1.f); + scene->AttachAutoreleaseObject(ibl); + DirectionalLight* light = new DirectionalLight(); light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); + scene->AttachAutoreleaseObject(light); scene->AttachLight(light); scene->AttachLight(ibl); diff --git a/App/Scene/camera.h b/App/Scene/camera.h index 19830287..1c5aacec 100644 --- a/App/Scene/camera.h +++ b/App/Scene/camera.h @@ -31,17 +31,15 @@ #include "math/float3.h" #include "math/float2.h" +#include "scene_object.h" + namespace Baikal { - class Camera + class Camera : public SceneObject { public: Camera() = default; virtual ~Camera() = default; - - // Forbidden stuff - Camera(Camera const&) = delete; - Camera& operator = (Camera const&) = delete; }; class PerspectiveCamera : public Camera diff --git a/App/Scene/light.h b/App/Scene/light.h index dc2329b6..e2890733 100644 --- a/App/Scene/light.h +++ b/App/Scene/light.h @@ -36,6 +36,8 @@ #include "iterator.h" +#include "scene_object.h" + namespace Baikal { class Texture; @@ -45,7 +47,7 @@ namespace Baikal High-level interface all light classes need to implement. */ - class Light + class Light : public SceneObject { public: // Constructor @@ -71,10 +73,6 @@ namespace Baikal // Iterator for all the textures used by the light virtual Iterator* CreateTextureIterator() const; - // Forbidden stuff - Light(Light const&) = delete; - Light& operator = (Light const&) = delete; - private: // Position RadeonRays::float3 m_p; diff --git a/App/Scene/material.h b/App/Scene/material.h index f062513c..09c662b7 100644 --- a/App/Scene/material.h +++ b/App/Scene/material.h @@ -35,6 +35,8 @@ #include "math/float3.h" +#include "scene_object.h" + namespace Baikal { class Iterator; @@ -45,7 +47,7 @@ namespace Baikal \details Base class for all CPU side material supported by the renderer. */ - class Material + class Material : public SceneObject { public: // Material input type diff --git a/App/Scene/scene1.cpp b/App/Scene/scene1.cpp index 6a1cc9b8..219bf862 100644 --- a/App/Scene/scene1.cpp +++ b/App/Scene/scene1.cpp @@ -6,12 +6,14 @@ #include #include #include +#include namespace Baikal { // Data structures for shapes and lights using ShapeList = std::vector; using LightList = std::vector; + using AutoreleasePool = std::set; // Internal data struct Scene1::SceneImpl @@ -21,6 +23,8 @@ namespace Baikal Camera const* m_camera; DirtyFlags m_dirty_flags; + + AutoreleasePool m_autorelease_pool; }; Scene1::Scene1() @@ -32,6 +36,10 @@ namespace Baikal Scene1::~Scene1() { + for(auto& i : m_impl->m_autorelease_pool) + { + delete i; + } } Scene1::DirtyFlags Scene1::GetDirtyFlags() const @@ -137,8 +145,43 @@ namespace Baikal return m_impl->m_shapes.size(); } + void Scene1::AttachAutoreleaseObject(SceneObject const* object) + { + assert(object); + + // Check if the light is already in the scene + AutoreleasePool::const_iterator citer = std::find(m_impl->m_autorelease_pool.cbegin(), m_impl->m_autorelease_pool.cend(), object); + + // And insert only if not + if (citer == m_impl->m_autorelease_pool.cend()) + { + m_impl->m_autorelease_pool.insert(object); + } + } + + void Scene1::DetachAutoreleaseObject(SceneObject const* object) + { + assert(object); + + // Check if the light is already in the scene + AutoreleasePool::const_iterator citer = std::find(m_impl->m_autorelease_pool.cbegin(), m_impl->m_autorelease_pool.cend(), object); + + // And insert only if not + if (citer != m_impl->m_autorelease_pool.cend()) + { + m_impl->m_autorelease_pool.erase(citer); + } + } + Iterator* Scene1::CreateLightIterator() const { return new IteratorImpl(m_impl->m_lights.begin(), m_impl->m_lights.end()); } + + bool Scene1::IsValid() const + { + return GetCamera() && + GetNumLights() > 0 && + GetNumShapes() > 0; + } } diff --git a/App/Scene/scene1.h b/App/Scene/scene1.h index 9a12aeac..f9f461cc 100644 --- a/App/Scene/scene1.h +++ b/App/Scene/scene1.h @@ -37,6 +37,7 @@ namespace Baikal class Volume; class Camera; class Iterator; + class SceneObject; /** \brief Scene class. @@ -93,7 +94,12 @@ namespace Baikal // Clear all flags void ClearDirtyFlags() const; - //TODO: add scene validation method + // Check if the scene is ready for rendering + bool IsValid() const; + + // Autorelase objects are deleted when scene is destroyed + void AttachAutoreleaseObject(SceneObject const* object); + void DetachAutoreleaseObject(SceneObject const* object); // Forbidden stuff Scene1(Scene1 const&) = delete; diff --git a/App/Scene/scene_object.h b/App/Scene/scene_object.h new file mode 100644 index 00000000..0830b56c --- /dev/null +++ b/App/Scene/scene_object.h @@ -0,0 +1,46 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file scene_object.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains base interface for scene graph objects + */ +#pragma once + +namespace Baikal +{ + class SceneObject + { + public: + SceneObject() = default; + virtual ~SceneObject() = 0; + + SceneObject(SceneObject const&) = delete; + SceneObject& operator = (SceneObject const&) = delete; + }; + + inline SceneObject::~SceneObject() + { + } +} diff --git a/App/Scene/shape.h b/App/Scene/shape.h index 4c2eabd4..dd340f6a 100644 --- a/App/Scene/shape.h +++ b/App/Scene/shape.h @@ -33,6 +33,8 @@ #include #include +#include "scene_object.h" + namespace Baikal { class Material; @@ -42,7 +44,7 @@ namespace Baikal High-level interface all shape classes need to implement. */ - class Shape + class Shape : public SceneObject { public: // Constructor diff --git a/App/Scene/texture.h b/App/Scene/texture.h index 4eb2c3a4..faa5cdcd 100644 --- a/App/Scene/texture.h +++ b/App/Scene/texture.h @@ -34,6 +34,8 @@ #include #include +#include "scene_object.h" + namespace Baikal { class Material; @@ -43,7 +45,7 @@ namespace Baikal Texture is used to host CPU memory for image data. */ - class Texture + class Texture : public SceneObject { public: enum class Format @@ -67,10 +69,7 @@ namespace Baikal virtual Format GetFormat() const; // Get data size in bytes virtual std::size_t GetSizeInBytes() const; - - Texture(Texture const&) = delete; - Texture& operator = (Texture const&) = delete; - + private: std::unique_ptr m_data; RadeonRays::int2 m_size; From 2708348d57e8fd0357de5e14aec6366e51d1c94d Mon Sep 17 00:00:00 2001 From: yozhijk Date: Tue, 17 Jan 2017 12:16:39 +0100 Subject: [PATCH 05/24] Start SceneIo implementation --- App/CL/integrator_pt.cl | 4 +- App/Scene/Collector/collector.h | 1 - App/Scene/Loaders/image_io.cpp | 2 +- App/Scene/Loaders/image_io.h | 14 +- App/Scene/Loaders/scene_io.cpp | 353 ++++++++++++++++++ .../Loaders/{scene_loader.h => scene_io.h} | 21 +- App/Scene/Loaders/scene_loader.cpp | 109 ------ App/Scene/iterator.h | 12 +- App/Scene/material.cpp | 14 +- App/Scene/material.h | 7 - App/Scene/scene_object.h | 28 +- App/Scene/scene_tracker.cpp | 11 + App/Scene/texture.h | 9 +- App/main.cpp | 12 +- 14 files changed, 450 insertions(+), 147 deletions(-) create mode 100644 App/Scene/Loaders/scene_io.cpp rename App/Scene/Loaders/{scene_loader.h => scene_io.h} (74%) delete mode 100644 App/Scene/Loaders/scene_loader.cpp diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index 8989ee1e..95e343ee 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -782,8 +782,8 @@ __kernel void ShadeBackground( // In case of a miss if (isects[globalid].shapeid < 0 && Path_IsAlive(path)) { - //float3 t = Path_GetThroughput(path); - //output[pixelidx].xyz += REASONABLE_RADIANCE(envmapmul * Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * t); + float3 t = Path_GetThroughput(path); + output[pixelidx].xyz += REASONABLE_RADIANCE(envmapmul * Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * t); } } } diff --git a/App/Scene/Collector/collector.h b/App/Scene/Collector/collector.h index 8bbd917c..8b9336f2 100644 --- a/App/Scene/Collector/collector.h +++ b/App/Scene/Collector/collector.h @@ -57,7 +57,6 @@ namespace Baikal class Collector { public: - using ExpandFunc = std::function(void const*)>; using ChangedFunc = std::function; using FinalizeFunc = std::function; diff --git a/App/Scene/Loaders/image_io.cpp b/App/Scene/Loaders/image_io.cpp index 77b9b59e..f5a0f20c 100644 --- a/App/Scene/Loaders/image_io.cpp +++ b/App/Scene/Loaders/image_io.cpp @@ -85,7 +85,7 @@ namespace Baikal return new Texture(texturedata, RadeonRays::int2(spec.width, spec.height), fmt); } - ImageIo* CreateImageIo() + ImageIo* ImageIo::CreateImageIo() { return new Oiio(); } diff --git a/App/Scene/Loaders/image_io.h b/App/Scene/Loaders/image_io.h index 4417deee..04dda430 100644 --- a/App/Scene/Loaders/image_io.h +++ b/App/Scene/Loaders/image_io.h @@ -34,17 +34,29 @@ namespace Baikal { class Texture; + /** + \brief Interface for image loading and writing + + ImageIO is responsible for texture loading from disk and keeping track of image reuse. + */ class ImageIo { public: + // Create default image IO + static ImageIo* CreateImageIo(); + + // Constructor ImageIo() = default; + // Destructor ~ImageIo() = default; + // Load texture from file virtual Texture* LoadImage(std::string const& filename) const = 0; + // Disallow copying ImageIo(ImageIo const&) = delete; ImageIo& operator = (ImageIo const&) = delete; }; - ImageIo* CreateImageIo(); + } diff --git a/App/Scene/Loaders/scene_io.cpp b/App/Scene/Loaders/scene_io.cpp new file mode 100644 index 00000000..3c2fccc4 --- /dev/null +++ b/App/Scene/Loaders/scene_io.cpp @@ -0,0 +1,353 @@ +#include "scene_io.h" +#include "image_io.h" +#include "../scene1.h" +#include "../shape.h" +#include "../material.h" +#include "../light.h" +#include "../texture.h" +#include "../scene_object.h" + + +#include "tiny_obj_loader.h" + +namespace Baikal +{ + class SceneIoObj : public SceneIo + { + public: + Scene1* LoadScene(std::string const& filename, std::string const& basepath) const override; + }; + + class SceneIoTest : public SceneIo + { + public: + Scene1* LoadScene(std::string const& filename, std::string const& basepath) const override; + }; + + + SceneIo* SceneIo::CreateSceneIoObj() + { + return new SceneIoObj(); + } + + SceneIo* SceneIo::CreateSceneIoTest() + { + return new SceneIoTest(); + } + + Scene1* SceneIoObj::LoadScene(std::string const& filename, std::string const& basepath) const + { + using namespace tinyobj; + + auto image_io(ImageIo::CreateImageIo()); + + // Loader data + std::vector objshapes; + std::vector objmaterials; + + // Try loading file + std::string res = LoadObj(objshapes, objmaterials, filename.c_str(), basepath.c_str()); + if (res != "") + { + throw std::runtime_error(res); + } + + // Allocate scene + Scene1* scene(new Scene1); + + // Enumerate and translate materials + std::vector materials(objmaterials.size()); + for (int i = 0; i < (int)objmaterials.size(); ++i) + { + materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + + if (!objmaterials[i].diffuse_texname.empty()) + { + auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); + materials[i]->SetInputValue("albedo", texture); + scene->AttachAutoreleaseObject(texture); + } + else + { + materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); + } + + materials[i]->SetTwoSided(false); + + scene->AttachAutoreleaseObject(materials[i]); + } + + // Enumerate all shapes in the scene + for (int s = 0; s < (int)objshapes.size(); ++s) + { + Mesh* mesh = new Mesh(); + + auto num_vertices = objshapes[s].mesh.positions.size() / 3; + mesh->SetVertices(&objshapes[s].mesh.positions[0], num_vertices); + + auto num_normals = objshapes[s].mesh.normals.size() / 3; + mesh->SetNormals(&objshapes[s].mesh.normals[0], num_normals); + + auto num_uvs = objshapes[s].mesh.texcoords.size() / 2; + + if (num_uvs) + { + mesh->SetUVs(&objshapes[s].mesh.texcoords[0], num_uvs); + } + else + { + std::vector zero(num_vertices); + mesh->SetUVs(&zero[0], num_vertices); + } + + auto num_indices = objshapes[s].mesh.indices.size(); + mesh->SetIndices(reinterpret_cast(&objshapes[s].mesh.indices[0]), num_indices); + + auto idx = objshapes[s].mesh.material_ids[0]; + mesh->SetMaterial(materials[idx]); + scene->AttachShape(mesh); + + scene->AttachAutoreleaseObject(mesh); + } + + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); + scene->AttachAutoreleaseObject(ibl); + + + DirectionalLight* light = new DirectionalLight(); + light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); + light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); + scene->AttachAutoreleaseObject(light); + + scene->AttachLight(light); + scene->AttachLight(ibl); + + return scene; + } + + static void CreateSphere(std::uint32_t lat, std::uint32_t lon, float r, RadeonRays::float3 const& c, + std::vector& vertices, + std::vector& normals, + std::vector& uvs, + std::vector& indices + ) + { + + float theta, phi; + int i, j, t, ntri, nvec; + + nvec = (lat-2)* lon+2; + ntri = (lat-2)*(lon-1)*2; + + vertices.resize( nvec ); + normals.resize( nvec ); + uvs.resize( nvec ); + indices.resize( ntri * 3 ); + + for( t=0, j=1; jSetVertices(vertices, 4); + mesh->SetNormals(normals, 4); + mesh->SetUVs(uvs, 4); + mesh->SetIndices(indices, 6); + scene->AttachShape(mesh); + scene->AttachAutoreleaseObject(mesh); + + auto mesh1 = new Mesh(); + mesh1->SetVertices(vertices1, 4); + mesh1->SetNormals(normals1, 4); + mesh1->SetUVs(uvs, 4); + mesh1->SetIndices(indices, 6); + scene->AttachShape(mesh1); + scene->AttachAutoreleaseObject(mesh1); + + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); + scene->AttachLight(ibl); + scene->AttachAutoreleaseObject(ibl); + + SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + green->SetInputValue("albedo", float4(0.1f, 0.2f, 0.1f, 1.f)); + + SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); + spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); + + MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + mix->SetInputValue("base_material", green); + mix->SetInputValue("top_material", spec); + mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); + + + mesh->SetMaterial(mix); + mesh1->SetMaterial(spec); + + scene->AttachAutoreleaseObject(green); + scene->AttachAutoreleaseObject(spec); + scene->AttachAutoreleaseObject(mix); + } + else if (filename == "sphere+ibl") + { + std::vector vertices; + std::vector normals; + std::vector uvs; + std::vector indices; + + CreateSphere(64, 32, 2.f, float3(), + vertices, + normals, + uvs, + indices); + + auto mesh = new Mesh(); + mesh->SetVertices(&vertices[0], vertices.size()); + mesh->SetNormals(&normals[0], normals.size()); + mesh->SetUVs(&uvs[0], uvs.size()); + mesh->SetIndices(&indices[0], indices.size()); + scene->AttachShape(mesh); + scene->AttachAutoreleaseObject(mesh); + + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); + scene->AttachLight(ibl); + scene->AttachAutoreleaseObject(ibl); + + SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + green->SetInputValue("albedo", float4(0.1f, 0.2f, 0.1f, 1.f)); + + SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); + spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); + + MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + mix->SetInputValue("base_material", green); + mix->SetInputValue("top_material", spec); + mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); + + + mesh->SetMaterial(mix); + + scene->AttachAutoreleaseObject(green); + scene->AttachAutoreleaseObject(spec); + scene->AttachAutoreleaseObject(mix); + + } + + return scene; + } +} diff --git a/App/Scene/Loaders/scene_loader.h b/App/Scene/Loaders/scene_io.h similarity index 74% rename from App/Scene/Loaders/scene_loader.h rename to App/Scene/Loaders/scene_io.h index 567c4fb5..d029dddd 100644 --- a/App/Scene/Loaders/scene_loader.h +++ b/App/Scene/Loaders/scene_io.h @@ -21,7 +21,7 @@ ********************************************************************/ /** - \file scene_loader.h + \file scene_io.h \author Dmitry Kozlov \version 1.0 \brief Contains a class representing material @@ -34,5 +34,22 @@ namespace Baikal { class Scene1; - Scene1* LoadFromObj(std::string const& filename, std::string const& basepath); + class SceneIo + { + public: + static SceneIo* CreateSceneIoObj(); + static SceneIo* CreateSceneIoTest(); + + SceneIo() = default; + virtual ~SceneIo() = 0; + + virtual Scene1* LoadScene(std::string const& filename, std::string const& basepath) const = 0; + + SceneIo(SceneIo const&) = delete; + SceneIo& operator = (SceneIo const&) = delete; + }; + + inline SceneIo::~SceneIo() + { + } } diff --git a/App/Scene/Loaders/scene_loader.cpp b/App/Scene/Loaders/scene_loader.cpp deleted file mode 100644 index b43c8199..00000000 --- a/App/Scene/Loaders/scene_loader.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "scene_loader.h" -#include "image_io.h" -#include "../scene1.h" -#include "../shape.h" -#include "../material.h" -#include "../light.h" -#include "../texture.h" -#include "../scene_object.h" - - -#include "tiny_obj_loader.h" - -namespace Baikal -{ - Scene1* LoadFromObj(std::string const& filename, std::string const& basepath) - { - using namespace tinyobj; - - auto image_io(CreateImageIo()); - - // Loader data - std::vector objshapes; - std::vector objmaterials; - - // Try loading file - std::string res = LoadObj(objshapes, objmaterials, filename.c_str(), basepath.c_str()); - if (res != "") - { - throw std::runtime_error(res); - } - - // Allocate scene - Scene1* scene(new Scene1); - - // Enumerate and translate materials - std::vector materials(objmaterials.size()); - for (int i = 0; i < (int)objmaterials.size(); ++i) - { - materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - - if (!objmaterials[i].diffuse_texname.empty()) - { - auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); - materials[i]->SetInputValue("albedo", texture); - scene->AttachAutoreleaseObject(texture); - } - else - { - materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); - } - - materials[i]->SetTwoSided(false); - - scene->AttachAutoreleaseObject(materials[i]); - } - - // Enumerate all shapes in the scene - for (int s = 0; s < (int)objshapes.size(); ++s) - { - Mesh* mesh = new Mesh(); - - auto num_vertices = objshapes[s].mesh.positions.size() / 3; - mesh->SetVertices(&objshapes[s].mesh.positions[0], num_vertices); - - auto num_normals = objshapes[s].mesh.normals.size() / 3; - mesh->SetNormals(&objshapes[s].mesh.normals[0], num_normals); - - auto num_uvs = objshapes[s].mesh.texcoords.size() / 2; - - if (num_uvs) - { - mesh->SetUVs(&objshapes[s].mesh.texcoords[0], num_uvs); - } - else - { - std::vector zero(num_vertices); - mesh->SetUVs(&zero[0], num_vertices); - } - - auto num_indices = objshapes[s].mesh.indices.size(); - mesh->SetIndices(reinterpret_cast(&objshapes[s].mesh.indices[0]), num_indices); - - auto idx = objshapes[s].mesh.material_ids[0]; - mesh->SetMaterial(materials[idx]); - scene->AttachShape(mesh); - - scene->AttachAutoreleaseObject(mesh); - } - - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); - scene->AttachAutoreleaseObject(ibl_texture); - - ImageBasedLight* ibl = new ImageBasedLight(); - ibl->SetTexture(ibl_texture); - ibl->SetMultiplier(1.f); - scene->AttachAutoreleaseObject(ibl); - - - DirectionalLight* light = new DirectionalLight(); - light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); - light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); - scene->AttachAutoreleaseObject(light); - - scene->AttachLight(light); - scene->AttachLight(ibl); - - return scene; - } -} diff --git a/App/Scene/iterator.h b/App/Scene/iterator.h index 5168154f..23d0e199 100644 --- a/App/Scene/iterator.h +++ b/App/Scene/iterator.h @@ -71,16 +71,17 @@ namespace Baikal class EmptyIterator : public Iterator { public: + // Constructor EmptyIterator() = default; - + // Destructor ~EmptyIterator() = default; - + // EmptyIterator is never valid bool IsValid() const override { return false; } - + // Nothing to go to void Next() override {} - + // Dereferencing always returns nullptr void const* Item() const override { return nullptr; } - + // Nothing to reset void Reset() override {} }; @@ -141,6 +142,7 @@ namespace Baikal Reset(); } + // Check if we reached end bool IsValid() const override { return m_cur != m_end; diff --git a/App/Scene/material.cpp b/App/Scene/material.cpp index a3a0b4e0..5d9b16c4 100644 --- a/App/Scene/material.cpp +++ b/App/Scene/material.cpp @@ -50,8 +50,7 @@ namespace Baikal Material::Material() - : m_dirty(true) - , m_twosided(false) + : m_twosided(false) { } @@ -235,16 +234,6 @@ namespace Baikal SetDirty(true); } - bool Material::IsDirty() const - { - return m_dirty; - } - - void Material::SetDirty(bool dirty) const - { - m_dirty = dirty; - } - SingleBxdf::SingleBxdf(BxdfType type) : m_type(type) { @@ -252,6 +241,7 @@ namespace Baikal RegisterInput("normal", "Normal map", {InputType::kTexture}); RegisterInput("ior", "Index of refraction", {InputType::kFloat4}); RegisterInput("fresnel", "Fresnel flag", {InputType::kFloat4}); + RegisterInput("roughness", "Roughness", {InputType::kFloat4}); SetInputValue("albedo", RadeonRays::float4(0.7f, 0.7f, 0.7f, 1.f)); } diff --git a/App/Scene/material.h b/App/Scene/material.h index 09c662b7..07510822 100644 --- a/App/Scene/material.h +++ b/App/Scene/material.h @@ -116,11 +116,6 @@ namespace Baikal // Set two-sidedness virtual void SetTwoSided(bool twosided); - // Check if material state has changed - virtual bool IsDirty() const; - // Set dirty state - virtual void SetDirty(bool dirty) const; - protected: // Register specific input void RegisterInput(std::string const& name, std::string const& desc, std::set&& supported_types); @@ -134,8 +129,6 @@ namespace Baikal using InputMap = std::map; // Input map InputMap m_inputs; - // Dirty flag - mutable bool m_dirty; // Sidedness flag bool m_twosided; }; diff --git a/App/Scene/scene_object.h b/App/Scene/scene_object.h index 0830b56c..c2d3e74a 100644 --- a/App/Scene/scene_object.h +++ b/App/Scene/scene_object.h @@ -33,14 +33,40 @@ namespace Baikal class SceneObject { public: - SceneObject() = default; + // Constructor + SceneObject(); + // Destructor virtual ~SceneObject() = 0; + // Check if the object has been changed since last reset + virtual bool IsDirty() const; + // Set dirty flag + virtual void SetDirty(bool dirty) const; + SceneObject(SceneObject const&) = delete; SceneObject& operator = (SceneObject const&) = delete; + + private: + mutable bool m_dirty; + }; + inline SceneObject::SceneObject() + : m_dirty(false) + { + } + inline SceneObject::~SceneObject() { } + + inline bool SceneObject::IsDirty() const + { + return m_dirty; + } + + inline void SceneObject::SetDirty(bool dirty) const + { + m_dirty = dirty; + } } diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index 194d7f84..f465b54d 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -745,6 +745,17 @@ namespace Baikal clw_material->ni = 1.f; } + value = material->GetInputValue("roughness"); + + if (value.type == Material::InputType::kFloat4) + { + clw_material->ns = value.float_value.x; + } + else + { + clw_material->ns = 0.99f; + } + break; } diff --git a/App/Scene/texture.h b/App/Scene/texture.h index faa5cdcd..1c495954 100644 --- a/App/Scene/texture.h +++ b/App/Scene/texture.h @@ -56,7 +56,7 @@ namespace Baikal }; // Constructor - // Note, that texture takes ownership of data array + // Note, that texture takes ownership of its data array Texture(char const* data, RadeonRays::int2 size, Format format); // Destructor (the data is destroyed as well) virtual ~Texture(); @@ -69,10 +69,17 @@ namespace Baikal virtual Format GetFormat() const; // Get data size in bytes virtual std::size_t GetSizeInBytes() const; + + // Disallow copying + Texture(Texture const&) = delete; + Texture& operator = (Texture const&) = delete; private: + // Image data std::unique_ptr m_data; + // Image dimensions RadeonRays::int2 m_size; + // Format Format m_format; }; diff --git a/App/main.cpp b/App/main.cpp index da60f0ae..7a118c6f 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -71,7 +71,7 @@ THE SOFTWARE. #include "CLW/clwoutput.h" #include "config_manager.h" #include "Scene/scene1.h" -#include "Scene/Loaders/scene_loader.h" +#include "Scene/Loaders/scene_io.h" Baikal::Scene1 scene; @@ -81,8 +81,8 @@ using namespace RadeonRays; char const* kHelpMessage = "App [-p path_to_models][-f model_name][-b][-r][-ns number_of_shadow_rays][-ao ao_radius][-w window_width][-h window_height][-nb number_of_indirect_bounces]"; char const* g_path = -"../Resources/CornellBox"; -char const* g_modelname = "orig.objm"; +"../Resources/be"; +char const* g_modelname = "Energia-Buran.obj"; char const* g_envmapname = "../Resources/Textures/studio015.hdr"; std::unique_ptr g_shader_manager; @@ -115,7 +115,6 @@ float g_camera_focal_length = 0.035f; // 35mm lens float g_camera_focus_distance = 0.f; float g_camera_aperture = 0.f; - bool g_recording_enabled = false; int g_frame_count = 0; bool g_benchmark = false; @@ -345,7 +344,10 @@ void InitData() basepath += "/"; std::string filename = basepath + g_modelname; - g_scene.reset(Baikal::LoadFromObj(filename, basepath)); + { + std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoTest()); + g_scene.reset(scene_io->LoadScene(/*filename*/"sphere+ibl", basepath)); + } g_camera.reset(new Baikal::PerspectiveCamera( g_camera_pos From 774113532fe491865e7591588a4f83239bc31f11 Mon Sep 17 00:00:00 2001 From: yozhijk Date: Wed, 18 Jan 2017 20:57:41 +0100 Subject: [PATCH 06/24] Implement more testing scenes --- App/Scene/Loaders/scene_io.cpp | 304 ++++++---------------------- App/Scene/Loaders/scene_io.h | 13 +- App/Scene/Loaders/scene_test_io.cpp | 287 ++++++++++++++++++++++++++ App/main.cpp | 6 +- 4 files changed, 366 insertions(+), 244 deletions(-) create mode 100644 App/Scene/Loaders/scene_test_io.cpp diff --git a/App/Scene/Loaders/scene_io.cpp b/App/Scene/Loaders/scene_io.cpp index 3c2fccc4..e58eeff9 100644 --- a/App/Scene/Loaders/scene_io.cpp +++ b/App/Scene/Loaders/scene_io.cpp @@ -5,36 +5,26 @@ #include "../material.h" #include "../light.h" #include "../texture.h" -#include "../scene_object.h" + #include "tiny_obj_loader.h" namespace Baikal { + // Obj scene loader class SceneIoObj : public SceneIo { public: + // Load scene from file Scene1* LoadScene(std::string const& filename, std::string const& basepath) const override; }; - class SceneIoTest : public SceneIo - { - public: - Scene1* LoadScene(std::string const& filename, std::string const& basepath) const override; - }; - - SceneIo* SceneIo::CreateSceneIoObj() { return new SceneIoObj(); } - SceneIo* SceneIo::CreateSceneIoTest() - { - return new SceneIoTest(); - } - Scene1* SceneIoObj::LoadScene(std::string const& filename, std::string const& basepath) const { using namespace tinyobj; @@ -56,32 +46,66 @@ namespace Baikal Scene1* scene(new Scene1); // Enumerate and translate materials + // Keep track of emissive subset + std::set emissives; std::vector materials(objmaterials.size()); for (int i = 0; i < (int)objmaterials.size(); ++i) { - materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + RadeonRays::float3 emission(objmaterials[i].emission[0], objmaterials[i].emission[1], objmaterials[i].emission[2]); - if (!objmaterials[i].diffuse_texname.empty()) + // Check if this is emissive + if (emission.sqnorm() > 0) { - auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); - materials[i]->SetInputValue("albedo", texture); - scene->AttachAutoreleaseObject(texture); + // If yes create emissive brdf + materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kEmissive); + + // Set albedo + if (!objmaterials[i].diffuse_texname.empty()) + { + auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); + materials[i]->SetInputValue("albedo", texture); + scene->AttachAutoreleaseObject(texture); + } + else + { + materials[i]->SetInputValue("albedo", emission); + } + + // Insert into emissive set + emissives.insert(materials[i]); } else { - materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); + // Otherwise create lambert + materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + + // Set albedo + if (!objmaterials[i].diffuse_texname.empty()) + { + auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); + materials[i]->SetInputValue("albedo", texture); + scene->AttachAutoreleaseObject(texture); + } + else + { + materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); + } } + // Disable normal flip materials[i]->SetTwoSided(false); + // Put into the pool of autoreleased objects scene->AttachAutoreleaseObject(materials[i]); } // Enumerate all shapes in the scene for (int s = 0; s < (int)objshapes.size(); ++s) { + // Create empty mesh Mesh* mesh = new Mesh(); + // Set vertex and index data auto num_vertices = objshapes[s].mesh.positions.size() / 3; mesh->SetVertices(&objshapes[s].mesh.positions[0], num_vertices); @@ -90,6 +114,7 @@ namespace Baikal auto num_uvs = objshapes[s].mesh.texcoords.size() / 2; + // If we do not have UVs, generate zeroes if (num_uvs) { mesh->SetUVs(&objshapes[s].mesh.texcoords[0], num_uvs); @@ -100,16 +125,35 @@ namespace Baikal mesh->SetUVs(&zero[0], num_vertices); } + // Set indices auto num_indices = objshapes[s].mesh.indices.size(); mesh->SetIndices(reinterpret_cast(&objshapes[s].mesh.indices[0]), num_indices); + // Set material auto idx = objshapes[s].mesh.material_ids[0]; mesh->SetMaterial(materials[idx]); + + // Attach to the scene scene->AttachShape(mesh); + // Attach for autorelease scene->AttachAutoreleaseObject(mesh); + + // If the mesh has emissive material we need to add area light for it + if (emissives.find(materials[idx]) != emissives.cend()) + { + // Add area light for each polygon of emissive mesh + for (int l = 0; l < mesh->GetNumIndices() / 3 ;++l) + { + AreaLight* light = new AreaLight(mesh, l); + scene->AttachLight(light); + scene->AttachAutoreleaseObject(light); + } + + } } + // TODO: temporary code, add IBL Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); scene->AttachAutoreleaseObject(ibl_texture); @@ -119,6 +163,7 @@ namespace Baikal scene->AttachAutoreleaseObject(ibl); + // TODO: temporary code to add directional light DirectionalLight* light = new DirectionalLight(); light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); @@ -129,225 +174,4 @@ namespace Baikal return scene; } - - static void CreateSphere(std::uint32_t lat, std::uint32_t lon, float r, RadeonRays::float3 const& c, - std::vector& vertices, - std::vector& normals, - std::vector& uvs, - std::vector& indices - ) - { - - float theta, phi; - int i, j, t, ntri, nvec; - - nvec = (lat-2)* lon+2; - ntri = (lat-2)*(lon-1)*2; - - vertices.resize( nvec ); - normals.resize( nvec ); - uvs.resize( nvec ); - indices.resize( ntri * 3 ); - - for( t=0, j=1; jSetVertices(vertices, 4); - mesh->SetNormals(normals, 4); - mesh->SetUVs(uvs, 4); - mesh->SetIndices(indices, 6); - scene->AttachShape(mesh); - scene->AttachAutoreleaseObject(mesh); - - auto mesh1 = new Mesh(); - mesh1->SetVertices(vertices1, 4); - mesh1->SetNormals(normals1, 4); - mesh1->SetUVs(uvs, 4); - mesh1->SetIndices(indices, 6); - scene->AttachShape(mesh1); - scene->AttachAutoreleaseObject(mesh1); - - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); - scene->AttachAutoreleaseObject(ibl_texture); - - ImageBasedLight* ibl = new ImageBasedLight(); - ibl->SetTexture(ibl_texture); - ibl->SetMultiplier(1.f); - scene->AttachLight(ibl); - scene->AttachAutoreleaseObject(ibl); - - SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - green->SetInputValue("albedo", float4(0.1f, 0.2f, 0.1f, 1.f)); - - SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); - spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); - spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); - - MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); - mix->SetInputValue("base_material", green); - mix->SetInputValue("top_material", spec); - mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); - - - mesh->SetMaterial(mix); - mesh1->SetMaterial(spec); - - scene->AttachAutoreleaseObject(green); - scene->AttachAutoreleaseObject(spec); - scene->AttachAutoreleaseObject(mix); - } - else if (filename == "sphere+ibl") - { - std::vector vertices; - std::vector normals; - std::vector uvs; - std::vector indices; - - CreateSphere(64, 32, 2.f, float3(), - vertices, - normals, - uvs, - indices); - - auto mesh = new Mesh(); - mesh->SetVertices(&vertices[0], vertices.size()); - mesh->SetNormals(&normals[0], normals.size()); - mesh->SetUVs(&uvs[0], uvs.size()); - mesh->SetIndices(&indices[0], indices.size()); - scene->AttachShape(mesh); - scene->AttachAutoreleaseObject(mesh); - - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); - scene->AttachAutoreleaseObject(ibl_texture); - - ImageBasedLight* ibl = new ImageBasedLight(); - ibl->SetTexture(ibl_texture); - ibl->SetMultiplier(1.f); - scene->AttachLight(ibl); - scene->AttachAutoreleaseObject(ibl); - - SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - green->SetInputValue("albedo", float4(0.1f, 0.2f, 0.1f, 1.f)); - - SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); - spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); - spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); - - MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); - mix->SetInputValue("base_material", green); - mix->SetInputValue("top_material", spec); - mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); - - - mesh->SetMaterial(mix); - - scene->AttachAutoreleaseObject(green); - scene->AttachAutoreleaseObject(spec); - scene->AttachAutoreleaseObject(mix); - - } - - return scene; - } } diff --git a/App/Scene/Loaders/scene_io.h b/App/Scene/Loaders/scene_io.h index d029dddd..d14c54f3 100644 --- a/App/Scene/Loaders/scene_io.h +++ b/App/Scene/Loaders/scene_io.h @@ -24,7 +24,7 @@ \file scene_io.h \author Dmitry Kozlov \version 1.0 - \brief Contains a class representing material + \brief Contains an interface for scene loading */ #pragma once @@ -34,17 +34,28 @@ namespace Baikal { class Scene1; + /** + \brief Interface for scene loading + + SceneIO implementation is responsible for translation of various scene formats into Baikal. + */ class SceneIo { public: + // Create OBJ scene loader static SceneIo* CreateSceneIoObj(); + // Create test scene loader static SceneIo* CreateSceneIoTest(); + // Constructor SceneIo() = default; + // Destructor virtual ~SceneIo() = 0; + // Load the scene from file using resourse base path virtual Scene1* LoadScene(std::string const& filename, std::string const& basepath) const = 0; + // Disallow copying SceneIo(SceneIo const&) = delete; SceneIo& operator = (SceneIo const&) = delete; }; diff --git a/App/Scene/Loaders/scene_test_io.cpp b/App/Scene/Loaders/scene_test_io.cpp new file mode 100644 index 00000000..f32c3b5f --- /dev/null +++ b/App/Scene/Loaders/scene_test_io.cpp @@ -0,0 +1,287 @@ +#include "scene_io.h" +#include "image_io.h" +#include "../scene1.h" +#include "../shape.h" +#include "../material.h" +#include "../light.h" +#include "../texture.h" + +#include + +namespace Baikal +{ + // Create fake test IO + class SceneIoTest : public SceneIo + { + public: + // Load scene (this class uses filename to determine what scene to generate) + Scene1* LoadScene(std::string const& filename, std::string const& basepath) const override; + }; + + // Create test IO + SceneIo* SceneIo::CreateSceneIoTest() + { + return new SceneIoTest(); + } + + + // Create spehere mesh + Mesh* CreateSphere(std::uint32_t lat, std::uint32_t lon, float r, RadeonRays::float3 const& c) + { + auto num_verts = (lat - 2) * lon + 2; + auto num_tris = (lat - 2) * (lon - 1 ) * 2; + + std::vector vertices(num_verts); + std::vector normals(num_verts); + std::vector uvs(num_verts); + std::vector indices (num_tris * 3); + + + auto t = 0U; + for(auto j = 1U; j < lat - 1; j++) + for(auto i = 0; i < lon; i++) + { + float theta = float(j) / (lat - 1) * M_PI; + float phi = float(i) / (lon - 1 ) * M_PI * 2; + vertices[t].x = r * sinf(theta) * cosf(phi) + c.x; + vertices[t].y = r * cosf(theta) + c.y; + vertices[t].z = r * -sinf(theta) * sinf(phi) + c.z; + normals[t].x = sinf(theta) * cosf(phi); + normals[t].y = cosf(theta); + normals[t].z = -sinf(theta) * sinf(phi); + uvs[t].x = phi / (2 * M_PI); + uvs[t].y = theta / (M_PI); + ++t; + } + + vertices[t].x=c.x; vertices[t].y = c.y + r; vertices[t].z = c.z; + normals[t].x=0; normals[t].y = 1; normals[t].z = 0; + uvs[t].x=0; uvs[t].y = 0; + ++t; + vertices[t].x=c.x; vertices[t].y = c.y-r; vertices[t].z = c.z; + normals[t].x=0; normals[t].y = -1; normals[t].z = 0; + uvs[t].x=1; uvs[t].y = 1; + ++t; + + t = 0U; + for(auto j = 0U; j < lat - 3; j++) + for(auto i = 0U; i < lon - 1; i++) + { + indices[t++] = j * lon + i; + indices[t++] = (j + 1) * lon + i + 1; + indices[t++] = j * lon + i + 1; + indices[t++] = j * lon + i; + indices[t++] = (j + 1) * lon + i; + indices[t++] = (j + 1) * lon + i + 1; + } + + for(auto i = 0U; i < lon - 1; i++) + { + indices[t++] = (lat - 2) * lon; + indices[t++] = i; + indices[t++] = i + 1; + indices[t++] = (lat - 2) * lon + 1; + indices[t++] = (lat - 3) * lon + i + 1; + indices[t++] = (lat - 3) * lon + i; + } + + auto mesh = new Mesh(); + mesh->SetVertices(&vertices[0], vertices.size()); + mesh->SetNormals(&normals[0], normals.size()); + mesh->SetUVs(&uvs[0], uvs.size()); + mesh->SetIndices(&indices[0], indices.size()); + + return mesh; + } + + + // Create quad + Mesh* CreateQuad(std::vector const& vertices, bool flip) + { + using namespace RadeonRays; + + auto u1 = normalize(vertices[1] - vertices[0]); + auto u2 = normalize(vertices[3] - vertices[0]); + auto n = -cross(u1, u2); + + if (flip) + { + n = -n; + } + + float3 normals[] = { n, n, n, n }; + + float2 uvs[] = + { + float2(0, 0), + float2(1, 0), + float2(1, 1), + float2(0, 1) + }; + + std::uint32_t indices[] = + { + 0, 1, 2, + 0, 2, 3 + }; + + auto mesh = new Mesh(); + mesh->SetVertices(&vertices[0], 4); + mesh->SetNormals(normals, 4); + mesh->SetUVs(uvs, 4); + mesh->SetIndices(indices, 6); + + return mesh; + } + + Scene1* SceneIoTest::LoadScene(std::string const& filename, std::string const& basepath) const + { + using namespace RadeonRays; + + Scene1* scene(new Scene1); + + auto image_io(ImageIo::CreateImageIo()); + + if (filename == "quad+ibl") + { + Mesh* quad = CreateQuad( + { + RadeonRays::float3(-5, 0, -5), + RadeonRays::float3(5, 0, -5), + RadeonRays::float3(5, 0, 5), + RadeonRays::float3(-5, 0, 5), + } + , false); + + scene->AttachShape(quad); + scene->AttachAutoreleaseObject(quad); + + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); + scene->AttachLight(ibl); + scene->AttachAutoreleaseObject(ibl); + + SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + green->SetInputValue("albedo", float4(0.1f, 0.2f, 0.1f, 1.f)); + + SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); + spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); + + MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + mix->SetInputValue("base_material", green); + mix->SetInputValue("top_material", spec); + mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); + + quad->SetMaterial(mix); + + scene->AttachAutoreleaseObject(green); + scene->AttachAutoreleaseObject(spec); + scene->AttachAutoreleaseObject(mix); + } + else if (filename == "sphere+ibl") + { + auto mesh = CreateSphere(64, 32, 2.f, float3()); + scene->AttachShape(mesh); + scene->AttachAutoreleaseObject(mesh); + + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); + scene->AttachLight(ibl); + scene->AttachAutoreleaseObject(ibl); + + SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + green->SetInputValue("albedo", float4(0.1f, 0.2f, 0.1f, 1.f)); + + SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); + spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); + + MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + mix->SetInputValue("base_material", green); + mix->SetInputValue("top_material", spec); + mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); + + + mesh->SetMaterial(mix); + + scene->AttachAutoreleaseObject(green); + scene->AttachAutoreleaseObject(spec); + scene->AttachAutoreleaseObject(mix); + + } + else if (filename == "sphere+plane+area") + { + auto mesh = CreateSphere(64, 32, 2.f, float3(0.f, 2.2f, 0.f)); + scene->AttachShape(mesh); + scene->AttachAutoreleaseObject(mesh); + + SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + green->SetInputValue("albedo", 2.f * float4(0.1f, 0.2f, 0.1f, 1.f)); + + SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); + spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); + + MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + mix->SetInputValue("base_material", green); + mix->SetInputValue("top_material", spec); + mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); + + mesh->SetMaterial(mix); + + Mesh* floor = CreateQuad( + { + RadeonRays::float3(-8, 0, -8), + RadeonRays::float3(8, 0, -8), + RadeonRays::float3(8, 0, 8), + RadeonRays::float3(-8, 0, 8), + } + , false); + scene->AttachShape(floor); + scene->AttachAutoreleaseObject(floor); + + floor->SetMaterial(green); + + SingleBxdf* emissive = new SingleBxdf(SingleBxdf::BxdfType::kEmissive); + emissive->SetInputValue("albedo", 5.f * float4(3.1f, 3.f, 2.8f, 1.f)); + + Mesh* light = CreateQuad( + { + RadeonRays::float3(-2, 6, -2), + RadeonRays::float3(2, 6, -2), + RadeonRays::float3(2, 6, 2), + RadeonRays::float3(-2, 6, 2), + } + , true); + scene->AttachShape(light); + scene->AttachAutoreleaseObject(light); + + light->SetMaterial(emissive); + + AreaLight* l1 = new AreaLight(light, 0); + AreaLight* l2 = new AreaLight(light, 1); + scene->AttachLight(l1); + scene->AttachLight(l2); + scene->AttachAutoreleaseObject(l1); + scene->AttachAutoreleaseObject(l2); + + scene->AttachAutoreleaseObject(emissive); + scene->AttachAutoreleaseObject(green); + scene->AttachAutoreleaseObject(spec); + scene->AttachAutoreleaseObject(mix); + + } + + return scene; + } +} + diff --git a/App/main.cpp b/App/main.cpp index 7a118c6f..bae42ffc 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -81,8 +81,8 @@ using namespace RadeonRays; char const* kHelpMessage = "App [-p path_to_models][-f model_name][-b][-r][-ns number_of_shadow_rays][-ao ao_radius][-w window_width][-h window_height][-nb number_of_indirect_bounces]"; char const* g_path = -"../Resources/be"; -char const* g_modelname = "Energia-Buran.obj"; +"../Resources/CornellBox"; +char const* g_modelname = "orig.objm"; char const* g_envmapname = "../Resources/Textures/studio015.hdr"; std::unique_ptr g_shader_manager; @@ -346,7 +346,7 @@ void InitData() { std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoTest()); - g_scene.reset(scene_io->LoadScene(/*filename*/"sphere+ibl", basepath)); + g_scene.reset(scene_io->LoadScene("sphere+plane+area", basepath)); } g_camera.reset(new Baikal::PerspectiveCamera( From f02af53d76ba9ec430e061ab90e3bae9c132927a Mon Sep 17 00:00:00 2001 From: yozhijk Date: Wed, 18 Jan 2017 21:08:07 +0100 Subject: [PATCH 07/24] Minor fixes --- App/Scene/Loaders/scene_test_io.cpp | 47 +++++++++++++++++++++++++++++ App/main.cpp | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/App/Scene/Loaders/scene_test_io.cpp b/App/Scene/Loaders/scene_test_io.cpp index f32c3b5f..2f7d754c 100644 --- a/App/Scene/Loaders/scene_test_io.cpp +++ b/App/Scene/Loaders/scene_test_io.cpp @@ -279,6 +279,53 @@ namespace Baikal scene->AttachAutoreleaseObject(spec); scene->AttachAutoreleaseObject(mix); + } + else if (filename == "sphere+plane+ibl") + { + auto mesh = CreateSphere(64, 32, 2.f, float3(0.f, 2.2f, 0.f)); + scene->AttachShape(mesh); + scene->AttachAutoreleaseObject(mesh); + + SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + green->SetInputValue("albedo", 2.f * float4(0.1f, 0.2f, 0.1f, 1.f)); + + SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); + spec->SetInputValue("roughness", float4(0.02f, 0.02f, 0.02f, 1.f)); + + MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + mix->SetInputValue("base_material", green); + mix->SetInputValue("top_material", spec); + mix->SetInputValue("ior", float4(3.33f, 3.33f, 3.33f, 3.33f)); + + mesh->SetMaterial(mix); + + Mesh* floor = CreateQuad( + { + RadeonRays::float3(-8, 0, -8), + RadeonRays::float3(8, 0, -8), + RadeonRays::float3(8, 0, 8), + RadeonRays::float3(-8, 0, 8), + } + , false); + scene->AttachShape(floor); + scene->AttachAutoreleaseObject(floor); + + floor->SetMaterial(green); + + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); + scene->AttachAutoreleaseObject(ibl_texture); + + ImageBasedLight* ibl = new ImageBasedLight(); + ibl->SetTexture(ibl_texture); + ibl->SetMultiplier(1.f); + scene->AttachLight(ibl); + scene->AttachAutoreleaseObject(ibl); + + scene->AttachAutoreleaseObject(green); + scene->AttachAutoreleaseObject(spec); + scene->AttachAutoreleaseObject(mix); + } return scene; diff --git a/App/main.cpp b/App/main.cpp index bae42ffc..34d9b758 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -346,7 +346,7 @@ void InitData() { std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoTest()); - g_scene.reset(scene_io->LoadScene("sphere+plane+area", basepath)); + g_scene.reset(scene_io->LoadScene("sphere+plane+ibl", basepath)); } g_camera.reset(new Baikal::PerspectiveCamera( From d7be88c247b2f4f4c8f17445bc621b155b05dd36 Mon Sep 17 00:00:00 2001 From: yozhijk Date: Thu, 19 Jan 2017 10:06:11 +0100 Subject: [PATCH 08/24] Improve dirty flags tracking and add dof controls --- App/Scene/camera.cpp | 8 +++ App/Scene/camera.h | 6 ++ App/Scene/light.h | 6 ++ App/Scene/scene_tracker.cpp | 101 ++++++++++++++++++++++---- App/Scene/shape.cpp | 14 ++++ App/main.cpp | 140 ++++++++++++++++++++++++++++++++++-- 6 files changed, 257 insertions(+), 18 deletions(-) diff --git a/App/Scene/camera.cpp b/App/Scene/camera.cpp index 38855700..ffd407a9 100644 --- a/App/Scene/camera.cpp +++ b/App/Scene/camera.cpp @@ -50,6 +50,7 @@ namespace Baikal void PerspectiveCamera::Rotate(float angle) { Rotate(float3(0.f, 1.f, 0.f), angle); + SetDirty(true); } void PerspectiveCamera::Rotate(float3 v, float angle) @@ -76,30 +77,35 @@ namespace Baikal m_up = normalize(float3(cam_matrix.m00, cam_matrix.m01, cam_matrix.m02)); m_right = normalize(float3(cam_matrix.m10, cam_matrix.m11, cam_matrix.m12)); m_forward = normalize(float3(cam_matrix.m20, cam_matrix.m21, cam_matrix.m22)); + SetDirty(true); } // Tilt camera void PerspectiveCamera::Tilt(float angle) { Rotate(m_right, angle); + SetDirty(true); } // Move along camera Z direction void PerspectiveCamera::MoveForward(float distance) { m_p += distance * m_forward; + SetDirty(true); } // Move along camera X direction void PerspectiveCamera::MoveRight(float distance) { m_p += distance * m_right; + SetDirty(true); } // Move along camera Y direction void PerspectiveCamera::MoveUp(float distance) { m_p += distance * m_up; + SetDirty(true); } RadeonRays::float3 PerspectiveCamera::GetForwardVector() const @@ -155,6 +161,7 @@ namespace Baikal dir = rotate_vector(dir, rotation_quaternion(float3(0.f, 1.f, 0.f), angle)); m_p = c - dir; + SetDirty(true); } void PerspectiveCamera::ArcballRotateVertically(float3 c, float angle) @@ -184,5 +191,6 @@ namespace Baikal dir = rotate_vector(dir, rotation_quaternion(m_right, angle)); m_p = c - dir; + SetDirty(true); } } diff --git a/App/Scene/camera.h b/App/Scene/camera.h index 1c5aacec..3ca13292 100644 --- a/App/Scene/camera.h +++ b/App/Scene/camera.h @@ -129,16 +129,19 @@ namespace Baikal inline void PerspectiveCamera::SetFocusDistance(float distance) { m_focus_distance = distance; + SetDirty(true); } inline void PerspectiveCamera::SetFocalLength(float length) { m_focal_length = length; + SetDirty(true); } inline void PerspectiveCamera::SetAperture(float aperture) { m_aperture = aperture; + SetDirty(true); } inline float PerspectiveCamera::GetFocusDistance() const @@ -164,10 +167,13 @@ namespace Baikal inline void PerspectiveCamera::SetSensorSize(RadeonRays::float2 const& size) { m_dim = size; + SetDirty(true); } + inline void PerspectiveCamera::SetDepthRange(RadeonRays::float2 const& range) { m_zcap = range; + SetDirty(true); } inline RadeonRays::float2 PerspectiveCamera::GetDepthRange() const diff --git a/App/Scene/light.h b/App/Scene/light.h index e2890733..9cac293d 100644 --- a/App/Scene/light.h +++ b/App/Scene/light.h @@ -195,6 +195,7 @@ namespace Baikal inline void Light::SetPosition(RadeonRays::float3 const& p) { m_p = p; + SetDirty(true); } inline RadeonRays::float3 Light::GetDirection() const @@ -205,6 +206,7 @@ namespace Baikal inline void Light::SetDirection(RadeonRays::float3 const& d) { m_d = normalize(d); + SetDirty(true); } inline Iterator* Light::CreateTextureIterator() const @@ -220,11 +222,13 @@ namespace Baikal inline void Light::SetEmittedRadiance(RadeonRays::float3 const& e) { m_e = e; + SetDirty(true); } inline void SpotLight::SetConeShape(RadeonRays::float2 angles) { m_angles = angles; + SetDirty(true); } inline RadeonRays::float2 SpotLight::GetConeShape() const @@ -235,6 +239,7 @@ namespace Baikal inline void ImageBasedLight::SetTexture(Texture const* texture) { m_texture = texture; + SetDirty(true); } inline Texture const* ImageBasedLight::GetTexture() const @@ -266,6 +271,7 @@ namespace Baikal inline void ImageBasedLight::SetMultiplier(float m) { m_multiplier = m; + SetDirty(true); } inline Iterator* ImageBasedLight::CreateTextureIterator() const diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index f465b54d..b49b729e 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -179,8 +179,10 @@ namespace Baikal // Set scene as current m_current_scene = &scene; + // Drop all dirty flags for the scene scene.ClearDirtyFlags(); + // Drop dirty flags for materials mat_collector.Finalize([](void const* item) { auto material = reinterpret_cast(item); @@ -195,27 +197,103 @@ namespace Baikal // Exctract cached scene entry auto& out = iter->second; auto dirty = scene.GetDirtyFlags(); + + // Check if we have valid camera + auto camera = scene.GetCamera(); + + if (!camera) + { + throw std::runtime_error("No camera in the scene"); + } + + // Check if camera parameters have been changed + auto camera_changed = camera->IsDirty(); // Update camera if needed - if (dirty & Scene1::kCamera) + if (dirty & Scene1::kCamera || camera_changed) { UpdateCamera(scene, mat_collector, tex_collector, out); + + // Drop camera dirty flag + camera->SetDirty(false); } - // Update lights if needed - if (dirty & Scene1::kLights) { - UpdateLights(scene, mat_collector, tex_collector, out); + // Check if we have lights in the scene + std::unique_ptr light_iter(scene.CreateLightIterator()); + + if (!light_iter->IsValid()) + { + throw std::runtime_error("No lights in the scene"); + } + + + // Check if light parameters have been changed + bool lights_changed = false; + + for (;light_iter->IsValid(); light_iter->Next()) + { + auto light = light_iter->ItemAs(); + + if (light->IsDirty()) + { + lights_changed = true; + break; + } + } + + + // Update lights if needed + if (dirty & Scene1::kLights || lights_changed) + { + UpdateLights(scene, mat_collector, tex_collector, out); + + // Drop dirty flags for lights + for (light_iter->Reset(); light_iter->IsValid(); light_iter->Next()) + { + auto light = light_iter->ItemAs(); + + light->SetDirty(false); + } + } } - // Update shapes if needed - if (dirty & Scene1::kShapes) - { - UpdateShapes(scene, mat_collector, tex_collector, out); - } - else if (dirty & Scene1::kShapeTransforms) { - // TODO: this is not yet supported in the renderer + // Check if we have shapes in the scene + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + + if (!shape_iter->IsValid()) + { + throw std::runtime_error("No shapes in the scene"); + } + + // Check if shape parameters have been changed + bool shapes_changed = false; + + for (;shape_iter->IsValid(); shape_iter->Next()) + { + auto shape = shape_iter->ItemAs(); + + if (shape->IsDirty()) + { + shapes_changed = true; + break; + } + } + + // Update shapes if needed + if (dirty & Scene1::kShapes || shapes_changed) + { + UpdateShapes(scene, mat_collector, tex_collector, out); + + // Drop shape dirty flags + for (;shape_iter->IsValid(); shape_iter->Next()) + { + auto shape = shape_iter->ItemAs(); + + shape->SetDirty(false); + } + } } // If materials need an update, do it. @@ -293,7 +371,6 @@ namespace Baikal // Unmap camera buffer m_context.UnmapBuffer(0, out.camera, data); - } void SceneTracker::UpdateShapes(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const diff --git a/App/Scene/shape.cpp b/App/Scene/shape.cpp index eebf692c..01ca0e8c 100644 --- a/App/Scene/shape.cpp +++ b/App/Scene/shape.cpp @@ -22,6 +22,8 @@ namespace Baikal std::copy(indices, indices + num_indices, m_indices.get()); m_num_indices = num_indices; + + SetDirty(true); } std::size_t Mesh::GetNumIndices() const @@ -45,6 +47,8 @@ namespace Baikal std::copy(vertices, vertices + num_vertices, m_vertices.get()); m_num_vertices = num_vertices; + + SetDirty(true); } void Mesh::SetVertices(float const* vertices, std::size_t num_vertices) @@ -64,6 +68,8 @@ namespace Baikal } m_num_vertices = num_vertices; + + SetDirty(true); } std::size_t Mesh::GetNumVertices() const @@ -87,6 +93,8 @@ namespace Baikal std::copy(normals, normals + num_normals, m_normals.get()); m_num_normals = num_normals; + + SetDirty(true); } void Mesh::SetNormals(float const* normals, std::size_t num_normals) @@ -106,6 +114,8 @@ namespace Baikal } m_num_normals = num_normals; + + SetDirty(true); } std::size_t Mesh::GetNumNormals() const @@ -129,6 +139,8 @@ namespace Baikal std::copy(uvs, uvs + num_uvs, m_uvs.get()); m_num_uvs = num_uvs; + + SetDirty(true); } void Mesh::SetUVs(float const* uvs, std::size_t num_uvs) @@ -146,6 +158,8 @@ namespace Baikal } m_num_uvs = num_uvs; + + SetDirty(true); } std::size_t Mesh::GetNumUVs() const diff --git a/App/main.cpp b/App/main.cpp index 34d9b758..e1a4b031 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -112,7 +112,7 @@ float3 g_camera_up = float3(0.f, 1.f, 0.f); float2 g_camera_sensor_size = float2(0.036f, 0.024f); // default full frame sensor 36x24 mm float2 g_camera_zcap = float2(0.0f, 100000.f); float g_camera_focal_length = 0.035f; // 35mm lens -float g_camera_focus_distance = 0.f; +float g_camera_focus_distance = 1.f; float g_camera_aperture = 0.f; bool g_recording_enabled = false; @@ -450,9 +450,6 @@ void OnKey(int key, int x, int y) case GLUT_KEY_END: g_is_end_pressed = true; break; - case GLUT_KEY_F1: - g_mouse_delta = float2(0, 0); - break; case GLUT_KEY_F3: g_benchmark = true; break; @@ -521,6 +518,138 @@ void OnKeyUp(int key, int x, int y) } } +void OnLetterKey(unsigned char key, int x, int y) +{ + switch(key) + { + case 'w': + { + float focal_length = g_camera->GetFocalLength(); + focal_length += 0.001f; + g_camera->SetFocalLength(focal_length); + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + + break; + } + + case 's': + { + float focal_length = g_camera->GetFocalLength(); + + if (focal_length > 0.f) + { + focal_length -= 0.001f; + g_camera->SetFocalLength(focal_length); + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + } + + break; + } + + case 'q': + { + + float aperture = g_camera->GetAperture(); + + if (aperture == 0.f) + { + g_camera->SetAperture(0.025f); + } + else + { + g_camera->SetAperture(0.0f); + } + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + + break; + } + + case 'd': + { + float aperture = g_camera->GetAperture(); + + if (aperture > 0.f) + { + aperture -= 0.001f; + g_camera->SetAperture(aperture); + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + } + + break; + } + + case 'a': + { + float aperture = g_camera->GetAperture(); + + if (aperture < 0.2f) + { + aperture += 0.001f; + g_camera->SetAperture(aperture); + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + } + + break; + } + + case 'z': + { + float focus_dist = g_camera->GetFocusDistance(); + + if (focus_dist > 0.f) + { + focus_dist -= 0.1f; + g_camera->SetFocusDistance(focus_dist); + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + } + + break; + } + + case 'x': + { + float focus_dist = g_camera->GetFocusDistance(); + + focus_dist += 0.1f; + g_camera->SetFocusDistance(focus_dist); + + for (int i = 0; i < g_cfgs.size(); ++i) + { + g_cfgs[i].renderer->Clear(float3(0, 0, 0), *g_outputs[i].output); + } + + break; + } + + default: + break; + } +} + void Update() { static auto prevtime = std::chrono::high_resolution_clock::now(); @@ -591,8 +720,6 @@ void Update() if (update) { - g_scene->SetDirtyFlag(Baikal::Scene1::kCamera); - if (g_num_samples > -1) { g_samplecount = 0; @@ -882,6 +1009,7 @@ int main(int argc, char * argv[]) glutSpecialFunc(OnKey); glutSpecialUpFunc(OnKeyUp); + glutKeyboardFunc(OnLetterKey); glutMouseFunc(OnMouseButton); glutMotionFunc(OnMouseMove); glutIdleFunc(Update); From 2dd028490733720a4815d7d7a5f89ff1b1b53981 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Thu, 19 Jan 2017 10:44:10 +0100 Subject: [PATCH 09/24] Fix windows issues --- App/Scene/Collector/collector.h | 1 + App/Scene/Loaders/image_io.cpp | 11 +++++++---- App/Scene/Loaders/scene_test_io.cpp | 13 ++++++++----- App/Scene/scene_tracker.cpp | 6 +++--- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/App/Scene/Collector/collector.h b/App/Scene/Collector/collector.h index 8b9336f2..377d0f33 100644 --- a/App/Scene/Collector/collector.h +++ b/App/Scene/Collector/collector.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace Baikal diff --git a/App/Scene/Loaders/image_io.cpp b/App/Scene/Loaders/image_io.cpp index f5a0f20c..c671042c 100644 --- a/App/Scene/Loaders/image_io.cpp +++ b/App/Scene/Loaders/image_io.cpp @@ -77,12 +77,15 @@ namespace Baikal // Close handle input->close(); } - + + // Return new texture + auto tex = new Texture(texturedata, RadeonRays::int2(spec.width, spec.height), fmt); + // Cleanup delete input; - - // Return new texture - return new Texture(texturedata, RadeonRays::int2(spec.width, spec.height), fmt); + + // + return tex; } ImageIo* ImageIo::CreateImageIo() diff --git a/App/Scene/Loaders/scene_test_io.cpp b/App/Scene/Loaders/scene_test_io.cpp index 2f7d754c..5f93a5b9 100644 --- a/App/Scene/Loaders/scene_test_io.cpp +++ b/App/Scene/Loaders/scene_test_io.cpp @@ -8,6 +8,9 @@ #include +#define _USE_MATH_DEFINES +#include + namespace Baikal { // Create fake test IO @@ -39,18 +42,18 @@ namespace Baikal auto t = 0U; for(auto j = 1U; j < lat - 1; j++) - for(auto i = 0; i < lon; i++) + for(auto i = 0U; i < lon; i++) { - float theta = float(j) / (lat - 1) * M_PI; - float phi = float(i) / (lon - 1 ) * M_PI * 2; + float theta = float(j) / (lat - 1) * (float)M_PI; + float phi = float(i) / (lon - 1 ) * (float)M_PI * 2; vertices[t].x = r * sinf(theta) * cosf(phi) + c.x; vertices[t].y = r * cosf(theta) + c.y; vertices[t].z = r * -sinf(theta) * sinf(phi) + c.z; normals[t].x = sinf(theta) * cosf(phi); normals[t].y = cosf(theta); normals[t].z = -sinf(theta) * sinf(phi); - uvs[t].x = phi / (2 * M_PI); - uvs[t].y = theta / (M_PI); + uvs[t].x = phi / (2 * (float)M_PI); + uvs[t].y = theta / ((float)M_PI); ++t; } diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index b49b729e..339bba7d 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -154,7 +154,7 @@ namespace Baikal { textures.emplace(tex_iter->ItemAs()); } - + // Return resulting set return textures; }); @@ -247,7 +247,7 @@ namespace Baikal if (dirty & Scene1::kLights || lights_changed) { UpdateLights(scene, mat_collector, tex_collector, out); - + // Drop dirty flags for lights for (light_iter->Reset(); light_iter->IsValid(); light_iter->Next()) { @@ -680,7 +680,7 @@ namespace Baikal } // Unmap material buffer - m_context.UnmapBuffer(0, out.textures, textures); + m_context.UnmapBuffer(0, out.texturedata, data); } From cd41446a3b5604c341cdf3e26d81966a0c0fc996 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Thu, 19 Jan 2017 11:43:47 +0100 Subject: [PATCH 10/24] Extend material interface --- App/Scene/Loaders/scene_io.cpp | 115 ++++++++++++++++++++------------- App/Scene/material.cpp | 20 ++++++ App/Scene/material.h | 52 +++++++++------ App/Scene/scene_tracker.cpp | 1 - 4 files changed, 122 insertions(+), 66 deletions(-) diff --git a/App/Scene/Loaders/scene_io.cpp b/App/Scene/Loaders/scene_io.cpp index e58eeff9..5c9791b2 100644 --- a/App/Scene/Loaders/scene_io.cpp +++ b/App/Scene/Loaders/scene_io.cpp @@ -18,13 +18,74 @@ namespace Baikal public: // Load scene from file Scene1* LoadScene(std::string const& filename, std::string const& basepath) const override; + private: + Material const* TranslateMaterial(ImageIo const& image_io, tinyobj::material_t const& mat, std::string const& basepath, Scene1& scene) const; + }; SceneIo* SceneIo::CreateSceneIoObj() { return new SceneIoObj(); } - + + + Material const* SceneIoObj::TranslateMaterial(ImageIo const& image_io, tinyobj::material_t const& mat, std::string const& basepath, Scene1& scene) const + { + RadeonRays::float3 emission(mat.emission[0], mat.emission[1], mat.emission[2]); + + Material* material = nullptr; + + // Check if this is emissive + if (emission.sqnorm() > 0) + { + // If yes create emissive brdf + material = new SingleBxdf(SingleBxdf::BxdfType::kEmissive); + + // Set albedo + if (!mat.diffuse_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.diffuse_texname); + material->SetInputValue("albedo", texture); + scene.AttachAutoreleaseObject(texture); + } + else + { + material->SetInputValue("albedo", emission); + } + } + else + { + // Otherwise create lambert + material = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + + // Set albedo + if (!mat.diffuse_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.diffuse_texname); + material->SetInputValue("albedo", texture); + scene.AttachAutoreleaseObject(texture); + } + else + { + material->SetInputValue("albedo", RadeonRays::float3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2])); + } + + // Set normal + if (!mat.normal_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.normal_texname); + material->SetInputValue("normal", texture); + scene.AttachAutoreleaseObject(texture); + } + } + + // Disable normal flip + material->SetTwoSided(false); + scene.AttachAutoreleaseObject(material); + + return material; + } + Scene1* SceneIoObj::LoadScene(std::string const& filename, std::string const& basepath) const { using namespace tinyobj; @@ -47,56 +108,18 @@ namespace Baikal // Enumerate and translate materials // Keep track of emissive subset - std::set emissives; - std::vector materials(objmaterials.size()); + std::set emissives; + std::vector materials(objmaterials.size()); for (int i = 0; i < (int)objmaterials.size(); ++i) { - RadeonRays::float3 emission(objmaterials[i].emission[0], objmaterials[i].emission[1], objmaterials[i].emission[2]); - - // Check if this is emissive - if (emission.sqnorm() > 0) + // Translate material + materials[i] = TranslateMaterial(*image_io, objmaterials[i], basepath, *scene); + + // Add to emissive subset if needed + if (materials[i]->HasEmission()) { - // If yes create emissive brdf - materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kEmissive); - - // Set albedo - if (!objmaterials[i].diffuse_texname.empty()) - { - auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); - materials[i]->SetInputValue("albedo", texture); - scene->AttachAutoreleaseObject(texture); - } - else - { - materials[i]->SetInputValue("albedo", emission); - } - - // Insert into emissive set emissives.insert(materials[i]); } - else - { - // Otherwise create lambert - materials[i] = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - - // Set albedo - if (!objmaterials[i].diffuse_texname.empty()) - { - auto texture = image_io->LoadImage(basepath + "/" + objmaterials[i].diffuse_texname); - materials[i]->SetInputValue("albedo", texture); - scene->AttachAutoreleaseObject(texture); - } - else - { - materials[i]->SetInputValue("albedo", RadeonRays::float3(objmaterials[i].diffuse[0], objmaterials[i].diffuse[1], objmaterials[i].diffuse[2])); - } - } - - // Disable normal flip - materials[i]->SetTwoSided(false); - - // Put into the pool of autoreleased objects - scene->AttachAutoreleaseObject(materials[i]); } // Enumerate all shapes in the scene diff --git a/App/Scene/material.cpp b/App/Scene/material.cpp index 5d9b16c4..9dba21ad 100644 --- a/App/Scene/material.cpp +++ b/App/Scene/material.cpp @@ -256,6 +256,11 @@ namespace Baikal m_type = type; SetDirty(true); } + + bool SingleBxdf::HasEmission() const + { + return m_type == BxdfType::kEmissive; + } MultiBxdf::MultiBxdf(Type type) : m_type(type) @@ -275,4 +280,19 @@ namespace Baikal m_type = type; SetDirty(true); } + + bool MultiBxdf::HasEmission() const + { + InputValue base = GetInputValue("base_material"); + InputValue top = GetInputValue("base_material"); + + if (base.mat_value && base.mat_value->HasEmission()) + return true; + if (top.mat_value && top.mat_value->HasEmission()) + return true; + + return false; + } + + } diff --git a/App/Scene/material.h b/App/Scene/material.h index 07510822..f6c6c70f 100644 --- a/App/Scene/material.h +++ b/App/Scene/material.h @@ -41,7 +41,7 @@ namespace Baikal { class Iterator; class Texture; - + /** \brief High level material interface @@ -57,7 +57,7 @@ namespace Baikal kTexture, kMaterial }; - + // Input description struct InputInfo { @@ -68,7 +68,7 @@ namespace Baikal // Set of supported types std::set supported_types; }; - + // Input value description struct InputValue { @@ -83,60 +83,68 @@ namespace Baikal Material const* mat_value; }; }; - + // Full input state struct Input { InputInfo info; InputValue value; }; - + // Constructor Material(); // Destructor virtual ~Material() = 0; - + // Iterator of dependent materials (plugged as inputs) virtual Iterator* CreateMaterialIterator() const; // Iterator of textures (plugged as inputs) virtual Iterator* CreateTextureIterator() const; // Iterator of inputs virtual Iterator* CreateInputIterator() const; - + // Set input value // If specific data type is not supported throws std::runtime_error virtual void SetInputValue(std::string const& name, RadeonRays::float4 const& value); virtual void SetInputValue(std::string const& name, Texture const* texture); virtual void SetInputValue(std::string const& name, Material const* material); - + virtual InputValue GetInputValue(std::string const& name) const; - + // Check if material is two-sided (normal direction does not matter and can be reversed) virtual bool IsTwoSided() const; // Set two-sidedness virtual void SetTwoSided(bool twosided); - + + // Check if material has emissive components + virtual bool HasEmission() const; + protected: // Register specific input void RegisterInput(std::string const& name, std::string const& desc, std::set&& supported_types); // Wipe out all the inputs void ClearInputs(); - + private: class InputIterator; - + using InputMap = std::map; // Input map InputMap m_inputs; // Sidedness flag bool m_twosided; }; - + inline Material::~Material() { } - + + inline bool Material::HasEmission() const + { + return false; + } + class SingleBxdf : public Material { public: @@ -155,12 +163,15 @@ namespace Baikal kMicrofacetRefractionGGX, kMicrofacetRefractionBeckmann }; - + SingleBxdf(BxdfType type); - + virtual BxdfType GetBxdfType() const; virtual void SetBxdfType(BxdfType type); - + + // Check if material has emissive components + bool HasEmission() const override; + private: BxdfType m_type; }; @@ -174,11 +185,14 @@ namespace Baikal kFresnelBlend, kMix }; - + MultiBxdf(Type type); - + virtual Type GetType() const; virtual void SetType(Type type); + + // Check if material has emissive components + bool HasEmission() const override; private: Type m_type; diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index 339bba7d..ed669d23 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -681,7 +681,6 @@ namespace Baikal // Unmap material buffer m_context.UnmapBuffer(0, out.texturedata, data); - } // Convert Material:: types to ClwScene:: types From 8c8cab245708ba5b55bb3d9f660c17aecb5a801e Mon Sep 17 00:00:00 2001 From: yozhijk Date: Thu, 19 Jan 2017 17:34:51 +0100 Subject: [PATCH 11/24] Implement XML material export --- App/Scene/{Loaders => IO}/image_io.cpp | 39 +- App/Scene/{Loaders => IO}/image_io.h | 1 + App/Scene/IO/material_io.cpp | 248 ++ App/Scene/IO/material_io.h | 64 + App/Scene/{Loaders => IO}/scene_io.cpp | 99 +- App/Scene/{Loaders => IO}/scene_io.h | 0 App/Scene/{Loaders => IO}/scene_test_io.cpp | 0 App/Scene/material.cpp | 1 + App/Scene/scene_object.h | 17 + App/XML/tinyxml2.cpp | 2682 +++++++++++++++++++ App/XML/tinyxml2.h | 2197 +++++++++++++++ App/main.cpp | 10 +- 12 files changed, 5336 insertions(+), 22 deletions(-) rename App/Scene/{Loaders => IO}/image_io.cpp (69%) rename App/Scene/{Loaders => IO}/image_io.h (95%) create mode 100644 App/Scene/IO/material_io.cpp create mode 100644 App/Scene/IO/material_io.h rename App/Scene/{Loaders => IO}/scene_io.cpp (62%) rename App/Scene/{Loaders => IO}/scene_io.h (100%) rename App/Scene/{Loaders => IO}/scene_test_io.cpp (100%) create mode 100755 App/XML/tinyxml2.cpp create mode 100755 App/XML/tinyxml2.h diff --git a/App/Scene/Loaders/image_io.cpp b/App/Scene/IO/image_io.cpp similarity index 69% rename from App/Scene/Loaders/image_io.cpp rename to App/Scene/IO/image_io.cpp index c671042c..2d8348ad 100644 --- a/App/Scene/Loaders/image_io.cpp +++ b/App/Scene/IO/image_io.cpp @@ -9,6 +9,7 @@ namespace Baikal { public: Texture* LoadImage(std::string const& filename) const override; + void SaveImage(std::string const& filename, Texture const* texture) const override; }; static Texture::Format GetTextureForemat(OIIO_NAMESPACE::ImageSpec const& spec) @@ -17,12 +18,24 @@ namespace Baikal if (spec.format.basetype == TypeDesc::UINT8) return Texture::Format::kRgba8; - if (spec.format.basetype == TypeDesc::HALF) + else if (spec.format.basetype == TypeDesc::HALF) return Texture::Format::kRgba16; else return Texture::Format::kRgba32; } + static OIIO_NAMESPACE::TypeDesc GetTextureForemat(Texture::Format fmt) + { + OIIO_NAMESPACE_USING + + if (fmt == Texture::Format::kRgba8) + return TypeDesc::UINT8; + else if (fmt == Texture::Format::kRgba16) + return TypeDesc::HALF; + else + return TypeDesc::FLOAT; + } + Texture* Oiio::LoadImage(const std::string &filename) const { OIIO_NAMESPACE_USING @@ -88,6 +101,30 @@ namespace Baikal return tex; } + void Oiio::SaveImage(std::string const& filename, Texture const* texture) const + { + OIIO_NAMESPACE_USING; + + ImageOutput* out = ImageOutput::create(filename); + + if (!out) + { + throw std::runtime_error("Can't create image file on disk"); + } + + auto dim = texture->GetSize(); + auto fmt = GetTextureForemat(texture->GetFormat()); + + ImageSpec spec(dim.x, dim.y, 4, fmt); + + out->open(filename, spec); + + out->write_image(fmt, texture->GetData()); + + out->close(); + + } + ImageIo* ImageIo::CreateImageIo() { return new Oiio(); diff --git a/App/Scene/Loaders/image_io.h b/App/Scene/IO/image_io.h similarity index 95% rename from App/Scene/Loaders/image_io.h rename to App/Scene/IO/image_io.h index 04dda430..4f6c5e52 100644 --- a/App/Scene/Loaders/image_io.h +++ b/App/Scene/IO/image_io.h @@ -52,6 +52,7 @@ namespace Baikal // Load texture from file virtual Texture* LoadImage(std::string const& filename) const = 0; + virtual void SaveImage(std::string const& filename, Texture const* texture) const = 0; // Disallow copying ImageIo(ImageIo const&) = delete; diff --git a/App/Scene/IO/material_io.cpp b/App/Scene/IO/material_io.cpp new file mode 100644 index 00000000..b598e466 --- /dev/null +++ b/App/Scene/IO/material_io.cpp @@ -0,0 +1,248 @@ +#include "material_io.h" + +#include "Scene/scene1.h" +#include "Scene/iterator.h" +#include "Scene/shape.h" +#include "Scene/material.h" + +#include "Scene/IO/image_io.h" +#include "Scene/Collector/Collector.h" + + +#include "XML/tinyxml2.h" + +#include +#include +#include + +namespace Baikal +{ + using namespace tinyxml2; + + // XML based material IO implememtation + class MaterialIoXML : public MaterialIo + { + public: + // Save materials to disk + void SaveMaterials(std::string const& filename, Scene1 const& scene) override; + + private: + // Write single material + void WriteMaterial(ImageIo& io, XMLPrinter& printer, Material const* material); + // Write single material input + void WriteInput(ImageIo& io, XMLPrinter& printer, std::string const& name, Material::InputValue value); + + // Texture to name map + std::map m_textures; + }; + + MaterialIo* MaterialIo::CreateMaterialIoXML() + { + return new MaterialIoXML(); + } + + static std::string Float4ToString(RadeonRays::float3 const& v) + { + std::ostringstream oss; + oss << v.x << " " << v.y << " " << v.z << " " << v.w; + return oss.str(); + } + + void MaterialIoXML::WriteInput(ImageIo& io, XMLPrinter& printer, std::string const& name, Material::InputValue value) + { + printer.OpenElement("Input"); + + printer.PushAttribute("name", name.c_str()); + + if (value.type == Material::InputType::kFloat4) + { + printer.PushAttribute("type", "float4"); + printer.PushAttribute("value", Float4ToString(value.float_value).c_str()); + } + else if (value.type == Material::InputType::kTexture) + { + printer.PushAttribute("type", "texture"); + + auto iter = m_textures.find(value.tex_value); + + if (iter != m_textures.cend()) + { + printer.PushAttribute("value", iter->second.c_str()); + } + else + { + std::ostringstream oss; + oss << (std::uint64_t)value.tex_value << ".jpg"; + + io.SaveImage(oss.str(), value.tex_value); + + m_textures[value.tex_value] = oss.str(); + + printer.PushAttribute("value", oss.str().c_str()); + } + } + else + { + printer.PushAttribute("type", "material"); + + printer.PushAttribute("value", (int)(reinterpret_cast(value.mat_value))); + } + + printer.CloseElement(); + } + + void MaterialIoXML::WriteMaterial(ImageIo& io, XMLPrinter& printer, Material const* material) + { + printer.OpenElement("Material"); + + printer.PushAttribute("name", material->GetName().c_str()); + + printer.PushAttribute("id", (int)(reinterpret_cast(material))); + + printer.PushAttribute("twosided", material->IsTwoSided()); + + SingleBxdf const* bxdf = dynamic_cast(material); + + if (bxdf) + { + printer.PushAttribute("type", "simple"); + + SingleBxdf::BxdfType type = bxdf->GetBxdfType(); + + printer.PushAttribute("bxdf", (int)type); + + SingleBxdf::InputValue albedo = bxdf->GetInputValue("albedo"); + + WriteInput(io, printer,"albedo", albedo); + + SingleBxdf::InputValue normal = bxdf->GetInputValue("normal"); + + if (normal.tex_value) + { + WriteInput(io, printer, "normal", normal); + } + + SingleBxdf::InputValue ior = bxdf->GetInputValue("ior"); + + WriteInput(io, printer,"ior", ior); + + SingleBxdf::InputValue fresnel = bxdf->GetInputValue("fresnel"); + + WriteInput(io, printer,"fresnel", fresnel); + + if (type == SingleBxdf::BxdfType::kMicrofacetGGX || + type == SingleBxdf::BxdfType::kMicrofacetBeckmann || + type == SingleBxdf::BxdfType::kMicrofacetBlinn || + type == SingleBxdf::BxdfType::kMicrofacetRefractionGGX || + type == SingleBxdf::BxdfType::kMicrofacetRefractionBeckmann) + { + SingleBxdf::InputValue roughness = bxdf->GetInputValue("roughness"); + WriteInput(io, printer,"roughness", roughness); + } + } + else + { + MultiBxdf const* blend = dynamic_cast(material); + + printer.PushAttribute("type", "blend"); + + MultiBxdf::Type type = blend->GetType(); + + printer.PushAttribute("blend_type", (int)type); + + Material::InputValue base = material->GetInputValue("base_material"); + + WriteInput(io, printer, "base_material", base); + + Material::InputValue top = material->GetInputValue("top_material"); + + WriteInput(io, printer, "top_material", top); + + if (type == MultiBxdf::Type::kFresnelBlend) + { + Material::InputValue ior = material->GetInputValue("ior"); + + WriteInput(io, printer, "ior", ior); + } + else + { + Material::InputValue weight = material->GetInputValue("weight"); + + WriteInput(io, printer, "weight", weight); + } + } + + printer.CloseElement(); + } + + void MaterialIoXML::SaveMaterials(std::string const& filename, Scene1 const& scene) + { + XMLDocument doc; + XMLPrinter printer; + + m_textures.clear(); + + std::unique_ptr image_io(ImageIo::CreateImageIo()); + + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + + Collector mat_collector; + // Collect materials from shapes first + mat_collector.Collect(shape_iter.get(), + // This function adds all materials to resulting map + // recursively via Material dependency API + [](void const* item) -> std::set + { + // Resulting material set + std::set mats; + // Material stack + std::stack material_stack; + + // Get material from current shape + auto shape = reinterpret_cast(item); + auto material = shape->GetMaterial(); + + // Push to stack as an initializer + material_stack.push(material); + + // Drain the stack + while (!material_stack.empty()) + { + // Get current material + Material const* m = material_stack.top(); + material_stack.pop(); + + // Emplace into the set + mats.emplace(m); + + // Create dependency iterator + std::unique_ptr mat_iter(m->CreateMaterialIterator()); + + // Push all dependencies into the stack + for (;mat_iter->IsValid(); mat_iter->Next()) + { + material_stack.push(mat_iter->ItemAs()); + } + } + + // Return resulting set + return mats; + }); + + std::unique_ptr mat_iter(mat_collector.CreateIterator()); + for (;mat_iter->IsValid(); mat_iter->Next()) + { + auto material = mat_iter->ItemAs(); + + if (material) + { + WriteMaterial(*image_io, printer, material); + } + } + + doc.Parse(printer.CStr()); + + doc.SaveFile(filename.c_str()); + } + +} diff --git a/App/Scene/IO/material_io.h b/App/Scene/IO/material_io.h new file mode 100644 index 00000000..5672bb9b --- /dev/null +++ b/App/Scene/IO/material_io.h @@ -0,0 +1,64 @@ +/********************************************************************** + Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ********************************************************************/ + +/** + \file material_io.h + \author Dmitry Kozlov + \version 1.0 + \brief Contains an interface for material input & output + */ +#pragma once + +#include + +namespace Baikal +{ + class Scene1; + + /** + \brief Interface for material loading and writing + + MaterialIO is responsible for material loading from disk. + */ + class MaterialIo + { + public: + // Create XML based material IO + static MaterialIo* CreateMaterialIoXML(); + + // Constructor + MaterialIo() = default; + // Destructor + virtual ~MaterialIo() = 0; + + // Save materials from scene into a file + virtual void SaveMaterials(std::string const& filename, Scene1 const& scene) = 0; + + // Disallow copying + MaterialIo(MaterialIo const&) = delete; + MaterialIo& operator = (MaterialIo const&) = delete; + }; + + inline MaterialIo::~MaterialIo() + { + } +} diff --git a/App/Scene/Loaders/scene_io.cpp b/App/Scene/IO/scene_io.cpp similarity index 62% rename from App/Scene/Loaders/scene_io.cpp rename to App/Scene/IO/scene_io.cpp index 5c9791b2..8ef46cbd 100644 --- a/App/Scene/Loaders/scene_io.cpp +++ b/App/Scene/IO/scene_io.cpp @@ -55,30 +55,93 @@ namespace Baikal } else { - // Otherwise create lambert - material = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - - // Set albedo - if (!mat.diffuse_texname.empty()) + auto s = RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]); + + if (s.sqnorm() > 0 || !mat.specular_texname.empty()) { - auto texture = image_io.LoadImage(basepath + "/" + mat.diffuse_texname); - material->SetInputValue("albedo", texture); - scene.AttachAutoreleaseObject(texture); + // Otherwise create lambert + material = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + material->SetInputValue("ior", RadeonRays::float4(1.5f, 1.5f, 1.5f, 1.5f)); + + Material* diffuse = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + Material* specular = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); + + specular->SetInputValue("roughness", 0.01f); + + // Set albedo + if (!mat.diffuse_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.diffuse_texname); + diffuse->SetInputValue("albedo", texture); + scene.AttachAutoreleaseObject(texture); + } + else + { + diffuse->SetInputValue("albedo", RadeonRays::float3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2])); + } + + // Set albedo + if (!mat.specular_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.specular_texname); + specular->SetInputValue("albedo", texture); + scene.AttachAutoreleaseObject(texture); + } + else + { + specular->SetInputValue("albedo", s); + } + + // Set normal + if (!mat.normal_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.normal_texname); + diffuse->SetInputValue("normal", texture); + specular->SetInputValue("normal", texture); + scene.AttachAutoreleaseObject(texture); + } + + diffuse->SetName(mat.name + "-diffuse"); + specular->SetName(mat.name + "-specular"); + + material->SetInputValue("base_material", diffuse); + material->SetInputValue("top_material", specular); + + scene.AttachAutoreleaseObject(diffuse); + scene.AttachAutoreleaseObject(specular); } else { - material->SetInputValue("albedo", RadeonRays::float3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2])); - } - - // Set normal - if (!mat.normal_texname.empty()) - { - auto texture = image_io.LoadImage(basepath + "/" + mat.normal_texname); - material->SetInputValue("normal", texture); - scene.AttachAutoreleaseObject(texture); + // Otherwise create lambert + Material* diffuse = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + + // Set albedo + if (!mat.diffuse_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.diffuse_texname); + diffuse->SetInputValue("albedo", texture); + scene.AttachAutoreleaseObject(texture); + } + else + { + diffuse->SetInputValue("albedo", RadeonRays::float3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2])); + } + + // Set normal + if (!mat.normal_texname.empty()) + { + auto texture = image_io.LoadImage(basepath + "/" + mat.normal_texname); + diffuse->SetInputValue("normal", texture); + scene.AttachAutoreleaseObject(texture); + } + + material = diffuse; } } - + + // Set material name + material->SetName(mat.name); + // Disable normal flip material->SetTwoSided(false); scene.AttachAutoreleaseObject(material); diff --git a/App/Scene/Loaders/scene_io.h b/App/Scene/IO/scene_io.h similarity index 100% rename from App/Scene/Loaders/scene_io.h rename to App/Scene/IO/scene_io.h diff --git a/App/Scene/Loaders/scene_test_io.cpp b/App/Scene/IO/scene_test_io.cpp similarity index 100% rename from App/Scene/Loaders/scene_test_io.cpp rename to App/Scene/IO/scene_test_io.cpp diff --git a/App/Scene/material.cpp b/App/Scene/material.cpp index 9dba21ad..39a863e9 100644 --- a/App/Scene/material.cpp +++ b/App/Scene/material.cpp @@ -268,6 +268,7 @@ namespace Baikal RegisterInput("base_material", "Base material", {InputType::kMaterial}); RegisterInput("top_material", "Top material", {InputType::kMaterial}); RegisterInput("ior", "Index of refraction", {InputType::kFloat4}); + RegisterInput("weight", "Blend weight", {InputType::kFloat4}); } MultiBxdf::Type MultiBxdf::GetType() const diff --git a/App/Scene/scene_object.h b/App/Scene/scene_object.h index c2d3e74a..c3b1f604 100644 --- a/App/Scene/scene_object.h +++ b/App/Scene/scene_object.h @@ -28,6 +28,8 @@ */ #pragma once +#include + namespace Baikal { class SceneObject @@ -43,12 +45,17 @@ namespace Baikal // Set dirty flag virtual void SetDirty(bool dirty) const; + // Set & get name + virtual void SetName(std::string const& name); + virtual std::string GetName() const; + SceneObject(SceneObject const&) = delete; SceneObject& operator = (SceneObject const&) = delete; private: mutable bool m_dirty; + std::string m_name; }; inline SceneObject::SceneObject() @@ -69,4 +76,14 @@ namespace Baikal { m_dirty = dirty; } + + inline std::string SceneObject::GetName() const + { + return m_name; + } + + inline void SceneObject::SetName(std::string const& name) + { + m_name = name; + } } diff --git a/App/XML/tinyxml2.cpp b/App/XML/tinyxml2.cpp new file mode 100755 index 00000000..018f9a9e --- /dev/null +++ b/App/XML/tinyxml2.cpp @@ -0,0 +1,2682 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml2.h" + +#include // yes, this one new style header, is in the Android SDK. +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +#else +# include +# include +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + // Microsoft Visual Studio, version 2005 and higher. Not WinCE. + /*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... + );*/ + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) + { + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; + } + + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) + { + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + return result; + } + + #define TIXML_VSCPRINTF _vscprintf + #define TIXML_SSCANF sscanf_s +#elif defined _MSC_VER + // Microsoft Visual Studio 2003 and earlier or WinCE + #define TIXML_SNPRINTF _snprintf + #define TIXML_VSNPRINTF _vsnprintf + #define TIXML_SSCANF sscanf + #if (_MSC_VER < 1400 ) && (!defined WINCE) + // Microsoft Visual Studio 2003 and not WinCE. + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. + #else + // Microsoft Visual Studio 2003 and earlier or WinCE. + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = 512; + for (;;) { + len = len*2; + char* str = new char[len](); + const int required = _vsnprintf(str, len, format, va); + delete[] str; + if ( required != -1 ) { + TIXMLASSERT( required >= 0 ); + len = required; + break; + } + } + TIXMLASSERT( len >= 0 ); + return len; + } + #endif +#else + // GCC version 3 and higher + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_VSNPRINTF vsnprintf + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = vsnprintf( 0, 0, format, va ); + TIXMLASSERT( len >= 0 ); + return len; + } + #define TIXML_SSCANF sscanf +#endif + + +static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +namespace tinyxml2 +{ + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::TransferTo( StrPair* other ) +{ + if ( this == other ) { + return; + } + // This in effect implements the assignment operator by "moving" + // ownership (as in auto_ptr). + + TIXMLASSERT( other != 0 ); + TIXMLASSERT( other->_flags == 0 ); + TIXMLASSERT( other->_start == 0 ); + TIXMLASSERT( other->_end == 0 ); + + other->Reset(); + + other->_flags = _flags; + other->_start = _start; + other->_end = _end; + + _flags = 0; + _start = 0; + _end = 0; +} + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + TIXMLASSERT( str ); + Reset(); + size_t len = strlen( str ); + TIXMLASSERT( _start == 0 ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( endTag && *endTag ); + TIXMLASSERT(curLineNumPtr); + + char* start = p; + char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } else if (*p == '\n') { + ++(*curLineNumPtr); + } + ++p; + TIXMLASSERT( p ); + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + if ( !p || !(*p) ) { + return 0; + } + if ( !XMLUtil::IsNameStartChar( *p ) ) { + return 0; + } + + char* const start = p; + ++p; + while ( *p && XMLUtil::IsNameChar( *p ) ) { + ++p; + } + + Set( start, p, 0 ); + return p; +} + + +void StrPair::CollapseWhitespace() +{ + // Adjusting _start would cause undefined behavior on delete[] + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start, 0 ); + + if ( *_start ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p, 0 ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + TIXMLASSERT( _start ); + TIXMLASSERT( _end ); + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + const int buflen = 10; + char buf[buflen] = { 0 }; + int len = 0; + char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + if ( adjusted == 0 ) { + *q = *p; + ++p; + ++q; + } + else { + TIXMLASSERT( 0 <= len && len <= buflen ); + TIXMLASSERT( q + len <= adjusted ); + p = adjusted; + memcpy( q, buf, len ); + q += len; + } + } + else { + bool entityFound = false; + for( int i = 0; i < NUM_ENTITIES; ++i ) { + const Entity& entity = entities[i]; + if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 + && *( p + entity.length + 1 ) == ';' ) { + // Found an entity - convert. + *q = entity.value; + ++q; + p += entity.length + 2; + entityFound = true; + break; + } + } + if ( !entityFound ) { + // fixme: treat as error? + ++p; + ++q; + } + } + } + else { + *q = *p; + ++p; + ++q; + } + } + *q = 0; + } + // The loop below has plenty going on, and this + // is a less useful mode. Break it out. + if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { + CollapseWhitespace(); + } + _flags = (_flags & NEEDS_DELETE); + } + TIXMLASSERT( _start ); + return _start; +} + + + + +// --------- XMLUtil ----------- // + +const char* XMLUtil::ReadBOM( const char* p, bool* bom ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( bom ); + *bom = false; + const unsigned char* pu = reinterpret_cast(p); + // Check for BOM: + if ( *(pu+0) == TIXML_UTF_LEAD_0 + && *(pu+1) == TIXML_UTF_LEAD_1 + && *(pu+2) == TIXML_UTF_LEAD_2 ) { + *bom = true; + p += 3; + } + TIXMLASSERT( p ); + return p; +} + + +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) { + *length = 1; + } + else if ( input < 0x800 ) { + *length = 2; + } + else if ( input < 0x10000 ) { + *length = 3; + } + else if ( input < 0x200000 ) { + *length = 4; + } + else { + *length = 0; // This code won't convert this correctly anyway. + return; + } + + output += *length; + + // Scary scary fall throughs. + switch (*length) { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + break; + default: + TIXMLASSERT( false ); + } +} + + +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +{ + // Presume an entity, and pull it out. + *length = 0; + + if ( *(p+1) == '#' && *(p+2) ) { + unsigned long ucs = 0; + TIXMLASSERT( sizeof( ucs ) >= 4 ); + ptrdiff_t delta = 0; + unsigned mult = 1; + static const char SEMICOLON = ';'; + + if ( *(p+2) == 'x' ) { + // Hexadecimal. + const char* q = p+3; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != 'x' ) { + unsigned int digit = 0; + + if ( *q >= '0' && *q <= '9' ) { + digit = *q - '0'; + } + else if ( *q >= 'a' && *q <= 'f' ) { + digit = *q - 'a' + 10; + } + else if ( *q >= 'A' && *q <= 'F' ) { + digit = *q - 'A' + 10; + } + else { + return 0; + } + TIXMLASSERT( digit < 16 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + TIXMLASSERT( mult <= UINT_MAX / 16 ); + mult *= 16; + --q; + } + } + else { + // Decimal. + const char* q = p+2; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != '#' ) { + if ( *q >= '0' && *q <= '9' ) { + const unsigned int digit = *q - '0'; + TIXMLASSERT( digit < 10 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + } + else { + return 0; + } + TIXMLASSERT( mult <= UINT_MAX / 10 ); + mult *= 10; + --q; + } + } + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + return p + delta + 1; + } + return p+1; +} + + +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? "true" : "false" ); +} + +/* + ToStr() of a number is a very tricky topic. + https://github.com/leethomason/tinyxml2/issues/106 +*/ +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); +} + + +void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) +{ + // horrible syntax trick to make the compiler happy about %lld + TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); +} + + +bool XMLUtil::ToInt( const char* str, int* value ) +{ + if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) +{ + if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToBool( const char* str, bool* value ) +{ + int ival = 0; + if ( ToInt( str, &ival )) { + *value = (ival==0) ? false : true; + return true; + } + if ( StringEqual( str, "true" ) ) { + *value = true; + return true; + } + else if ( StringEqual( str, "false" ) ) { + *value = false; + return true; + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToInt64(const char* str, int64_t* value) +{ + long long v = 0; // horrible syntax trick to make the compiler happy about %lld + if (TIXML_SSCANF(str, "%lld", &v) == 1) { + *value = (int64_t)v; + return true; + } + return false; +} + + +char* XMLDocument::Identify( char* p, XMLNode** node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( p ); + char* const start = p; + int const startLine = _parseCurLineNum; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + if( !*p ) { + *node = 0; + TIXMLASSERT( p ); + return p; + } + + // These strings define the matching patterns: + static const char* xmlHeader = { "_parseLineNum = _parseCurLineNum; + returnNode->_memPool = &_commentPool; + p += xmlHeaderLen; + } + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); + returnNode = new (_commentPool.Alloc()) XMLComment( this ); + returnNode->_parseLineNum = _parseCurLineNum; + returnNode->_memPool = &_commentPool; + p += commentHeaderLen; + } + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); + XMLText* text = new (_textPool.Alloc()) XMLText( this ); + returnNode = text; + returnNode->_parseLineNum = _parseCurLineNum; + returnNode->_memPool = &_textPool; + p += cdataHeaderLen; + text->SetCData( true ); + } + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); + returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); + returnNode->_parseLineNum = _parseCurLineNum; + returnNode->_memPool = &_commentPool; + p += dtdHeaderLen; + } + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); + returnNode = new (_elementPool.Alloc()) XMLElement( this ); + returnNode->_parseLineNum = _parseCurLineNum; + returnNode->_memPool = &_elementPool; + p += elementHeaderLen; + } + else { + TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); + returnNode = new (_textPool.Alloc()) XMLText( this ); + returnNode->_memPool = &_textPool; + returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character + p = start; // Back it up, all the text counts. + _parseCurLineNum = startLine; + } + + TIXMLASSERT( returnNode ); + TIXMLASSERT( p ); + *node = returnNode; + return p; +} + + +bool XMLDocument::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLNode ----------- // + +XMLNode::XMLNode( XMLDocument* doc ) : + _document( doc ), + _parent( 0 ), + _parseLineNum( 0 ), + _firstChild( 0 ), _lastChild( 0 ), + _prev( 0 ), _next( 0 ), + _userData( 0 ), + _memPool( 0 ) +{ +} + + +XMLNode::~XMLNode() +{ + DeleteChildren(); + if ( _parent ) { + _parent->Unlink( this ); + } +} + +const char* XMLNode::Value() const +{ + // Edge case: XMLDocuments don't have a Value. Return null. + if ( this->ToDocument() ) + return 0; + return _value.GetStr(); +} + +void XMLNode::SetValue( const char* str, bool staticMem ) +{ + if ( staticMem ) { + _value.SetInternedStr( str ); + } + else { + _value.SetStr( str ); + } +} + + +void XMLNode::DeleteChildren() +{ + while( _firstChild ) { + TIXMLASSERT( _lastChild ); + DeleteChild( _firstChild ); + } + _firstChild = _lastChild = 0; +} + + +void XMLNode::Unlink( XMLNode* child ) +{ + TIXMLASSERT( child ); + TIXMLASSERT( child->_document == _document ); + TIXMLASSERT( child->_parent == this ); + if ( child == _firstChild ) { + _firstChild = _firstChild->_next; + } + if ( child == _lastChild ) { + _lastChild = _lastChild->_prev; + } + + if ( child->_prev ) { + child->_prev->_next = child->_next; + } + if ( child->_next ) { + child->_next->_prev = child->_prev; + } + child->_parent = 0; +} + + +void XMLNode::DeleteChild( XMLNode* node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( node->_document == _document ); + TIXMLASSERT( node->_parent == this ); + Unlink( node ); + DeleteNode( node ); +} + + +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _lastChild ) { + TIXMLASSERT( _firstChild ); + TIXMLASSERT( _lastChild->_next == 0 ); + _lastChild->_next = addThis; + addThis->_prev = _lastChild; + _lastChild = addThis; + + addThis->_next = 0; + } + else { + TIXMLASSERT( _firstChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _firstChild ) { + TIXMLASSERT( _lastChild ); + TIXMLASSERT( _firstChild->_prev == 0 ); + + _firstChild->_prev = addThis; + addThis->_next = _firstChild; + _firstChild = addThis; + + addThis->_prev = 0; + } + else { + TIXMLASSERT( _lastChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + + TIXMLASSERT( afterThis ); + + if ( afterThis->_parent != this ) { + TIXMLASSERT( false ); + return 0; + } + + if ( afterThis->_next == 0 ) { + // The last node or the only node. + return InsertEndChild( addThis ); + } + InsertChildPreamble( addThis ); + addThis->_prev = afterThis; + addThis->_next = afterThis->_next; + afterThis->_next->_prev = addThis; + afterThis->_next = addThis; + addThis->_parent = this; + return addThis; +} + + + + +const XMLElement* XMLNode::FirstChildElement( const char* name ) const +{ + for( const XMLNode* node = _firstChild; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::LastChildElement( const char* name ) const +{ + for( const XMLNode* node = _lastChild; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _next; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _prev; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +char* XMLNode::ParseDeep( char* p, StrPair* parentEnd, int* curLineNumPtr ) +{ + // This is a recursive method, but thinking about it "at the current level" + // it is a pretty simple flat list: + // + // + // + // With a special case: + // + // + // + // + // Where the closing element (/foo) *must* be the next thing after the opening + // element, and the names must match. BUT the tricky bit is that the closing + // element will be read by the child. + // + // 'endTag' is the end tag for this node, it is returned by a call to a child. + // 'parentEnd' is the end tag for the parent, which is filled in and returned. + + while( p && *p ) { + XMLNode* node = 0; + + p = _document->Identify( p, &node ); + TIXMLASSERT( p ); + if ( node == 0 ) { + break; + } + + int initialLineNum = node->_parseLineNum; + + StrPair endTag; + p = node->ParseDeep( p, &endTag, curLineNumPtr ); + if ( !p ) { + DeleteNode( node ); + if ( !_document->Error() ) { + _document->SetError( XML_ERROR_PARSING, 0, 0, initialLineNum); + } + break; + } + + XMLDeclaration* decl = node->ToDeclaration(); + if ( decl ) { + // Declarations are only allowed at document level + bool wellLocated = ( ToDocument() != 0 ); + if ( wellLocated ) { + // Multiple declarations are allowed but all declarations + // must occur before anything else + for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) { + if ( !existingNode->ToDeclaration() ) { + wellLocated = false; + break; + } + } + } + if ( !wellLocated ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0, initialLineNum); + DeleteNode( node ); + break; + } + } + + XMLElement* ele = node->ToElement(); + if ( ele ) { + // We read the end tag. Return it to the parent. + if ( ele->ClosingType() == XMLElement::CLOSING ) { + if ( parentEnd ) { + ele->_value.TransferTo( parentEnd ); + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DeleteNode( node ); + return p; + } + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + bool mismatch = false; + if ( endTag.Empty() ) { + if ( ele->ClosingType() == XMLElement::OPEN ) { + mismatch = true; + } + } + else { + if ( ele->ClosingType() != XMLElement::OPEN ) { + mismatch = true; + } + else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { + mismatch = true; + } + } + if ( mismatch ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0, initialLineNum); + DeleteNode( node ); + break; + } + } + InsertEndChild( node ); + } + return 0; +} + +void XMLNode::DeleteNode( XMLNode* node ) +{ + if ( node == 0 ) { + return; + } + MemPool* pool = node->_memPool; + node->~XMLNode(); + pool->Free( node ); +} + +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const +{ + TIXMLASSERT( insertThis ); + TIXMLASSERT( insertThis->_document == _document ); + + if ( insertThis->_parent ) + insertThis->_parent->Unlink( insertThis ); + else + insertThis->_memPool->SetTracked(); +} + +const XMLElement* XMLNode::ToElementWithName( const char* name ) const +{ + const XMLElement* element = this->ToElement(); + if ( element == 0 ) { + return 0; + } + if ( name == 0 ) { + return element; + } + if ( XMLUtil::StringEqual( element->Name(), name ) ) { + return element; + } + return 0; +} + +// --------- XMLText ---------- // +char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + const char* start = p; + if ( this->CData() ) { + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_CDATA, start, 0, _parseLineNum ); + } + return p; + } + else { + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { + flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; + } + + p = _value.ParseText( p, "<", flags, curLineNumPtr ); + if ( p && *p ) { + return p-1; + } + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, start, 0, _parseLineNum ); + } + } + return 0; +} + + +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? + text->SetCData( this->CData() ); + return text; +} + + +bool XMLText::ShallowEqual( const XMLNode* compare ) const +{ + const XMLText* text = compare->ToText(); + return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); +} + + +bool XMLText::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLComment ---------- // + +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLComment::~XMLComment() +{ +} + + +char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Comment parses as text. + const char* start = p; + p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0, _parseLineNum ); + } + return p; +} + + +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? + return comment; +} + + +bool XMLComment::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLComment* comment = compare->ToComment(); + return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); +} + + +bool XMLComment::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLDeclaration ---------- // + +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLDeclaration::~XMLDeclaration() +{ + //printf( "~XMLDeclaration\n" ); +} + + +char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Declaration parses as text. + const char* start = p; + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0, _parseLineNum ); + } + return p; +} + + +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? + return dec; +} + + +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLDeclaration* declaration = compare->ToDeclaration(); + return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); +} + + + +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLUnknown ---------- // + +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLUnknown::~XMLUnknown() +{ +} + + +char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Unknown parses as text. + const char* start = p; + + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0, _parseLineNum ); + } + return p; +} + + +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? + return text; +} + + +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLUnknown* unknown = compare->ToUnknown(); + return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); +} + + +bool XMLUnknown::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLAttribute ---------- // + +const char* XMLAttribute::Name() const +{ + return _name.GetStr(); +} + +const char* XMLAttribute::Value() const +{ + return _value.GetStr(); +} + +char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) +{ + // Parse using the name rules: bug fix, was using ParseText before + p = _name.ParseName( p ); + if ( !p || !*p ) { + return 0; + } + + // Skip white space before = + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '=' ) { + return 0; + } + + ++p; // move up to opening quote + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '\"' && *p != '\'' ) { + return 0; + } + + char endTag[2] = { *p, 0 }; + ++p; // move past opening quote + + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); + return p; +} + + +void XMLAttribute::SetName( const char* n ) +{ + _name.SetStr( n ); +} + + +XMLError XMLAttribute::QueryIntValue( int* value ) const +{ + if ( XMLUtil::ToInt( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const +{ + if ( XMLUtil::ToUnsigned( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const +{ + if (XMLUtil::ToInt64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryBoolValue( bool* value ) const +{ + if ( XMLUtil::ToBool( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryFloatValue( float* value ) const +{ + if ( XMLUtil::ToFloat( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +void XMLAttribute::SetAttribute( const char* v ) +{ + _value.SetStr( v ); +} + + +void XMLAttribute::SetAttribute( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + + + +void XMLAttribute::SetAttribute( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +// --------- XMLElement ---------- // +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), + _closingType( 0 ), + _rootAttribute( 0 ) +{ +} + + +XMLElement::~XMLElement() +{ + while( _rootAttribute ) { + XMLAttribute* next = _rootAttribute->_next; + DeleteAttribute( _rootAttribute ); + _rootAttribute = next; + } +} + + +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const +{ + for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const char* XMLElement::Attribute( const char* name, const char* value ) const +{ + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return 0; + } + if ( !value || XMLUtil::StringEqual( a->Value(), value )) { + return a->Value(); + } + return 0; +} + +int XMLElement::IntAttribute(const char* name, int defaultValue) const +{ + int i = defaultValue; + QueryIntAttribute(name, &i); + return i; +} + +unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedAttribute(name, &i); + return i; +} + +int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Attribute(name, &i); + return i; +} + +bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolAttribute(name, &b); + return b; +} + +double XMLElement::DoubleAttribute(const char* name, double defaultValue) const +{ + double d = defaultValue; + QueryDoubleAttribute(name, &d); + return d; +} + +float XMLElement::FloatAttribute(const char* name, float defaultValue) const +{ + float f = defaultValue; + QueryFloatAttribute(name, &f); + return f; +} + +const char* XMLElement::GetText() const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + return FirstChild()->Value(); + } + return 0; +} + + +void XMLElement::SetText( const char* inText ) +{ + if ( FirstChild() && FirstChild()->ToText() ) + FirstChild()->SetValue( inText ); + else { + XMLText* theText = GetDocument()->NewText( inText ); + InsertFirstChild( theText ); + } +} + + +void XMLElement::SetText( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + + +void XMLElement::SetText( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +XMLError XMLElement::QueryIntText( int* ival ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToInt( t, ival ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToUnsigned( t, uval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryInt64Text(int64_t* ival) const +{ + if (FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if (XMLUtil::ToInt64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryBoolText( bool* bval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToBool( t, bval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryDoubleText( double* dval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToDouble( t, dval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryFloatText( float* fval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToFloat( t, fval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + +int XMLElement::IntText(int defaultValue) const +{ + int i = defaultValue; + QueryIntText(&i); + return i; +} + +unsigned XMLElement::UnsignedText(unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedText(&i); + return i; +} + +int64_t XMLElement::Int64Text(int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Text(&i); + return i; +} + +bool XMLElement::BoolText(bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolText(&b); + return b; +} + +double XMLElement::DoubleText(double defaultValue) const +{ + double d = defaultValue; + QueryDoubleText(&d); + return d; +} + +float XMLElement::FloatText(float defaultValue) const +{ + float f = defaultValue; + QueryFloatText(&f); + return f; +} + + +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) +{ + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = _rootAttribute; + attrib; + last = attrib, attrib = attrib->_next ) { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } + if ( !attrib ) { + attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + if ( last ) { + TIXMLASSERT( last->_next == 0 ); + last->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + attrib->SetName( name ); + } + return attrib; +} + + +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->_next = a->_next; + } + else { + _rootAttribute = a->_next; + } + DeleteAttribute( a ); + break; + } + prev = a; + } +} + + +char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) +{ + const char* start = p; + XMLAttribute* prevAttribute = 0; + + // Read the attributes. + while( p ) { + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( !(*p) ) { + _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name(), _parseLineNum ); + return 0; + } + + // attribute. + if (XMLUtil::IsNameStartChar( *p ) ) { + XMLAttribute* attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + attrib->_parseLineNum = _document->_parseCurLineNum; + + int attrLineNum = attrib->_parseLineNum; + + p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); + if ( !p || Attribute( attrib->Name() ) ) { + DeleteAttribute( attrib ); + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p, attrLineNum ); + return 0; + } + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + TIXMLASSERT( prevAttribute->_next == 0 ); + prevAttribute->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + prevAttribute = attrib; + } + // end of the tag + else if ( *p == '>' ) { + ++p; + break; + } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } + else { + _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p, _parseLineNum ); + return 0; + } + } + return p; +} + +void XMLElement::DeleteAttribute( XMLAttribute* attribute ) +{ + if ( attribute == 0 ) { + return; + } + MemPool* pool = attribute->_memPool; + attribute->~XMLAttribute(); + pool->Free( attribute ); +} + +XMLAttribute* XMLElement::CreateAttribute() +{ + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + attrib->_memPool = &_document->_attributePool; + attrib->_memPool->SetTracked(); + return attrib; +} + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* strPair, int* curLineNumPtr ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + + // The closing element is the form. It is + // parsed just like a regular element then deleted from + // the DOM. + if ( *p == '/' ) { + _closingType = CLOSING; + ++p; + } + + p = _value.ParseName( p ); + if ( _value.Empty() ) { + return 0; + } + + p = ParseAttributes( p, curLineNumPtr ); + if ( !p || !*p || _closingType ) { + return p; + } + + p = XMLNode::ParseDeep( p, strPair, curLineNumPtr ); + return p; +} + + + +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? + } + return element; +} + + +bool XMLElement::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLElement* other = compare->ToElement(); + if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { + + const XMLAttribute* a=FirstAttribute(); + const XMLAttribute* b=other->FirstAttribute(); + + while ( a && b ) { + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { + return false; + } + a = a->Next(); + b = b->Next(); + } + if ( a || b ) { + // different count + return false; + } + return true; + } + return false; +} + + +bool XMLElement::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this, _rootAttribute ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLDocument ----------- // + +// Warning: List must match 'enum XMLError' +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { + "XML_SUCCESS", + "XML_NO_ATTRIBUTE", + "XML_WRONG_ATTRIBUTE_TYPE", + "XML_ERROR_FILE_NOT_FOUND", + "XML_ERROR_FILE_COULD_NOT_BE_OPENED", + "XML_ERROR_FILE_READ_ERROR", + "XML_ERROR_ELEMENT_MISMATCH", + "XML_ERROR_PARSING_ELEMENT", + "XML_ERROR_PARSING_ATTRIBUTE", + "XML_ERROR_IDENTIFYING_TAG", + "XML_ERROR_PARSING_TEXT", + "XML_ERROR_PARSING_CDATA", + "XML_ERROR_PARSING_COMMENT", + "XML_ERROR_PARSING_DECLARATION", + "XML_ERROR_PARSING_UNKNOWN", + "XML_ERROR_EMPTY_DOCUMENT", + "XML_ERROR_MISMATCHED_ELEMENT", + "XML_ERROR_PARSING", + "XML_CAN_NOT_CONVERT_TEXT", + "XML_NO_TEXT_NODE" +}; + + +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : + XMLNode( 0 ), + _writeBOM( false ), + _processEntities( processEntities ), + _errorID(XML_SUCCESS), + _whitespace( whitespace ), + _charBuffer( 0 ) +{ + // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) + _document = this; +} + + +XMLDocument::~XMLDocument() +{ + Clear(); +} + + +void XMLDocument::Clear() +{ + DeleteChildren(); + +#ifdef DEBUG + const bool hadError = Error(); +#endif + ClearError(); + + delete [] _charBuffer; + _charBuffer = 0; + +#if 0 + _textPool.Trace( "text" ); + _elementPool.Trace( "element" ); + _commentPool.Trace( "comment" ); + _attributePool.Trace( "attribute" ); +#endif + +#ifdef DEBUG + if ( !hadError ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif +} + + +XMLElement* XMLDocument::NewElement( const char* name ) +{ + TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); + XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); + ele->_memPool = &_elementPool; + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); + XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); + comment->_memPool = &_commentPool; + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); + XMLText* text = new (_textPool.Alloc()) XMLText( this ); + text->_memPool = &_textPool; + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); + XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); + dec->_memPool = &_commentPool; + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); + XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); + unk->_memPool = &_commentPool; + unk->SetValue( str ); + return unk; +} + +static FILE* callfopen( const char* filepath, const char* mode ) +{ + TIXMLASSERT( filepath ); + TIXMLASSERT( mode ); +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filepath, mode ); + if ( err ) { + return 0; + } +#else + FILE* fp = fopen( filepath, mode ); +#endif + return fp; +} + +void XMLDocument::DeleteNode( XMLNode* node ) { + TIXMLASSERT( node ); + TIXMLASSERT(node->_document == this ); + if (node->_parent) { + node->_parent->DeleteChild( node ); + } + else { + // Isn't in the tree. + // Use the parent delete. + // Also, we need to mark it tracked: we 'know' + // it was never used. + node->_memPool->SetTracked(); + // Call the static XMLNode version: + XMLNode::DeleteNode(node); + } +} + + +XMLError XMLDocument::LoadFile( const char* filename ) +{ + Clear(); + FILE* fp = callfopen( filename, "rb" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0, 0 ); + return _errorID; + } + LoadFile( fp ); + fclose( fp ); + return _errorID; +} + +// This is likely overengineered template art to have a check that unsigned long value incremented +// by one still fits into size_t. If size_t type is larger than unsigned long type +// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit +// -Wtype-limits warning. This piece makes the compiler select code with a check when a check +// is useful and code with no check when a check is redundant depending on how size_t and unsigned long +// types sizes relate to each other. +template += sizeof(size_t))> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long value ) + { + return value < (size_t)-1; + } +}; + +template <> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long ) + { + return true; + } +}; + +XMLError XMLDocument::LoadFile( FILE* fp ) +{ + Clear(); + + fseek( fp, 0, SEEK_SET ); + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); + return _errorID; + } + + fseek( fp, 0, SEEK_END ); + const long filelength = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + if ( filelength == -1L ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); + return _errorID; + } + TIXMLASSERT( filelength >= 0 ); + + if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { + // Cannot handle files which won't fit in buffer together with null terminator + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); + return _errorID; + } + + if ( filelength == 0 ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 ); + return _errorID; + } + + const size_t size = filelength; + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[size+1]; + size_t read = fread( _charBuffer, 1, size, fp ); + if ( read != size ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); + return _errorID; + } + + _charBuffer[size] = 0; + + Parse(); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( const char* filename, bool compact ) +{ + FILE* fp = callfopen( filename, "w" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0, 0 ); + return _errorID; + } + SaveFile(fp, compact); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) +{ + // Clear any error from the last save, otherwise it will get reported + // for *this* call. + ClearError(); + XMLPrinter stream( fp, compact ); + Print( &stream ); + return _errorID; +} + + +XMLError XMLDocument::Parse( const char* p, size_t len ) +{ + Clear(); + + if ( len == 0 || !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 ); + return _errorID; + } + if ( len == (size_t)(-1) ) { + len = strlen( p ); + } + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[ len+1 ]; + memcpy( _charBuffer, p, len ); + _charBuffer[len] = 0; + + Parse(); + if ( Error() ) { + // clean up now essentially dangling memory. + // and the parse fail can put objects in the + // pools that are dead and inaccessible. + DeleteChildren(); + _elementPool.Clear(); + _attributePool.Clear(); + _textPool.Clear(); + _commentPool.Clear(); + } + return _errorID; +} + + +void XMLDocument::Print( XMLPrinter* streamer ) const +{ + if ( streamer ) { + Accept( streamer ); + } + else { + XMLPrinter stdoutStreamer( stdout ); + Accept( &stdoutStreamer ); + } +} + + +void XMLDocument::SetError( XMLError error, const char* str1, const char* str2, int lineNum ) +{ + TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); + _errorID = error; + + _errorStr1.Reset(); + _errorStr2.Reset(); + _errorLineNum = lineNum; + + if (str1) + _errorStr1.SetStr(str1); + if (str2) + _errorStr2.SetStr(str2); +} + +/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) +{ + TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); + const char* errorName = _errorNames[errorID]; + TIXMLASSERT( errorName && errorName[0] ); + return errorName; +} + +const char* XMLDocument::ErrorName() const +{ + return ErrorIDToName(_errorID); +} + +void XMLDocument::PrintError() const +{ + if ( Error() ) { + static const int LEN = 20; + char buf1[LEN] = { 0 }; + char buf2[LEN] = { 0 }; + + if ( !_errorStr1.Empty() ) { + TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1.GetStr() ); + } + if ( !_errorStr2.Empty() ) { + TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2.GetStr() ); + } + + // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that + // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning + TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX ); + printf( "XMLDocument error id=%d '%s' str1=%s str2=%s line=%d\n", + static_cast( _errorID ), ErrorName(), buf1, buf2, _errorLineNum ); + } +} + +void XMLDocument::Parse() +{ + TIXMLASSERT( NoChildren() ); // Clear() must have been called previously + TIXMLASSERT( _charBuffer ); + _parseCurLineNum = 1; + _parseLineNum = 1; + char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); + if ( !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 ); + return; + } + ParseDeep(p, 0, &_parseCurLineNum ); +} + +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : + _elementJustOpened( false ), + _firstElement( true ), + _fp( file ), + _depth( depth ), + _textDepth( -1 ), + _processEntities( true ), + _compactMode( compact ) +{ + for( int i=0; i'] = true; // not required, but consistency is nice + _buffer.Push( 0 ); +} + + +void XMLPrinter::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( _fp ) { + vfprintf( _fp, format, va ); + } + else { + const int len = TIXML_VSCPRINTF( format, va ); + // Close out and re-start the va-args + va_end( va ); + TIXMLASSERT( len >= 0 ); + va_start( va, format ); + TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); + char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. + TIXML_VSNPRINTF( p, len+1, format, va ); + } + va_end( va ); +} + + +void XMLPrinter::PrintSpace( int depth ) +{ + for( int i=0; i 0 && *q < ENTITY_RANGE ) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if ( flag[(unsigned char)(*q)] ) { + while ( p < q ) { + const size_t delta = q - p; + // %.*s accepts type int as "precision" + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; + Print( "%.*s", toPrint, p ); + p += toPrint; + } + bool entityPatternPrinted = false; + for( int i=0; i" ); + } + else { + if ( _textDepth < 0 && !compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + Print( "", name ); + } + + if ( _textDepth == _depth ) { + _textDepth = -1; + } + if ( _depth == 0 && !compactMode) { + Print( "\n" ); + } + _elementJustOpened = false; +} + + +void XMLPrinter::SealElementIfJustOpened() +{ + if ( !_elementJustOpened ) { + return; + } + _elementJustOpened = false; + Print( ">" ); +} + + +void XMLPrinter::PushText( const char* text, bool cdata ) +{ + _textDepth = _depth-1; + + SealElementIfJustOpened(); + if ( cdata ) { + Print( "", text ); + } + else { + PrintString( text, true ); + } +} + +void XMLPrinter::PushText( int64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushComment( const char* comment ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", comment ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", value ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", value ); +} + + +bool XMLPrinter::VisitEnter( const XMLDocument& doc ) +{ + _processEntities = doc.ProcessEntities(); + if ( doc.HasBOM() ) { + PushHeader( true, false ); + } + return true; +} + + +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) +{ + const XMLElement* parentElem = 0; + if ( element.Parent() ) { + parentElem = element.Parent()->ToElement(); + } + const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; + OpenElement( element.Name(), compactMode ); + while ( attribute ) { + PushAttribute( attribute->Name(), attribute->Value() ); + attribute = attribute->Next(); + } + return true; +} + + +bool XMLPrinter::VisitExit( const XMLElement& element ) +{ + CloseElement( CompactMode(element) ); + return true; +} + + +bool XMLPrinter::Visit( const XMLText& text ) +{ + PushText( text.Value(), text.CData() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLComment& comment ) +{ + PushComment( comment.Value() ); + return true; +} + +bool XMLPrinter::Visit( const XMLDeclaration& declaration ) +{ + PushDeclaration( declaration.Value() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLUnknown& unknown ) +{ + PushUnknown( unknown.Value() ); + return true; +} + +} // namespace tinyxml2 + diff --git a/App/XML/tinyxml2.h b/App/XML/tinyxml2.h new file mode 100755 index 00000000..de589bde --- /dev/null +++ b/App/XML/tinyxml2.h @@ -0,0 +1,2197 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +# include +# include +# include +# if defined(__PS3__) +# include +# endif +#else +# include +# include +# include +# include +# include +#endif +#include + +/* + TODO: intern strings instead of allocation. +*/ +/* + gcc: + g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) +# ifndef DEBUG +# define DEBUG +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4251) +#endif + +#ifdef _WIN32 +# ifdef TINYXML2_EXPORT +# define TINYXML2_LIB __declspec(dllexport) +# elif defined(TINYXML2_IMPORT) +# define TINYXML2_LIB __declspec(dllimport) +# else +# define TINYXML2_LIB +# endif +#elif __GNUC__ >= 4 +# define TINYXML2_LIB __attribute__((visibility("default"))) +#else +# define TINYXML2_LIB +#endif + + +#if defined(DEBUG) +# if defined(_MSC_VER) +# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like +# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# else +# include +# define TIXMLASSERT assert +# endif +#else +# define TIXMLASSERT( x ) {} +#endif + + +/* Versioning, past 1.0.14: + http://semver.org/ +*/ +static const int TIXML2_MAJOR_VERSION = 4; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 1; + +namespace tinyxml2 +{ +class XMLDocument; +class XMLElement; +class XMLAttribute; +class XMLComment; +class XMLText; +class XMLDeclaration; +class XMLUnknown; +class XMLPrinter; + +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity translation if actually read. Can also store (and memory + manage) a traditional char[] +*/ +class StrPair +{ +public: + enum { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + NEEDS_WHITESPACE_COLLAPSING = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + TIXMLASSERT( start ); + TIXMLASSERT( end ); + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); + char* ParseName( char* in ); + + void TransferTo( StrPair* other ); + void Reset(); + +private: + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + int _flags; + char* _start; + char* _end; + + StrPair( const StrPair& other ); // not supported + void operator=( StrPair& other ); // not supported, use TransferTo() +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray() { + _mem = _pool; + _allocated = INITIAL_SIZE; + _size = 0; + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Clear() { + _size = 0; + } + + void Push( T t ) { + TIXMLASSERT( _size < INT_MAX ); + EnsureCapacity( _size+1 ); + _mem[_size] = t; + ++_size; + } + + T* PushArr( int count ) { + TIXMLASSERT( count >= 0 ); + TIXMLASSERT( _size <= INT_MAX - count ); + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + TIXMLASSERT( _size > 0 ); + --_size; + return _mem[_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& PeekTop() const { + TIXMLASSERT( _size > 0 ); + return _mem[ _size - 1]; + } + + int Size() const { + TIXMLASSERT( _size >= 0 ); + return _size; + } + + int Capacity() const { + TIXMLASSERT( _allocated >= INITIAL_SIZE ); + return _allocated; + } + + const T* Mem() const { + TIXMLASSERT( _mem ); + return _mem; + } + + T* Mem() { + TIXMLASSERT( _mem ); + return _mem; + } + +private: + DynArray( const DynArray& ); // not supported + void operator=( const DynArray& ); // not supported + + void EnsureCapacity( int cap ) { + TIXMLASSERT( cap > 0 ); + if ( cap > _allocated ) { + TIXMLASSERT( cap <= INT_MAX / 2 ); + int newAllocated = cap * 2; + T* newMem = new T[newAllocated]; + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[INITIAL_SIZE]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; + virtual void Clear() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int ITEM_SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + Clear(); + } + + void Clear() { + // Delete the blocks. + while( !_blockPtrs.Empty()) { + Block* b = _blockPtrs.Pop(); + delete b; + } + _root = 0; + _currentAllocs = 0; + _nAllocs = 0; + _maxAllocs = 0; + _nUntracked = 0; + } + + virtual int ItemSize() const { + return ITEM_SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() { + if ( !_root ) { + // Need a new block. + Block* block = new Block(); + _blockPtrs.Push( block ); + + Item* blockItems = block->items; + for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { + blockItems[i].next = &(blockItems[i + 1]); + } + blockItems[ITEMS_PER_BLOCK - 1].next = 0; + _root = blockItems; + } + Item* const result = _root; + TIXMLASSERT( result != 0 ); + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + ++_nAllocs; + ++_nUntracked; + return result; + } + + virtual void Free( void* mem ) { + if ( !mem ) { + return; + } + --_currentAllocs; + Item* item = static_cast( mem ); +#ifdef DEBUG + memset( item, 0xfe, sizeof( *item ) ); +#endif + item->next = _root; + _root = item; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, + ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() { + --_nUntracked; + } + + int Untracked() const { + return _nUntracked; + } + + // This number is perf sensitive. 4k seems like a good tradeoff on my machine. + // The test file is large, 170k. + // Release: VS2010 gcc(no opt) + // 1k: 4000 + // 2k: 4000 + // 4k: 3900 21000 + // 16k: 5200 + // 32k: 4300 + // 64k: 4000 21000 + // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK + // in private part if ITEMS_PER_BLOCK is private + enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; + +private: + MemPoolT( const MemPoolT& ); // not supported + void operator=( const MemPoolT& ); // not supported + + union Item { + Item* next; + char itemData[ITEM_SIZE]; + }; + struct Block { + Item items[ITEMS_PER_BLOCK]; + }; + DynArray< Block*, 10 > _blockPtrs; + Item* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its siblings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the XMLDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class TINYXML2_LIB XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + +// WARNING: must match XMLDocument::_errorNames[] +enum XMLError { + XML_SUCCESS = 0, + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_ELEMENT_MISMATCH, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_IDENTIFYING_TAG, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE, + + XML_ERROR_COUNT +}; + + +/* + Utility functionality. +*/ +class XMLUtil +{ +public: + static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { + TIXMLASSERT( p ); + + while( IsWhiteSpace(*p) ) { + if (curLineNumPtr && *p == '\n') { + ++(*curLineNumPtr); + } + ++p; + } + TIXMLASSERT( p ); + return p; + } + static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) { + return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); + } + + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool IsNameStartChar( unsigned char ch ) { + if ( ch >= 128 ) { + // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() + return true; + } + if ( isalpha( ch ) ) { + return true; + } + return ch == ':' || ch == '_'; + } + + inline static bool IsNameChar( unsigned char ch ) { + return IsNameStartChar( ch ) + || isdigit( ch ) + || ch == '.' + || ch == '-'; + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + if ( p == q ) { + return true; + } + TIXMLASSERT( p ); + TIXMLASSERT( q ); + TIXMLASSERT( nChar >= 0 ); + return strncmp( p, q, nChar ) == 0; + } + + inline static bool IsUTF8Continuation( char p ) { + return ( p & 0x80 ) != 0; + } + + static const char* ReadBOM( const char* p, bool* hasBOM ); + // p is the starting location, + // the UTF-8 value of the entity will be placed in value, and length filled in. + static const char* GetCharacterRef( const char* p, char* value, int* length ); + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + + // converts primitive types to strings + static void ToStr( int v, char* buffer, int bufferSize ); + static void ToStr( unsigned v, char* buffer, int bufferSize ); + static void ToStr( bool v, char* buffer, int bufferSize ); + static void ToStr( float v, char* buffer, int bufferSize ); + static void ToStr( double v, char* buffer, int bufferSize ); + static void ToStr(int64_t v, char* buffer, int bufferSize); + + // converts strings to primitive types + static bool ToInt( const char* str, int* value ); + static bool ToUnsigned( const char* str, unsigned* value ); + static bool ToBool( const char* str, bool* value ); + static bool ToFloat( const char* str, float* value ); + static bool ToDouble( const char* str, double* value ); + static bool ToInt64(const char* str, int64_t* value); +}; + + +/** XMLNode is a base class for every object that is in the + XML Document Object Model (DOM), except XMLAttributes. + Nodes have siblings, a parent, and children which can + be navigated. A node is always in a XMLDocument. + The type of a XMLNode can be queried, and it can + be cast to its more defined type. + + A XMLDocument allocates memory for all its Nodes. + When the XMLDocument gets deleted, all its Nodes + will also be deleted. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + @endverbatim +*/ +class TINYXML2_LIB XMLNode +{ + friend class XMLDocument; + friend class XMLElement; +public: + + /// Get the XMLDocument that owns this XMLNode. + const XMLDocument* GetDocument() const { + TIXMLASSERT( _document ); + return _document; + } + /// Get the XMLDocument that owns this XMLNode. + XMLDocument* GetDocument() { + TIXMLASSERT( _document ); + return _document; + } + + /// Safely cast to an Element, or null. + virtual XMLElement* ToElement() { + return 0; + } + /// Safely cast to Text, or null. + virtual XMLText* ToText() { + return 0; + } + /// Safely cast to a Comment, or null. + virtual XMLComment* ToComment() { + return 0; + } + /// Safely cast to a Document, or null. + virtual XMLDocument* ToDocument() { + return 0; + } + /// Safely cast to a Declaration, or null. + virtual XMLDeclaration* ToDeclaration() { + return 0; + } + /// Safely cast to an Unknown, or null. + virtual XMLUnknown* ToUnknown() { + return 0; + } + + virtual const XMLElement* ToElement() const { + return 0; + } + virtual const XMLText* ToText() const { + return 0; + } + virtual const XMLComment* ToComment() const { + return 0; + } + virtual const XMLDocument* ToDocument() const { + return 0; + } + virtual const XMLDeclaration* ToDeclaration() const { + return 0; + } + virtual const XMLUnknown* ToUnknown() const { + return 0; + } + + /** The meaning of 'value' changes for the specific type. + @verbatim + Document: empty (NULL is returned, not an empty string) + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + const char* Value() const; + + /** Set the Value of an XML node. + @sa Value() + */ + void SetValue( const char* val, bool staticMem=false ); + + /// Gets the line number the node is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// Get the parent of this node on the DOM. + const XMLNode* Parent() const { + return _parent; + } + + XMLNode* Parent() { + return _parent; + } + + /// Returns true if this node has no children. + bool NoChildren() const { + return !_firstChild; + } + + /// Get the first child node, or null if none exists. + const XMLNode* FirstChild() const { + return _firstChild; + } + + XMLNode* FirstChild() { + return _firstChild; + } + + /** Get the first child element, or optionally the first child + element with the specified name. + */ + const XMLElement* FirstChildElement( const char* name = 0 ) const; + + XMLElement* FirstChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->FirstChildElement( name )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return _lastChild; + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* name = 0 ) const; + + XMLElement* LastChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->LastChildElement(name) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an optionally supplied name. + const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; + + XMLElement* PreviousSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an optionally supplied name. + const XMLElement* NextSiblingElement( const char* name = 0 ) const; + + XMLElement* NextSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->NextSiblingElement( name ) ); + } + + /** + Add a child node as the last (right) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the afterThis node + is not a child of this node, or if the node does not + belong to the same document. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the XMLVisitor interface. + + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + XMLPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + /** + Set user data into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void SetUserData(void* userData) { _userData = userData; } + + /** + Get user data set into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void* GetUserData() const { return _userData; } + +protected: + XMLNode( XMLDocument* ); + virtual ~XMLNode(); + + virtual char* ParseDeep( char*, StrPair*, int* ); + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + int _parseLineNum; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + + void* _userData; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); + static void DeleteNode( XMLNode* node ); + void InsertChildPreamble( XMLNode* insertThis ) const; + const XMLElement* ToElementWithName( const char* name ) const; + + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCData() and query it with CData(). +*/ +class TINYXML2_LIB XMLText : public XMLNode +{ + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLText* ToText() { + return this; + } + virtual const XMLText* ToText() const { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + + char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr ); + +private: + bool _isCData; + + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported +}; + + +/** An XML Comment. */ +class TINYXML2_LIB XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() { + return this; + } + virtual const XMLComment* ToComment() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + + char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr); + +private: + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML-2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class TINYXML2_LIB XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + + char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr ); + +private: + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that TinyXML-2 doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into XMLUnknowns. +*/ +class TINYXML2_LIB XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() { + return this; + } + virtual const XMLUnknown* ToUnknown() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + + char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr ); + +private: + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class TINYXML2_LIB XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const; + + /// The value of the attribute. + const char* Value() const; + + /// Gets the line number the attribute is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntValue interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntValue() if you need error checking. + */ + int IntValue() const { + int i = 0; + QueryIntValue(&i); + return i; + } + + int64_t Int64Value() const { + int64_t i = 0; + QueryInt64Value(&i); + return i; + } + + /// Query as an unsigned integer. See IntValue() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntValue() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntValue() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntValue() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntValue interprets the attribute as an integer, and returns the value + in the provided parameter. The function will return XML_SUCCESS on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntValue + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntValue + XMLError QueryInt64Value(int64_t* value) const; + /// See QueryIntValue + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntValue + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntValue + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute(int64_t value); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _next( 0 ), _memPool( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); + + mutable StrPair _name; + mutable StrPair _value; + int _parseLineNum; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TINYXML2_LIB XMLElement : public XMLNode +{ + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() { + return this; + } + virtual const XMLElement* ToElement() const { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. The default + value will be returned if the attribute isn't present, + or if there is an error. (For a method with error + checking, see QueryIntAttribute()). + */ + int IntAttribute(const char* name, int defaultValue = 0) const; + /// See IntAttribute() + unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; + /// See IntAttribute() + int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; + /// See IntAttribute() + bool BoolAttribute(const char* name, bool defaultValue = false) const; + /// See IntAttribute() + double DoubleAttribute(const char* name, double defaultValue = 0) const; + /// See IntAttribute() + float FloatAttribute(const char* name, float defaultValue = 0) const; + + /** Given an attribute name, QueryIntAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryInt64Attribute(const char* name, int64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryInt64Value(value); + } + + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + + /** Given an attribute name, QueryAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. It is overloaded for the primitive types, + and is a generally more convenient replacement of + QueryIntAttribute() and related functions. + + If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + int QueryAttribute( const char* name, int* value ) const { + return QueryIntAttribute( name, value ); + } + + int QueryAttribute( const char* name, unsigned int* value ) const { + return QueryUnsignedAttribute( name, value ); + } + + int QueryAttribute(const char* name, int64_t* value) const { + return QueryInt64Attribute(name, value); + } + + int QueryAttribute( const char* name, bool* value ) const { + return QueryBoolAttribute( name, value ); + } + + int QueryAttribute( const char* name, double* value ) const { + return QueryDoubleAttribute( name, value ); + } + + int QueryAttribute( const char* name, float* value ) const { + return QueryFloatAttribute( name, value ); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /// Sets the named attribute to value. + void SetAttribute(const char* name, int64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, float value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the XMLText child + and accessing it directly. + + If the first child of 'this' is a XMLText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, SetText() is limited compared to creating an XMLText child + and mutating it directly. + + If the first child of 'this' is a XMLText, SetText() sets its value to + the given string, otherwise it will create a first child that is an XMLText. + + This is a convenient method for setting the text of simple contained text: + @verbatim + This is text + fooElement->SetText( "Hullaballoo!" ); + Hullaballoo! + @endverbatim + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then it will not change "This is text", but rather prefix it with a text element: + @verbatim + Hullaballoo!This is text + @endverbatim + + For this XML: + @verbatim + + @endverbatim + SetText() will generate + @verbatim + Hullaballoo! + @endverbatim + */ + void SetText( const char* inText ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( int value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( unsigned value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(int64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( bool value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( double value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( float value ); + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryInt64Text(int64_t* uval) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + int IntText(int defaultValue = 0) const; + + /// See QueryIntText() + unsigned UnsignedText(unsigned defaultValue = 0) const; + /// See QueryIntText() + int64_t Int64Text(int64_t defaultValue = 0) const; + /// See QueryIntText() + bool BoolText(bool defaultValue = false) const; + /// See QueryIntText() + double DoubleText(double defaultValue = 0) const; + /// See QueryIntText() + float FloatText(float defaultValue = 0) const; + + // internal: + enum { + OPEN, // + CLOSED, // + CLOSING // + }; + int ClosingType() const { + return _closingType; + } + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + char* ParseDeep( char* p, StrPair* endTag, int* curLineNumPtr ); + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindAttribute( const char* name ) { + return const_cast(const_cast(this)->FindAttribute( name )); + } + XMLAttribute* FindOrCreateAttribute( const char* name ); + //void LinkAttribute( XMLAttribute* attrib ); + char* ParseAttributes( char* p, int* curLineNumPtr ); + static void DeleteAttribute( XMLAttribute* attribute ); + XMLAttribute* CreateAttribute(); + + enum { BUF_SIZE = 200 }; + int _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class TINYXML2_LIB XMLDocument : public XMLNode +{ + friend class XMLElement; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() { + TIXMLASSERT( this == _document ); + return this; + } + virtual const XMLDocument* ToDocument() const { + TIXMLASSERT( this == _document ); + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_SUCCESS (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML-2 will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); + + /** + Load an XML file from disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + NOTE: The file should be opened as binary ("rb") + not text in order for TinyXML-2 to correctly + do newline normalization. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespace; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ) const; + virtual bool Accept( XMLVisitor* visitor ) const; + + /** + Create a new Element associated with + this Document. The memory for the Element + is managed by the Document. + */ + XMLElement* NewElement( const char* name ); + /** + Create a new Comment associated with + this Document. The memory for the Comment + is managed by the Document. + */ + XMLComment* NewComment( const char* comment ); + /** + Create a new Text associated with + this Document. The memory for the Text + is managed by the Document. + */ + XMLText* NewText( const char* text ); + /** + Create a new Declaration associated with + this Document. The memory for the object + is managed by the Document. + + If the 'text' param is null, the standard + declaration is used.: + @verbatim + + @endverbatim + */ + XMLDeclaration* NewDeclaration( const char* text=0 ); + /** + Create a new Unknown associated with + this Document. The memory for the object + is managed by the Document. + */ + XMLUnknown* NewUnknown( const char* text ); + + /** + Delete a node associated with this document. + It will be unlinked from the DOM. + */ + void DeleteNode( XMLNode* node ); + + void SetError( XMLError error, const char* str1, const char* str2, int lineNum ); + + void ClearError() { + SetError(XML_SUCCESS, 0, 0, 0); + } + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_SUCCESS; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + const char* ErrorName() const; + static const char* ErrorIDToName(XMLError errorID); + + /// Return a possibly helpful diagnostic location or string. + const char* GetErrorStr1() const { + return _errorStr1.GetStr(); + } + /// Return a possibly helpful secondary diagnostic location or string. + const char* GetErrorStr2() const { + return _errorStr2.GetStr(); + } + /// Return the line where the error occured, or zero if unknown. + int GetErrorLineNum() const + { + return _errorLineNum; + } + /// If there is an error, print it to stdout. + void PrintError() const; + + /// Clear the document, resetting it to the initial state. + void Clear(); + + // internal + char* Identify( char* p, XMLNode** node ); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespace; + mutable StrPair _errorStr1; + mutable StrPair _errorStr2; + int _errorLineNum; + char* _charBuffer; + int _parseCurLineNum; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; + + static const char* _errorNames[XML_ERROR_COUNT]; + + void Parse(); +}; + + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class TINYXML2_LIB XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + XMLHandle( XMLNode* node ) { + _node = node; + } + /// Create a handle from a node. + XMLHandle( XMLNode& node ) { + _node = &node; + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) { + _node = ref._node; + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( _node ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( _node ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( _node ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class TINYXML2_LIB XMLConstHandle +{ +public: + XMLConstHandle( const XMLNode* node ) { + _node = node; + } + XMLConstHandle( const XMLNode& node ) { + _node = &node; + } + XMLConstHandle( const XMLConstHandle& ref ) { + _node = ref._node; + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( _node ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( _node ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( _node ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class TINYXML2_LIB XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); + virtual ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name, bool compactMode=false ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute(const char* name, int64_t value); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + virtual void CloseElement( bool compactMode=false ); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from an unsigned. + void PushText(int64_t value); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ); + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); + virtual bool VisitExit( const XMLElement& element ); + + virtual bool Visit( const XMLText& text ); + virtual bool Visit( const XMLComment& comment ); + virtual bool Visit( const XMLDeclaration& declaration ); + virtual bool Visit( const XMLUnknown& unknown ); + + /** + If in print to memory mode, return a pointer to + the XML file in memory. + */ + const char* CStr() const { + return _buffer.Mem(); + } + /** + If in print to memory mode, return the size + of the XML file in memory. (Note the size returned + includes the terminating null.) + */ + int CStrSize() const { + return _buffer.Size(); + } + /** + If in print to memory mode, reset the buffer to the + beginning. + */ + void ClearBuffer() { + _buffer.Clear(); + _buffer.Push(0); + } + +protected: + virtual bool CompactMode( const XMLElement& ) { return _compactMode; } + + /** Prints out the space before an element. You may override to change + the space and tabs used. A PrintSpace() override should call Print(). + */ + virtual void PrintSpace( int depth ); + void Print( const char* format, ... ); + + void SealElementIfJustOpened(); + bool _elementJustOpened; + DynArray< const char*, 10 > _stack; + +private: + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< char, 20 > _buffer; +}; + + +} // tinyxml2 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TINYXML2_INCLUDED diff --git a/App/main.cpp b/App/main.cpp index e1a4b031..392d5693 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -71,7 +71,8 @@ THE SOFTWARE. #include "CLW/clwoutput.h" #include "config_manager.h" #include "Scene/scene1.h" -#include "Scene/Loaders/scene_io.h" +#include "Scene/IO/scene_io.h" +#include "Scene/IO/material_io.h" Baikal::Scene1 scene; @@ -345,8 +346,11 @@ void InitData() std::string filename = basepath + g_modelname; { - std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoTest()); - g_scene.reset(scene_io->LoadScene("sphere+plane+ibl", basepath)); + std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoObj()); + g_scene.reset(scene_io->LoadScene(filename, basepath)); + + //std::unique_ptr material_io(Baikal::MaterialIo::CreateMaterialIoXML()); + //material_io->SaveMaterials("materials.xml", *g_scene); } g_camera.reset(new Baikal::PerspectiveCamera( From dfa5ebcf3a00c1cee4f75368819c33b6900297f9 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Fri, 20 Jan 2017 14:43:21 +0100 Subject: [PATCH 12/24] Implement XML materials override --- App/Scene/IO/material_io.cpp | 471 +++++++++++++++++++++++++++-------- App/Scene/IO/material_io.h | 30 ++- App/Scene/IO/scene_io.cpp | 9 +- App/Scene/iterator.h | 2 +- App/main.cpp | 14 +- 5 files changed, 408 insertions(+), 118 deletions(-) diff --git a/App/Scene/IO/material_io.cpp b/App/Scene/IO/material_io.cpp index b598e466..98929cb5 100644 --- a/App/Scene/IO/material_io.cpp +++ b/App/Scene/IO/material_io.cpp @@ -24,16 +24,40 @@ namespace Baikal { public: // Save materials to disk - void SaveMaterials(std::string const& filename, Scene1 const& scene) override; - + void SaveMaterials(std::string const& filename, Iterator& iterator) override; + + // Load materials from disk + Iterator* LoadMaterials(std::string const& filename) override; + private: // Write single material void WriteMaterial(ImageIo& io, XMLPrinter& printer, Material const* material); // Write single material input void WriteInput(ImageIo& io, XMLPrinter& printer, std::string const& name, Material::InputValue value); - + // Load single material + Material* LoadMaterial(ImageIo& io, XMLElement& element); + // Load single input + void LoadInput(ImageIo& io, Material* material, XMLElement& element); + // Texture to name map - std::map m_textures; + std::map m_tex2name; + + std::map m_name2tex; + std::map m_id2mat; + + struct ResolveRequest + { + Material* material; + std::string input; + std::uint64_t id; + + bool operator < (ResolveRequest const& rhs) const + { + return id < rhs.id; + } + }; + + std::set m_resolve_requests; }; MaterialIo* MaterialIo::CreateMaterialIoXML() @@ -47,13 +71,74 @@ namespace Baikal oss << v.x << " " << v.y << " " << v.z << " " << v.w; return oss.str(); } - + + static std::string BxdfToString(SingleBxdf::BxdfType type) + { + switch (type) + { + case Baikal::SingleBxdf::BxdfType::kZero: + return "zero"; + case Baikal::SingleBxdf::BxdfType::kLambert: + return "lambert"; + case Baikal::SingleBxdf::BxdfType::kIdealReflect: + return "ideal_reflect"; + case Baikal::SingleBxdf::BxdfType::kIdealRefract: + return "ideal_refract";; + case Baikal::SingleBxdf::BxdfType::kMicrofacetBlinn: + return "microfacet_blinn"; + case Baikal::SingleBxdf::BxdfType::kMicrofacetBeckmann: + return "microfacet_beckmann"; + case Baikal::SingleBxdf::BxdfType::kMicrofacetGGX: + return "microfacet_ggx"; + case Baikal::SingleBxdf::BxdfType::kEmissive: + return "emissive"; + case Baikal::SingleBxdf::BxdfType::kPassthrough: + return "passthrough"; + case Baikal::SingleBxdf::BxdfType::kTranslucent: + return "translucent"; + case Baikal::SingleBxdf::BxdfType::kMicrofacetRefractionGGX: + return "microfacet_refraction_ggx"; + case Baikal::SingleBxdf::BxdfType::kMicrofacetRefractionBeckmann: + return "microfacet_refraction_beckmann"; + default: + return "lambert"; + } + } + + static SingleBxdf::BxdfType StringToBxdf(std::string const& bxdf) + { + static std::map bxdf_map = + { + { "zero" , Baikal::SingleBxdf::BxdfType::kZero }, + { "lambert" , Baikal::SingleBxdf::BxdfType::kLambert }, + { "ideal_reflect" , Baikal::SingleBxdf::BxdfType::kIdealReflect }, + { "ideal_refract" , Baikal::SingleBxdf::BxdfType::kIdealRefract }, + { "microfacet_blinn" , Baikal::SingleBxdf::BxdfType::kMicrofacetBlinn }, + { "microfacet_beckmann" , Baikal::SingleBxdf::BxdfType::kMicrofacetBeckmann }, + { "microfacet_ggx" , Baikal::SingleBxdf::BxdfType::kMicrofacetGGX }, + { "emissive" , Baikal::SingleBxdf::BxdfType::kEmissive }, + { "passthrough" , Baikal::SingleBxdf::BxdfType::kPassthrough }, + { "translucent" , Baikal::SingleBxdf::BxdfType::kTranslucent }, + { "microfacet_refraction_ggx" , Baikal::SingleBxdf::BxdfType::kMicrofacetRefractionGGX }, + { "microfacet_refraction_beckmann" , Baikal::SingleBxdf::BxdfType::kMicrofacetRefractionBeckmann }, + }; + + auto iter = bxdf_map.find(bxdf); + + if (iter != bxdf_map.cend()) + { + return iter->second; + } + + return Baikal::SingleBxdf::BxdfType::kLambert; + } + void MaterialIoXML::WriteInput(ImageIo& io, XMLPrinter& printer, std::string const& name, Material::InputValue value) { printer.OpenElement("Input"); - + printer.PushAttribute("name", name.c_str()); - + if (value.type == Material::InputType::kFloat4) { printer.PushAttribute("type", "float4"); @@ -62,10 +147,10 @@ namespace Baikal else if (value.type == Material::InputType::kTexture) { printer.PushAttribute("type", "texture"); - - auto iter = m_textures.find(value.tex_value); - - if (iter != m_textures.cend()) + + auto iter = m_tex2name.find(value.tex_value); + + if (iter != m_tex2name.cend()) { printer.PushAttribute("value", iter->second.c_str()); } @@ -73,11 +158,11 @@ namespace Baikal { std::ostringstream oss; oss << (std::uint64_t)value.tex_value << ".jpg"; - + io.SaveImage(oss.str(), value.tex_value); - - m_textures[value.tex_value] = oss.str(); - + + m_tex2name[value.tex_value] = oss.str(); + printer.PushAttribute("value", oss.str().c_str()); } } @@ -87,49 +172,49 @@ namespace Baikal printer.PushAttribute("value", (int)(reinterpret_cast(value.mat_value))); } - + printer.CloseElement(); } - + void MaterialIoXML::WriteMaterial(ImageIo& io, XMLPrinter& printer, Material const* material) { printer.OpenElement("Material"); - + printer.PushAttribute("name", material->GetName().c_str()); - + printer.PushAttribute("id", (int)(reinterpret_cast(material))); - + printer.PushAttribute("twosided", material->IsTwoSided()); - + SingleBxdf const* bxdf = dynamic_cast(material); - + if (bxdf) { printer.PushAttribute("type", "simple"); - + SingleBxdf::BxdfType type = bxdf->GetBxdfType(); - - printer.PushAttribute("bxdf", (int)type); - + + printer.PushAttribute("bxdf", BxdfToString(type).c_str()); + SingleBxdf::InputValue albedo = bxdf->GetInputValue("albedo"); - + WriteInput(io, printer,"albedo", albedo); - + SingleBxdf::InputValue normal = bxdf->GetInputValue("normal"); - + if (normal.tex_value) { WriteInput(io, printer, "normal", normal); } - + SingleBxdf::InputValue ior = bxdf->GetInputValue("ior"); - + WriteInput(io, printer,"ior", ior); - + SingleBxdf::InputValue fresnel = bxdf->GetInputValue("fresnel"); - + WriteInput(io, printer,"fresnel", fresnel); - + if (type == SingleBxdf::BxdfType::kMicrofacetGGX || type == SingleBxdf::BxdfType::kMicrofacetBeckmann || type == SingleBxdf::BxdfType::kMicrofacetBlinn || @@ -143,21 +228,21 @@ namespace Baikal else { MultiBxdf const* blend = dynamic_cast(material); - + printer.PushAttribute("type", "blend"); - + MultiBxdf::Type type = blend->GetType(); - + printer.PushAttribute("blend_type", (int)type); - + Material::InputValue base = material->GetInputValue("base_material"); - + WriteInput(io, printer, "base_material", base); - + Material::InputValue top = material->GetInputValue("top_material"); - + WriteInput(io, printer, "top_material", top); - + if (type == MultiBxdf::Type::kFresnelBlend) { Material::InputValue ior = material->GetInputValue("ior"); @@ -171,69 +256,23 @@ namespace Baikal WriteInput(io, printer, "weight", weight); } } - + printer.CloseElement(); } - - void MaterialIoXML::SaveMaterials(std::string const& filename, Scene1 const& scene) + + void MaterialIoXML::SaveMaterials(std::string const& filename, Iterator& mat_iter) { XMLDocument doc; XMLPrinter printer; - - m_textures.clear(); - + + m_tex2name.clear(); + std::unique_ptr image_io(ImageIo::CreateImageIo()); - - std::unique_ptr shape_iter(scene.CreateShapeIterator()); - - Collector mat_collector; - // Collect materials from shapes first - mat_collector.Collect(shape_iter.get(), - // This function adds all materials to resulting map - // recursively via Material dependency API - [](void const* item) -> std::set - { - // Resulting material set - std::set mats; - // Material stack - std::stack material_stack; - - // Get material from current shape - auto shape = reinterpret_cast(item); - auto material = shape->GetMaterial(); - - // Push to stack as an initializer - material_stack.push(material); - - // Drain the stack - while (!material_stack.empty()) - { - // Get current material - Material const* m = material_stack.top(); - material_stack.pop(); - - // Emplace into the set - mats.emplace(m); - - // Create dependency iterator - std::unique_ptr mat_iter(m->CreateMaterialIterator()); - - // Push all dependencies into the stack - for (;mat_iter->IsValid(); mat_iter->Next()) - { - material_stack.push(mat_iter->ItemAs()); - } - } - - // Return resulting set - return mats; - }); - - std::unique_ptr mat_iter(mat_collector.CreateIterator()); - for (;mat_iter->IsValid(); mat_iter->Next()) + + for (mat_iter.Reset();mat_iter.IsValid(); mat_iter.Next()) { - auto material = mat_iter->ItemAs(); - + auto material = mat_iter.ItemAs(); + if (material) { WriteMaterial(*image_io, printer, material); @@ -241,8 +280,242 @@ namespace Baikal } doc.Parse(printer.CStr()); - + doc.SaveFile(filename.c_str()); } + void MaterialIoXML::LoadInput(ImageIo& io, Material* material, XMLElement& element) + { + std::string type(element.Attribute("type")); + std::string name(element.Attribute("name")); + + if (type == "float4") + { + std::istringstream iss(element.Attribute("value")); + + RadeonRays::float4 value; + + iss >> value.x >> value.y >> value.z >> value.w; + + material->SetInputValue(name, value); + + } + else if (type == "texture") + { + std::string filename(element.Attribute("value")); + + auto iter = m_name2tex.find(filename); + + if (iter != m_name2tex.cend()) + { + material->SetInputValue(name, iter->second); + } + else + { + auto texture = io.LoadImage(filename); + material->SetInputValue(name, texture); + m_name2tex[name] = texture; + } + } + else if (type == "material") + { + auto id = static_cast(std::atoi(element.Attribute("value"))); + + auto iter = m_id2mat.find(id); + + if (iter != m_id2mat.cend()) + { + material->SetInputValue(name, iter->second); + } + else + { + m_resolve_requests.emplace(ResolveRequest{ material, name, id }); + } + } + else + { + throw std::runtime_error("Unsupported input type"); + } + } + + Material* MaterialIoXML::LoadMaterial(ImageIo& io, XMLElement& element) + { + std::string name(element.Attribute("name")); + std::string type(element.Attribute("type")); + std::string twosided(element.Attribute("twosided")); + auto id = static_cast(std::atoi(element.Attribute("id"))); + + Material* material = nullptr; + + if (type == "simple") + { + auto bxdf = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + + auto bxdf_type = StringToBxdf(element.Attribute("bxdf")); + + bxdf->SetBxdfType(bxdf_type); + + material = bxdf; + } + else if (type == "blend") + { + auto blend = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); + + auto blend_type = static_cast(std::atoi(element.Attribute("blend_type"))); + + blend->SetType(blend_type); + + material = blend; + } + else + { + throw std::runtime_error("Unsupported material type"); + } + + material->SetName(name); + + material->SetTwoSided(twosided == "true" ? true : false); + + for (auto input = element.FirstChildElement(); input; input = input->NextSiblingElement()) + { + LoadInput(io, material, *input); + } + + m_id2mat[id] = material; + + return material; + } + + Iterator* MaterialIoXML::LoadMaterials(std::string const& filename) + { + m_id2mat.clear(); + m_name2tex.clear(); + m_resolve_requests.clear(); + + XMLDocument doc; + doc.LoadFile(filename.c_str()); + + std::unique_ptr image_io(ImageIo::CreateImageIo()); + + std::set materials; + for (auto element = doc.FirstChildElement(); element; element = element->NextSiblingElement()) + { + Material* material = LoadMaterial(*image_io, *element); + materials.insert(material); + } + + // Fix up non-resolved stuff + for (auto& i : m_resolve_requests) + { + i.material->SetInputValue(i.input, m_id2mat[i.id]); + } + + return new ContainerIterator>(std::move(materials)); + } + + void MaterialIo::SaveMaterialsFromScene(std::string const& filename, Scene1 const& scene) + { + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + + Collector mat_collector; + // Collect materials from shapes first + mat_collector.Collect(shape_iter.get(), + // This function adds all materials to resulting map + // recursively via Material dependency API + [](void const* item) -> std::set + { + // Resulting material set + std::set mats; + // Material stack + std::stack material_stack; + + // Get material from current shape + auto shape = reinterpret_cast(item); + auto material = shape->GetMaterial(); + + // Push to stack as an initializer + material_stack.push(material); + + // Drain the stack + while (!material_stack.empty()) + { + // Get current material + Material const* m = material_stack.top(); + material_stack.pop(); + + // Emplace into the set + mats.emplace(m); + + // Create dependency iterator + std::unique_ptr mat_iter(m->CreateMaterialIterator()); + + // Push all dependencies into the stack + for (; mat_iter->IsValid(); mat_iter->Next()) + { + material_stack.push(mat_iter->ItemAs()); + } + } + + // Return resulting set + return mats; + }); + + std::unique_ptr mat_iter(mat_collector.CreateIterator()); + + SaveMaterials(filename, *mat_iter); + } + + void MaterialIo::ReplaceSceneMaterials(Scene1& scene, Iterator& iterator, MaterialMap const& mapping) + { + std::map name2mat; + + for (iterator.Reset(); iterator.IsValid(); iterator.Next()) + { + auto material = iterator.ItemAs(); + auto name = material->GetName(); + name2mat[name] = material; + } + + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + + for (; shape_iter->IsValid(); shape_iter->Next()) + { + // TODO: remove this hack + auto shape = const_cast(shape_iter->ItemAs()); + auto material = shape->GetMaterial(); + + if (!material) + continue; + + auto name = material->GetName(); + auto citer = mapping.find(name); + + if (citer != mapping.cend()) + { + auto mat_iter = name2mat.find(citer->second); + + if (mat_iter != name2mat.cend()) + { + shape->SetMaterial(mat_iter->second); + } + } + } + } + + MaterialIo::MaterialMap MaterialIo::LoadMaterialMapping(std::string const& filename) + { + MaterialMap map; + + XMLDocument doc; + doc.LoadFile(filename.c_str()); + + for (auto element = doc.FirstChildElement(); element; element = element->NextSiblingElement()) + { + std::string from(element->Attribute("from")); + std::string to(element->Attribute("to")); + map.emplace(from, to); + } + + return map; + } } diff --git a/App/Scene/IO/material_io.h b/App/Scene/IO/material_io.h index 5672bb9b..a13ff8de 100644 --- a/App/Scene/IO/material_io.h +++ b/App/Scene/IO/material_io.h @@ -29,14 +29,16 @@ #pragma once #include +#include namespace Baikal { class Scene1; - + class Iterator; + /** \brief Interface for material loading and writing - + MaterialIO is responsible for material loading from disk. */ class MaterialIo @@ -44,20 +46,34 @@ namespace Baikal public: // Create XML based material IO static MaterialIo* CreateMaterialIoXML(); - + + using MaterialMap = std::map; + // Constructor MaterialIo() = default; // Destructor virtual ~MaterialIo() = 0; - + // Save materials from scene into a file - virtual void SaveMaterials(std::string const& filename, Scene1 const& scene) = 0; - + virtual void SaveMaterials(std::string const& filename, Iterator& iterator) = 0; + + // Load materials from disk + virtual Iterator* LoadMaterials(std::string const& filename) = 0; + + // Helper method: save all materials in the scene + void SaveMaterialsFromScene(std::string const& filename, Scene1 const& scene); + + // Helper methos: Replace scene materials using name mapping + void ReplaceSceneMaterials(Scene1& scene, Iterator& iterator, MaterialMap const& mapping); + + // Load material mapping from disk + MaterialMap LoadMaterialMapping(std::string const& filename); + // Disallow copying MaterialIo(MaterialIo const&) = delete; MaterialIo& operator = (MaterialIo const&) = delete; }; - + inline MaterialIo::~MaterialIo() { } diff --git a/App/Scene/IO/scene_io.cpp b/App/Scene/IO/scene_io.cpp index 8ef46cbd..db13fbd3 100644 --- a/App/Scene/IO/scene_io.cpp +++ b/App/Scene/IO/scene_io.cpp @@ -55,7 +55,7 @@ namespace Baikal } else { - auto s = RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]); + auto s = RadeonRays::float3(1.f, 1.f, 1.f);// RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]); if (s.sqnorm() > 0 || !mat.specular_texname.empty()) { @@ -143,7 +143,7 @@ namespace Baikal material->SetName(mat.name); // Disable normal flip - material->SetTwoSided(false); + material->SetTwoSided(true); scene.AttachAutoreleaseObject(material); return material; @@ -240,15 +240,14 @@ namespace Baikal } // TODO: temporary code, add IBL - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/space.jpg"); scene->AttachAutoreleaseObject(ibl_texture); ImageBasedLight* ibl = new ImageBasedLight(); ibl->SetTexture(ibl_texture); ibl->SetMultiplier(1.f); scene->AttachAutoreleaseObject(ibl); - - + // TODO: temporary code to add directional light DirectionalLight* light = new DirectionalLight(); light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); diff --git a/App/Scene/iterator.h b/App/Scene/iterator.h index 23d0e199..18f90cb9 100644 --- a/App/Scene/iterator.h +++ b/App/Scene/iterator.h @@ -50,7 +50,7 @@ namespace Baikal // Retrieve underlying object virtual void const* Item() const = 0; - + // Sets the iterator into its initial state (beginning of the sequence) virtual void Reset() = 0; diff --git a/App/main.cpp b/App/main.cpp index 392d5693..84a95c2f 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -349,8 +349,10 @@ void InitData() std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoObj()); g_scene.reset(scene_io->LoadScene(filename, basepath)); - //std::unique_ptr material_io(Baikal::MaterialIo::CreateMaterialIoXML()); - //material_io->SaveMaterials("materials.xml", *g_scene); + std::unique_ptr material_io(Baikal::MaterialIo::CreateMaterialIoXML()); + auto mats = material_io->LoadMaterials("materials.xml"); + auto mapping = material_io->LoadMaterialMapping("mapping.xml"); + material_io->ReplaceSceneMaterials(*g_scene, *mats, mapping); } g_camera.reset(new Baikal::PerspectiveCamera( @@ -672,16 +674,16 @@ void Update() if (std::abs(camroty) > 0.001f) { - //g_scene->camera_->Tilt(camroty); - g_camera->ArcballRotateVertically(float3(0, 0, 0), camroty); + g_camera->Tilt(camroty); + //g_camera->ArcballRotateVertically(float3(0, 0, 0), camroty); update = true; } if (std::abs(camrotx) > 0.001f) { - //g_scene->camera_->Rotate(camrotx); - g_camera->ArcballRotateHorizontally(float3(0, 0, 0), camrotx); + g_camera->Rotate(camrotx); + //g_camera->ArcballRotateHorizontally(float3(0, 0, 0), camrotx); update = true; } From fd7843e7fa036a920eeef3a66119dd5ac7c492e9 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Mon, 23 Jan 2017 20:43:03 +0100 Subject: [PATCH 13/24] Fix XML mapping --- App/Scene/IO/material_io.cpp | 47 ++++++++++++++++++++++++++++++++++-- App/Scene/IO/material_io.h | 3 +++ App/Scene/IO/scene_io.cpp | 13 +++++----- App/Scene/scene_tracker.cpp | 24 +++++++++--------- App/Scene/shape.cpp | 4 +-- App/main.cpp | 31 ++++++++++++++++++++---- 6 files changed, 95 insertions(+), 27 deletions(-) diff --git a/App/Scene/IO/material_io.cpp b/App/Scene/IO/material_io.cpp index 98929cb5..e38a7066 100644 --- a/App/Scene/IO/material_io.cpp +++ b/App/Scene/IO/material_io.cpp @@ -58,6 +58,7 @@ namespace Baikal }; std::set m_resolve_requests; + std::string m_base_path; }; MaterialIo* MaterialIo::CreateMaterialIoXML() @@ -159,7 +160,7 @@ namespace Baikal std::ostringstream oss; oss << (std::uint64_t)value.tex_value << ".jpg"; - io.SaveImage(oss.str(), value.tex_value); + io.SaveImage(m_base_path + oss.str(), value.tex_value); m_tex2name[value.tex_value] = oss.str(); @@ -262,6 +263,13 @@ namespace Baikal void MaterialIoXML::SaveMaterials(std::string const& filename, Iterator& mat_iter) { + auto slash = filename.find_last_of('/'); + if (slash == std::string::npos) slash = filename.find_last_of('\\'); + if (slash != std::string::npos) + m_base_path.assign(filename.cbegin(), filename.cbegin() + slash + 1); + else + m_base_path.clear(); + XMLDocument doc; XMLPrinter printer; @@ -312,7 +320,7 @@ namespace Baikal } else { - auto texture = io.LoadImage(filename); + auto texture = io.LoadImage(m_base_path + filename); material->SetInputValue(name, texture); m_name2tex[name] = texture; } @@ -392,6 +400,13 @@ namespace Baikal m_name2tex.clear(); m_resolve_requests.clear(); + auto slash = filename.find_last_of('/'); + if (slash == std::string::npos) slash = filename.find_last_of('\\'); + if (slash != std::string::npos) + m_base_path.assign(filename.cbegin(), filename.cbegin() + slash + 1); + else + m_base_path.clear(); + XMLDocument doc; doc.LoadFile(filename.c_str()); @@ -518,4 +533,32 @@ namespace Baikal return map; } + + void MaterialIo::SaveIdentityMapping(std::string const& filename, Scene1 const& scene) + { + XMLDocument doc; + XMLPrinter printer; + + std::unique_ptr shape_iter(scene.CreateShapeIterator()); + std::set serialized_mats; + + for (; shape_iter->IsValid(); shape_iter->Next()) + { + auto material = shape_iter->ItemAs()->GetMaterial(); + + if (serialized_mats.find(material) == serialized_mats.cend()) + { + auto name = material->GetName(); + printer.OpenElement("Mapping"); + printer.PushAttribute("from", name.c_str()); + printer.PushAttribute("to", name.c_str()); + printer.CloseElement(); + serialized_mats.emplace(material); + } + } + + doc.Parse(printer.CStr()); + + doc.SaveFile(filename.c_str()); + } } diff --git a/App/Scene/IO/material_io.h b/App/Scene/IO/material_io.h index a13ff8de..ad45653c 100644 --- a/App/Scene/IO/material_io.h +++ b/App/Scene/IO/material_io.h @@ -63,6 +63,9 @@ namespace Baikal // Helper method: save all materials in the scene void SaveMaterialsFromScene(std::string const& filename, Scene1 const& scene); + // Create identity mapping + void SaveIdentityMapping(std::string const& filename, Scene1 const& scene); + // Helper methos: Replace scene materials using name mapping void ReplaceSceneMaterials(Scene1& scene, Iterator& iterator, MaterialMap const& mapping); diff --git a/App/Scene/IO/scene_io.cpp b/App/Scene/IO/scene_io.cpp index db13fbd3..a6343b8d 100644 --- a/App/Scene/IO/scene_io.cpp +++ b/App/Scene/IO/scene_io.cpp @@ -55,7 +55,7 @@ namespace Baikal } else { - auto s = RadeonRays::float3(1.f, 1.f, 1.f);// RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]); + auto s = RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]); if (s.sqnorm() > 0 || !mat.specular_texname.empty()) { @@ -208,6 +208,7 @@ namespace Baikal else { std::vector zero(num_vertices); + std::fill(zero.begin(), zero.end(), RadeonRays::float2(0, 0)); mesh->SetUVs(&zero[0], num_vertices); } @@ -240,7 +241,7 @@ namespace Baikal } // TODO: temporary code, add IBL - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/space.jpg"); + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); scene->AttachAutoreleaseObject(ibl_texture); ImageBasedLight* ibl = new ImageBasedLight(); @@ -249,14 +250,14 @@ namespace Baikal scene->AttachAutoreleaseObject(ibl); // TODO: temporary code to add directional light - DirectionalLight* light = new DirectionalLight(); + /*DirectionalLight* light = new DirectionalLight(); light->SetDirection(RadeonRays::float3(-0.3f, -1.f, -0.4f)); light->SetEmittedRadiance(2.f * RadeonRays::float3(1.f, 1.f, 1.f)); scene->AttachAutoreleaseObject(light); - - scene->AttachLight(light); + + scene->AttachLight(light);*/ scene->AttachLight(ibl); - + return scene; } } diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index ed669d23..a78f1c4d 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -407,14 +407,14 @@ namespace Baikal out.indices = m_context.CreateBuffer(num_indices, CL_MEM_READ_ONLY); out.shapes = m_context.CreateBuffer(scene.GetNumShapes(), CL_MEM_READ_ONLY); out.materialids = m_context.CreateBuffer(num_indices / 3, CL_MEM_READ_ONLY); - + float3* vertices = nullptr; float3* normals = nullptr; float2* uvs = nullptr; int* indices = nullptr; int* matids = nullptr; ClwScene::Shape* shapes = nullptr; - + // Map arrays and prepare to write data m_context.MapBuffer(0, out.vertices, CL_MAP_WRITE, &vertices); m_context.MapBuffer(0, out.normals, CL_MAP_WRITE, &normals); @@ -441,7 +441,7 @@ namespace Baikal auto mesh_index_array = mesh->GetIndices(); auto mesh_num_indices = mesh->GetNumIndices(); - + // Prepare shape descriptor ClwScene::Shape shape; shape.numprims = static_cast(mesh_num_indices / 3); @@ -454,34 +454,34 @@ namespace Baikal shape.m3 = float4(0.0f, 0.f, 0.f, 1.f); shape.linearvelocity = float3(0.0f, 0.f, 0.f); shape.angularvelocity = float3(0.f, 0.f, 0.f, 1.f); - + std::copy(mesh_vertex_array, mesh_vertex_array + mesh_num_vertices, vertices + num_vertices_written); num_vertices_written += mesh_num_vertices; - + std::copy(mesh_normal_array, mesh_normal_array + mesh_num_normals, normals + num_normals_written); num_normals_written += mesh_num_normals; - + std::copy(mesh_uv_array, mesh_uv_array + mesh_num_uvs, uvs + num_uvs_written); num_uvs_written += mesh_num_uvs; - + std::copy(mesh_index_array, mesh_index_array + mesh_num_indices, indices + num_indices_written); num_indices_written += mesh_num_indices; - + shapes[num_shapes_written] = shape; ++num_shapes_written; - + auto matidx = mat_collector.GetItemIndex(mesh->GetMaterial()); std::fill(matids + num_matids_written, matids + num_matids_written + mesh_num_indices / 3, matidx); - + num_matids_written += mesh_num_indices / 3; } - + m_context.UnmapBuffer(0, out.vertices, vertices); m_context.UnmapBuffer(0, out.normals, normals); m_context.UnmapBuffer(0, out.uvs, uvs); m_context.UnmapBuffer(0, out.indices, indices); m_context.UnmapBuffer(0, out.materialids, matids); - m_context.UnmapBuffer(0, out.shapes, shapes); + m_context.UnmapBuffer(0, out.shapes, shapes).Wait(); } void SceneTracker::UpdateMaterials(Scene1 const& scene, Collector& mat_collector, Collector& tex_collector, ClwScene& out) const diff --git a/App/Scene/shape.cpp b/App/Scene/shape.cpp index 01ca0e8c..6ed38fdb 100644 --- a/App/Scene/shape.cpp +++ b/App/Scene/shape.cpp @@ -91,9 +91,9 @@ namespace Baikal m_normals.reset(new RadeonRays::float3[num_normals]); std::copy(normals, normals + num_normals, m_normals.get()); - + m_num_normals = num_normals; - + SetDirty(true); } diff --git a/App/main.cpp b/App/main.cpp index 84a95c2f..fd30ec34 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -50,6 +50,7 @@ THE SOFTWARE. #include #include #include +#include #define _USE_MATH_DEFINES #include @@ -104,7 +105,7 @@ int g_num_samples = -1; int g_samplecount = 0; float g_ao_radius = 1.f; float g_envmapmul = 1.f; -float g_cspeed = 100.25f; +float g_cspeed = 10.25f; float3 g_camera_pos = float3(0.f, 1.f, 4.f); float3 g_camera_at = float3(0.f, 1.f, 0.f); @@ -346,13 +347,33 @@ void InitData() std::string filename = basepath + g_modelname; { + // Load OBJ scene std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoObj()); g_scene.reset(scene_io->LoadScene(filename, basepath)); - + + // Enable this to generate new materal mapping for a model +#if 0 std::unique_ptr material_io(Baikal::MaterialIo::CreateMaterialIoXML()); - auto mats = material_io->LoadMaterials("materials.xml"); - auto mapping = material_io->LoadMaterialMapping("mapping.xml"); - material_io->ReplaceSceneMaterials(*g_scene, *mats, mapping); + material_io->SaveMaterialsFromScene(basepath + "materials.xml", *g_scene); + material_io->SaveIdentityMapping(basepath + "mapping.xml", *g_scene); +#endif + + // Check it we have material remapping + std::ifstream in_materials(basepath + "materials.xml"); + std::ifstream in_mapping(basepath + "mapping.xml"); + + if (in_materials && in_mapping) + { + in_materials.close(); + in_mapping.close(); + + std::unique_ptr material_io(Baikal::MaterialIo::CreateMaterialIoXML()); + + auto mats = material_io->LoadMaterials(basepath + "materials.xml"); + auto mapping = material_io->LoadMaterialMapping(basepath + "mapping.xml"); + + material_io->ReplaceSceneMaterials(*g_scene, *mats, mapping); + } } g_camera.reset(new Baikal::PerspectiveCamera( From 34c0d6f491f2030c8cecde1d41567b5599820d42 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Tue, 24 Jan 2017 11:12:28 +0100 Subject: [PATCH 14/24] FIx some shading issues --- App/CL/bxdf.cl | 40 ++++++++++++++-------------------------- App/CL/integrator_pt.cl | 23 +++++++++++++---------- App/CL/scene.cl | 19 ++++++++++--------- 3 files changed, 37 insertions(+), 45 deletions(-) diff --git a/App/CL/bxdf.cl b/App/CL/bxdf.cl index eea8a13c..6bd6adb6 100755 --- a/App/CL/bxdf.cl +++ b/App/CL/bxdf.cl @@ -70,7 +70,7 @@ float FresnelDielectric(float etai, float etat, float ndotwi, float ndotwt) // Distribution fucntion float MicrofacetDistribution_Beckmann_D(float roughness, float3 m, float3 n) { - float ndotm = dot(m, n); + float ndotm = fabs(dot(m, n)); if (ndotm <= 0.f) return 0.f; @@ -99,7 +99,7 @@ float MicrofacetDistribution_Beckmann_GetPdf( { // We need to convert pdf(wh)->pdf(wo) float3 m = normalize(wi + wo); - float wodotm = dot(wo, m); + float wodotm = fabs(dot(wo, m)); if (wodotm <= 0.f) return 0.f; @@ -146,7 +146,7 @@ void MicrofacetDistribution_Beckmann_Sample(// Roughness float3 wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); // Reflect wi around wh - *wo = -wi + 2.f*dot(wi, wh) * wh; + *wo = -wi + 2.f*fabs(dot(wi, wh)) * wh; // Calc pdf *pdf = MicrofacetDistribution_Beckmann_GetPdf(roughness, dg, wi, *wo, TEXTURE_ARGS); @@ -223,8 +223,8 @@ float3 MicrofacetBeckmann_Evaluate( // Incident and reflected zenith angles - float costhetao = dot(dg->n, wo); - float costhetai = dot(dg->n, wi); + float costhetao = fabs(dot(dg->n, wo)); + float costhetai = fabs(dot(dg->n, wi)); // Calc halfway vector float3 wh = normalize(wi + wo); @@ -268,13 +268,7 @@ float3 MicrofacetBeckmann_Sample( float* pdf ) { - float ndotwi = dot(dg->n, wi); - - if (ndotwi <= 0.f) - { - *pdf = 0.f; - return 0.f; - } + float ndotwi = fabs(dot(dg->n, wi)); const float roughness = Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)); MicrofacetDistribution_Beckmann_Sample(roughness, dg, wi, TEXTURE_ARGS, sample, wo, pdf); @@ -427,8 +421,8 @@ float3 MicrofacetGGX_Evaluate( const float roughness = Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)); // Incident and reflected zenith angles - float costhetao = dot(dg->n, wo); - float costhetai = dot(dg->n, wi); + float costhetao = fabs(dot(dg->n, wo)); + float costhetai = fabs(dot(dg->n, wi)); // Calc halfway vector float3 wh = normalize(wi + wo); @@ -510,9 +504,9 @@ float MicrofacetDistribution_Blinn_GetPdf(// Shininess // We need to convert pdf(wh)->pdf(wo) float3 wh = normalize(wi + wo); // costheta - float ndotwh = dot(dg->n, wh); + float ndotwh = fabs(dot(dg->n, wh)); // See Humphreys and Pharr for derivation - float denom = (2.f * PI * 4.f * dot(wo, wh)); + float denom = (2.f * PI * 4.f * fabs(dot(wo, wh))); return denom > DENOM_EPS ? ((shininess + 1.f) * native_powr(ndotwh, shininess)) / denom : 0.f; } @@ -550,7 +544,7 @@ void MicrofacetDistribution_Blinn_Sample(// Shininess param float3 wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); // Reflect wi around wh - *wo = -wi + 2.f*dot(wi, wh) * wh; + *wo = -wi + 2.f*fabs(dot(wi, wh)) * wh; // Calc pdf *pdf = MicrofacetDistribution_Blinn_GetPdf(shininess, dg, wi, *wo, TEXTURE_ARGS); @@ -585,8 +579,8 @@ float3 MicrofacetBlinn_Evaluate( const float shininess = dg->mat.ns; // Incident and reflected zenith angles - float costhetao = dot(dg->n, wo); - float costhetai = dot(dg->n, wi); + float costhetao = fabs(dot(dg->n, wo)); + float costhetai = fabs(dot(dg->n, wi)); // Calc halfway vector float3 wh = normalize(wi + wo); @@ -631,12 +625,6 @@ float3 MicrofacetBlinn_Sample( float* pdf ) { - if (dot(dg->n, wi) <= 0.f) - { - *pdf = 0.f; - return 0.f; - } - const float shininess = dg->mat.ns; MicrofacetDistribution_Blinn_Sample(shininess, dg, wi, TEXTURE_ARGS, sample, wo, pdf); return MicrofacetBlinn_Evaluate(dg, wi, *wo, TEXTURE_ARGS); @@ -828,7 +816,7 @@ float3 IdealReflect_Sample( const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); const float eta = dg->mat.ni; - float ndotwi = dot(dg->n, wi); + float ndotwi = fabs(dot(dg->n, wi)); // Mirror reflect wi *wo = normalize(2.f * ndotwi * dg->n - wi); diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index 95e343ee..ee4e1b38 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -366,9 +366,10 @@ __kernel void ShadeSurface( FillDifferentialGeometry(&scene, &isect, &diffgeo); // Check if we are hitting from the inside - float ndotwi = dot(diffgeo.n, wi); + + float backfacing = dot(diffgeo.ng, wi) < 0.f; int twosided = diffgeo.mat.twosided; - if (twosided && ndotwi < 0.f) + if (twosided && backfacing) { // Reverse normal and tangents in this case // but not for BTDFs, since BTDFs rely @@ -379,6 +380,8 @@ __kernel void ShadeSurface( diffgeo.dpdv = -diffgeo.dpdv; } + float ndotwi = dot(diffgeo.n, wi); + // Select BxDF Material_Select( &scene, wi, TEXTURE_ARGS, @@ -390,12 +393,10 @@ __kernel void ShadeSurface( &diffgeo ); - ndotwi = dot(diffgeo.n, wi); - // Terminate if emissive if (Bxdf_IsEmissive(&diffgeo)) { - if (ndotwi > 0.f) + if (!backfacing) { float weight = 1.f; @@ -426,17 +427,19 @@ __kernel void ShadeSurface( float s = Bxdf_IsBtdf(&diffgeo) ? (-sign(ndotwi)) : 1.f; - if (!twosided && ndotwi < 0.f && !Bxdf_IsBtdf(&diffgeo)) + if (!twosided && backfacing && !Bxdf_IsBtdf(&diffgeo)) { - // Reverse normal and tangents in this case - // but not for BTDFs, since BTDFs rely - // on normal direction in order to arrange - // indices of refraction + //Reverse normal and tangents in this case + //but not for BTDFs, since BTDFs rely + //on normal direction in order to arrange + //indices of refraction diffgeo.n = -diffgeo.n; diffgeo.dpdu = -diffgeo.dpdu; diffgeo.dpdv = -diffgeo.dpdv; } + + // TODO: this is test code, need to // maintain proper volume stack here //if (Bxdf_IsBtdf(&diffgeo)) diff --git a/App/CL/scene.cl b/App/CL/scene.cl index 5041b563..d8bf07b2 100755 --- a/App/CL/scene.cl +++ b/App/CL/scene.cl @@ -96,6 +96,11 @@ void FillDifferentialGeometry(// Scene diffgeo->n = normalize(transform_vector((1.f - uv.x - uv.y) * n0 + uv.x * n1 + uv.y * n2, shape.m0, shape.m1, shape.m2, shape.m3)); diffgeo->p = transform_point((1.f - uv.x - uv.y) * v0 + uv.x * v1 + uv.y * v2, shape.m0, shape.m1, shape.m2, shape.m3); diffgeo->uv = (1.f - uv.x - uv.y) * uv0 + uv.x * uv1 + uv.y * uv2; + + diffgeo->ng = normalize(cross(v1 - v0, v2 - v0)); + + if (dot(diffgeo->ng, diffgeo->n) < 0.f) + diffgeo->ng = -diffgeo->ng; // Get material at shading point int matidx = scene->materialids[shape.startidx / 3 + primid]; @@ -113,7 +118,7 @@ void FillDifferentialGeometry(// Scene float det = du1 * dv2 - dv1 * du2; - if (0 && det != 0.f) + if (det != 0.f) { float invdet = 1.f / det; diffgeo->dpdu = normalize( (dv2 * dp1 - dv1 * dp2) * invdet ); @@ -121,18 +126,14 @@ void FillDifferentialGeometry(// Scene } else { - diffgeo->dpdu = normalize(GetOrthoVector(diffgeo->n)); - diffgeo->dpdv = normalize(cross(diffgeo->n, diffgeo->dpdu)); + diffgeo->dpdu = normalize(GetOrthoVector(diffgeo->ng)); + diffgeo->dpdv = normalize(cross(diffgeo->ng, diffgeo->dpdu)); } - - diffgeo->ng = normalize(cross(diffgeo->dpdv, diffgeo->dpdu)); - //if (dot(diffgeo->ng, diffgeo->n) < 0.f) - //diffgeo->ng = -diffgeo->ng; // Fix all to be orthogonal - //diffgeo->dpdv = normalize(cross(diffgeo->ng, diffgeo->dpdu)); - //diffgeo->dpdu = normalize(cross(diffgeo->dpdv, diffgeo->ng)); + diffgeo->dpdv = normalize(cross(diffgeo->n, diffgeo->dpdu)); + diffgeo->dpdu = normalize(cross(diffgeo->dpdv, diffgeo->n)); float3 p0 = transform_point(v0, shape.m0, shape.m1, shape.m2, shape.m3); float3 p1 = transform_point(v1, shape.m0, shape.m1, shape.m2, shape.m3); From 5230ca14fce8d8b321c0a955d26dc8fa1f059513 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Wed, 25 Jan 2017 16:33:46 +0100 Subject: [PATCH 15/24] Clean up shading --- App/CL/bxdf.cl | 361 +++++++++++------------------------ App/CL/integrator_pt.cl | 10 +- App/CL/normalmap.cl | 20 +- App/CL/payload.cl | 21 +- App/CL/scene.cl | 40 ++-- App/CL/utils.cl | 99 ++++++++-- App/Scene/IO/material_io.cpp | 4 - App/Scene/IO/scene_io.cpp | 2 +- App/Scene/material.cpp | 5 +- App/Scene/material.h | 1 - App/Scene/scene_tracker.cpp | 6 +- 11 files changed, 263 insertions(+), 306 deletions(-) diff --git a/App/CL/bxdf.cl b/App/CL/bxdf.cl index 6bd6adb6..ba7b0bec 100755 --- a/App/CL/bxdf.cl +++ b/App/CL/bxdf.cl @@ -68,9 +68,9 @@ float FresnelDielectric(float etai, float etat, float ndotwi, float ndotwt) */ // Distribution fucntion -float MicrofacetDistribution_Beckmann_D(float roughness, float3 m, float3 n) +float MicrofacetDistribution_Beckmann_D(float roughness, float3 m) { - float ndotm = fabs(dot(m, n)); + float ndotm = fabs(m.y); if (ndotm <= 0.f) return 0.f; @@ -105,7 +105,7 @@ float MicrofacetDistribution_Beckmann_GetPdf( return 0.f; // - float mpdf = MicrofacetDistribution_Beckmann_D(roughness, m, dg->n) * fabs(dot(dg->n, m)); + float mpdf = MicrofacetDistribution_Beckmann_D(roughness, m) * fabs(m.y); // See Humphreys and Pharr for derivation return mpdf / (4.f * wodotm); @@ -143,7 +143,7 @@ void MicrofacetDistribution_Beckmann_Sample(// Roughness float sinphi = native_sqrt(1.f - clamp(cosphi * cosphi, 0.f, 1.f)); // Calculate wh - float3 wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); + float3 wh = make_float3(sintheta * cosphi, costheta, sintheta * sinphi); // Reflect wi around wh *wo = -wi + 2.f*fabs(dot(wi, wh)) * wh; @@ -180,12 +180,12 @@ void MicrofacetDistribution_Beckmann_SampleNormal(// Roughness float sinphi = native_sqrt(1.f - clamp(cosphi * cosphi, 0.f, 1.f)); // Reflect wi around wh - *wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); + *wh = make_float3(sintheta * cosphi, costheta, sintheta * sinphi); } -float MicrofacetDistribution_Beckmann_G1(float roughness, float3 v, float3 m, float3 n) +float MicrofacetDistribution_Beckmann_G1(float roughness, float3 v, float3 m) { - float ndotv = fabs(dot(n, v)); + float ndotv = fabs(v.y); float mdotv = fabs(dot(m, v)); float sinnv = native_sqrt(1.f - clamp(ndotv * ndotv, 0.f, 1.f)); @@ -200,9 +200,9 @@ float MicrofacetDistribution_Beckmann_G1(float roughness, float3 v, float3 m, fl } // Shadowing function also depends on microfacet distribution -float MicrofacetDistribution_Beckmann_G(float roughness, float3 wi, float3 wo, float3 wh, float3 n) +float MicrofacetDistribution_Beckmann_G(float roughness, float3 wi, float3 wo, float3 wh) { - return MicrofacetDistribution_Beckmann_G1(roughness, wi, wh, n) * MicrofacetDistribution_Beckmann_G1(roughness, wo, wh, n); + return MicrofacetDistribution_Beckmann_G1(roughness, wi, wh) * MicrofacetDistribution_Beckmann_G1(roughness, wo, wh); } @@ -223,8 +223,8 @@ float3 MicrofacetBeckmann_Evaluate( // Incident and reflected zenith angles - float costhetao = fabs(dot(dg->n, wo)); - float costhetai = fabs(dot(dg->n, wi)); + float costhetao = fabs(wo.y); + float costhetai = fabs(wi.y); // Calc halfway vector float3 wh = normalize(wi + wo); @@ -234,7 +234,7 @@ float3 MicrofacetBeckmann_Evaluate( float denom = 4.f * costhetao * costhetai; // F(eta) * D * G * ks / (4 * cosa * cosi) - return denom > DENOM_EPS ? F * ks * MicrofacetDistribution_Beckmann_G(roughness, wi, wo, wh, dg->n) * MicrofacetDistribution_Beckmann_D(roughness, wh, dg->n) / denom : 0.f; + return denom > DENOM_EPS ? F * ks * MicrofacetDistribution_Beckmann_G(roughness, wi, wo, wh) * MicrofacetDistribution_Beckmann_D(roughness, wh) / denom : 0.f; } @@ -268,21 +268,20 @@ float3 MicrofacetBeckmann_Sample( float* pdf ) { - float ndotwi = fabs(dot(dg->n, wi)); + float ndotwi = fabs(wi.y); const float roughness = Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)); MicrofacetDistribution_Beckmann_Sample(roughness, dg, wi, TEXTURE_ARGS, sample, wo, pdf); return MicrofacetBeckmann_Evaluate(dg, wi, *wo, TEXTURE_ARGS); } - /* Microfacet GGX */ // Distribution fucntion -float MicrofacetDistribution_GGX_D(float roughness, float3 m, float3 n) +float MicrofacetDistribution_GGX_D(float roughness, float3 m) { - float ndotm = fabs(dot(m, n)); + float ndotm = fabs(m.y); float ndotm2 = ndotm * ndotm; float sinmn = native_sqrt(1.f - clamp(ndotm * ndotm, 0.f, 1.f)); float tanmn = ndotm > DENOM_EPS ? sinmn / ndotm : 0.f; @@ -307,7 +306,7 @@ float MicrofacetDistribution_GGX_GetPdf( TEXTURE_ARG_LIST ) { - float mpdf = MicrofacetDistribution_GGX_D(roughness, m, dg->n) * fabs(dot(dg->n, m)); + float mpdf = MicrofacetDistribution_GGX_D(roughness, m) * fabs(m.y); // See Humphreys and Pharr for derivation float denom = (4.f * fabs(dot(wo, m))); @@ -347,7 +346,7 @@ void MicrofacetDistribution_GGX_Sample( float sinphi = native_sqrt(1.f - clamp(cosphi * cosphi, 0.f, 1.f)); // Calculate wh - float3 wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); + float3 wh = make_float3(sintheta * cosphi, costheta, sintheta * sinphi); // Reflect wi around wh *wo = -wi + 2.f*fabs(dot(wi, wh)) * wh; @@ -385,13 +384,13 @@ void MicrofacetDistribution_GGX_SampleNormal( float sinphi = native_sqrt(1.f - clamp(cosphi * cosphi, 0.f, 1.f)); // Calculate wh - *wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); + *wh = make_float3(sintheta * cosphi, costheta, sintheta * sinphi); } // -float MicrofacetDistribution_GGX_G1(float roughness, float3 v, float3 m, float3 n) +float MicrofacetDistribution_GGX_G1(float roughness, float3 v, float3 m) { - float ndotv = fabs(dot(n, v)); + float ndotv = fabs(v.y); float mdotv = fabs(dot(m, v)); float sinnv = native_sqrt(1.f - clamp(ndotv * ndotv, 0.f, 1.f)); @@ -401,9 +400,9 @@ float MicrofacetDistribution_GGX_G1(float roughness, float3 v, float3 m, float3 } // Shadowing function also depends on microfacet distribution -float MicrofacetDistribution_GGX_G(float roughness, float3 wi, float3 wo, float3 wh, float3 n) +float MicrofacetDistribution_GGX_G(float roughness, float3 wi, float3 wo, float3 wh) { - return MicrofacetDistribution_GGX_G1(roughness, wi, wh, n) * MicrofacetDistribution_GGX_G1(roughness, wo, wh, n); + return MicrofacetDistribution_GGX_G1(roughness, wi, wh) * MicrofacetDistribution_GGX_G1(roughness, wo, wh); } float3 MicrofacetGGX_Evaluate( @@ -421,8 +420,8 @@ float3 MicrofacetGGX_Evaluate( const float roughness = Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)); // Incident and reflected zenith angles - float costhetao = fabs(dot(dg->n, wo)); - float costhetai = fabs(dot(dg->n, wi)); + float costhetao = fabs(wo.y); + float costhetai = fabs(wi.y); // Calc halfway vector float3 wh = normalize(wi + wo); @@ -432,7 +431,7 @@ float3 MicrofacetGGX_Evaluate( float denom = (4.f * costhetao * costhetai); // F(eta) * D * G * ks / (4 * cosa * cosi) - return denom > 0.f ? F * ks * MicrofacetDistribution_GGX_G(roughness, wi, wo, wh, dg->n) * MicrofacetDistribution_GGX_D(roughness, wh, dg->n) / denom : 0.f; + return denom > 0.f ? F * ks * MicrofacetDistribution_GGX_G(roughness, wi, wo, wh) * MicrofacetDistribution_GGX_D(roughness, wh) / denom : 0.f; } @@ -477,159 +476,6 @@ float3 MicrofacetGGX_Sample( } -/* - Microfacet Blinn - */ - - // Distribution fucntion -float MicrofacetDistribution_Blinn_D(float shininess, float3 w, float3 n) -{ - float ndotw = fabs(dot(n, w)); - return (1.f / (2 * PI)) * (shininess + 2) * native_powr(ndotw, shininess); -} - -// PDF of the given direction -float MicrofacetDistribution_Blinn_GetPdf(// Shininess - float shininess, - // Geometry - DifferentialGeometry const* dg, - // Incoming direction - float3 wi, - // Outgoing direction - float3 wo, - // Texture args - TEXTURE_ARG_LIST - ) -{ - // We need to convert pdf(wh)->pdf(wo) - float3 wh = normalize(wi + wo); - // costheta - float ndotwh = fabs(dot(dg->n, wh)); - // See Humphreys and Pharr for derivation - float denom = (2.f * PI * 4.f * fabs(dot(wo, wh))); - return denom > DENOM_EPS ? ((shininess + 1.f) * native_powr(ndotwh, shininess)) / denom : 0.f; -} - - -// Sample the distribution -void MicrofacetDistribution_Blinn_Sample(// Shininess param - float shininess, - // Geometry - DifferentialGeometry const* dg, - // Incoming direction - float3 wi, - // Texture args - TEXTURE_ARG_LIST, - // Sample - float2 sample, - // Outgoing direction - float3* wo, - // PDF at wo - float* pdf - ) -{ - // - float r1 = sample.x; - float r2 = sample.y; - - // Sample halfway vector first, then reflect wi around that - float costheta = native_powr(r1, 1.f / (shininess + 1.f)); - float sintheta = native_sqrt(1.f - costheta * costheta); - - // phi = 2*PI*ksi2 - float cosphi = native_cos(2.f*PI*r2); - float sinphi = native_sqrt(1.f - cosphi * cosphi); - - // Calculate wh - float3 wh = normalize(dg->dpdu * sintheta * cosphi + dg->dpdv * sintheta * sinphi + dg->n * costheta); - - // Reflect wi around wh - *wo = -wi + 2.f*fabs(dot(wi, wh)) * wh; - - // Calc pdf - *pdf = MicrofacetDistribution_Blinn_GetPdf(shininess, dg, wi, *wo, TEXTURE_ARGS); -} - - -// Shadowing function also depends on microfacet distribution -float MicrofacetDistribution_Blinn_G(float3 wi, float3 wo, float3 wh, float3 n) -{ - float ndotwh = fabs(dot(n, wh)); - float ndotwo = fabs(dot(n, wo)); - float ndotwi = fabs(dot(n, wi)); - float wodotwh = fabs(dot(wo, wh)); - - return min(1.f, min(2.f * ndotwh * ndotwo / wodotwh, 2.f * ndotwh * ndotwi / wodotwh)); -} - -/// Lambert BRDF evaluation -float3 MicrofacetBlinn_Evaluate( - // Geometry - DifferentialGeometry const* dg, - // Incoming direction - float3 wi, - // Outgoing direction - float3 wo, - // Texture args - TEXTURE_ARG_LIST - ) -{ - const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); - const float eta = dg->mat.ni; - const float shininess = dg->mat.ns; - - // Incident and reflected zenith angles - float costhetao = fabs(dot(dg->n, wo)); - float costhetai = fabs(dot(dg->n, wi)); - - // Calc halfway vector - float3 wh = normalize(wi + wo); - - float F = dg->mat.fresnel; - - float denom = (4.f * costhetao * costhetai); - - // F(eta) * D * G * ks / (4 * cosa * cosi) - return denom > DENOM_EPS ? F * ks * MicrofacetDistribution_Blinn_G(wi, wo, wh, dg->n) * MicrofacetDistribution_Blinn_D(shininess, wh, dg->n) / denom : 0.f; -} - -/// Lambert BRDF PDF -float MicrofacetBlinn_GetPdf( - // Geometry - DifferentialGeometry const* dg, - // Incoming direction - float3 wi, - // Outgoing direction - float3 wo, - // Texture args - TEXTURE_ARG_LIST - ) -{ - const float shininess = dg->mat.ns; - return MicrofacetDistribution_Blinn_GetPdf(shininess, dg, wi, wo, TEXTURE_ARGS); -} - -/// Lambert BRDF sampling -float3 MicrofacetBlinn_Sample( - // Geometry - DifferentialGeometry const* dg, - // Incoming direction - float3 wi, - // Texture args - TEXTURE_ARG_LIST, - // Sample - float2 sample, - // Outgoing direction - float3* wo, - // PDF at wo - float* pdf - ) -{ - const float shininess = dg->mat.ns; - MicrofacetDistribution_Blinn_Sample(shininess, dg, wi, TEXTURE_ARGS, sample, wo, pdf); - return MicrofacetBlinn_Evaluate(dg, wi, *wo, TEXTURE_ARGS); -} - /* Lambert BRDF */ @@ -664,7 +510,7 @@ float Lambert_GetPdf( TEXTURE_ARG_LIST ) { - return fabs(dot(dg->n, wo)) / PI; + return fabs(wo.y) / PI; } /// Lambert BRDF sampling @@ -685,11 +531,11 @@ float3 Lambert_Sample( { const float3 kd = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); - *wo = Sample_MapToHemisphere(sample, dg->n, 1.f); + *wo = Sample_MapToHemisphere(sample, make_float3(0.f, 1.f, 0.f) , 1.f); float F = dg->mat.fresnel; - *pdf = fabs(dot(dg->n, *wo)) / PI; + *pdf = fabs(wo->y) / PI; return F * kd / PI; } @@ -729,13 +575,13 @@ float3 Translucent_Sample( { const float3 kd = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); - float ndotwi = dot(dg->n, wi); + float ndotwi = wi.y; - float3 n = ndotwi > DENOM_EPS ? -dg->n : dg->n; + float3 n = ndotwi > DENOM_EPS ? make_float3(0.f, -1.f, 0.f) : make_float3(0.f, 1.f, 0.f); *wo = normalize(Sample_MapToHemisphere(sample, n, 1.f)); - *pdf = fabs(dot(n, *wo)) / PI; + *pdf = fabs(wo->y) / PI; return kd / PI; } @@ -754,8 +600,8 @@ float3 Translucent_Evaluate( { const float3 kd = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); - float ndotwi = dot(dg->n, wi); - float ndotwo = dot(dg->n, wo); + float ndotwi = wi.y; + float ndotwo = wo.y; if (ndotwi * ndotwo > 0.f) return 0.f; @@ -775,8 +621,8 @@ float Translucent_GetPdf( TEXTURE_ARG_LIST ) { - float ndotwi = dot(dg->n, wi); - float ndotwo = dot(dg->n, wo); + float ndotwi = wi.y; + float ndotwo = wo.y; if (ndotwi * ndotwo > 0) return 0.f; @@ -816,10 +662,8 @@ float3 IdealReflect_Sample( const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); const float eta = dg->mat.ni; - float ndotwi = fabs(dot(dg->n, wi)); - // Mirror reflect wi - *wo = normalize(2.f * ndotwi * dg->n - wi); + *wo = normalize(make_float3(-wi.x, wi.y, -wi.z)); // PDF is infinite at that point, but deltas are going to cancel out while evaluating // so set it to 1.f @@ -827,7 +671,7 @@ float3 IdealReflect_Sample( float F = dg->mat.fresnel; - float coswo = fabs(dot(dg->n, *wo)); + float coswo = fabs(wo->y); // Return reflectance value return coswo > DENOM_EPS ? (F * ks * (1.f / coswo)) : 0.f; @@ -884,10 +728,9 @@ float3 IdealRefract_Sample( float etai = 1.f; float etat = dg->mat.ni; - float cosi = dot(dg->n, wi); + float cosi = wi.y; bool entering = cosi > 0.f; - float3 n = dg->n; // Revert normal and eta if needed if (!entering) @@ -895,13 +738,10 @@ float3 IdealRefract_Sample( float tmp = etai; etai = etat; etat = tmp; - n = -dg->n; - cosi = -cosi; } float eta = etai / etat; float sini2 = 1.f - cosi * cosi; - float sint2 = eta * eta * sini2; if (sint2 >= 1.f) @@ -915,18 +755,14 @@ float3 IdealRefract_Sample( // Transmitted ray float F = dg->mat.fresnel; - *wo = normalize(-n * cost + normalize(n * cosi - wi) * native_sqrt(max(sint2, 0.f))); + *wo = normalize(make_float3(eta * -wi.x, entering ? -cost : cost, eta * -wi.z)); - // PDF is infinite at that point, but deltas are going to cancel out while evaluating - // so set it to 1.f *pdf = 1.f; - return cost > 0.0001f ? F * (((etai * etai) / (etat * etat)) * ks / cost) : 0.f; + return cost > DENOM_EPS ? (F * eta * eta * ks / cost) : 0.f; } - - float3 MicrofacetRefractionGGX_Evaluate( // Geometry DifferentialGeometry const* dg, @@ -941,8 +777,8 @@ float3 MicrofacetRefractionGGX_Evaluate( const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); const float roughness = max(Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)), ROUGHNESS_EPS); - float ndotwi = dot(dg->n, wi); - float ndotwo = dot(dg->n, wo); + float ndotwi = wi.y; + float ndotwo = wo.y; if (ndotwi * ndotwo >= 0.f) { @@ -973,7 +809,7 @@ float3 MicrofacetRefractionGGX_Evaluate( denom *= (fabs(ndotwi) * fabs(ndotwo)); return denom > DENOM_EPS ? (F * ks * (widotwh * wodotwh) * (etat)* (etat)* - MicrofacetDistribution_GGX_G(roughness, wi, wo, wh, dg->n) * MicrofacetDistribution_GGX_D(roughness, wh, dg->n) / denom) : 0.f; + MicrofacetDistribution_GGX_G(roughness, wi, wo, wh) * MicrofacetDistribution_GGX_D(roughness, wh) / denom) : 0.f; } @@ -990,8 +826,8 @@ float MicrofacetRefractionGGX_GetPdf( ) { const float roughness = max(Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)), ROUGHNESS_EPS); - float ndotwi = dot(dg->n, wi); - float ndotwo = dot(dg->n, wo); + float ndotwi = wi.y; + float ndotwo = wo.y; float etai = 1.f; float etat = dg->mat.ni; @@ -1016,7 +852,7 @@ float MicrofacetRefractionGGX_GetPdf( float wodotwh = fabs(dot(wo, wh)); - float whpdf = MicrofacetDistribution_GGX_D(roughness, wh, dg->n) * fabs(dot(wh, dg->n)); + float whpdf = MicrofacetDistribution_GGX_D(roughness, wh) * fabs(wh.y); float whwo = wodotwh * etat * etat; @@ -1043,7 +879,7 @@ float3 MicrofacetRefractionGGX_Sample( const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); const float roughness = max(Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)), ROUGHNESS_EPS); - float ndotwi = dot(dg->n, wi); + float ndotwi = wi.y; if (ndotwi == 0.f) { @@ -1101,8 +937,8 @@ float3 MicrofacetRefractionBeckmann_Evaluate( const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); const float roughness = max(Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)), ROUGHNESS_EPS); - float ndotwi = dot(dg->n, wi); - float ndotwo = dot(dg->n, wo); + float ndotwi = wi.y; + float ndotwo = wo.y; float etai = 1.f; float etat = dg->mat.ni; @@ -1128,7 +964,7 @@ float3 MicrofacetRefractionBeckmann_Evaluate( denom *= (fabs(ndotwi) * fabs(ndotwo)); return denom > DENOM_EPS ? (F * ks * (widotwh * wodotwh) * (etat)* (etat)* - MicrofacetDistribution_Beckmann_G(roughness, wi, wo, wh, dg->n) * MicrofacetDistribution_Beckmann_D(roughness, wh, dg->n) / denom) : 0.f; + MicrofacetDistribution_Beckmann_G(roughness, wi, wo, wh) * MicrofacetDistribution_Beckmann_D(roughness, wh) / denom) : 0.f; } @@ -1145,8 +981,8 @@ float MicrofacetRefractionBeckmann_GetPdf( ) { const float roughness = Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)); - float ndotwi = dot(dg->n, wi); - float ndotwo = dot(dg->n, wo); + float ndotwi = wi.y; + float ndotwo = wo.y; float etai = 1.f; float etat = dg->mat.ni; @@ -1166,7 +1002,7 @@ float MicrofacetRefractionBeckmann_GetPdf( float wodotwh = fabs(dot(wo, wh)); - float whpdf = MicrofacetDistribution_Beckmann_D(roughness, wh, dg->n) * fabs(dot(wh, dg->n)); + float whpdf = MicrofacetDistribution_Beckmann_D(roughness, wh) * fabs(wh.y); float whwo = wodotwh * etat * etat; @@ -1193,7 +1029,7 @@ float3 MicrofacetRefractionBeckmann_Sample( const float3 ks = Texture_GetValue3f(dg->mat.kx.xyz, dg->uv, TEXTURE_ARGS_IDX(dg->mat.kxmapidx)); const float roughness = Texture_GetValue1f(dg->mat.ns, dg->uv, TEXTURE_ARGS_IDX(dg->mat.nsmapidx)); - float ndotwi = dot(dg->n, wi); + float ndotwi = wi.y; float etai = 1.f; float etat = dg->mat.ni; @@ -1246,7 +1082,7 @@ float3 Passthrough_Sample( { *wo = -wi; - float coswo = fabs(dot(dg->n, *wo)); + float coswo = fabs(wo->y); // PDF is infinite at that point, but deltas are going to cancel out while evaluating // so set it to 1.f @@ -1270,27 +1106,29 @@ float3 Bxdf_Evaluate( TEXTURE_ARG_LIST ) { + // Transform vectors into tangent space + float3 wi_t = matrix_mul_vector3(dg->world_to_tangent, wi); + float3 wo_t = matrix_mul_vector3(dg->world_to_tangent, wo); + int mattype = dg->mat.type; switch (mattype) { case kLambert: - return Lambert_Evaluate(dg, wi, wo, TEXTURE_ARGS); - case kMicrofacetBlinn: - return MicrofacetBlinn_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return Lambert_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetGGX: - return MicrofacetGGX_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetGGX_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetBeckmann: - return MicrofacetBeckmann_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetBeckmann_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kIdealReflect: - return IdealReflect_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return IdealReflect_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kIdealRefract: - return IdealRefract_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return IdealRefract_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kTranslucent: - return Translucent_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return Translucent_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetRefractionGGX: - return MicrofacetRefractionGGX_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetRefractionGGX_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetRefractionBeckmann: - return MicrofacetRefractionBeckmann_Evaluate(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetRefractionBeckmann_Evaluate(dg, wi_t, wo_t, TEXTURE_ARGS); } return 0.f; @@ -1311,33 +1149,50 @@ float3 Bxdf_Sample( float* pdf ) { + // Transform vectors into tangent space + float3 wi_t = matrix_mul_vector3(dg->world_to_tangent, wi); + float3 wo_t; + + float3 res = 0.f; + int mattype = dg->mat.type; switch (mattype) { case kLambert: - return Lambert_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); - case kMicrofacetBlinn: - return MicrofacetBlinn_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = Lambert_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kMicrofacetGGX: - return MicrofacetGGX_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = MicrofacetGGX_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kMicrofacetBeckmann: - return MicrofacetBeckmann_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = MicrofacetBeckmann_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kIdealReflect: - return IdealReflect_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = IdealReflect_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kIdealRefract: - return IdealRefract_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = IdealRefract_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kTranslucent: - return Translucent_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = Translucent_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kPassthrough: - return Passthrough_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = Passthrough_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kMicrofacetRefractionGGX: - return MicrofacetRefractionGGX_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = MicrofacetRefractionGGX_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; case kMicrofacetRefractionBeckmann: - return MicrofacetRefractionBeckmann_Sample(dg, wi, TEXTURE_ARGS, sample, wo, pdf); + res = MicrofacetRefractionBeckmann_Sample(dg, wi_t, TEXTURE_ARGS, sample, &wo_t, pdf); + break; + default: + *pdf = 0.f; + break; } - *pdf = 0.f; - return make_float3(0.f, 0.f, 0.f); + *wo = matrix_mul_vector3(dg->tangent_to_world, wo_t); + + return res; } float Bxdf_GetPdf( @@ -1351,29 +1206,31 @@ float Bxdf_GetPdf( TEXTURE_ARG_LIST ) { + // Transform vectors into tangent space + float3 wi_t = matrix_mul_vector3(dg->world_to_tangent, wi); + float3 wo_t = matrix_mul_vector3(dg->world_to_tangent, wo); + int mattype = dg->mat.type; switch (mattype) { case kLambert: - return Lambert_GetPdf(dg, wi, wo, TEXTURE_ARGS); - case kMicrofacetBlinn: - return MicrofacetBlinn_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return Lambert_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetGGX: - return MicrofacetGGX_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetGGX_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetBeckmann: - return MicrofacetBeckmann_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetBeckmann_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kIdealReflect: - return IdealReflect_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return IdealReflect_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kIdealRefract: - return IdealRefract_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return IdealRefract_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kTranslucent: - return Translucent_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return Translucent_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kPassthrough: return 0.f; case kMicrofacetRefractionGGX: - return MicrofacetRefractionGGX_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetRefractionGGX_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); case kMicrofacetRefractionBeckmann: - return MicrofacetRefractionBeckmann_GetPdf(dg, wi, wo, TEXTURE_ARGS); + return MicrofacetRefractionBeckmann_GetPdf(dg, wi_t, wo_t, TEXTURE_ARGS); } return 0.f; diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index ee4e1b38..ddb30120 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -363,7 +363,7 @@ __kernel void ShadeSurface( // Fill surface data DifferentialGeometry diffgeo; - FillDifferentialGeometry(&scene, &isect, &diffgeo); + DifferentialGeometry_Fill(&scene, &isect, &diffgeo); // Check if we are hitting from the inside @@ -444,13 +444,15 @@ __kernel void ShadeSurface( // maintain proper volume stack here //if (Bxdf_IsBtdf(&diffgeo)) //{ - // If we entering set the volume - //path->volume = ndotwi > 0.f ? 0 : -1; + // // If we entering set the volume + // path->volume = !backfacing ? 0 : -1; //} // Check if we need to apply normal map //ApplyNormalMap(&diffgeo, TEXTURE_ARGS); - ApplyBumpMap(&diffgeo, TEXTURE_ARGS); + DifferentialGeometry_ApplyBumpMap(&diffgeo, TEXTURE_ARGS); + DifferentialGeometry_CalculateTangentTransforms(&diffgeo); + float lightpdf = 0.f; float bxdflightpdf = 0.f; float bxdfpdf = 0.f; diff --git a/App/CL/normalmap.cl b/App/CL/normalmap.cl index 134404c0..d30de174 100755 --- a/App/CL/normalmap.cl +++ b/App/CL/normalmap.cl @@ -26,29 +26,33 @@ THE SOFTWARE. #include <../App/CL/texture.cl> #include <../App/CL/payload.cl> -void ApplyNormalMap(DifferentialGeometry* dg, TEXTURE_ARG_LIST) +void DifferentialGeometry_ApplyNormalMap(DifferentialGeometry* diffgeo, TEXTURE_ARG_LIST) { - int nmapidx = dg->mat.nmapidx; + int nmapidx = diffgeo->mat.nmapidx; if (nmapidx != -1) { // Now n, dpdu, dpdv is orthonormal basis - float3 mappednormal = 2.f * Texture_Sample2D(dg->uv, TEXTURE_ARGS_IDX(nmapidx)).xyz - make_float3(1.f, 1.f, 1.f); + float3 mappednormal = 2.f * Texture_Sample2D(diffgeo->uv, TEXTURE_ARGS_IDX(nmapidx)).xyz - make_float3(1.f, 1.f, 1.f); // Return mapped version - dg->n = normalize(mappednormal.z * dg->n * 0.5f + mappednormal.x * dg->dpdu + mappednormal.y * dg->dpdv); + diffgeo->n = normalize(mappednormal.z * diffgeo->n * 0.5f + mappednormal.x * diffgeo->dpdu + mappednormal.y * diffgeo->dpdv); + diffgeo->dpdv = normalize(cross(diffgeo->n, diffgeo->dpdu)); + diffgeo->dpdu = normalize(cross(diffgeo->dpdv, diffgeo->n)); } } -void ApplyBumpMap(DifferentialGeometry* dg, TEXTURE_ARG_LIST) +void DifferentialGeometry_ApplyBumpMap(DifferentialGeometry* diffgeo, TEXTURE_ARG_LIST) { - int nmapidx = dg->mat.nmapidx; + int nmapidx = diffgeo->mat.nmapidx; if (nmapidx != -1) { // Now n, dpdu, dpdv is orthonormal basis - float3 mappednormal = 2.f * Texture_SampleBump(dg->uv, TEXTURE_ARGS_IDX(nmapidx)) - make_float3(1.f, 1.f, 1.f); + float3 mappednormal = 2.f * Texture_SampleBump(diffgeo->uv, TEXTURE_ARGS_IDX(nmapidx)) - make_float3(1.f, 1.f, 1.f); // Return mapped version - dg->n = normalize(mappednormal.z * dg->n + mappednormal.x * dg->dpdu + mappednormal.y * dg->dpdv); + diffgeo->n = normalize(mappednormal.z * diffgeo->n * 0.5f + mappednormal.x * diffgeo->dpdu + mappednormal.y * diffgeo->dpdv); + diffgeo->dpdv = normalize(cross(diffgeo->n, diffgeo->dpdu)); + diffgeo->dpdu = normalize(cross(diffgeo->dpdv, diffgeo->n)); } } diff --git a/App/CL/payload.cl b/App/CL/payload.cl index 47548106..7e02fada 100755 --- a/App/CL/payload.cl +++ b/App/CL/payload.cl @@ -74,7 +74,6 @@ enum Bxdf kLambert, kIdealReflect, kIdealRefract, - kMicrofacetBlinn, kMicrofacetBeckmann, kMicrofacetGGX, kLayered, @@ -236,6 +235,22 @@ typedef int extra; } Texture; +typedef struct __matrix +{ + union + { + struct + { + float4 m0; + float4 m1; + float4 m2; + float4 m3; + }; + + float m[16]; + }; +} matrix; + // Hit data typedef struct _DifferentialGeometry { @@ -251,6 +266,10 @@ typedef struct _DifferentialGeometry float3 dpdu; float3 dpdv; float area; + + matrix world_to_tangent; + matrix tangent_to_world; + // Material Material mat; } DifferentialGeometry; diff --git a/App/CL/scene.cl b/App/CL/scene.cl index d8bf07b2..a0ca3e48 100755 --- a/App/CL/scene.cl +++ b/App/CL/scene.cl @@ -52,7 +52,7 @@ typedef struct _Scene } Scene; /// Fill DifferentialGeometry structure based on intersection info from RadeonRays -void FillDifferentialGeometry(// Scene +void DifferentialGeometry_Fill(// Scene Scene const* scene, // RadeonRays intersection Intersection const* isect, @@ -107,7 +107,6 @@ void FillDifferentialGeometry(// Scene diffgeo->mat = scene->materials[matidx]; /// From PBRT book - /// Construct tangent basis on the fly and apply normal map float du1 = uv0.x - uv2.x; float du2 = uv1.x - uv2.x; float dv1 = uv0.y - uv2.y; @@ -118,7 +117,7 @@ void FillDifferentialGeometry(// Scene float det = du1 * dv2 - dv1 * du2; - if (det != 0.f) + if (0 && det != 0.f) { float invdet = 1.f / det; diffgeo->dpdu = normalize( (dv2 * dp1 - dv1 * dp2) * invdet ); @@ -126,25 +125,42 @@ void FillDifferentialGeometry(// Scene } else { - diffgeo->dpdu = normalize(GetOrthoVector(diffgeo->ng)); - diffgeo->dpdv = normalize(cross(diffgeo->ng, diffgeo->dpdu)); + diffgeo->dpdu = normalize(GetOrthoVector(diffgeo->n)); + diffgeo->dpdv = normalize(cross(diffgeo->n, diffgeo->dpdu)); } + // Fix all to be orthogonal - diffgeo->dpdv = normalize(cross(diffgeo->n, diffgeo->dpdu)); - diffgeo->dpdu = normalize(cross(diffgeo->dpdv, diffgeo->n)); + //diffgeo->dpdv = normalize(cross(diffgeo->ng, diffgeo->dpdu)); + //diffgeo->dpdu = normalize(cross(diffgeo->dpdv, diffgeo->ng)); float3 p0 = transform_point(v0, shape.m0, shape.m1, shape.m2, shape.m3); float3 p1 = transform_point(v1, shape.m0, shape.m1, shape.m2, shape.m3); float3 p2 = transform_point(v2, shape.m0, shape.m1, shape.m2, shape.m3); diffgeo->area = 0.5f * length(cross(p2 - p0, p2 - p1)); - - // Apply transform & linear motion blur - //v += (linearvelocity * time); - // MT^-1 should be used if scale is present - //n = rotate_vector(n, angularvelocity); +} + +void DifferentialGeometry_CalculateTangentTransforms(DifferentialGeometry* diffgeo) +{ + diffgeo->world_to_tangent = matrix_from_rows3( + diffgeo->dpdu, + diffgeo->n, + diffgeo->dpdv); + + diffgeo->world_to_tangent.m0.w = -dot(diffgeo->dpdu, diffgeo->p); + diffgeo->world_to_tangent.m1.w = -dot(diffgeo->n, diffgeo->p); + diffgeo->world_to_tangent.m2.w = -dot(diffgeo->dpdv, diffgeo->p); + + diffgeo->tangent_to_world = matrix_from_cols3( + diffgeo->world_to_tangent.m0.xyz, + diffgeo->world_to_tangent.m1.xyz, + diffgeo->world_to_tangent.m2.xyz); + + diffgeo->tangent_to_world.m0.w = diffgeo->p.x; + diffgeo->tangent_to_world.m1.w = diffgeo->p.y; + diffgeo->tangent_to_world.m2.w = diffgeo->p.z; } int Scene_SampleLight(Scene const* scene, float sample, float* pdf) diff --git a/App/CL/utils.cl b/App/CL/utils.cl index 3afb1b16..1b106074 100755 --- a/App/CL/utils.cl +++ b/App/CL/utils.cl @@ -24,13 +24,7 @@ THE SOFTWARE. #define PI 3.14159265358979323846f -// 2D distribution function -typedef struct __Distribution2D -{ - int w; - int h; - __global float const* data; -} Distribution2D; +#include <../App/CL/payload.cl> #ifndef APPLE /// These functions are defined on OSX already @@ -70,6 +64,87 @@ int2 make_int2(int x, int y) } #endif +float matrix_get(matrix m, int i, int j) +{ + return m.m[i * 4 + j]; +} + +matrix matrix_from_cols(float4 c0, float4 c1, float4 c2, float4 c3) +{ + matrix m; + m.m0 = make_float4(c0.x, c1.x, c2.x, c3.x); + m.m1 = make_float4(c0.y, c1.y, c2.y, c3.y); + m.m2 = make_float4(c0.z, c1.z, c2.z, c3.z); + m.m3 = make_float4(c0.w, c1.w, c2.w, c3.w); + return m; +} + +matrix matrix_from_rows(float4 c0, float4 c1, float4 c2, float4 c3) +{ + matrix m; + m.m0 = c0; + m.m1 = c1; + m.m2 = c2; + m.m3 = c3; + return m; +} + +matrix matrix_from_rows3(float3 c0, float3 c1, float3 c2) +{ + matrix m; + m.m0.xyz = c0; m.m0.w = 0; + m.m1.xyz = c1; m.m1.w = 0; + m.m2.xyz = c2; m.m2.w = 0; + m.m3 = make_float4(0.f, 0.f, 0.f, 1.f); + return m; +} + +matrix matrix_from_cols3(float3 c0, float3 c1, float3 c2) +{ + matrix m; + m.m0 = make_float4(c0.x, c1.x, c2.x, 0.f); + m.m1 = make_float4(c0.y, c1.y, c2.y, 0.f); + m.m2 = make_float4(c0.z, c1.z, c2.z, 0.f); + m.m3 = make_float4(0.f, 0.f, 0.f, 1.f); + return m; +} + +matrix matrix_transpose(matrix m) +{ + return matrix_from_cols(m.m0, m.m1, m.m2, m.m3); +} + +float4 matrix_mul_vector4(matrix m, float4 v) +{ + float4 res; + res.x = dot(m.m0, v); + res.y = dot(m.m1, v); + res.z = dot(m.m2, v); + res.w = dot(m.m3, v); + return res; +} + +float3 matrix_mul_vector3(matrix m, float3 v) +{ + float3 res; + res.x = dot(m.m0.xyz, v); + res.y = dot(m.m1.xyz, v); + res.z = dot(m.m2.xyz, v); + return res; +} + +float3 matrix_mul_point3(matrix m, float3 v) +{ + float3 res; + res.x = dot(m.m0.xyz, v) + m.m0.w; + res.y = dot(m.m1.xyz, v) + m.m1.w; + res.z = dot(m.m2.xyz, v) + m.m2.w; + return res; +} + + + + /// Transform point with transformation matrix. /// m0...m3 are matrix rows @@ -169,16 +244,6 @@ float3 GetOrthoVector(float3 n) return normalize(p); } -float2 Distribution2D_Sample(Distribution2D const* dist, float2 sample, float* pdf) -{ - return make_float2(0.f, 0.f); -} - -float Distribution2D_GetPdf(Distribution2D const* dist, float2 sample) -{ - return 0.f; -} - uint upper_power_of_two(uint v) { v--; diff --git a/App/Scene/IO/material_io.cpp b/App/Scene/IO/material_io.cpp index e38a7066..7ccfa57c 100644 --- a/App/Scene/IO/material_io.cpp +++ b/App/Scene/IO/material_io.cpp @@ -85,8 +85,6 @@ namespace Baikal return "ideal_reflect"; case Baikal::SingleBxdf::BxdfType::kIdealRefract: return "ideal_refract";; - case Baikal::SingleBxdf::BxdfType::kMicrofacetBlinn: - return "microfacet_blinn"; case Baikal::SingleBxdf::BxdfType::kMicrofacetBeckmann: return "microfacet_beckmann"; case Baikal::SingleBxdf::BxdfType::kMicrofacetGGX: @@ -114,7 +112,6 @@ namespace Baikal { "lambert" , Baikal::SingleBxdf::BxdfType::kLambert }, { "ideal_reflect" , Baikal::SingleBxdf::BxdfType::kIdealReflect }, { "ideal_refract" , Baikal::SingleBxdf::BxdfType::kIdealRefract }, - { "microfacet_blinn" , Baikal::SingleBxdf::BxdfType::kMicrofacetBlinn }, { "microfacet_beckmann" , Baikal::SingleBxdf::BxdfType::kMicrofacetBeckmann }, { "microfacet_ggx" , Baikal::SingleBxdf::BxdfType::kMicrofacetGGX }, { "emissive" , Baikal::SingleBxdf::BxdfType::kEmissive }, @@ -218,7 +215,6 @@ namespace Baikal if (type == SingleBxdf::BxdfType::kMicrofacetGGX || type == SingleBxdf::BxdfType::kMicrofacetBeckmann || - type == SingleBxdf::BxdfType::kMicrofacetBlinn || type == SingleBxdf::BxdfType::kMicrofacetRefractionGGX || type == SingleBxdf::BxdfType::kMicrofacetRefractionBeckmann) { diff --git a/App/Scene/IO/scene_io.cpp b/App/Scene/IO/scene_io.cpp index a6343b8d..adda776e 100644 --- a/App/Scene/IO/scene_io.cpp +++ b/App/Scene/IO/scene_io.cpp @@ -57,7 +57,7 @@ namespace Baikal { auto s = RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]); - if (s.sqnorm() > 0 || !mat.specular_texname.empty()) + if ((s.sqnorm() > 0 || !mat.specular_texname.empty())) { // Otherwise create lambert material = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); diff --git a/App/Scene/material.cpp b/App/Scene/material.cpp index 39a863e9..d7bcbd83 100644 --- a/App/Scene/material.cpp +++ b/App/Scene/material.cpp @@ -241,9 +241,10 @@ namespace Baikal RegisterInput("normal", "Normal map", {InputType::kTexture}); RegisterInput("ior", "Index of refraction", {InputType::kFloat4}); RegisterInput("fresnel", "Fresnel flag", {InputType::kFloat4}); - RegisterInput("roughness", "Roughness", {InputType::kFloat4}); + RegisterInput("roughness", "Roughness", {InputType::kFloat4, InputType::kTexture}); SetInputValue("albedo", RadeonRays::float4(0.7f, 0.7f, 0.7f, 1.f)); + SetInputValue("normal", static_cast(nullptr)); } SingleBxdf::BxdfType SingleBxdf::GetBxdfType() const @@ -268,7 +269,7 @@ namespace Baikal RegisterInput("base_material", "Base material", {InputType::kMaterial}); RegisterInput("top_material", "Top material", {InputType::kMaterial}); RegisterInput("ior", "Index of refraction", {InputType::kFloat4}); - RegisterInput("weight", "Blend weight", {InputType::kFloat4}); + RegisterInput("weight", "Blend weight", {InputType::kFloat4, InputType::kTexture}); } MultiBxdf::Type MultiBxdf::GetType() const diff --git a/App/Scene/material.h b/App/Scene/material.h index f6c6c70f..d54440a3 100644 --- a/App/Scene/material.h +++ b/App/Scene/material.h @@ -154,7 +154,6 @@ namespace Baikal kLambert, kIdealReflect, kIdealRefract, - kMicrofacetBlinn, kMicrofacetBeckmann, kMicrofacetGGX, kEmissive, diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index a78f1c4d..cea7002c 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -546,10 +546,10 @@ namespace Baikal UpdateTextures(scene, mat_collector, tex_collector, out); - //Volume vol = {1, 0, 0, 0, {0.9f, 0.6f, 0.9f}, {5.1f, 1.8f, 5.1f}, {0.0f, 0.0f, 0.0f}}; + //Volume vol = // Temporary code - ClwScene::Volume vol;// = { 1, 0, 0, 0, {1.2f, 0.4f, 1.2f },{ 5.1f, 4.8f, 5.1f },{ 0.0f, 0.0f, 0.0f } }; + ClwScene::Volume vol = {(ClwScene::VolumeType)1, (ClwScene::PhaseFunction)0, 0, 0, {0.09f, 0.09f, 0.09f}, {0.1f, 0.1f, 0.1f}, {0.0f, 0.0f, 0.0f}}; out.volumes = m_context.CreateBuffer(1, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &vol); @@ -700,7 +700,6 @@ namespace Baikal case SingleBxdf::BxdfType::kIdealRefract: return ClwScene::Bxdf::kIdealRefract; case SingleBxdf::BxdfType::kMicrofacetGGX: return ClwScene::Bxdf::kMicrofacetGGX; case SingleBxdf::BxdfType::kMicrofacetBeckmann: return ClwScene::Bxdf::kMicrofacetBeckmann; - case SingleBxdf::BxdfType::kMicrofacetBlinn: return ClwScene::Bxdf::kMicrofacetBlinn; case SingleBxdf::BxdfType::kMicrofacetRefractionGGX: return ClwScene::Bxdf::kMicrofacetRefractionGGX; case SingleBxdf::BxdfType::kMicrofacetRefractionBeckmann: return ClwScene::Bxdf::kMicrofacetRefractionBeckmann; } @@ -737,7 +736,6 @@ namespace Baikal break; // We need to convert roughness for the following materials - case ClwScene::Bxdf::kMicrofacetBlinn: case ClwScene::Bxdf::kMicrofacetGGX: case ClwScene::Bxdf::kMicrofacetBeckmann: case ClwScene::Bxdf::kMicrofacetRefractionGGX: From 2ba56d9b9730c3f03d582229865196aa3600920c Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Thu, 26 Jan 2017 09:42:11 +0100 Subject: [PATCH 16/24] Format --- App/CL/bxdf.cl | 1 - 1 file changed, 1 deletion(-) diff --git a/App/CL/bxdf.cl b/App/CL/bxdf.cl index ba7b0bec..e3d21ba6 100755 --- a/App/CL/bxdf.cl +++ b/App/CL/bxdf.cl @@ -31,7 +31,6 @@ THE SOFTWARE. #define ROUGHNESS_EPS 0.0001f - enum BxdfFlags { kReflection = (1 << 0), From 1a829f073173ab798bf942d6dd5055f54c19479c Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Thu, 26 Jan 2017 12:26:01 +0100 Subject: [PATCH 17/24] CMJ experiment --- App/CL/camera.cl | 1 + App/CL/integrator_pt.cl | 41 +++++++++++++++++++++-- App/CL/material.cl | 1 + App/CL/sampling.cl | 59 ++++++++++++++++++++++++++++++++++ App/PT/ptrenderer.cpp | 7 ++++ App/PT/ptrenderer.h | 1 + App/Scene/IO/scene_test_io.cpp | 42 +++++++++--------------- App/Scene/scene_tracker.cpp | 7 ++-- App/main.cpp | 14 +++++--- 9 files changed, 136 insertions(+), 37 deletions(-) diff --git a/App/CL/camera.cl b/App/CL/camera.cl index daaa6903..d81825d0 100755 --- a/App/CL/camera.cl +++ b/App/CL/camera.cl @@ -28,6 +28,7 @@ THE SOFTWARE. #include <../App/CL/utils.cl> #include <../App/CL/path.cl> +//#define SOBOL diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index ddb30120..633d8cab 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -43,10 +43,12 @@ THE SOFTWARE. #define CRAZY_LOW_THROUGHPUT 0.0f #define CRAZY_HIGH_RADIANCE 3.f #define CRAZY_HIGH_DISTANCE 1000000.f -#define CRAZY_LOW_DISTANCE 0.001f +#define CRAZY_LOW_DISTANCE 0.01f #define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE)) #define NON_BLACK(x) (length(x) > 0.f) +#define CMJ 1 + // This kernel only handles scattered paths. // It applies direct illumination and generates @@ -235,6 +237,8 @@ __kernel void ShadeVolume( } } +#define CMJ_DIM 16 + // Handle ray-surface interaction possibly generating path continuation. // This is only applied to non-scattered paths. __kernel void ShadeSurface( @@ -280,6 +284,8 @@ __kernel void ShadeSurface( __global uint const* sobolmat, // Current bounce int bounce, + // Frame + int frame, // Volume data __global Volume const* volumes, // Shadow rays @@ -329,7 +335,7 @@ __kernel void ShadeSurface( // Fetch incoming ray direction float3 wi = -normalize(rays[hitidx].d.xyz); -#ifdef SOBOL +#if (SOBOL == 1) // Sample light __global SobolSampler* sampler = samplers + pixelidx; @@ -350,7 +356,7 @@ __kernel void ShadeSurface( sample3.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectV), sampler->s0, sobolmat); float sample4 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kRR), sampler->s0, sobolmat); -#else +#elif (RANDOM == 1) // Prepare RNG Rng rng; InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); @@ -359,6 +365,35 @@ __kernel void ShadeSurface( float2 sample2 = UniformSampler_Sample2D(&rng); float2 sample3 = UniformSampler_Sample2D(&rng); float sample4 = UniformSampler_Sample2D(&rng).x; +#elif (CMJ == 1) + Rng rng; + InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); + float2 sample0 = UniformSampler_Sample2D(&rng); + float2 sample1 = UniformSampler_Sample2D(&rng); + float2 sample2 = UniformSampler_Sample2D(&rng); + float2 sample3 = UniformSampler_Sample2D(&rng); + float sample4 = UniformSampler_Sample2D(&rng).x; + + int pass = frame / (CMJ_DIM * CMJ_DIM); + int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce) * 0xc517e953); + int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 1) * 0xc517e953); + int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 2) * 0xc517e953); + int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 3) * 0xc517e953); + int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 4) * 0xc517e953); + + int pattern0 = (pass + bounce + pixelidx) % 13331; + int pattern1 = (pass + bounce + pixelidx + 1) % 13331; + int pattern2 = (pass + bounce + pixelidx + 2) % 13331; + int pattern3 = (pass + bounce + pixelidx + 3) % 13331; + int pattern4 = (pass + bounce + pixelidx + 4) % 13331; + + sample0 = cmj(subsample0, CMJ_DIM, pattern0); + sample1 = cmj(subsample1, CMJ_DIM, pattern1); + sample2 = cmj(subsample2, CMJ_DIM, pattern2); + sample3 = cmj(subsample3, CMJ_DIM, pattern2); + sample4 = cmj(subsample4, CMJ_DIM, pattern2).x; +#else + #endif // Fill surface data diff --git a/App/CL/material.cl b/App/CL/material.cl index 9b2c4d8f..3e65ef2c 100755 --- a/App/CL/material.cl +++ b/App/CL/material.cl @@ -27,6 +27,7 @@ THE SOFTWARE. #include <../App/CL/texture.cl> #include <../App/CL/payload.cl> #include <../App/CL/bxdf.cl> +//#define SOBOL void Material_Select( // Scene data diff --git a/App/CL/sampling.cl b/App/CL/sampling.cl index c7ccb4ec..f2028fb6 100755 --- a/App/CL/sampling.cl +++ b/App/CL/sampling.cl @@ -205,5 +205,64 @@ float SobolSampler_Sample1D(uint index, uint dimension, uint scramble, __global return result * (1.f / (1UL << 32)); } +uint permute(uint i, uint l, uint p) +{ + unsigned w = l - 1; + w |= w >> 1; + w |= w >> 2; + w |= w >> 4; + w |= w >> 8; + w |= w >> 16; + + do + { + i ^= p; + i *= 0xe170893d; + i ^= p >> 16; + i ^= (i & w) >> 4; + i ^= p >> 8; + i *= 0x0929eb3f; + i ^= p >> 23; + i ^= (i & w) >> 1; + i *= 1 | p >> 27; + i *= 0x6935fa69; + i ^= (i & w) >> 11; + i *= 0x74dcb303; + i ^= (i & w) >> 2; + i *= 0x9e501cc3; + i ^= (i & w) >> 2; + i *= 0xc860a3df; + i &= w; + i ^= i >> 5; + } while (i >= l); + return (i + p) % l; +} + +float randfloat(uint i, uint p) +{ + i ^= p; + i ^= i >> 17; + i ^= i >> 10; + i *= 0xb36534e5; + i ^= i >> 12; + i ^= i >> 21; + i *= 0x93fc4795; + i ^= 0xdf6e307f; + i ^= i >> 17; + i *= 1 | p >> 18; + return i * (1.0f / 4294967808.0f); +} + +float2 cmj(int s, int n, int p) +{ + int sx = permute(s % n, n, p * 0xa511e9b3); + int sy = permute(s / n, n, p * 0x63d83595); + float jx = randfloat(s, p * 0xa399d265); + float jy = randfloat(s, p * 0x711ad6a5); + + return make_float2((s % n + (sy + jx) / n) / n, + (s / n + (sx + jy) / n) / n); +} + #endif // SAMPLING_CL diff --git a/App/PT/ptrenderer.cpp b/App/PT/ptrenderer.cpp index 8ce9b61b..1ad98411 100755 --- a/App/PT/ptrenderer.cpp +++ b/App/PT/ptrenderer.cpp @@ -114,6 +114,7 @@ namespace Baikal , m_resetsampler(true) , m_scene_tracker(context, devidx) , m_num_bounces(num_bounces) + , m_framecnt(0) { std::string buildopts; @@ -179,6 +180,9 @@ namespace Baikal auto api = m_scene_tracker.GetIntersectionApi(); auto& clwscene = m_scene_tracker.CompileScene(scene, m_render_data->mat_collector, m_render_data->tex_collector); + if (m_resetsampler) + m_framecnt = 0; + // Check output assert(m_output); @@ -247,6 +251,8 @@ namespace Baikal // m_context.Flush(0); } + + ++m_framecnt; } void PtRenderer::SetOutput(Output* output) @@ -386,6 +392,7 @@ namespace Baikal shadekernel.SetArg(argc++, m_render_data->samplers); shadekernel.SetArg(argc++, m_render_data->sobolmat); shadekernel.SetArg(argc++, pass); + shadekernel.SetArg(argc++, m_framecnt); shadekernel.SetArg(argc++, scene.volumes); shadekernel.SetArg(argc++, m_render_data->shadowrays); shadekernel.SetArg(argc++, m_render_data->lightsamples); diff --git a/App/PT/ptrenderer.h b/App/PT/ptrenderer.h index b8feb4a5..4c5352c7 100755 --- a/App/PT/ptrenderer.h +++ b/App/PT/ptrenderer.h @@ -113,6 +113,7 @@ namespace Baikal private: int m_num_bounces; + int m_framecnt; }; } diff --git a/App/Scene/IO/scene_test_io.cpp b/App/Scene/IO/scene_test_io.cpp index 5f93a5b9..3abc9ea3 100644 --- a/App/Scene/IO/scene_test_io.cpp +++ b/App/Scene/IO/scene_test_io.cpp @@ -65,7 +65,7 @@ namespace Baikal normals[t].x=0; normals[t].y = -1; normals[t].z = 0; uvs[t].x=1; uvs[t].y = 1; ++t; - + t = 0U; for(auto j = 0U; j < lat - 3; j++) for(auto i = 0U; i < lon - 1; i++) @@ -223,24 +223,15 @@ namespace Baikal } else if (filename == "sphere+plane+area") { - auto mesh = CreateSphere(64, 32, 2.f, float3(0.f, 2.2f, 0.f)); + auto mesh = CreateSphere(64, 32, 2.f, float3(0.f, 2.5f, 0.f)); scene->AttachShape(mesh); scene->AttachAutoreleaseObject(mesh); - - SingleBxdf* green = new SingleBxdf(SingleBxdf::BxdfType::kLambert); - green->SetInputValue("albedo", 2.f * float4(0.1f, 0.2f, 0.1f, 1.f)); - - SingleBxdf* spec = new SingleBxdf(SingleBxdf::BxdfType::kMicrofacetGGX); - spec->SetInputValue("albedo", float4(0.9f, 0.9f, 0.9f, 1.f)); - spec->SetInputValue("roughness", float4(0.002f, 0.002f, 0.002f, 1.f)); - - MultiBxdf* mix = new MultiBxdf(MultiBxdf::Type::kFresnelBlend); - mix->SetInputValue("base_material", green); - mix->SetInputValue("top_material", spec); - mix->SetInputValue("ior", float4(1.33f, 1.33f, 1.33f, 1.33f)); - - mesh->SetMaterial(mix); - + + SingleBxdf* grey = new SingleBxdf(SingleBxdf::BxdfType::kLambert); + grey->SetInputValue("albedo", float4(0.7f, 0.7f, 0.7f, 1.f)); + grey->SetTwoSided(true); + + Mesh* floor = CreateQuad( { RadeonRays::float3(-8, 0, -8), @@ -252,10 +243,11 @@ namespace Baikal scene->AttachShape(floor); scene->AttachAutoreleaseObject(floor); - floor->SetMaterial(green); - + floor->SetMaterial(grey); + mesh->SetMaterial(grey); + SingleBxdf* emissive = new SingleBxdf(SingleBxdf::BxdfType::kEmissive); - emissive->SetInputValue("albedo", 5.f * float4(3.1f, 3.f, 2.8f, 1.f)); + emissive->SetInputValue("albedo", 2.f * float4(3.1f, 3.f, 2.8f, 1.f)); Mesh* light = CreateQuad( { @@ -269,19 +261,17 @@ namespace Baikal scene->AttachAutoreleaseObject(light); light->SetMaterial(emissive); - + AreaLight* l1 = new AreaLight(light, 0); AreaLight* l2 = new AreaLight(light, 1); + scene->AttachLight(l1); scene->AttachLight(l2); scene->AttachAutoreleaseObject(l1); scene->AttachAutoreleaseObject(l2); - + scene->AttachAutoreleaseObject(emissive); - scene->AttachAutoreleaseObject(green); - scene->AttachAutoreleaseObject(spec); - scene->AttachAutoreleaseObject(mix); - + scene->AttachAutoreleaseObject(grey); } else if (filename == "sphere+plane+ibl") { diff --git a/App/Scene/scene_tracker.cpp b/App/Scene/scene_tracker.cpp index cea7002c..940f5c9f 100755 --- a/App/Scene/scene_tracker.cpp +++ b/App/Scene/scene_tracker.cpp @@ -970,15 +970,14 @@ namespace Baikal { // TODO: optimize this linear search auto shape = static_cast(light)->GetShape(); - - std::size_t idx = -1; - + + std::size_t idx = 0; for (auto iter = scene.CreateShapeIterator(); iter->IsValid(); iter->Next(), ++idx) { if (iter->ItemAs() == shape) break; } - + clw_light->shapeidx = static_cast(idx); clw_light->primidx = static_cast(static_cast(light)->GetPrimitiveIdx()); break; diff --git a/App/main.cpp b/App/main.cpp index fd30ec34..db93e7b6 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -107,7 +107,7 @@ float g_ao_radius = 1.f; float g_envmapmul = 1.f; float g_cspeed = 10.25f; -float3 g_camera_pos = float3(0.f, 1.f, 4.f); +float3 g_camera_pos = float3(0.f, 4.f, 12.f); float3 g_camera_at = float3(0.f, 1.f, 0.f); float3 g_camera_up = float3(0.f, 1.f, 0.f); @@ -348,8 +348,8 @@ void InitData() { // Load OBJ scene - std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoObj()); - g_scene.reset(scene_io->LoadScene(filename, basepath)); + std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoTest()); + g_scene.reset(scene_io->LoadScene("sphere+plane+area", basepath)); // Enable this to generate new materal mapping for a model #if 0 @@ -767,9 +767,15 @@ void Update() }*/ } - if (g_num_samples == -1 || g_samplecount++ < g_num_samples) + if (g_num_samples == -1 || g_samplecount < g_num_samples) { g_cfgs[g_primary].renderer->Render(*g_scene.get()); + ++g_samplecount; + } + else if (g_samplecount == g_num_samples) + { + std::cout << "Target sample count reached\n"; + ++g_samplecount; } //if (std::chrono::duration_cast(time - updatetime).count() > 1) From 50e1ea24fd85cf36fb8d6fdc5e8dcd62ea0b888a Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Thu, 26 Jan 2017 12:48:12 +0100 Subject: [PATCH 18/24] Fix CMJ --- App/CL/integrator_pt.cl | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index 633d8cab..f1a1ff27 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -368,18 +368,13 @@ __kernel void ShadeSurface( #elif (CMJ == 1) Rng rng; InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); - float2 sample0 = UniformSampler_Sample2D(&rng); - float2 sample1 = UniformSampler_Sample2D(&rng); - float2 sample2 = UniformSampler_Sample2D(&rng); - float2 sample3 = UniformSampler_Sample2D(&rng); - float sample4 = UniformSampler_Sample2D(&rng).x; int pass = frame / (CMJ_DIM * CMJ_DIM); - int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce) * 0xc517e953); - int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 1) * 0xc517e953); - int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 2) * 0xc517e953); - int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 3) * 0xc517e953); - int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 4) * 0xc517e953); + int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce) * 0xc517e953); + int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 1) * 0xc517e953); + int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 2) * 0xc517e953); + int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 3) * 0xc517e953); + int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 4) * 0xc517e953); int pattern0 = (pass + bounce + pixelidx) % 13331; int pattern1 = (pass + bounce + pixelidx + 1) % 13331; @@ -387,11 +382,11 @@ __kernel void ShadeSurface( int pattern3 = (pass + bounce + pixelidx + 3) % 13331; int pattern4 = (pass + bounce + pixelidx + 4) % 13331; - sample0 = cmj(subsample0, CMJ_DIM, pattern0); - sample1 = cmj(subsample1, CMJ_DIM, pattern1); - sample2 = cmj(subsample2, CMJ_DIM, pattern2); - sample3 = cmj(subsample3, CMJ_DIM, pattern2); - sample4 = cmj(subsample4, CMJ_DIM, pattern2).x; + float2 sample0 = cmj(subsample0, CMJ_DIM, pattern0); + float2 sample1 = cmj(subsample1, CMJ_DIM, pattern1); + float2 sample2 = cmj(subsample2, CMJ_DIM, pattern2); + float2 sample3 = cmj(subsample3, CMJ_DIM, pattern3); + float sample4 = cmj(subsample4, CMJ_DIM, pattern4).x; #else #endif From 3d0c59fbb7e891799a9e8a73af7c52f479cedf52 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Thu, 26 Jan 2017 15:32:27 +0100 Subject: [PATCH 19/24] Add more decorellation --- App/CL/camera.cl | 18 ++++++++++++------ App/CL/integrator_pt.cl | 31 +++++++++++++++++-------------- App/CL/material.cl | 3 ++- App/PT/ptrenderer.cpp | 3 ++- App/main.cpp | 2 +- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/App/CL/camera.cl b/App/CL/camera.cl index d81825d0..3a34623e 100755 --- a/App/CL/camera.cl +++ b/App/CL/camera.cl @@ -28,9 +28,8 @@ THE SOFTWARE. #include <../App/CL/utils.cl> #include <../App/CL/path.cl> -//#define SOBOL - - +#define CMJ 1 +#define CMJ_DIM 4 /// Ray generation kernel for perspective camera. @@ -49,7 +48,8 @@ __kernel void PerspectiveCamera_GeneratePaths( __global ray* rays, __global SobolSampler* samplers, __global uint const* sobolmat, - int reset + int reset, + int frame #ifndef NO_PATH_DATA ,__global Path* paths #endif @@ -73,7 +73,7 @@ __kernel void PerspectiveCamera_GeneratePaths( Rng rng; InitRng(randseed + globalid.x * 157 + 10433 * globalid.y, &rng); -#ifdef SOBOL +#if SOBOL == 1 __global SobolSampler* sampler = samplers + globalid.y * imgwidth + globalid.x; if (reset) @@ -89,8 +89,14 @@ __kernel void PerspectiveCamera_GeneratePaths( float2 sample0; sample0.x = SobolSampler_Sample1D(sampler->seq, kPixelX, sampler->s0, sobolmat); sample0.y = SobolSampler_Sample1D(sampler->seq, kPixelY, sampler->s0, sobolmat); -#else +#elif RANDOM == 1 float2 sample0 = UniformSampler_Sample2D(&rng); +#elif CMJ == 1 + // Pass defines current current light selection and current material selection + int pass = frame / (CMJ_DIM * CMJ_DIM); + int pattern0 = permute(globalid.y * imgwidth + globalid.x, (1024 * 1024), pass * 0xc13719e1); + int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern0 * 0xc517e953); + float2 sample0 = cmj(subsample0, CMJ_DIM, pattern0); #endif // Calculate [0..1] image plane sample diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index f1a1ff27..41c873e1 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -43,7 +43,7 @@ THE SOFTWARE. #define CRAZY_LOW_THROUGHPUT 0.0f #define CRAZY_HIGH_RADIANCE 3.f #define CRAZY_HIGH_DISTANCE 1000000.f -#define CRAZY_LOW_DISTANCE 0.01f +#define CRAZY_LOW_DISTANCE 0.001f #define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE)) #define NON_BLACK(x) (length(x) > 0.f) @@ -237,7 +237,7 @@ __kernel void ShadeVolume( } } -#define CMJ_DIM 16 +#define CMJ_DIM 4 // Handle ray-surface interaction possibly generating path continuation. // This is only applied to non-scattered paths. @@ -317,6 +317,9 @@ __kernel void ShadeSurface( num_lights }; + // Pass defines current current light selection and current material selection + int pass = frame / (CMJ_DIM * CMJ_DIM); + // Only applied to active rays after compaction if (globalid < *numhits) { @@ -369,18 +372,17 @@ __kernel void ShadeSurface( Rng rng; InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); - int pass = frame / (CMJ_DIM * CMJ_DIM); - int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce) * 0xc517e953); - int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 1) * 0xc517e953); - int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 2) * 0xc517e953); - int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 3) * 0xc517e953); - int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 4) * 0xc517e953); + int pattern0 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx); + int pattern1 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 1); + int pattern2 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 2); + int pattern3 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 3); + int pattern4 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 4); - int pattern0 = (pass + bounce + pixelidx) % 13331; - int pattern1 = (pass + bounce + pixelidx + 1) % 13331; - int pattern2 = (pass + bounce + pixelidx + 2) % 13331; - int pattern3 = (pass + bounce + pixelidx + 3) % 13331; - int pattern4 = (pass + bounce + pixelidx + 4) % 13331; + int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern4 * pixelidx * 0xc117d953); + int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern3 * pixelidx * 0xc117d953); + int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern2 * pixelidx * 0xc117d953); + int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern1 * pixelidx * 0xc117d953); + int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern0 * pixelidx * 0xc117d953); float2 sample0 = cmj(subsample0, CMJ_DIM, pattern0); float2 sample1 = cmj(subsample1, CMJ_DIM, pattern1); @@ -495,7 +497,8 @@ __kernel void ShadeSurface( float bxdfweight = 1.f; float lightweight = 1.f; - int light_idx = num_lights > 0 ? Scene_SampleLight(&scene, sample0.y, &selection_pdf) : -1; + int light_idx = pass % num_lights; + selection_pdf = 1.f / num_lights; float3 throughput = Path_GetThroughput(path); diff --git a/App/CL/material.cl b/App/CL/material.cl index 3e65ef2c..b5690c88 100755 --- a/App/CL/material.cl +++ b/App/CL/material.cl @@ -22,12 +22,13 @@ THE SOFTWARE. #ifndef MATERIAL_CL #define MATERIAL_CL +#define CMJ 1 + #include <../App/CL/utils.cl> #include <../App/CL/random.cl> #include <../App/CL/texture.cl> #include <../App/CL/payload.cl> #include <../App/CL/bxdf.cl> -//#define SOBOL void Material_Select( // Scene data diff --git a/App/PT/ptrenderer.cpp b/App/PT/ptrenderer.cpp index 1ad98411..afd93f66 100755 --- a/App/PT/ptrenderer.cpp +++ b/App/PT/ptrenderer.cpp @@ -351,7 +351,8 @@ namespace Baikal genkernel.SetArg(5, m_render_data->samplers); genkernel.SetArg(6, m_render_data->sobolmat); genkernel.SetArg(7, m_resetsampler); - genkernel.SetArg(8, m_render_data->paths); + genkernel.SetArg(8, m_framecnt); + genkernel.SetArg(9, m_render_data->paths); m_resetsampler = 0; // Run generation kernel diff --git a/App/main.cpp b/App/main.cpp index db93e7b6..dc0c0e7f 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -107,7 +107,7 @@ float g_ao_radius = 1.f; float g_envmapmul = 1.f; float g_cspeed = 10.25f; -float3 g_camera_pos = float3(0.f, 4.f, 12.f); +float3 g_camera_pos = float3(0.f, 4.f, 14.f); float3 g_camera_at = float3(0.f, 1.f, 0.f); float3 g_camera_up = float3(0.f, 1.f, 0.f); From 3a3daf1a79f8cdb552a9812838459b70784eff59 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Fri, 27 Jan 2017 16:52:15 +0100 Subject: [PATCH 20/24] Clean up sampling functionality --- App/CL/bxdf.cl | 1 - App/CL/camera.cl | 102 ++++------ App/CL/{random.cl => common.cl} | 108 ++++------- App/CL/integrator_ao.cl | 1 - App/CL/integrator_pt.cl | 152 ++++----------- App/CL/light.cl | 1 - App/CL/material.cl | 32 +-- App/CL/sampling.cl | 334 ++++++++++++++++++++------------ App/CL/volumetrics.cl | 32 +-- App/PT/ptrenderer.cpp | 43 ++-- App/PT/ptrenderer.h | 7 +- App/main.cpp | 20 +- 12 files changed, 389 insertions(+), 444 deletions(-) rename App/CL/{random.cl => common.cl} (60%) mode change 100755 => 100644 diff --git a/App/CL/bxdf.cl b/App/CL/bxdf.cl index e3d21ba6..ada3727a 100755 --- a/App/CL/bxdf.cl +++ b/App/CL/bxdf.cl @@ -23,7 +23,6 @@ THE SOFTWARE. #define BXDF_CL #include <../App/CL/utils.cl> -#include <../App/CL/random.cl> #include <../App/CL/texture.cl> #include <../App/CL/payload.cl> diff --git a/App/CL/camera.cl b/App/CL/camera.cl index 3a34623e..c49b469b 100755 --- a/App/CL/camera.cl +++ b/App/CL/camera.cl @@ -22,15 +22,12 @@ THE SOFTWARE. #ifndef CAMERA_CL #define CAMERA_CL +#include <../App/CL/common.cl> #include <../App/CL/payload.cl> -#include <../App/CL/random.cl> #include <../App/CL/sampling.cl> #include <../App/CL/utils.cl> #include <../App/CL/path.cl> -#define CMJ 1 -#define CMJ_DIM 4 - /// Ray generation kernel for perspective camera. /// Rays are generated from camera position to viewing plane @@ -43,12 +40,11 @@ __kernel void PerspectiveCamera_GeneratePaths( int imgwidth, int imgheight, // RNG seed value - int randseed, + uint rngseed, // Output rays __global ray* rays, - __global SobolSampler* samplers, + __global uint const* random, __global uint const* sobolmat, - int reset, int frame #ifndef NO_PATH_DATA ,__global Path* paths @@ -69,36 +65,22 @@ __kernel void PerspectiveCamera_GeneratePaths( __global Path* mypath = paths + globalid.y * imgwidth + globalid.x; #endif - // Prepare RNG - Rng rng; - InitRng(randseed + globalid.x * 157 + 10433 * globalid.y, &rng); - -#if SOBOL == 1 - __global SobolSampler* sampler = samplers + globalid.y * imgwidth + globalid.x; - - if (reset) - { - sampler->seq = 0; - sampler->s0 = RandUint(&rng); - } - else - { - sampler->seq++; - } - - float2 sample0; - sample0.x = SobolSampler_Sample1D(sampler->seq, kPixelX, sampler->s0, sobolmat); - sample0.y = SobolSampler_Sample1D(sampler->seq, kPixelY, sampler->s0, sobolmat); -#elif RANDOM == 1 - float2 sample0 = UniformSampler_Sample2D(&rng); -#elif CMJ == 1 - // Pass defines current current light selection and current material selection - int pass = frame / (CMJ_DIM * CMJ_DIM); - int pattern0 = permute(globalid.y * imgwidth + globalid.x, (1024 * 1024), pass * 0xc13719e1); - int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern0 * 0xc517e953); - float2 sample0 = cmj(subsample0, CMJ_DIM, pattern0); + Sampler sampler; +#if SAMPLER == SOBOL + uint scramble = random[globalid.x + imgwidth * globalid.y] * 0x1fe3434f; + Sampler_Init(&sampler, frame, SAMPLE_DIM_CAMERA_OFFSET, scramble); +#elif SAMPLER == RANDOM + uint scramble = globalid.x + imgwidth * globalid.y * rngseed; + Sampler_Init(&sampler, scramble); +#elif SAMPLER == CMJ + uint rnd = random[globalid.x + imgwidth * globalid.y]; + uint scramble = rnd * 0x1fe3434f * ((frame + 133 * rnd) / (CMJ_DIM * CMJ_DIM)); + Sampler_Init(&sampler, frame % (CMJ_DIM * CMJ_DIM), SAMPLE_DIM_CAMERA_OFFSET, scramble); #endif + // Generate sample + float2 sample0 = Sampler_Sample2D(&sampler, SAMPLER_ARGS); + // Calculate [0..1] image plane sample float2 imgsample; imgsample.x = (float)globalid.x / imgwidth + sample0.x / imgwidth; @@ -142,12 +124,12 @@ __kernel void PerspectiveCameraDof_GeneratePaths( int imgwidth, int imgheight, // RNG seed value - int randseed, + uint rngseed, // Output rays __global ray* rays, - __global SobolSampler* samplers, + __global uint* random, __global uint const* sobolmat, - int reset + int frame #ifndef NO_PATH_DATA , __global Path* paths #endif @@ -167,36 +149,24 @@ __kernel void PerspectiveCameraDof_GeneratePaths( __global Path* mypath = paths + globalid.y * imgwidth + globalid.x; #endif - // Prepare RNG - Rng rng; - InitRng(randseed + globalid.x * 157 + 10433 * globalid.y, &rng); - - // -#ifdef SOBOL - __global SobolSampler* sampler = samplers + globalid.y * imgwidth + globalid.x; - - if (reset) - { - sampler->seq = 0; - sampler->s0 = RandUint(&rng); - } - else - { - sampler->seq++; - } - - float2 sample0; - sample0.x = SobolSampler_Sample1D(sampler->seq, kPixelX, sampler->s0, sobolmat); - sample0.y = SobolSampler_Sample1D(sampler->seq, kPixelY, sampler->s0, sobolmat); - - float2 sample1; - sample1.x = SobolSampler_Sample1D(sampler->seq, kLensX, sampler->s0, sobolmat); - sample1.y = SobolSampler_Sample1D(sampler->seq, kLensY, sampler->s0, sobolmat); -#else - float2 sample0 = UniformSampler_Sample2D(&rng); - float2 sample1 = UniformSampler_Sample2D(&rng); + Sampler sampler; +#if SAMPLER == SOBOL + uint scramble = random[globalid.x + imgwidth * globalid.y] * 0x1fe3434f; + Sampler_Init(&sampler, frame, SAMPLE_DIM_CAMERA_OFFSET, scramble); +#elif SAMPLER == RANDOM + uint scramble = globalid.x + imgwidth * globalid.y * rngseed; + Sampler_Init(&sampler, scramble); +#elif SAMPLER == CMJ + uint rnd = random[globalid.x + imgwidth * globalid.y]; + uint scramble = rnd * 0x1fe3434f * ((frame + 133 * rnd) / (CMJ_DIM * CMJ_DIM)); + Sampler_Init(&sampler, frame % (CMJ_DIM * CMJ_DIM), SAMPLE_DIM_CAMERA_OFFSET, scramble); #endif + // Generate pixel and lens samples + float2 sample0 = Sampler_Sample2D(&sampler, SAMPLER_ARGS); + float2 sample1 = Sampler_Sample2D(&sampler, SAMPLER_ARGS); + + // Calculate [0..1] image plane sample float2 imgsample; imgsample.x = (float)globalid.x / imgwidth + sample0.x / imgwidth; diff --git a/App/CL/random.cl b/App/CL/common.cl old mode 100755 new mode 100644 similarity index 60% rename from App/CL/random.cl rename to App/CL/common.cl index c3d755c5..7263b119 --- a/App/CL/random.cl +++ b/App/CL/common.cl @@ -1,65 +1,43 @@ -/********************************************************************** -Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -********************************************************************/ -#ifndef RANDOM_CL -#define RANDOM_CL - -/// Pseudo-random number generator state -typedef struct _Rng - { - uint val; - } Rng; - -/// Hash function -uint WangHash(uint seed) -{ - seed = (seed ^ 61) ^ (seed >> 16); - seed *= 9; - seed = seed ^ (seed >> 4); - seed *= 0x27d4eb2d; - seed = seed ^ (seed >> 15); - return seed; -} - -/// Return random unsigned -uint RandUint(Rng* rng) -{ - rng->val = WangHash(1664525U * rng->val + 1013904223U); - return rng->val; -} - -/// Return random float -float RandFloat(Rng* rng) -{ - return ((float)RandUint(rng)) / 0xffffffffU; -} - -/// Initialize RNG -void InitRng(uint seed, Rng* rng) -{ - rng->val = WangHash(seed); - for (int i=0;i< 100;++i) - RandFloat(rng); -} - -#endif // RANDOM_CL - - +/********************************************************************** +Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +********************************************************************/ +#ifndef COMMON_CL +#define COMMON_CL + + +#define CRAZY_LOW_THROUGHPUT 0.0f +#define CRAZY_HIGH_RADIANCE 3.f +#define CRAZY_HIGH_DISTANCE 1000000.f +#define CRAZY_LOW_DISTANCE 0.001f +#define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE)) +#define NON_BLACK(x) (length(x) > 0.f) + +#define MULTISCATTER + +#define RANDOM 1 +#define SOBOL 2 +#define CMJ 3 + +#define SAMPLER CMJ + +#define CMJ_DIM 4 + +#endif // COMMON_CL \ No newline at end of file diff --git a/App/CL/integrator_ao.cl b/App/CL/integrator_ao.cl index ee201867..764a37da 100755 --- a/App/CL/integrator_ao.cl +++ b/App/CL/integrator_ao.cl @@ -1,5 +1,4 @@ #include <../App/CL/utils.cl> -#include <../App/CL/random.cl> #include <../App/CL/payload.cl> #include <../App/CL/texture.cl> #include <../App/CL/sampling.cl> diff --git a/App/CL/integrator_pt.cl b/App/CL/integrator_pt.cl index 41c873e1..d273ffc0 100755 --- a/App/CL/integrator_pt.cl +++ b/App/CL/integrator_pt.cl @@ -21,13 +21,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ********************************************************************/ -//#define SOBOL -#define MULTISCATTER + +#include <../App/CL/common.cl> #include <../App/CL/ray.cl> #include <../App/CL/isect.cl> #include <../App/CL/utils.cl> -#include <../App/CL/random.cl> #include <../App/CL/payload.cl> #include <../App/CL/texture.cl> #include <../App/CL/sampling.cl> @@ -40,15 +39,6 @@ THE SOFTWARE. #include <../App/CL/volumetrics.cl> #include <../App/CL/path.cl> -#define CRAZY_LOW_THROUGHPUT 0.0f -#define CRAZY_HIGH_RADIANCE 3.f -#define CRAZY_HIGH_DISTANCE 1000000.f -#define CRAZY_LOW_DISTANCE 0.001f -#define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE)) -#define NON_BLACK(x) (length(x) > 0.f) - -#define CMJ 1 - // This kernel only handles scattered paths. // It applies direct illumination and generates @@ -89,13 +79,15 @@ __kernel void ShadeVolume( // Number of emissive objects int num_lights, // RNG seed - int rngseed, + uint rngseed, // Sampler state - __global SobolSampler* samplers, + __global uint* random, // Sobol matrices __global uint const* sobolmat, // Current bounce int bounce, + // Current frame + int frame, // Volume data __global Volume const* volumes, // Shadow rays @@ -146,27 +138,20 @@ __kernel void ShadeVolume( float3 o = rays[hitidx].o.xyz; float3 wi = rays[hitidx].d.xyz; -#ifdef SOBOL - __global SobolSampler* sampler = samplers + pixelidx; - float sample0 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLight), sampler->s0, sobolmat); - float2 sample1; - sample1.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLightU), sampler->s0, sobolmat); - sample1.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLightV), sampler->s0, sobolmat); -#ifdef MULTISCATTER - float2 sample2; - sample2.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeIndirectU), sampler->s0, sobolmat); - sample2.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeIndirectV), sampler->s0, sobolmat); -#endif -#else - // Prepare RNG for sampling - Rng rng; - InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); - float sample0 = UniformSampler_Sample2D(&rng).x; - float2 sample1 = UniformSampler_Sample2D(&rng); -#ifdef MULTISCATTER - float2 sample2 = UniformSampler_Sample2D(&rng); -#endif + Sampler sampler; +#if SAMPLER == SOBOL + uint scramble = random[pixelidx] * 0x1fe3434f; + Sampler_Init(&sampler, frame, SAMPLE_DIM_SURFACE_OFFSET + bounce * SAMPLE_DIMS_PER_BOUNCE + SAMPLE_DIM_VOLUME_EVALUATE_OFFSET, scramble); +#elif SAMPLER == RANDOM + uint scramble = pixelidx * rngseed; + Sampler_Init(&sampler, scramble); +#elif SAMPLER == CMJ + uint rnd = random[pixelidx]; + uint scramble = rnd * 0x1fe3434f * ((frame + 13 * rnd) / (CMJ_DIM * CMJ_DIM)); + Sampler_Init(&sampler, frame % (CMJ_DIM * CMJ_DIM), SAMPLE_DIM_SURFACE_OFFSET + bounce * SAMPLE_DIMS_PER_BOUNCE + SAMPLE_DIM_VOLUME_EVALUATE_OFFSET, scramble); #endif + + // Here we know that volidx != -1 since this is a precondition // for scattering event int volidx = Path_GetVolumeIdx(path); @@ -176,7 +161,7 @@ __kernel void ShadeVolume( float selection_pdf = 0.f; float3 wo; - int light_idx = Scene_SampleLight(&scene, sample0, &selection_pdf); + int light_idx = Scene_SampleLight(&scene, Sampler_Sample1D(&sampler, SAMPLER_ARGS), &selection_pdf); // Here we need fake differential geometry for light sampling procedure DifferentialGeometry dg; @@ -184,7 +169,7 @@ __kernel void ShadeVolume( // since EvaluateVolume has put it there dg.p = o + wi * Intersection_GetDistance(isects + hitidx); // Get light sample intencity - float3 le = Light_Sample(light_idx, &scene, &dg, TEXTURE_ARGS, sample1, &wo, &pdf); + float3 le = Light_Sample(light_idx, &scene, &dg, TEXTURE_ARGS, Sampler_Sample2D(&sampler, SAMPLER_ARGS), &wo, &pdf); // Generate shadow ray float shadow_ray_length = 0.999f * length(wo); @@ -220,7 +205,7 @@ __kernel void ShadeVolume( #ifdef MULTISCATTER // This is highly brute-force // TODO: investigate importance sampling techniques here - wo = Sample_MapToSphere(sample2); + wo = Sample_MapToSphere(Sampler_Sample2D(&sampler, SAMPLER_ARGS)); pdf = 1.f / (4.f * PI); // Generate new path segment @@ -237,7 +222,7 @@ __kernel void ShadeVolume( } } -#define CMJ_DIM 4 + // Handle ray-surface interaction possibly generating path continuation. // This is only applied to non-scattered paths. @@ -277,9 +262,9 @@ __kernel void ShadeSurface( // Number of emissive objects int num_lights, // RNG seed - int rngseed, + uint rngseed, // Sampler states - __global SobolSampler* samplers, + __global uint* random, // Sobol matrices __global uint const* sobolmat, // Current bounce @@ -317,9 +302,6 @@ __kernel void ShadeSurface( num_lights }; - // Pass defines current current light selection and current material selection - int pass = frame / (CMJ_DIM * CMJ_DIM); - // Only applied to active rays after compaction if (globalid < *numhits) { @@ -338,59 +320,18 @@ __kernel void ShadeSurface( // Fetch incoming ray direction float3 wi = -normalize(rays[hitidx].d.xyz); -#if (SOBOL == 1) - // Sample light - __global SobolSampler* sampler = samplers + pixelidx; - - float2 sample0; - sample0.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdf), sampler->s0, sobolmat); - sample0.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLight), sampler->s0, sobolmat); - - float2 sample1; - sample1.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLightU), sampler->s0, sobolmat); - sample1.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLightV), sampler->s0, sobolmat); - - float2 sample2; - sample2.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdfU), sampler->s0, sobolmat); - sample2.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdfV), sampler->s0, sobolmat); - - float2 sample3; - sample3.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectU), sampler->s0, sobolmat); - sample3.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectV), sampler->s0, sobolmat); - - float sample4 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kRR), sampler->s0, sobolmat); -#elif (RANDOM == 1) - // Prepare RNG - Rng rng; - InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); - float2 sample0 = UniformSampler_Sample2D(&rng); - float2 sample1 = UniformSampler_Sample2D(&rng); - float2 sample2 = UniformSampler_Sample2D(&rng); - float2 sample3 = UniformSampler_Sample2D(&rng); - float sample4 = UniformSampler_Sample2D(&rng).x; -#elif (CMJ == 1) - Rng rng; - InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); - - int pattern0 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx); - int pattern1 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 1); - int pattern2 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 2); - int pattern3 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 3); - int pattern4 = permute(pixelidx, (1024 * 1024), pass * 1024 * 1024 * 20 * 5 + bounce * 1024 * 1024 * 5 + pixelidx + 4); - - int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern4 * pixelidx * 0xc117d953); - int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern3 * pixelidx * 0xc117d953); - int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern2 * pixelidx * 0xc117d953); - int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern1 * pixelidx * 0xc117d953); - int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, pattern0 * pixelidx * 0xc117d953); - - float2 sample0 = cmj(subsample0, CMJ_DIM, pattern0); - float2 sample1 = cmj(subsample1, CMJ_DIM, pattern1); - float2 sample2 = cmj(subsample2, CMJ_DIM, pattern2); - float2 sample3 = cmj(subsample3, CMJ_DIM, pattern3); - float sample4 = cmj(subsample4, CMJ_DIM, pattern4).x; -#else + Sampler sampler; +#if SAMPLER == SOBOL + uint scramble = random[pixelidx] * 0x1fe3434f; + Sampler_Init(&sampler, frame, SAMPLE_DIM_SURFACE_OFFSET + bounce * SAMPLE_DIMS_PER_BOUNCE, scramble); +#elif SAMPLER == RANDOM + uint scramble = pixelidx * rngseed; + Sampler_Init(&sampler, scramble); +#elif SAMPLER == CMJ + uint rnd = random[pixelidx]; + uint scramble = rnd * 0x1fe3434f * ((frame + 331 * rnd) / (CMJ_DIM * CMJ_DIM)); + Sampler_Init(&sampler, frame % (CMJ_DIM * CMJ_DIM), SAMPLE_DIM_SURFACE_OFFSET + bounce * SAMPLE_DIMS_PER_BOUNCE, scramble); #endif // Fill surface data @@ -415,15 +356,7 @@ __kernel void ShadeSurface( float ndotwi = dot(diffgeo.n, wi); // Select BxDF - Material_Select( - &scene, wi, TEXTURE_ARGS, -#ifdef SOBOL - sampler, sobolmat, bounce, -#else - &rng, -#endif - &diffgeo - ); + Material_Select(&scene, wi, &sampler, TEXTURE_ARGS, SAMPLER_ARGS, &diffgeo); // Terminate if emissive if (Bxdf_IsEmissive(&diffgeo)) @@ -470,8 +403,6 @@ __kernel void ShadeSurface( diffgeo.dpdv = -diffgeo.dpdv; } - - // TODO: this is test code, need to // maintain proper volume stack here //if (Bxdf_IsBtdf(&diffgeo)) @@ -497,19 +428,18 @@ __kernel void ShadeSurface( float bxdfweight = 1.f; float lightweight = 1.f; - int light_idx = pass % num_lights; - selection_pdf = 1.f / num_lights; + int light_idx = Scene_SampleLight(&scene, Sampler_Sample1D(&sampler, SAMPLER_ARGS), &selection_pdf); float3 throughput = Path_GetThroughput(path); // Sample bxdf - float3 bxdf = Bxdf_Sample(&diffgeo, wi, TEXTURE_ARGS, sample2, &bxdfwo, &bxdfpdf); + float3 bxdf = Bxdf_Sample(&diffgeo, wi, TEXTURE_ARGS, Sampler_Sample2D(&sampler, SAMPLER_ARGS), &bxdfwo, &bxdfpdf); // If we have light to sample we can hopefully do mis if (light_idx > -1) { // Sample light - float3 le = Light_Sample(light_idx, &scene, &diffgeo, TEXTURE_ARGS, sample1, &lightwo, &lightpdf); + float3 le = Light_Sample(light_idx, &scene, &diffgeo, TEXTURE_ARGS, Sampler_Sample2D(&sampler, SAMPLER_ARGS), &lightwo, &lightpdf); lightbxdfpdf = Bxdf_GetPdf(&diffgeo, wi, normalize(lightwo), TEXTURE_ARGS); lightweight = Light_IsSingular(&scene.lights[light_idx]) ? 1.f : BalanceHeuristic(1, lightpdf, 1, lightbxdfpdf); @@ -558,7 +488,7 @@ __kernel void ShadeSurface( 0.2126f * throughput.x + 0.7152f * throughput.y + 0.0722f * throughput.z), 0.01f); // Only if it is 3+ bounce bool rr_apply = bounce > 3; - bool rr_stop = sample4 > q && rr_apply; + bool rr_stop = Sampler_Sample1D(&sampler, SAMPLER_ARGS) > q && rr_apply; if (rr_apply) { diff --git a/App/CL/light.cl b/App/CL/light.cl index ebafe06d..81f42ce6 100755 --- a/App/CL/light.cl +++ b/App/CL/light.cl @@ -24,7 +24,6 @@ THE SOFTWARE. #include <../App/CL/utils.cl> #include <../App/CL/payload.cl> -#include <../App/CL/random.cl> #include <../App/CL/texture.cl> #include <../App/CL/scene.cl> diff --git a/App/CL/material.cl b/App/CL/material.cl index b5690c88..71390b9e 100755 --- a/App/CL/material.cl +++ b/App/CL/material.cl @@ -22,10 +22,8 @@ THE SOFTWARE. #ifndef MATERIAL_CL #define MATERIAL_CL -#define CMJ 1 - +#include <../App/CL/common.cl> #include <../App/CL/utils.cl> -#include <../App/CL/random.cl> #include <../App/CL/texture.cl> #include <../App/CL/payload.cl> #include <../App/CL/bxdf.cl> @@ -35,20 +33,12 @@ void Material_Select( Scene const* scene, // Incoming direction float3 wi, + // Sampler + Sampler* sampler, // Texture args TEXTURE_ARG_LIST, - -#ifdef SOBOL - // Sampler state - __global SobolSampler* sampler, - // Sobol matrices - __global uint const* sobolmat, - // Current bounce - int bounce, -#else - Rng* rng, -#endif - + // Sampler args + SAMPLER_ARG_LIST, // Geometry DifferentialGeometry* dg ) @@ -134,11 +124,7 @@ void Material_Select( fresnel = FresnelDielectric(etai, etat, cosi, cost); } -#ifdef SOBOL - float sample = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kMaterial + iter), sampler->s0, sobolmat); -#else - float sample = UniformSampler_Sample2D(rng).x; -#endif + float sample = Sampler_Sample1D(sampler, SAMPLER_ARGS); if (sample < fresnel) { @@ -159,12 +145,8 @@ void Material_Select( } else { + float sample = Sampler_Sample1D(sampler, SAMPLER_ARGS); -#ifdef SOBOL - float sample = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kMaterial + iter), sampler->s0, sobolmat); -#else - float sample = UniformSampler_Sample2D(rng).x; -#endif float weight = Texture_GetValue1f(mat.ns, dg->uv, TEXTURE_ARGS_IDX(mat.nsmapidx)); if (sample < weight) diff --git a/App/CL/sampling.cl b/App/CL/sampling.cl index f2028fb6..8ffd2216 100755 --- a/App/CL/sampling.cl +++ b/App/CL/sampling.cl @@ -23,41 +23,219 @@ THE SOFTWARE. #define SAMPLING_CL #include <../App/CL/utils.cl> -#include <../App/CL/random.cl> -typedef enum + +#define SAMPLE_DIMS_PER_BOUNCE 300 +#define SAMPLE_DIM_CAMERA_OFFSET 0 +#define SAMPLE_DIM_SURFACE_OFFSET 4 +#define SAMPLE_DIM_VOLUME_APPLY_OFFSET 100 +#define SAMPLE_DIM_VOLUME_EVALUATE_OFFSET 200 + +typedef struct +{ + uint seq; + uint s0; + uint s1; + uint s2; +} SobolSampler; + +typedef struct _Sampler +{ + uint index; + uint dimension; + uint scramble; + uint padding; +} Sampler; + +#if SAMPLER == SOBOL +#define SAMPLER_ARG_LIST __global uint const* sobolmat +#define SAMPLER_ARGS sobolmat +#elif SAMPLER == RANDOM +#define SAMPLER_ARG_LIST int unused +#define SAMPLER_ARGS 0 +#elif SAMPLER == CMJ +#define SAMPLER_ARG_LIST int unused +#define SAMPLER_ARGS 0 +#endif + +/** + Sobol sampler +**/ +#define MATSIZE 52 + +// The code is taken from: http://gruenschloss.org/sobol/kuo-2d-proj-single-precision.zip +// +float SobolSampler_Sample1D(Sampler* sampler, __global uint const* mat) +{ + uint result = sampler->scramble; + uint index = sampler->index; + for (uint i = sampler->dimension * MATSIZE; index; index >>= 1, ++i) { - kPixelX = 0, - kPixelY = 1, - kLensX = 2, - kLensY = 3, - kPathBase = 4, - kBrdf = 0, - kLight = 1, - kLightU = 2, - kLightV = 3, - kBrdfU = 4, - kBrdfV = 5, - kIndirectU = 6, - kIndirectV = 7, - kRR = 8, - kVolume = 9, - kVolumeLight = 10, - kVolumeLightU = 11, - kVolumeLightV = 12, - kMaterial = 13, -#ifdef MULTISCATTER - kVolumeIndirectU = 14, - kVolumeIndirectV = 15, - kNumPerBounce = 16, -#else - kNumPerBounce = 14 + if (index & 1) + result ^= mat[i]; + } + + return result * (1.f / (1UL << 32)); +} + +/** + Random sampler +**/ + +/// Hash function +uint WangHash(uint seed) +{ + seed = (seed ^ 61) ^ (seed >> 16); + seed *= 9; + seed = seed ^ (seed >> 4); + seed *= 0x27d4eb2d; + seed = seed ^ (seed >> 15); + return seed; +} + +/// Return random unsigned +uint UniformSampler_SampleUint(Sampler* sampler) +{ + sampler->index = WangHash(1664525U * sampler->index + 1013904223U); + return sampler->index; +} + +/// Return random float +float UniformSampler_Sample1D(Sampler* sampler) +{ + return ((float)UniformSampler_SampleUint(sampler)) / 0xffffffffU; +} + + +/** + Correllated multi-jittered +**/ + +uint permute(uint i, uint l, uint p) +{ + unsigned w = l - 1; + w |= w >> 1; + w |= w >> 2; + w |= w >> 4; + w |= w >> 8; + w |= w >> 16; + + do + { + i ^= p; + i *= 0xe170893d; + i ^= p >> 16; + i ^= (i & w) >> 4; + i ^= p >> 8; + i *= 0x0929eb3f; + i ^= p >> 23; + i ^= (i & w) >> 1; + i *= 1 | p >> 27; + i *= 0x6935fa69; + i ^= (i & w) >> 11; + i *= 0x74dcb303; + i ^= (i & w) >> 2; + i *= 0x9e501cc3; + i ^= (i & w) >> 2; + i *= 0xc860a3df; + i &= w; + i ^= i >> 5; + } while (i >= l); + return (i + p) % l; +} + +float randfloat(uint i, uint p) +{ + i ^= p; + i ^= i >> 17; + i ^= i >> 10; + i *= 0xb36534e5; + i ^= i >> 12; + i ^= i >> 21; + i *= 0x93fc4795; + i ^= 0xdf6e307f; + i ^= i >> 17; + i *= 1 | p >> 18; + return i * (1.0f / 4294967808.0f); +} + +float2 cmj(int s, int n, int p) +{ + int sx = permute(s % n, n, p * 0xa511e9b3); + int sy = permute(s / n, n, p * 0x63d83595); + float jx = randfloat(s, p * 0xa399d265); + float jy = randfloat(s, p * 0x711ad6a5); + + return make_float2((s % n + (sy + jx) / n) / n, + (s / n + (sx + jy) / n) / n); +} + +float2 CmjSampler_Sample2D(Sampler* sampler) +{ + int idx = permute(sampler->index, CMJ_DIM * CMJ_DIM, 0xa399d265 * sampler->scramble); + return cmj(idx, CMJ_DIM, sampler->dimension * sampler->scramble); +} + +#if SAMPLER == SOBOL +void Sampler_Init(Sampler* sampler, uint index, uint start_dimension, uint scramble) +{ + sampler->index = index; + sampler->scramble = scramble; + sampler->dimension = start_dimension; +} +#elif SAMPLER == RANDOM +void Sampler_Init(Sampler* sampler, uint seed) +{ + sampler->index = seed; + sampler->scramble = 0; + sampler->dimension = 0; +} +#elif SAMPLER == CMJ +void Sampler_Init(Sampler* sampler, uint index, uint dimension, uint scramble) +{ + sampler->index = index; + sampler->scramble = scramble; + sampler->dimension = dimension; +} #endif - } SampleDim; -int GetSampleDim(int pass, SampleDim dim) + +float2 Sampler_Sample2D(Sampler* sampler, SAMPLER_ARG_LIST) { - return kPathBase + pass * kNumPerBounce + dim; +#if SAMPLER == SOBOL + float2 sample; + sample.x = SobolSampler_Sample1D(sampler, SAMPLER_ARGS); + ++(sampler->dimension); + sample.y = SobolSampler_Sample1D(sampler, SAMPLER_ARGS); + ++(sampler->dimension); + return sample; +#elif SAMPLER == RANDOM + float2 sample; + sample.x = UniformSampler_Sample1D(sampler); + sample.y = UniformSampler_Sample1D(sampler); + return sample; +#elif SAMPLER == CMJ + float2 sample; + sample = CmjSampler_Sample2D(sampler); + ++(sampler->dimension); + return sample; +#endif +} + +float Sampler_Sample1D(Sampler* sampler, SAMPLER_ARG_LIST) +{ +#if SAMPLER == SOBOL + float sample = SobolSampler_Sample1D(sampler, SAMPLER_ARGS); + ++(sampler->dimension); + return sample; +#elif SAMPLER == RANDOM + return UniformSampler_Sample1D(sampler); +#elif SAMPLER == CMJ + float2 sample; + sample = CmjSampler_Sample2D(sampler); + ++(sampler->dimension); + return sample.x; +#endif } /// Sample hemisphere with cos weight @@ -143,17 +321,12 @@ float3 Sample_MapToSphere( float2 Sample_MapToPolygon(int n, float2 sample, float sample1) { float theta = 2.f * PI / n; - int edge = clamp((int)(sample1 * n), 0, n - 1); - float t = native_sqrt(sample.x); float u = 1.f - t; float v = t * sample.y; - float2 v1 = make_float2(native_cos(theta * edge), native_sin(theta * edge)); float2 v2 = make_float2(native_cos(theta * (edge + 1)), native_sin(theta * (edge + 1))); - - return u*v1 + v*v2;; } @@ -173,96 +346,5 @@ float BalanceHeuristic(int nf, float fpdf, int ng, float gpdf) return (f) / (f + g); } -typedef struct -{ - uint seq; - uint s0; - uint s1; - uint s2; -} SobolSampler; - -float2 UniformSampler_Sample2D(Rng* rng) -{ - float2 sample; - sample.x = RandFloat(rng); - sample.y = RandFloat(rng); - return sample; -} - -#define MATSIZE 52 - -// The code is taken from: http://gruenschloss.org/sobol/kuo-2d-proj-single-precision.zip -// -float SobolSampler_Sample1D(uint index, uint dimension, uint scramble, __global uint const* mat) -{ - uint result = scramble; - for (uint i = dimension * MATSIZE; index; index >>= 1, ++i) - { - if (index & 1) - result ^= mat[i]; - } - - return result * (1.f / (1UL << 32)); -} - -uint permute(uint i, uint l, uint p) -{ - unsigned w = l - 1; - w |= w >> 1; - w |= w >> 2; - w |= w >> 4; - w |= w >> 8; - w |= w >> 16; - - do - { - i ^= p; - i *= 0xe170893d; - i ^= p >> 16; - i ^= (i & w) >> 4; - i ^= p >> 8; - i *= 0x0929eb3f; - i ^= p >> 23; - i ^= (i & w) >> 1; - i *= 1 | p >> 27; - i *= 0x6935fa69; - i ^= (i & w) >> 11; - i *= 0x74dcb303; - i ^= (i & w) >> 2; - i *= 0x9e501cc3; - i ^= (i & w) >> 2; - i *= 0xc860a3df; - i &= w; - i ^= i >> 5; - } while (i >= l); - return (i + p) % l; -} - -float randfloat(uint i, uint p) -{ - i ^= p; - i ^= i >> 17; - i ^= i >> 10; - i *= 0xb36534e5; - i ^= i >> 12; - i ^= i >> 21; - i *= 0x93fc4795; - i ^= 0xdf6e307f; - i ^= i >> 17; - i *= 1 | p >> 18; - return i * (1.0f / 4294967808.0f); -} - -float2 cmj(int s, int n, int p) -{ - int sx = permute(s % n, n, p * 0xa511e9b3); - int sy = permute(s / n, n, p * 0x63d83595); - float jx = randfloat(s, p * 0xa399d265); - float jy = randfloat(s, p * 0x711ad6a5); - - return make_float2((s % n + (sy + jx) / n) / n, - (s / n + (sx + jy) / n) / n); -} - #endif // SAMPLING_CL diff --git a/App/CL/volumetrics.cl b/App/CL/volumetrics.cl index 0ed85c4a..172d6768 100755 --- a/App/CL/volumetrics.cl +++ b/App/CL/volumetrics.cl @@ -22,14 +22,12 @@ THE SOFTWARE. #ifndef VOLUMETRICS_CL #define VOLUMETRICS_CL +#include <../App/CL/common.cl> #include <../App/CL/payload.cl> #include <../App/CL/path.cl> #define FAKE_SHAPE_SENTINEL 0xFFFFFF - - - // The following functions are taken from PBRT float PhaseFunction_Uniform(float3 wi, float3 wo) { @@ -61,7 +59,6 @@ float PhaseFunction_HG(float3 wi, float3 wo, float g) (1.f - g*g) / native_powr(1.f + g*g - 2.f * g * costheta, 1.5f); } - // Evaluate volume transmittance along the ray [0, dist] segment float3 Volume_Transmittance(__global Volume const* volume, __global ray const* ray, float dist) { @@ -130,13 +127,15 @@ __kernel void EvaluateVolume( // Textures TEXTURE_ARG_LIST, // RNG seed - int rngseed, + uint rngseed, // Sampler state - __global SobolSampler* samplers, + __global uint* random, // Sobol matrices __global uint const* sobolmat, // Current bounce int bounce, + // Current frame + int frame, // Intersection data __global Intersection* isects, // Current paths @@ -164,18 +163,23 @@ __kernel void EvaluateVolume( // Check if we are inside some volume if (volidx != -1) { -#ifdef SOBOL - __global SobolSampler* sampler = samplers + pixelidx; - float sample = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolume), sampler->s0, sobolmat); -#else - Rng rng; - InitRng(rngseed + (globalid << 2) * 157 + 13, &rng); - float sample = UniformSampler_Sample2D(&rng).x; + Sampler sampler; +#if SAMPLER == SOBOL + uint scramble = random[pixelidx] * 0x1fe3434f; + Sampler_Init(&sampler, frame, SAMPLE_DIM_SURFACE_OFFSET + bounce * SAMPLE_DIMS_PER_BOUNCE + SAMPLE_DIM_VOLUME_APPLY_OFFSET, scramble); +#elif SAMPLER == RANDOM + uint scramble = pixelidx * rngseed; + Sampler_Init(&sampler, scramble); +#elif SAMPLER == CMJ + uint rnd = random[pixelidx]; + uint scramble = rnd * 0x1fe3434f * ((frame + 71 * rnd) / (CMJ_DIM * CMJ_DIM)); + Sampler_Init(&sampler, frame % (CMJ_DIM * CMJ_DIM), SAMPLE_DIM_SURFACE_OFFSET + bounce * SAMPLE_DIMS_PER_BOUNCE + SAMPLE_DIM_VOLUME_APPLY_OFFSET, scramble); #endif + // Try sampling volume for a next scattering event float pdf = 0.f; float maxdist = Intersection_GetDistance(isects + globalid); - float d = Volume_SampleDistance(&volumes[volidx], &rays[globalid], maxdist, sample, &pdf); + float d = Volume_SampleDistance(&volumes[volidx], &rays[globalid], maxdist, Sampler_Sample1D(&sampler, SAMPLER_ARGS), &pdf); // Check if we shall skip the event (it is either outside of a volume or not happened at all) bool skip = d < 0 || d > maxdist || pdf <= 0.f; diff --git a/App/PT/ptrenderer.cpp b/App/PT/ptrenderer.cpp index afd93f66..1ade90c0 100755 --- a/App/PT/ptrenderer.cpp +++ b/App/PT/ptrenderer.cpp @@ -29,6 +29,8 @@ THE SOFTWARE. #include #include #include +#include +#include #include "sobol.h" @@ -42,14 +44,6 @@ namespace Baikal static int const kMaxLightSamples = 1; - struct PtRenderer::QmcSampler - { - std::uint32_t seq; - std::uint32_t s0; - std::uint32_t s1; - std::uint32_t s2; - }; - struct PtRenderer::PathState { float4 throughput; @@ -75,8 +69,8 @@ namespace Baikal CLWBuffer lightsamples; CLWBuffer paths; - CLWBuffer samplers; - CLWBuffer sobolmat; + CLWBuffer random; + CLWBuffer sobolmat; CLWBuffer hitcount; CLWProgram program; @@ -111,7 +105,6 @@ namespace Baikal , m_output(nullptr) , m_render_data(new RenderData) , m_vidmemws(0) - , m_resetsampler(true) , m_scene_tracker(context, devidx) , m_num_bounces(num_bounces) , m_framecnt(0) @@ -162,7 +155,7 @@ namespace Baikal void PtRenderer::Clear(RadeonRays::float3 const& val, Output& output) const { static_cast(output).Clear(val); - m_resetsampler = true; + m_framecnt = 0; } void PtRenderer::SetNumBounces(int num_bounces) @@ -180,9 +173,6 @@ namespace Baikal auto api = m_scene_tracker.GetIntersectionApi(); auto& clwscene = m_scene_tracker.CompileScene(scene, m_render_data->mat_collector, m_render_data->tex_collector); - if (m_resetsampler) - m_framecnt = 0; - // Check output assert(m_output); @@ -294,8 +284,11 @@ namespace Baikal m_render_data->paths = m_context.CreateBuffer(output.width() * output.height(), CL_MEM_READ_WRITE); m_vidmemws += output.width() * output.height() * sizeof(PathState); - m_render_data->samplers = m_context.CreateBuffer(output.width() * output.height(), CL_MEM_READ_WRITE); - m_vidmemws += output.width() * output.height() * sizeof(QmcSampler); + std::vector random_buffer(output.width() * output.height()); + std::generate(random_buffer.begin(), random_buffer.end(), std::rand); + + m_render_data->random = m_context.CreateBuffer(output.width() * output.height(), CL_MEM_READ_ONLY, &random_buffer[0]); + m_vidmemws += output.width() * output.height() * sizeof(std::uint32_t); std::vector initdata(output.width() * output.height()); std::iota(initdata.begin(), initdata.end(), 0); @@ -348,12 +341,10 @@ namespace Baikal genkernel.SetArg(2, m_output->height()); genkernel.SetArg(3, (int)rand_uint()); genkernel.SetArg(4, m_render_data->rays[0]); - genkernel.SetArg(5, m_render_data->samplers); + genkernel.SetArg(5, m_render_data->random); genkernel.SetArg(6, m_render_data->sobolmat); - genkernel.SetArg(7, m_resetsampler); - genkernel.SetArg(8, m_framecnt); - genkernel.SetArg(9, m_render_data->paths); - m_resetsampler = 0; + genkernel.SetArg(7, m_framecnt); + genkernel.SetArg(8, m_render_data->paths); // Run generation kernel { @@ -390,7 +381,7 @@ namespace Baikal shadekernel.SetArg(argc++, scene.lights); shadekernel.SetArg(argc++, scene.num_lights); shadekernel.SetArg(argc++, rand_uint()); - shadekernel.SetArg(argc++, m_render_data->samplers); + shadekernel.SetArg(argc++, m_render_data->random); shadekernel.SetArg(argc++, m_render_data->sobolmat); shadekernel.SetArg(argc++, pass); shadekernel.SetArg(argc++, m_framecnt); @@ -434,9 +425,10 @@ namespace Baikal shadekernel.SetArg(argc++, scene.lights); shadekernel.SetArg(argc++, scene.num_lights); shadekernel.SetArg(argc++, rand_uint()); - shadekernel.SetArg(argc++, m_render_data->samplers); + shadekernel.SetArg(argc++, m_render_data->random); shadekernel.SetArg(argc++, m_render_data->sobolmat); shadekernel.SetArg(argc++, pass); + shadekernel.SetArg(argc++, m_framecnt); shadekernel.SetArg(argc++, scene.volumes); shadekernel.SetArg(argc++, m_render_data->shadowrays); shadekernel.SetArg(argc++, m_render_data->lightsamples); @@ -466,9 +458,10 @@ namespace Baikal evalkernel.SetArg(argc++, scene.textures); evalkernel.SetArg(argc++, scene.texturedata); evalkernel.SetArg(argc++, rand_uint()); - evalkernel.SetArg(argc++, m_render_data->samplers); + evalkernel.SetArg(argc++, m_render_data->random); evalkernel.SetArg(argc++, m_render_data->sobolmat); evalkernel.SetArg(argc++, pass); + evalkernel.SetArg(argc++, m_framecnt); evalkernel.SetArg(argc++, m_render_data->intersections); evalkernel.SetArg(argc++, m_render_data->paths); evalkernel.SetArg(argc++, m_output->data()); diff --git a/App/PT/ptrenderer.h b/App/PT/ptrenderer.h index 4c5352c7..e44cd581 100755 --- a/App/PT/ptrenderer.h +++ b/App/PT/ptrenderer.h @@ -92,13 +92,10 @@ namespace Baikal CLWContext m_context; // Output object ClwOutput* m_output; - // Flag to reset the sampler - mutable bool m_resetsampler; // Scene tracker SceneTracker m_scene_tracker; // GPU data - struct QmcSampler; struct PathState; struct RenderData; @@ -112,8 +109,8 @@ namespace Baikal size_t m_vidmemws; private: - int m_num_bounces; - int m_framecnt; + std::uint32_t m_num_bounces; + mutable std::uint32_t m_framecnt; }; } diff --git a/App/main.cpp b/App/main.cpp index dc0c0e7f..26d3acfc 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -107,7 +107,7 @@ float g_ao_radius = 1.f; float g_envmapmul = 1.f; float g_cspeed = 10.25f; -float3 g_camera_pos = float3(0.f, 4.f, 14.f); +float3 g_camera_pos = float3(0.f, 1.f, 3.f); float3 g_camera_at = float3(0.f, 1.f, 0.f); float3 g_camera_up = float3(0.f, 1.f, 0.f); @@ -348,8 +348,8 @@ void InitData() { // Load OBJ scene - std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoTest()); - g_scene.reset(scene_io->LoadScene("sphere+plane+area", basepath)); + std::unique_ptr scene_io(Baikal::SceneIo::CreateSceneIoObj()); + g_scene.reset(scene_io->LoadScene(filename, basepath)); // Enable this to generate new materal mapping for a model #if 0 @@ -484,7 +484,7 @@ void OnKey(int key, int x, int y) if (!g_interop) { std::ostringstream oss; - oss << "aov_color_" << g_frame_count << ".hdr"; + oss << "aov_color" << g_num_samples << ".png"; SaveFrameBuffer(oss.str(), &g_outputs[g_primary].fdata[0]); break; } @@ -1101,6 +1101,18 @@ void SaveFrameBuffer(std::string const& name, float3 const* data) std::vector tempbuf(g_window_width * g_window_height); tempbuf.assign(data, data + g_window_width*g_window_height); + for (auto y = 0; y < g_window_height; ++y) + for (auto x = 0; x < g_window_width; ++x) + { + + float3 val = data[(g_window_height - 1 - y) * g_window_width + x]; + tempbuf[y * g_window_width + x] = (1.f / val.w) * val; + + tempbuf[y * g_window_width + x].x = std::pow(tempbuf[y * g_window_width + x].x, 1.f / 2.2f); + tempbuf[y * g_window_width + x].y = std::pow(tempbuf[y * g_window_width + x].y, 1.f / 2.2f); + tempbuf[y * g_window_width + x].z = std::pow(tempbuf[y * g_window_width + x].z, 1.f / 2.2f); + } + ImageOutput* out = ImageOutput::create(name); if (!out) From eed26250e51ba303655e4d4d6cc4bee27c265a14 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Fri, 27 Jan 2017 17:03:38 +0100 Subject: [PATCH 21/24] Fix CMJ --- App/CL/sampling.cl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/App/CL/sampling.cl b/App/CL/sampling.cl index 8ffd2216..7fdf5763 100755 --- a/App/CL/sampling.cl +++ b/App/CL/sampling.cl @@ -172,7 +172,7 @@ float2 cmj(int s, int n, int p) float2 CmjSampler_Sample2D(Sampler* sampler) { - int idx = permute(sampler->index, CMJ_DIM * CMJ_DIM, 0xa399d265 * sampler->scramble); + int idx = permute(sampler->index, CMJ_DIM * CMJ_DIM, 0xa399d265 * sampler->dimension * sampler->scramble); return cmj(idx, CMJ_DIM, sampler->dimension * sampler->scramble); } From 0bcf130fd3dc3b810a20c6ca38d27113ac7e5cd7 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Fri, 27 Jan 2017 17:21:12 +0100 Subject: [PATCH 22/24] Fix Sobol --- App/CL/camera.cl | 14 +++++++++++++- App/CL/common.cl | 2 +- App/PT/ptrenderer.cpp | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/App/CL/camera.cl b/App/CL/camera.cl index c49b469b..29d60ff9 100755 --- a/App/CL/camera.cl +++ b/App/CL/camera.cl @@ -43,7 +43,7 @@ __kernel void PerspectiveCamera_GeneratePaths( uint rngseed, // Output rays __global ray* rays, - __global uint const* random, + __global uint* random, __global uint const* sobolmat, int frame #ifndef NO_PATH_DATA @@ -68,6 +68,12 @@ __kernel void PerspectiveCamera_GeneratePaths( Sampler sampler; #if SAMPLER == SOBOL uint scramble = random[globalid.x + imgwidth * globalid.y] * 0x1fe3434f; + + if (frame & 0xF) + { + random[globalid.x + imgwidth * globalid.y] = WangHash(scramble); + } + Sampler_Init(&sampler, frame, SAMPLE_DIM_CAMERA_OFFSET, scramble); #elif SAMPLER == RANDOM uint scramble = globalid.x + imgwidth * globalid.y * rngseed; @@ -152,6 +158,12 @@ __kernel void PerspectiveCameraDof_GeneratePaths( Sampler sampler; #if SAMPLER == SOBOL uint scramble = random[globalid.x + imgwidth * globalid.y] * 0x1fe3434f; + + if (frame & 0xF) + { + random[globalid.x + imgwidth * globalid.y] = WangHash(scramble); + } + Sampler_Init(&sampler, frame, SAMPLE_DIM_CAMERA_OFFSET, scramble); #elif SAMPLER == RANDOM uint scramble = globalid.x + imgwidth * globalid.y * rngseed; diff --git a/App/CL/common.cl b/App/CL/common.cl index 7263b119..bacbfd40 100644 --- a/App/CL/common.cl +++ b/App/CL/common.cl @@ -36,7 +36,7 @@ THE SOFTWARE. #define SOBOL 2 #define CMJ 3 -#define SAMPLER CMJ +#define SAMPLER SOBOL #define CMJ_DIM 4 diff --git a/App/PT/ptrenderer.cpp b/App/PT/ptrenderer.cpp index 1ade90c0..48515c99 100755 --- a/App/PT/ptrenderer.cpp +++ b/App/PT/ptrenderer.cpp @@ -287,7 +287,7 @@ namespace Baikal std::vector random_buffer(output.width() * output.height()); std::generate(random_buffer.begin(), random_buffer.end(), std::rand); - m_render_data->random = m_context.CreateBuffer(output.width() * output.height(), CL_MEM_READ_ONLY, &random_buffer[0]); + m_render_data->random = m_context.CreateBuffer(output.width() * output.height(), CL_MEM_READ_WRITE, &random_buffer[0]); m_vidmemws += output.width() * output.height() * sizeof(std::uint32_t); std::vector initdata(output.width() * output.height()); From 1034e1d3df7bc74610c28578cf197a5f573a361b Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Mon, 30 Jan 2017 12:15:58 +0100 Subject: [PATCH 23/24] Make CMJ default sampler --- App/CL/common.cl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/App/CL/common.cl b/App/CL/common.cl index bacbfd40..628bbd8e 100644 --- a/App/CL/common.cl +++ b/App/CL/common.cl @@ -22,7 +22,6 @@ THE SOFTWARE. #ifndef COMMON_CL #define COMMON_CL - #define CRAZY_LOW_THROUGHPUT 0.0f #define CRAZY_HIGH_RADIANCE 3.f #define CRAZY_HIGH_DISTANCE 1000000.f @@ -36,8 +35,8 @@ THE SOFTWARE. #define SOBOL 2 #define CMJ 3 -#define SAMPLER SOBOL +#define SAMPLER CMJ -#define CMJ_DIM 4 +#define CMJ_DIM 16 #endif // COMMON_CL \ No newline at end of file From 48173722e90664d4faa7e11131baf19f5570458e Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Mon, 30 Jan 2017 12:39:23 +0100 Subject: [PATCH 24/24] Remove references to missing textures --- App/Scene/IO/scene_io.cpp | 2 +- App/Scene/IO/scene_test_io.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/App/Scene/IO/scene_io.cpp b/App/Scene/IO/scene_io.cpp index adda776e..cdda5e61 100644 --- a/App/Scene/IO/scene_io.cpp +++ b/App/Scene/IO/scene_io.cpp @@ -241,7 +241,7 @@ namespace Baikal } // TODO: temporary code, add IBL - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); scene->AttachAutoreleaseObject(ibl_texture); ImageBasedLight* ibl = new ImageBasedLight(); diff --git a/App/Scene/IO/scene_test_io.cpp b/App/Scene/IO/scene_test_io.cpp index 3abc9ea3..8f21feca 100644 --- a/App/Scene/IO/scene_test_io.cpp +++ b/App/Scene/IO/scene_test_io.cpp @@ -159,7 +159,7 @@ namespace Baikal scene->AttachShape(quad); scene->AttachAutoreleaseObject(quad); - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); scene->AttachAutoreleaseObject(ibl_texture); ImageBasedLight* ibl = new ImageBasedLight(); @@ -192,7 +192,7 @@ namespace Baikal scene->AttachShape(mesh); scene->AttachAutoreleaseObject(mesh); - Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/parking.hdr"); + Texture* ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr"); scene->AttachAutoreleaseObject(ibl_texture); ImageBasedLight* ibl = new ImageBasedLight();