first commit

This commit is contained in:
Nick Fisher
2021-09-15 20:07:11 +08:00
commit a0f877be48
292 changed files with 100157 additions and 0 deletions

68
ios/include/MaterialKey.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <gltfio/MaterialProvider.h>
#define JAVA_MATERIAL_KEY "com/google/android/filament/gltfio/MaterialProvider$MaterialKey"
class MaterialKeyHelper {
public:
static MaterialKeyHelper& get();
void copy(JNIEnv* env, gltfio::MaterialKey& dst, jobject src);
void copy(JNIEnv* env, jobject dst, const gltfio::MaterialKey& src);
void init(JNIEnv* env); // called only from the Java static class constructor
private:
jfieldID doubleSided;
jfieldID unlit;
jfieldID hasVertexColors;
jfieldID hasBaseColorTexture;
jfieldID hasNormalTexture;
jfieldID hasOcclusionTexture;
jfieldID hasEmissiveTexture;
jfieldID useSpecularGlossiness;
jfieldID alphaMode;
jfieldID enableDiagnostics;
jfieldID hasMetallicRoughnessTexture;
jfieldID metallicRoughnessUV;
jfieldID baseColorUV;
jfieldID hasClearCoatTexture;
jfieldID clearCoatUV;
jfieldID hasClearCoatRoughnessTexture;
jfieldID clearCoatRoughnessUV;
jfieldID hasClearCoatNormalTexture;
jfieldID clearCoatNormalUV;
jfieldID hasClearCoat;
jfieldID hasTransmission;
jfieldID hasTextureTransforms;
jfieldID emissiveUV;
jfieldID aoUV;
jfieldID normalUV;
jfieldID hasTransmissionTexture;
jfieldID transmissionUV;
jfieldID hasSheenColorTexture;
jfieldID sheenColorUV;
jfieldID hasSheenRoughnessTexture;
jfieldID sheenRoughnessUV;
jfieldID hasVolumeThicknessTexture;
jfieldID volumeThicknessUV;
jfieldID hasSheen;
jfieldID hasIOR;
};

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_DRIVER_BUFFERDESCRIPTOR_H
#define TNT_FILAMENT_DRIVER_BUFFERDESCRIPTOR_H
#include <utils/compiler.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace backend {
/**
* A CPU memory-buffer descriptor, typically used to transfer data from the CPU to the GPU.
*
* A BufferDescriptor owns the memory buffer it references, therefore BufferDescriptor cannot
* be copied, but can be moved.
*
* BufferDescriptor releases ownership of the memory-buffer when it's destroyed.
*/
class UTILS_PUBLIC BufferDescriptor {
public:
/**
* Callback used to destroy the buffer data.
* Guarantees:
* Called on the main filament thread.
*
* Limitations:
* Must be lightweight.
* Must not call filament APIs.
*/
using Callback = void(*)(void* buffer, size_t size, void* user);
//! creates an empty descriptor
BufferDescriptor() noexcept = default;
//! calls the callback to advertise BufferDescriptor no-longer owns the buffer
~BufferDescriptor() noexcept {
if (callback) {
callback(buffer, size, user);
}
}
BufferDescriptor(const BufferDescriptor& rhs) = delete;
BufferDescriptor& operator=(const BufferDescriptor& rhs) = delete;
BufferDescriptor(BufferDescriptor&& rhs) noexcept
: buffer(rhs.buffer), size(rhs.size), callback(rhs.callback), user(rhs.user) {
rhs.buffer = nullptr;
rhs.callback = nullptr;
}
BufferDescriptor& operator=(BufferDescriptor&& rhs) noexcept {
if (this != &rhs) {
buffer = rhs.buffer;
size = rhs.size;
callback = rhs.callback;
user = rhs.user;
rhs.buffer = nullptr;
rhs.callback = nullptr;
}
return *this;
}
/**
* Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t size,
Callback callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), callback(callback), user(user) {
}
/**
* Set or replace the release callback function
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(Callback callback, void* user = nullptr) noexcept {
this->callback = callback;
this->user = user;
}
//! Returns whether a release callback is set
bool hasCallback() const noexcept { return callback != nullptr; }
//! Returns the currently set release callback function
Callback getCallback() const noexcept {
return callback;
}
//! Returns the user opaque pointer associated to this BufferDescriptor
void* getUser() const noexcept {
return user;
}
//! CPU mempry-buffer virtual address
void* buffer = nullptr;
//! CPU memory-buffer size in bytes
size_t size = 0;
private:
// callback when the buffer is consumed.
Callback callback = nullptr;
void* user = nullptr;
};
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_BUFFERDESCRIPTOR_H

View File

@@ -0,0 +1,929 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_DRIVER_DRIVERENUMS_H
#define TNT_FILAMENT_DRIVER_DRIVERENUMS_H
#include <utils/BitmaskEnum.h>
#include <utils/unwindows.h> // Because we define ERROR in the FenceStatus enum.
#include <backend/PresentCallable.h>
#include <math/vec4.h>
#include <array> // FIXME: STL headers are not allowed in public headers
#include <stddef.h>
#include <stdint.h>
namespace filament {
/**
* Types and enums used by filament's driver.
*
* Effectively these types are public but should not be used directly. Instead use public classes
* internal redeclaration of these types.
* For e.g. Use Texture::Sampler instead of filament::SamplerType.
*/
namespace backend {
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 16; // Matches the Adreno Vulkan driver.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
"The number of buffer objects that can be attached to a VertexBuffer must be "
"less than or equal to the maximum number of vertex attributes.");
static constexpr size_t CONFIG_BINDING_COUNT = 8;
/**
* Selects which driver a particular Engine should use.
*/
enum class Backend : uint8_t {
DEFAULT = 0, //!< Automatically selects an appropriate driver for the platform.
OPENGL = 1, //!< Selects the OpenGL/ES driver (default on Android)
VULKAN = 2, //!< Selects the Vulkan driver if the platform supports it (default on Linux/Windows)
METAL = 3, //!< Selects the Metal driver if the platform supports it (default on MacOS/iOS).
NOOP = 4, //!< Selects the no-op driver for testing purposes.
};
static constexpr const char* backendToString(Backend backend) {
switch (backend) {
case Backend::NOOP:
return "Noop";
case Backend::OPENGL:
return "OpenGL";
case Backend::VULKAN:
return "Vulkan";
case Backend::METAL:
return "Metal";
default:
return "Unknown";
}
}
/**
* Bitmask for selecting render buffers
*/
enum class TargetBufferFlags : uint32_t {
NONE = 0x0u, //!< No buffer selected.
COLOR0 = 0x00000001u, //!< Color buffer selected.
COLOR1 = 0x00000002u, //!< Color buffer selected.
COLOR2 = 0x00000004u, //!< Color buffer selected.
COLOR3 = 0x00000008u, //!< Color buffer selected.
COLOR4 = 0x00000010u, //!< Color buffer selected.
COLOR5 = 0x00000020u, //!< Color buffer selected.
COLOR6 = 0x00000040u, //!< Color buffer selected.
COLOR7 = 0x00000080u, //!< Color buffer selected.
COLOR = COLOR0, //!< \deprecated
COLOR_ALL = COLOR0 | COLOR1 | COLOR2 | COLOR3 | COLOR4 | COLOR5 | COLOR6 | COLOR7,
DEPTH = 0x10000000u, //!< Depth buffer selected.
STENCIL = 0x20000000u, //!< Stencil buffer selected.
DEPTH_AND_STENCIL = DEPTH | STENCIL, //!< depth and stencil buffer selected.
ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected.
};
inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
if (index == 0u) return TargetBufferFlags::COLOR0;
if (index == 1u) return TargetBufferFlags::COLOR1;
if (index == 2u) return TargetBufferFlags::COLOR2;
if (index == 3u) return TargetBufferFlags::COLOR3;
if (index == 4u) return TargetBufferFlags::COLOR4;
if (index == 5u) return TargetBufferFlags::COLOR5;
if (index == 6u) return TargetBufferFlags::COLOR6;
if (index == 7u) return TargetBufferFlags::COLOR7;
if (index == 8u) return TargetBufferFlags::DEPTH;
if (index == 9u) return TargetBufferFlags::STENCIL;
return TargetBufferFlags::NONE;
}
/**
* Frequency at which a buffer is expected to be modified and used. This is used as an hint
* for the driver to make better decisions about managing memory internally.
*/
enum class BufferUsage : uint8_t {
STATIC, //!< content modified once, used many times
DYNAMIC, //!< content modified frequently, used many times
STREAM, //!< content invalidated and modified frequently, used many times
};
/**
* Defines a viewport, which is the origin and extent of the clip-space.
* All drawing is clipped to the viewport.
*/
struct Viewport {
int32_t left; //!< left coordinate in window space.
int32_t bottom; //!< bottom coordinate in window space.
uint32_t width; //!< width in pixels
uint32_t height; //!< height in pixels
//! get the right coordinate in window space of the viewport
int32_t right() const noexcept { return left + width; }
//! get the top coordinate in window space of the viewport
int32_t top() const noexcept { return bottom + height; }
};
/**
* Specifies the mapping of the near and far clipping plane to window coordinates.
*/
struct DepthRange {
float near = 0.0f; //!< mapping of the near plane to window coordinates.
float far = 1.0f; //!< mapping of the far plane to window coordinates.
};
/**
* Error codes for Fence::wait()
* @see Fence, Fence::wait()
*/
enum class FenceStatus : int8_t {
ERROR = -1, //!< An error occured. The Fence condition is not satisfied.
CONDITION_SATISFIED = 0, //!< The Fence condition is satisfied.
TIMEOUT_EXPIRED = 1, //!< wait()'s timeout expired. The Fence condition is not satisfied.
};
/**
* Status codes for sync objects
*/
enum class SyncStatus : int8_t {
ERROR = -1, //!< An error occured. The Sync is not signaled.
SIGNALED = 0, //!< The Sync is signaled.
NOT_SIGNALED = 1, //!< The Sync is not signaled yet
};
static constexpr uint64_t FENCE_WAIT_FOR_EVER = uint64_t(-1);
/**
* Shader model.
*
* These enumerants are used across all backends and refer to a level of functionality, rather
* than to an OpenGL specific shader model.
*/
enum class ShaderModel : uint8_t {
//! For testing
UNKNOWN = 0,
GL_ES_30 = 1, //!< Mobile level functionality
GL_CORE_41 = 2, //!< Desktop level functionality
};
static constexpr size_t SHADER_MODEL_COUNT = 3;
/**
* Primitive types
*/
enum class PrimitiveType : uint8_t {
// don't change the enums values (made to match GL)
POINTS = 0, //!< points
LINES = 1, //!< lines
TRIANGLES = 4, //!< triangles
NONE = 0xFF
};
/**
* Supported uniform types
*/
enum class UniformType : uint8_t {
BOOL,
BOOL2,
BOOL3,
BOOL4,
FLOAT,
FLOAT2,
FLOAT3,
FLOAT4,
INT,
INT2,
INT3,
INT4,
UINT,
UINT2,
UINT3,
UINT4,
MAT3, //!< a 3x3 float matrix
MAT4 //!< a 4x4 float matrix
};
enum class Precision : uint8_t {
LOW,
MEDIUM,
HIGH,
DEFAULT
};
//! Texture sampler type
enum class SamplerType : uint8_t {
SAMPLER_2D, //!< 2D texture
SAMPLER_2D_ARRAY, //!< 2D array texture
SAMPLER_CUBEMAP, //!< Cube map texture
SAMPLER_EXTERNAL, //!< External texture
SAMPLER_3D, //!< 3D texture
};
//! Subpass type
enum class SubpassType : uint8_t {
SUBPASS_INPUT
};
//! Texture sampler format
enum class SamplerFormat : uint8_t {
INT = 0, //!< signed integer sampler
UINT = 1, //!< unsigned integer sampler
FLOAT = 2, //!< float sampler
SHADOW = 3 //!< shadow sampler (PCF)
};
/**
* Supported element types
*/
enum class ElementType : uint8_t {
BYTE,
BYTE2,
BYTE3,
BYTE4,
UBYTE,
UBYTE2,
UBYTE3,
UBYTE4,
SHORT,
SHORT2,
SHORT3,
SHORT4,
USHORT,
USHORT2,
USHORT3,
USHORT4,
INT,
UINT,
FLOAT,
FLOAT2,
FLOAT3,
FLOAT4,
HALF,
HALF2,
HALF3,
HALF4,
};
//! Buffer object binding type
enum class BufferObjectBinding : uint8_t {
VERTEX,
UNIFORM
};
//! Face culling Mode
enum class CullingMode : uint8_t {
NONE, //!< No culling, front and back faces are visible
FRONT, //!< Front face culling, only back faces are visible
BACK, //!< Back face culling, only front faces are visible
FRONT_AND_BACK //!< Front and Back, geometry is not visible
};
//! Pixel Data Format
enum class PixelDataFormat : uint8_t {
R, //!< One Red channel, float
R_INTEGER, //!< One Red channel, integer
RG, //!< Two Red and Green channels, float
RG_INTEGER, //!< Two Red and Green channels, integer
RGB, //!< Three Red, Green and Blue channels, float
RGB_INTEGER, //!< Three Red, Green and Blue channels, integer
RGBA, //!< Four Red, Green, Blue and Alpha channels, float
RGBA_INTEGER, //!< Four Red, Green, Blue and Alpha channels, integer
UNUSED, // used to be rgbm
DEPTH_COMPONENT, //!< Depth, 16-bit or 24-bits usually
DEPTH_STENCIL, //!< Two Depth (24-bits) + Stencil (8-bits) channels
ALPHA //! One Alpha channel, float
};
//! Pixel Data Type
enum class PixelDataType : uint8_t {
UBYTE, //!< unsigned byte
BYTE, //!< signed byte
USHORT, //!< unsigned short (16-bit)
SHORT, //!< signed short (16-bit)
UINT, //!< unsigned int (16-bit)
INT, //!< signed int (32-bit)
HALF, //!< half-float (16-bit float)
FLOAT, //!< float (32-bits float)
COMPRESSED, //!< compressed pixels, @see CompressedPixelDataType
UINT_10F_11F_11F_REV, //!< three low precision floating-point numbers
USHORT_565, //!< unsigned int (16-bit), encodes 3 RGB channels
UINT_2_10_10_10_REV, //!< unsigned normalized 10 bits RGB, 2 bits alpha
};
//! Compressed pixel data types
enum class CompressedPixelDataType : uint16_t {
// Mandatory in GLES 3.0 and GL 4.3
EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED,
ETC2_RGB8, ETC2_SRGB8,
ETC2_RGB8_A1, ETC2_SRGB8_A1,
ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8,
// Available everywhere except Android/iOS
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA,
DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA,
// ASTC formats are available with a GLES extension
RGBA_ASTC_4x4,
RGBA_ASTC_5x4,
RGBA_ASTC_5x5,
RGBA_ASTC_6x5,
RGBA_ASTC_6x6,
RGBA_ASTC_8x5,
RGBA_ASTC_8x6,
RGBA_ASTC_8x8,
RGBA_ASTC_10x5,
RGBA_ASTC_10x6,
RGBA_ASTC_10x8,
RGBA_ASTC_10x10,
RGBA_ASTC_12x10,
RGBA_ASTC_12x12,
SRGB8_ALPHA8_ASTC_4x4,
SRGB8_ALPHA8_ASTC_5x4,
SRGB8_ALPHA8_ASTC_5x5,
SRGB8_ALPHA8_ASTC_6x5,
SRGB8_ALPHA8_ASTC_6x6,
SRGB8_ALPHA8_ASTC_8x5,
SRGB8_ALPHA8_ASTC_8x6,
SRGB8_ALPHA8_ASTC_8x8,
SRGB8_ALPHA8_ASTC_10x5,
SRGB8_ALPHA8_ASTC_10x6,
SRGB8_ALPHA8_ASTC_10x8,
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12,
};
/** Supported texel formats
* These formats are typically used to specify a texture's internal storage format.
*
* Enumerants syntax format
* ========================
*
* `[components][size][type]`
*
* `components` : List of stored components by this format.\n
* `size` : Size in bit of each component.\n
* `type` : Type this format is stored as.\n
*
*
* Name | Component
* :--------|:-------------------------------
* R | Linear Red
* RG | Linear Red, Green
* RGB | Linear Red, Green, Blue
* RGBA | Linear Red, Green Blue, Alpha
* SRGB | sRGB encoded Red, Green, Blue
* DEPTH | Depth
* STENCIL | Stencil
*
* \n
* Name | Type
* :--------|:---------------------------------------------------
* (none) | Unsigned Normalized Integer [0, 1]
* _SNORM | Signed Normalized Integer [-1, 1]
* UI | Unsigned Integer @f$ [0, 2^{size}] @f$
* I | Signed Integer @f$ [-2^{size-1}, 2^{size-1}-1] @f$
* F | Floating-point
*
*
* Special color formats
* ---------------------
*
* There are a few special color formats that don't follow the convention above:
*
* Name | Format
* :----------------|:--------------------------------------------------------------------------
* RGB565 | 5-bits for R and B, 6-bits for G.
* RGB5_A1 | 5-bits for R, G and B, 1-bit for A.
* RGB10_A2 | 10-bits for R, G and B, 2-bits for A.
* RGB9_E5 | **Unsigned** floating point. 9-bits mantissa for RGB, 5-bits shared exponent
* R11F_G11F_B10F | **Unsigned** floating point. 6-bits mantissa, for R and G, 5-bits for B. 5-bits exponent.
* SRGB8_A8 | sRGB 8-bits with linear 8-bits alpha.
* DEPTH24_STENCIL8 | 24-bits unsigned normalized integer depth, 8-bits stencil.
* DEPTH32F_STENCIL8| 32-bits floating-point depth, 8-bits stencil.
*
*
* Compressed texture formats
* --------------------------
*
* Many compressed texture formats are supported as well, which include (but are not limited to)
* the following list:
*
* Name | Format
* :----------------|:--------------------------------------------------------------------------
* EAC_R11 | Compresses R11UI
* EAC_R11_SIGNED | Compresses R11I
* EAC_RG11 | Compresses RG11UI
* EAC_RG11_SIGNED | Compresses RG11I
* ETC2_RGB8 | Compresses RGB8
* ETC2_SRGB8 | compresses SRGB8
* ETC2_EAC_RGBA8 | Compresses RGBA8
* ETC2_EAC_SRGBA8 | Compresses SRGB8_A8
* ETC2_RGB8_A1 | Compresses RGB8 with 1-bit alpha
* ETC2_SRGB8_A1 | Compresses sRGB8 with 1-bit alpha
*
*
* @see Texture
*/
enum class TextureFormat : uint16_t {
// 8-bits per element
R8, R8_SNORM, R8UI, R8I, STENCIL8,
// 16-bits per element
R16F, R16UI, R16I,
RG8, RG8_SNORM, RG8UI, RG8I,
RGB565,
RGB9_E5, // 9995 is actually 32 bpp but it's here for historical reasons.
RGB5_A1,
RGBA4,
DEPTH16,
// 24-bits per element
RGB8, SRGB8, RGB8_SNORM, RGB8UI, RGB8I,
DEPTH24,
// 32-bits per element
R32F, R32UI, R32I,
RG16F, RG16UI, RG16I,
R11F_G11F_B10F,
RGBA8, SRGB8_A8,RGBA8_SNORM,
UNUSED, // used to be rgbm
RGB10_A2, RGBA8UI, RGBA8I,
DEPTH32F, DEPTH24_STENCIL8, DEPTH32F_STENCIL8,
// 48-bits per element
RGB16F, RGB16UI, RGB16I,
// 64-bits per element
RG32F, RG32UI, RG32I,
RGBA16F, RGBA16UI, RGBA16I,
// 96-bits per element
RGB32F, RGB32UI, RGB32I,
// 128-bits per element
RGBA32F, RGBA32UI, RGBA32I,
// compressed formats
// Mandatory in GLES 3.0 and GL 4.3
EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED,
ETC2_RGB8, ETC2_SRGB8,
ETC2_RGB8_A1, ETC2_SRGB8_A1,
ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8,
// Available everywhere except Android/iOS
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA,
DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA,
// ASTC formats are available with a GLES extension
RGBA_ASTC_4x4,
RGBA_ASTC_5x4,
RGBA_ASTC_5x5,
RGBA_ASTC_6x5,
RGBA_ASTC_6x6,
RGBA_ASTC_8x5,
RGBA_ASTC_8x6,
RGBA_ASTC_8x8,
RGBA_ASTC_10x5,
RGBA_ASTC_10x6,
RGBA_ASTC_10x8,
RGBA_ASTC_10x10,
RGBA_ASTC_12x10,
RGBA_ASTC_12x12,
SRGB8_ALPHA8_ASTC_4x4,
SRGB8_ALPHA8_ASTC_5x4,
SRGB8_ALPHA8_ASTC_5x5,
SRGB8_ALPHA8_ASTC_6x5,
SRGB8_ALPHA8_ASTC_6x6,
SRGB8_ALPHA8_ASTC_8x5,
SRGB8_ALPHA8_ASTC_8x6,
SRGB8_ALPHA8_ASTC_8x8,
SRGB8_ALPHA8_ASTC_10x5,
SRGB8_ALPHA8_ASTC_10x6,
SRGB8_ALPHA8_ASTC_10x8,
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12,
};
//! Bitmask describing the intended Texture Usage
enum class TextureUsage : uint8_t {
NONE = 0x0,
COLOR_ATTACHMENT = 0x1, //!< Texture can be used as a color attachment
DEPTH_ATTACHMENT = 0x2, //!< Texture can be used as a depth attachment
STENCIL_ATTACHMENT = 0x4, //!< Texture can be used as a stencil attachment
UPLOADABLE = 0x8, //!< Data can be uploaded into this texture (default)
SAMPLEABLE = 0x10, //!< Texture can be sampled (default)
SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
};
//! Texture swizzle
enum class TextureSwizzle : uint8_t {
SUBSTITUTE_ZERO,
SUBSTITUTE_ONE,
CHANNEL_0,
CHANNEL_1,
CHANNEL_2,
CHANNEL_3
};
//! returns whether this format a depth format
static constexpr bool isDepthFormat(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::DEPTH32F:
case TextureFormat::DEPTH24:
case TextureFormat::DEPTH16:
case TextureFormat::DEPTH32F_STENCIL8:
case TextureFormat::DEPTH24_STENCIL8:
return true;
default:
return false;
}
}
//! returns whether this format a compressed format
static constexpr bool isCompressedFormat(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11;
}
//! returns whether this format is an ETC2 compressed format
static constexpr bool isETC2Compression(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8;
}
//! returns whether this format is an ETC3 compressed format
static constexpr bool isS3TCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA;
}
static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA;
}
//! Texture Cubemap Face
enum class TextureCubemapFace : uint8_t {
// don't change the enums values
POSITIVE_X = 0, //!< +x face
NEGATIVE_X = 1, //!< -x face
POSITIVE_Y = 2, //!< +y face
NEGATIVE_Y = 3, //!< -y face
POSITIVE_Z = 4, //!< +z face
NEGATIVE_Z = 5, //!< -z face
};
//! Face offsets for all faces of a cubemap
struct FaceOffsets {
using size_type = size_t;
union {
struct {
size_type px; //!< +x face offset in bytes
size_type nx; //!< -x face offset in bytes
size_type py; //!< +y face offset in bytes
size_type ny; //!< -y face offset in bytes
size_type pz; //!< +z face offset in bytes
size_type nz; //!< -z face offset in bytes
};
size_type offsets[6];
};
size_type operator[](size_t n) const noexcept { return offsets[n]; }
size_type& operator[](size_t n) { return offsets[n]; }
FaceOffsets() noexcept = default;
explicit FaceOffsets(size_type faceSize) noexcept {
px = faceSize * 0;
nx = faceSize * 1;
py = faceSize * 2;
ny = faceSize * 3;
pz = faceSize * 4;
nz = faceSize * 5;
}
FaceOffsets(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
}
FaceOffsets& operator=(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
return *this;
}
};
//! Sampler Wrap mode
enum class SamplerWrapMode : uint8_t {
CLAMP_TO_EDGE, //!< clamp-to-edge. The edge of the texture extends to infinity.
REPEAT, //!< repeat. The texture infinitely repeats in the wrap direction.
MIRRORED_REPEAT, //!< mirrored-repeat. The texture infinitely repeats and mirrors in the wrap direction.
};
//! Sampler minification filter
enum class SamplerMinFilter : uint8_t {
// don't change the enums values
NEAREST = 0, //!< No filtering. Nearest neighbor is used.
LINEAR = 1, //!< Box filtering. Weighted average of 4 neighbors is used.
NEAREST_MIPMAP_NEAREST = 2, //!< Mip-mapping is activated. But no filtering occurs.
LINEAR_MIPMAP_NEAREST = 3, //!< Box filtering within a mip-map level.
NEAREST_MIPMAP_LINEAR = 4, //!< Mip-map levels are interpolated, but no other filtering occurs.
LINEAR_MIPMAP_LINEAR = 5 //!< Both interpolated Mip-mapping and linear filtering are used.
};
//! Sampler magnification filter
enum class SamplerMagFilter : uint8_t {
// don't change the enums values
NEAREST = 0, //!< No filtering. Nearest neighbor is used.
LINEAR = 1, //!< Box filtering. Weighted average of 4 neighbors is used.
};
//! Sampler compare mode
enum class SamplerCompareMode : uint8_t {
// don't change the enums values
NONE = 0,
COMPARE_TO_TEXTURE = 1
};
//! comparison function for the depth sampler
enum class SamplerCompareFunc : uint8_t {
// don't change the enums values
LE = 0, //!< Less or equal
GE, //!< Greater or equal
L, //!< Strictly less than
G, //!< Strictly greater than
E, //!< Equal
NE, //!< Not equal
A, //!< Always. Depth testing is deactivated.
N //!< Never. The depth test always fails.
};
//! Sampler paramters
struct SamplerParams { // NOLINT
union {
struct {
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST)
SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE)
SamplerWrapMode wrapT : 2; //!< t-coordinate wrap mode (CLAMP_TO_EDGE)
SamplerWrapMode wrapR : 2; //!< r-coordinate wrap mode (CLAMP_TO_EDGE)
uint8_t anisotropyLog2 : 3; //!< anisotropy level (0)
SamplerCompareMode compareMode : 1; //!< sampler compare mode (NONE)
uint8_t padding0 : 2; //!< reserved. must be 0.
SamplerCompareFunc compareFunc : 3; //!< sampler comparison function (LE)
uint8_t padding1 : 5; //!< reserved. must be 0.
uint8_t padding2 : 8; //!< reserved. must be 0.
};
uint32_t u;
};
private:
friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) {
return lhs.u < rhs.u;
}
};
static_assert(sizeof(SamplerParams) == sizeof(uint32_t), "SamplerParams must be 32 bits");
//! blending equation function
enum class BlendEquation : uint8_t {
ADD, //!< the fragment is added to the color buffer
SUBTRACT, //!< the fragment is subtracted from the color buffer
REVERSE_SUBTRACT, //!< the color buffer is subtracted from the fragment
MIN, //!< the min between the fragment and color buffer
MAX //!< the max between the fragment and color buffer
};
//! blending function
enum class BlendFunction : uint8_t {
ZERO, //!< f(src, dst) = 0
ONE, //!< f(src, dst) = 1
SRC_COLOR, //!< f(src, dst) = src
ONE_MINUS_SRC_COLOR, //!< f(src, dst) = 1-src
DST_COLOR, //!< f(src, dst) = dst
ONE_MINUS_DST_COLOR, //!< f(src, dst) = 1-dst
SRC_ALPHA, //!< f(src, dst) = src.a
ONE_MINUS_SRC_ALPHA, //!< f(src, dst) = 1-src.a
DST_ALPHA, //!< f(src, dst) = dst.a
ONE_MINUS_DST_ALPHA, //!< f(src, dst) = 1-dst.a
SRC_ALPHA_SATURATE //!< f(src, dst) = (1,1,1) * min(src.a, 1 - dst.a), 1
};
//! Stream for external textures
enum class StreamType {
NATIVE, //!< Not synchronized but copy-free. Good for video.
TEXTURE_ID, //!< Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26.
ACQUIRED, //!< Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+.
};
//! Releases an ACQUIRED external texture, guaranteed to be called on the application thread.
using StreamCallback = void(*)(void* image, void* user);
//! Vertex attribute descriptor
struct Attribute {
//! attribute is normalized (remapped between 0 and 1)
static constexpr uint8_t FLAG_NORMALIZED = 0x1;
//! attribute is an integer
static constexpr uint8_t FLAG_INTEGER_TARGET = 0x2;
static constexpr uint8_t BUFFER_UNUSED = 0xFF;
uint32_t offset = 0; //!< attribute offset in bytes
uint8_t stride = 0; //!< attribute stride in bytes
uint8_t buffer = BUFFER_UNUSED; //!< attribute buffer index
ElementType type = ElementType::BYTE; //!< attribute element type
uint8_t flags = 0x0; //!< attribute flags
};
using AttributeArray = std::array<Attribute, MAX_VERTEX_ATTRIBUTE_COUNT>;
//! Raster state descriptor
struct RasterState {
using CullingMode = CullingMode;
using DepthFunc = SamplerCompareFunc;
using BlendEquation = BlendEquation;
using BlendFunction = BlendFunction;
RasterState() noexcept { // NOLINT
static_assert(sizeof(RasterState) == sizeof(uint32_t),
"RasterState size not what was intended");
culling = CullingMode::BACK;
blendEquationRGB = BlendEquation::ADD;
blendEquationAlpha = BlendEquation::ADD;
blendFunctionSrcRGB = BlendFunction::ONE;
blendFunctionSrcAlpha = BlendFunction::ONE;
blendFunctionDstRGB = BlendFunction::ZERO;
blendFunctionDstAlpha = BlendFunction::ZERO;
}
bool operator == (RasterState rhs) const noexcept { return u == rhs.u; }
bool operator != (RasterState rhs) const noexcept { return u != rhs.u; }
void disableBlending() noexcept {
blendEquationRGB = BlendEquation::ADD;
blendEquationAlpha = BlendEquation::ADD;
blendFunctionSrcRGB = BlendFunction::ONE;
blendFunctionSrcAlpha = BlendFunction::ONE;
blendFunctionDstRGB = BlendFunction::ZERO;
blendFunctionDstAlpha = BlendFunction::ZERO;
}
// note: clang reduces this entire function to a simple load/mask/compare
bool hasBlending() const noexcept {
// This is used to decide if blending needs to be enabled in the h/w
return !(blendEquationRGB == BlendEquation::ADD &&
blendEquationAlpha == BlendEquation::ADD &&
blendFunctionSrcRGB == BlendFunction::ONE &&
blendFunctionSrcAlpha == BlendFunction::ONE &&
blendFunctionDstRGB == BlendFunction::ZERO &&
blendFunctionDstAlpha == BlendFunction::ZERO);
}
union {
struct {
//! culling mode
CullingMode culling : 2; // 2
//! blend equation for the red, green and blue components
BlendEquation blendEquationRGB : 3; // 5
//! blend equation for the alpha component
BlendEquation blendEquationAlpha : 3; // 8
//! blending function for the source color
BlendFunction blendFunctionSrcRGB : 4; // 12
//! blending function for the source alpha
BlendFunction blendFunctionSrcAlpha : 4; // 16
//! blending function for the destination color
BlendFunction blendFunctionDstRGB : 4; // 20
//! blending function for the destination alpha
BlendFunction blendFunctionDstAlpha : 4; // 24
//! Whether depth-buffer writes are enabled
bool depthWrite : 1; // 25
//! Depth test function
DepthFunc depthFunc : 3; // 28
//! Whether color-buffer writes are enabled
bool colorWrite : 1; // 29
//! use alpha-channel as coverage mask for anti-aliasing
bool alphaToCoverage : 1; // 30
//! whether front face winding direction must be inverted
bool inverseFrontFaces : 1; // 31
//! padding, must be 0
uint8_t padding : 1; // 32
};
uint32_t u = 0;
};
};
/**
**********************************************************************************************
* \privatesection
*/
enum ShaderType : uint8_t {
VERTEX = 0,
FRAGMENT = 1
};
static constexpr size_t PIPELINE_STAGE_COUNT = 2;
/**
* Selects which buffers to clear at the beginning of the render pass, as well as which buffers
* can be discarded at the beginning and end of the render pass.
*
*/
struct RenderPassFlags {
/**
* bitmask indicating which buffers to clear at the beginning of a render pass.
* This implies discard.
*/
TargetBufferFlags clear;
/**
* bitmask indicating which buffers to discard at the beginning of a render pass.
* Discarded buffers have uninitialized content, they must be entirely drawn over or cleared.
*/
TargetBufferFlags discardStart;
/**
* bitmask indicating which buffers to discard at the end of a render pass.
* Discarded buffers' content becomes invalid, they must not be read from again.
*/
TargetBufferFlags discardEnd;
};
/**
* Parameters of a render pass.
*/
struct RenderPassParams {
RenderPassFlags flags{}; //!< operations performed on the buffers for this pass
Viewport viewport{}; //!< viewport for this pass
DepthRange depthRange{}; //!< depth range for this pass
//! Color to use to clear the COLOR buffer. RenderPassFlags::clear must be set.
math::float4 clearColor = {};
//! Depth value to clear the depth buffer with
double clearDepth = 0.0;
//! Stencil value to clear the stencil buffer with
uint32_t clearStencil = 0;
/**
* The subpass mask specifies which color attachments are designated for read-back in the second
* subpass. If this is zero, the render pass has only one subpass. The least significant bit
* specifies that the first color attachment in the render target is a subpass input.
*
* For now only 2 subpasses are supported, so only the lower 4 bits are used, one for each color
* attachment (see MRT::TARGET_COUNT).
*/
uint32_t subpassMask = 0;
};
struct PolygonOffset {
float slope = 0; // factor in GL-speak
float constant = 0; // units in GL-speak
};
using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
using FrameCompletedCallback = void(*)(void* user);
} // namespace backend
} // namespace filament
template<> struct utils::EnableBitMaskOperators<filament::backend::TargetBufferFlags>
: public std::true_type {};
template<> struct utils::EnableBitMaskOperators<filament::backend::TextureUsage>
: public std::true_type {};
#endif // TNT_FILAMENT_DRIVER_DRIVERENUMS_H

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_DRIVER_HANDLE_H
#define TNT_FILAMENT_DRIVER_HANDLE_H
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/debug.h>
namespace filament {
namespace backend {
struct HwBufferObject;
struct HwFence;
struct HwIndexBuffer;
struct HwProgram;
struct HwRenderPrimitive;
struct HwRenderTarget;
struct HwSamplerGroup;
struct HwStream;
struct HwSwapChain;
struct HwSync;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBuffer;
/*
* A type handle to a h/w resource
*/
//! \privatesection
class HandleBase {
public:
using HandleId = uint32_t;
static constexpr const HandleId nullid = HandleId{ std::numeric_limits<HandleId>::max() };
constexpr HandleBase() noexcept: object(nullid) {}
explicit HandleBase(HandleId id) noexcept : object(id) {
assert_invariant(object != nullid); // usually means an uninitialized handle is used
}
HandleBase(HandleBase const& rhs) noexcept = default;
HandleBase(HandleBase&& rhs) noexcept : object(rhs.object) {
rhs.object = nullid;
}
HandleBase& operator=(HandleBase const& rhs) noexcept = default;
HandleBase& operator=(HandleBase&& rhs) noexcept {
std::swap(object, rhs.object);
return *this;
}
explicit operator bool() const noexcept { return object != nullid; }
void clear() noexcept { object = nullid; }
bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; }
bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; }
// get this handle's handleId
HandleId getId() const noexcept { return object; }
protected:
HandleId object;
};
template <typename T>
struct Handle : public HandleBase {
using HandleBase::HandleBase;
template<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions)
private:
#if !defined(NDEBUG)
template <typename U>
friend utils::io::ostream& operator<<(utils::io::ostream& out, const Handle<U>& h) noexcept;
#endif
};
// Types used by the command stream
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
using BufferObjectHandle = Handle<HwBufferObject>;
using FenceHandle = Handle<HwFence>;
using IndexBufferHandle = Handle<HwIndexBuffer>;
using ProgramHandle = Handle<HwProgram>;
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using SamplerGroupHandle = Handle<HwSamplerGroup>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using SyncHandle = Handle<HwSync>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_HANDLE_H

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_DRIVER_PIPELINESTATE_H
#define TNT_FILAMENT_DRIVER_PIPELINESTATE_H
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <limits>
#include <stdint.h>
namespace filament {
namespace backend {
//! \privatesection
struct PipelineState {
Handle<HwProgram> program;
RasterState rasterState;
PolygonOffset polygonOffset;
Viewport scissor{ 0, 0,
(uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max()
};
};
} // namespace backend
} // namespace filament
#endif //TNT_FILAMENT_DRIVER_PIPELINESTATE_H

View File

@@ -0,0 +1,216 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_DRIVER_PIXEL_BUFFERDESCRIPTOR_H
#define TNT_FILAMENT_DRIVER_PIXEL_BUFFERDESCRIPTOR_H
#include <backend/BufferDescriptor.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace backend {
/**
* A descriptor to an image in main memory, typically used to transfer image data from the CPU
* to the GPU.
*
* A PixelBufferDescriptor owns the memory buffer it references, therefore PixelBufferDescriptor
* cannot be copied, but can be moved.
*
* PixelBufferDescriptor releases ownership of the memory-buffer when it's destroyed.
*/
class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor {
public:
using PixelDataFormat = backend::PixelDataFormat;
using PixelDataType = backend::PixelDataType;
PixelBufferDescriptor() = default;
/**
* Creates a new PixelBufferDescriptor referencing an image in main memory
*
* @param buffer Virtual address of the buffer containing the image
* @param size Size in bytes of the buffer containing the image
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param alignment Alignment in bytes of pixel rows
* @param left Left coordinate in pixels
* @param top Top coordinate in pixels
* @param stride Stride of a row in pixels
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment = 1,
uint32_t left = 0, uint32_t top = 0, uint32_t stride = 0,
Callback callback = nullptr, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, callback, user),
left(left), top(top), stride(stride),
format(format), type(type), alignment(alignment) {
}
/**
* Creates a new PixelBufferDescriptor referencing an image in main memory
*
* @param buffer Virtual address of the buffer containing the image
* @param size Size in bytes of the buffer containing the image
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type,
Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, callback, user),
stride(0), format(format), type(type), alignment(1) {
}
/**
* Creates a new PixelBufferDescriptor referencing a compressed image in main memory
*
* @param buffer Virtual address of the buffer containing the image
* @param size Size in bytes of the buffer containing the image
* @param format Compressed format of the image
* @param imageSize Compressed size of the image
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize,
Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, callback, user),
imageSize(imageSize), compressedFormat(format), type(PixelDataType::COMPRESSED),
alignment(1) {
}
/**
* Computes the size in bytes needed to fit an image of given dimensions and format
*
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param stride Stride of a row in pixels
* @param height Height of the image in rows
* @param alignment Alignment in bytes of pixel rows
* @return The buffer size needed to fit this image in bytes
*/
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
size_t stride, size_t height, size_t alignment) noexcept {
assert_invariant(alignment);
if (type == PixelDataType::COMPRESSED) {
return 0;
}
size_t n = 0;
switch (format) {
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
n = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
n = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
n = 3;
break;
case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm)
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
n = 4;
break;
}
size_t bpp = n;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
bpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
bpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
bpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
bpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
bpp = 2;
break;
}
size_t bpr = bpp * stride;
size_t bprAligned = (bpr + (alignment - 1)) & -alignment;
return bprAligned * height;
}
//! left coordinate in pixels
uint32_t left = 0;
//! top coordinate in pixels
uint32_t top = 0;
union {
struct {
//! stride in pixels
uint32_t stride;
//! Pixel data format
PixelDataFormat format;
};
struct {
//! compressed image size
uint32_t imageSize;
//! compressed image format
backend::CompressedPixelDataType compressedFormat;
};
};
//! pixel data type
PixelDataType type : 4;
//! row alignment in bytes
uint8_t alignment : 4;
};
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_PIXEL_BUFFERDESCRIPTOR_H

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_DRIVER_PLATFORM_H
#define TNT_FILAMENT_DRIVER_PLATFORM_H
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
namespace filament {
namespace backend {
class Driver;
class UTILS_PUBLIC Platform {
public:
struct SwapChain {};
struct Fence {};
struct Stream {};
struct ExternalTexture {
uintptr_t image = 0;
};
virtual ~Platform() noexcept;
/**
* Queries the underlying OS version.
* @return The OS version.
*/
virtual int getOSVersion() const noexcept = 0;
/**
* Creates and initializes the low-level API (e.g. an OpenGL context or Vulkan instance),
* then creates the concrete Driver.
* The caller takes ownership of the returned Driver* and must destroy it with delete.
*
* @param sharedContext an optional shared context. This is not meaningful with all graphic
* APIs and platforms.
* For EGL platforms, this is an EGLContext.
*
* @return nullptr on failure, or a pointer to the newly created driver.
*/
virtual backend::Driver* createDriver(void* sharedContext) noexcept = 0;
/**
* Processes the platform's event queue when called from its primary event-handling thread.
*
* Internally, Filament might need to call this when waiting on a fence. It is only implemented
* on platforms that need it, such as macOS + OpenGL. Returns false if this is not the main
* thread, or if the platform does not need to perform any special processing.
*/
virtual bool pumpEvents() noexcept { return false; }
};
class UTILS_PUBLIC DefaultPlatform : public Platform {
public:
~DefaultPlatform() noexcept override;
/**
* Creates a Platform configured for the requested backend if available
*
* @param backendHint Preferred backend, if not available the backend most suitable for the
* underlying platform is returned and \p backendHint is updated
* accordingly. Can't be nullptr.
*
* @return A pointer to the Plaform object.
*
* @see destroy
*/
static DefaultPlatform* create(backend::Backend* backendHint) noexcept;
/**
* Destroys a Platform object returned by create()
*
* @param platform a reference (as a pointer) to the DefaultPlatform pointer to destroy.
* \p platform is cleared upon return.
*
* @see create
*/
static void destroy(DefaultPlatform** platform) noexcept;
};
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_PLATFORM_H

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_PRESENT_CALLABLE
#define TNT_FILAMENT_BACKEND_PRESENT_CALLABLE
#include <utils/compiler.h>
namespace filament {
namespace backend {
/**
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
* a SwapChain.
*
* Typically, Filament's backend is responsible scheduling a frame's presentation. However, there
* are certain cases where the application might want to control when a frame is scheduled for
* presentation.
*
* For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present
* within a CATransation:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void myFrameScheduledCallback(PresentCallable presentCallable, void* user) {
* [CATransaction begin];
* // Update other UI elements...
* presentCallable();
* [CATransaction commit];
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To obtain a PresentCallable, set a SwapChain::FrameScheduledCallback on a SwapChain with the
* SwapChain::setFrameScheduledCallback method. The callback is called with a PresentCallable object
* and optional user data:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* swapChain->setFrameScheduledCallback(myFrameScheduledCallback, nullptr);
* if (renderer->beginFrame(swapChain)) {
* renderer->render(view);
* renderer->endFrame();
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
*
* @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread.
*
* Applications *must* call each PresentCallable they receive. Each PresentCallable represents a
* frame that is waiting to be presented. If an application fails to call a PresentCallable, a
* memory leak could occur. To "cancel" the presentation of a frame, pass false to the
* PresentCallable, which will cancel the presentation of the frame and release associated memory.
*
* @see Renderer, SwapChain::setFrameScheduledCallback
*/
class UTILS_PUBLIC PresentCallable {
public:
using PresentFn = void(*)(bool presentFrame, void* user);
PresentCallable(PresentFn fn, void* user) noexcept;
~PresentCallable() noexcept = default;
PresentCallable(const PresentCallable& rhs) = default;
PresentCallable& operator=(const PresentCallable& rhs) = default;
/**
* Call this PresentCallable, scheduling the associated frame for presentation. Pass false for
* presentFrame to effectively "cancel" the presentation of the frame.
*
* @param presentFrame if false, will not present the frame but releases associated memory
*/
void operator()(bool presentFrame = true) noexcept;
private:
PresentFn mPresentFn;
void* mUser = nullptr;
};
/**
* @deprecated, FrameFinishedCallback has been renamed to SwapChain::FrameScheduledCallback.
*/
using FrameFinishedCallback UTILS_DEPRECATED = void(*)(PresentCallable callable, void* user);
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PRESENT_FRAME_CALLABLE

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_DRIVER_TARGETBUFFERINFO_H
#define TNT_FILAMENT_DRIVER_TARGETBUFFERINFO_H
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <stdint.h>
namespace filament {
namespace backend {
//! \privatesection
class TargetBufferInfo {
public:
// ctor for 2D textures
TargetBufferInfo(Handle<HwTexture> h, uint8_t level = 0) noexcept // NOLINT(google-explicit-constructor)
: handle(h), level(level) { }
// ctor for cubemaps
TargetBufferInfo(Handle<HwTexture> h, uint8_t level, TextureCubemapFace face) noexcept
: handle(h), level(level), face(face) { }
// ctor for 3D textures
TargetBufferInfo(Handle<HwTexture> h, uint8_t level, uint16_t layer) noexcept
: handle(h), level(level), layer(layer) { }
explicit TargetBufferInfo(TextureCubemapFace face) noexcept : face(face) {}
explicit TargetBufferInfo(uint16_t layer) noexcept : layer(layer) {}
// texture to be used as render target
Handle<HwTexture> handle;
// level to be used
uint8_t level = 0;
union {
// face if texture is a cubemap
TextureCubemapFace face;
// for 3D textures
uint16_t layer = 0;
};
TargetBufferInfo() noexcept { }
};
class MRT {
public:
static constexpr uint8_t MIN_SUPPORTED_RENDER_TARGET_COUNT = 4u;
// When updating this, make sure to also take care of RenderTarget.java
static constexpr uint8_t MAX_SUPPORTED_RENDER_TARGET_COUNT = 8u;
private:
TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT];
public:
TargetBufferInfo const& operator[](size_t i) const noexcept {
return mInfos[i];
}
TargetBufferInfo& operator[](size_t i) noexcept {
return mInfos[i];
}
MRT() noexcept = default;
MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions)
: mInfos{ color } {
}
MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1) noexcept
: mInfos{ color0, color1 } {
}
MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1,
TargetBufferInfo const& color2) noexcept
: mInfos{ color0, color1, color2 } {
}
MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1,
TargetBufferInfo const& color2, TargetBufferInfo const& color3) noexcept
: mInfos{ color0, color1, color2, color3 } {
}
// this is here for backward compatibility
MRT(Handle<HwTexture> h, uint8_t level, uint16_t layer) noexcept
: mInfos{{ h, level, layer }} {
}
};
} // namespace backend
} // namespace filament
#endif //TNT_FILAMENT_DRIVER_TARGETBUFFERINFO_H

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_BOOKMARK_H
#define CAMUTILS_BOOKMARK_H
#include <camutils/compiler.h>
#include <math/vec2.h>
#include <math/vec3.h>
namespace filament {
namespace camutils {
template <typename FLOAT> class FreeFlightManipulator;
template <typename FLOAT> class OrbitManipulator;
template <typename FLOAT> class MapManipulator;
template <typename FLOAT> class Manipulator;
enum class Mode { ORBIT, MAP, FREE_FLIGHT };
/**
* Opaque memento to a viewing position and orientation (e.g. the "home" camera position).
*
* This little struct is meant to be passed around by value and can be used to track camera
* animation between waypoints. In map mode this implements Van Wijk interpolation.
*
* @see Manipulator::getCurrentBookmark, Manipulator::jumpToBookmark
*/
template <typename FLOAT>
struct CAMUTILS_PUBLIC Bookmark {
/**
* Interpolates between two bookmarks. The t argument must be between 0 and 1 (inclusive), and
* the two endpoints must have the same mode (ORBIT or MAP).
*/
static Bookmark<FLOAT> interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t);
/**
* Recommends a duration for animation between two MAP endpoints. The return value is a unitless
* multiplier.
*/
static double duration(Bookmark<FLOAT> a, Bookmark<FLOAT> b);
private:
struct MapParams {
FLOAT extent;
filament::math::vec2<FLOAT> center;
};
struct OrbitParams {
FLOAT phi;
FLOAT theta;
FLOAT distance;
filament::math::vec3<FLOAT> pivot;
};
struct FlightParams {
FLOAT pitch;
FLOAT yaw;
filament::math::vec3<FLOAT> position;
};
Mode mode;
MapParams map;
OrbitParams orbit;
FlightParams flight;
friend class FreeFlightManipulator<FLOAT>;
friend class OrbitManipulator<FLOAT>;
friend class MapManipulator<FLOAT>;
};
} // namespace camutils
} // namespace filament
#endif // CAMUTILS_BOOKMARK_H

View File

@@ -0,0 +1,298 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_MANIPULATOR_H
#define CAMUTILS_MANIPULATOR_H
#include <camutils/Bookmark.h>
#include <camutils/compiler.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <cstdint>
namespace filament {
namespace camutils {
enum class Fov { VERTICAL, HORIZONTAL };
/**
* Helper that enables camera interaction similar to sketchfab or Google Maps.
*
* Clients notify the camera manipulator of various mouse or touch events, then periodically call
* its getLookAt() method so that they can adjust their camera(s). Three modes are supported: ORBIT,
* MAP, and FREE_FLIGHT. To construct a manipulator instance, the desired mode is passed into the
* create method.
*
* Usage example:
*
* using CameraManipulator = camutils::Manipulator<float>;
* CameraManipulator* manip;
*
* void init() {
* manip = CameraManipulator::Builder()
* .viewport(1024, 768)
* .build(camutils::Mode::ORBIT);
* }
*
* void onMouseDown(int x, int y) {
* manip->grabBegin(x, y, false);
* }
*
* void onMouseMove(int x, int y) {
* manip->grabUpdate(x, y);
* }
*
* void onMouseUp(int x, int y) {
* manip->grabEnd();
* }
*
* void gameLoop() {
* while (true) {
* filament::math::float3 eye, center, up;
* manip->getLookAt(&eye, &center, &up);
* camera->lookAt(eye, center, up);
* render();
* }
* }
*
* @see Bookmark
*/
template <typename FLOAT>
class CAMUTILS_PUBLIC Manipulator {
public:
using vec2 = filament::math::vec2<FLOAT>;
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
/** Opaque handle to a viewing position and orientation to facilitate camera animation. */
using Bookmark = filament::camutils::Bookmark<FLOAT>;
/** Optional raycasting function to enable perspective-correct panning. */
typedef bool (*RayCallback)(const vec3& origin, const vec3& dir, FLOAT* t, void* userdata);
/** Builder state, direct access is allowed but Builder methods are preferred. **/
struct Config {
int viewport[2];
vec3 targetPosition;
vec3 upVector;
FLOAT zoomSpeed;
vec3 orbitHomePosition;
vec2 orbitSpeed;
Fov fovDirection;
FLOAT fovDegrees;
FLOAT farPlane;
vec2 mapExtent;
FLOAT mapMinDistance;
vec3 flightStartPosition;
FLOAT flightStartPitch;
FLOAT flightStartYaw;
FLOAT flightMaxSpeed;
FLOAT flightSpeedSteps;
vec2 flightPanSpeed;
FLOAT flightMoveDamping;
vec4 groundPlane;
RayCallback raycastCallback;
void* raycastUserdata;
};
struct Builder {
// Common properties
Builder& viewport(int width, int height); //! Width and height of the viewing area
Builder& targetPosition(FLOAT x, FLOAT y, FLOAT z); //! World-space position of interest, defaults to (0,0,0)
Builder& upVector(FLOAT x, FLOAT y, FLOAT z); //! Orientation for the home position, defaults to (0,1,0)
Builder& zoomSpeed(FLOAT val); //! Multiplied with scroll delta, defaults to 0.01
// Orbit mode properties
Builder& orbitHomePosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,1)
Builder& orbitSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01
// Map mode properties
Builder& fovDirection(Fov fov); //! The axis that's held constant when viewport changes
Builder& fovDegrees(FLOAT degrees); //! The full FOV (not the half-angle)
Builder& farPlane(FLOAT distance); //! The distance to the far plane
Builder& mapExtent(FLOAT worldWidth, FLOAT worldHeight); //! The ground size for computing home position
Builder& mapMinDistance(FLOAT mindist); //! Constrains the zoom-in level
// Free flight properties
Builder& flightStartPosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,0)
Builder& flightStartOrientation(FLOAT pitch, FLOAT yaw); //! Initial orientation in pitch and yaw, defaults to (0,0)
Builder& flightMaxMoveSpeed(FLOAT maxSpeed); //! The maximum camera speed in world units per second, defaults to 10
Builder& flightSpeedSteps(int steps); //! The number of speed steps adjustable with scroll wheel, defaults to 80
Builder& flightPanSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01,0.01
Builder& flightMoveDamping(FLOAT damping); //! Applies a deceleration to camera movement, defaults to 0 (no damping)
//! Lower values give slower damping times, a good default is 15
//! Too high a value may lead to instability
// Raycast properties
Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback
Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan
/**
* Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT.
*
* Clients can simply use "delete" to destroy the manipulator.
*/
Manipulator* build(Mode mode);
Config details = {};
};
virtual ~Manipulator() = default;
/**
* Gets the immutable mode of the manipulator.
*/
Mode getMode() const { return mMode; }
/**
* Sets the viewport dimensions. The manipulator uses this to process grab events and raycasts.
*/
void setViewport(int width, int height);
/**
* Gets the current orthonormal basis; this is usually called once per frame.
*/
void getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const;
/**
* Given a viewport coordinate, picks a point in the ground plane, or in the actual scene if the
* raycast callback was provided.
*/
bool raycast(int x, int y, vec3* result) const;
/**
* Given a viewport coordinate, computes a picking ray (origin + direction).
*/
void getRay(int x, int y, vec3* origin, vec3* dir) const;
/**
* Starts a grabbing session (i.e. the user is dragging around in the viewport).
*
* In MAP mode, this starts a panning session.
* In ORBIT mode, this starts either rotating or strafing.
* In FREE_FLIGHT mode, this starts a nodal panning session.
*
* @param x X-coordinate for point of interest in viewport space
* @param y Y-coordinate for point of interest in viewport space
* @param strafe ORBIT mode only; if true, starts a translation rather than a rotation
*/
virtual void grabBegin(int x, int y, bool strafe) = 0;
/**
* Updates a grabbing session.
*
* This must be called at least once between grabBegin / grabEnd to dirty the camera.
*/
virtual void grabUpdate(int x, int y) = 0;
/**
* Ends a grabbing session.
*/
virtual void grabEnd() = 0;
/**
* Keys used to translate the camera in FREE_FLIGHT mode.
* FORWARD and BACKWARD dolly the camera forwards and backwards.
* LEFT and RIGHT strafe the camera left and right.
* UP and DOWN boom the camera upwards and downwards.
*/
enum class Key {
FORWARD,
LEFT,
BACKWARD,
RIGHT,
UP,
DOWN,
COUNT
};
/**
* Signals that a key is now in the down state.
*
* In FREE_FLIGHT mode, the camera is translated forward and backward and strafed left and right
* depending on the depressed keys. This allows WASD-style movement.
*/
virtual void keyDown(Key key);
/**
* Signals that a key is now in the up state.
*
* @see keyDown
*/
virtual void keyUp(Key key);
/**
* In MAP and ORBIT modes, dollys the camera along the viewing direction.
* In FREE_FLIGHT mode, adjusts the move speed of the camera.
*
* @param x X-coordinate for point of interest in viewport space, ignored in FREE_FLIGHT mode
* @param y Y-coordinate for point of interest in viewport space, ignored in FREE_FLIGHT mode
* @param scrolldelta In MAP and ORBIT modes, negative means "zoom in", positive means "zoom out"
* In FREE_FLIGHT mode, negative means "slower", positive means "faster"
*/
virtual void scroll(int x, int y, FLOAT scrolldelta) = 0;
/**
* Processes input and updates internal state.
*
* This must be called once every frame before getLookAt is valid.
*
* @param deltaTime The amount of time, in seconds, passed since the previous call to update.
*/
virtual void update(FLOAT deltaTime);
/**
* Gets a handle that can be used to reset the manipulator back to its current position.
*
* @see jumpToBookmark
*/
virtual Bookmark getCurrentBookmark() const = 0;
/**
* Gets a handle that can be used to reset the manipulator back to its home position.
*
* @see jumpToBookmark
*/
virtual Bookmark getHomeBookmark() const = 0;
/**
* Sets the manipulator position and orientation back to a stashed state.
*
* @see getCurrentBookmark, getHomeBookmark
*/
virtual void jumpToBookmark(const Bookmark& bookmark) = 0;
protected:
Manipulator(Mode mode, const Config& props);
virtual void setProperties(const Config& props);
vec3 raycastFarPlane(int x, int y) const;
const Mode mMode;
Config mProps;
vec3 mEye;
vec3 mTarget;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_MANIPULATOR_H */

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_COMPILER_H
#define CAMUTILS_COMPILER_H
#if __has_attribute(visibility)
# define CAMUTILS_PUBLIC __attribute__((visibility("default")))
#else
# define CAMUTILS_PUBLIC
#endif
#endif // CAMUTILS_COMPILER_H

6327
ios/include/cgltf.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_ENUMMANAGER_H
#define TNT_ENUMMANAGER_H
#include <algorithm>
#include <string>
#include <unordered_map>
#include <filamat/MaterialBuilder.h>
namespace filamat {
using Property = MaterialBuilder::Property;
using UniformType = MaterialBuilder::UniformType;
using SamplerType = MaterialBuilder::SamplerType;
using SubpassType = MaterialBuilder::SubpassType;
using SamplerFormat = MaterialBuilder::SamplerFormat;
using ParameterPrecision = MaterialBuilder::ParameterPrecision;
using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
// Convenience methods to convert std::string to Enum and also iterate over Enum values.
class Enums {
public:
// Returns true if string "s" is a valid string representation of an element of enum T.
template<typename T>
static bool isValid(const std::string& s) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map.find(s) != map.end();
}
// Return enum matching its string representation. Returns undefined if s is not a valid enum T
// value. You should always call isValid() first to validate a string before calling toEnum().
template<typename T>
static T toEnum(const std::string& s) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map.at(s);
}
template<typename T>
static std::string toString(T t) noexcept;
// Return a map of all values in an enum with their string representation.
template<typename T>
static std::unordered_map<std::string, T>& map() noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map;
};
private:
template<typename T>
static std::unordered_map<std::string, T>& getMap() noexcept;
static std::unordered_map<std::string, Property> mStringToProperty;
static std::unordered_map<std::string, UniformType> mStringToUniformType;
static std::unordered_map<std::string, SamplerType> mStringToSamplerType;
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
};
template<typename T>
std::string Enums::toString(T t) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
auto result = std::find_if(map.begin(), map.end(), [t](auto& pair) {
return pair.second == t;
});
if (result != map.end()) {
return result->first;
}
return "";
}
} // namespace filamat
#endif //TNT_ENUMMANAGER_H

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_INCLUDER_H
#define TNT_FILAMAT_INCLUDER_H
#include <utils/CString.h>
#include <functional>
namespace filamat {
struct IncludeResult {
// The include name of the root file, as if it were being included.
// I.e., 'foobar.h' in the case of #include "foobar.h"
const utils::CString includeName;
// The following fields should be filled out by the IncludeCallback when processing an include,
// or when calling resolveIncludes for the root file.
// The full contents of the include file. This may contain additional, recursive include
// directives.
utils::CString text;
// The line number for the first line of text (first line is 0).
size_t lineNumberOffset = 0;
// The name of the include file. This gets passed as "includerName" for any includes inside of
// source. This field isn't used by the include system; it's up to the callback to give meaning
// to this value and interpret it accordingly. In the case of DirIncluder, this is an empty
// string to represent the root include file, and a canonical path for subsequent included
// files.
utils::CString name;
};
/**
* A callback invoked by the include system when an #include "file.h" directive is found.
*
* For example, if a file main.h includes file.h on line 10, then IncludeCallback would be called
* with the following:
* includeCallback("main.h", {.includeName = "file.h" })
* It's then up to the IncludeCallback to fill out the .text, .name, and (optionally)
* lineNumberOffset fields.
*
* @param includedBy is the value that was given to IncludeResult.name for this source file, or
* the empty string for the root source file.
* @param result is the IncludeResult that the callback should fill out.
* @return true, if the include was resolved successfully, false otherwise.
*
* For an example of implementing this callback, see tools/matc/src/matc/DirIncluder.h.
*/
using IncludeCallback = std::function<bool(
const utils::CString& includedBy,
IncludeResult& result)>;
} // namespace filamat
#endif

View File

@@ -0,0 +1,746 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H
#define TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H
#include <cstddef>
#include <cstdint>
#include <atomic>
#include <string>
#include <vector>
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <filament/MaterialEnums.h>
#include <filamat/IncludeCallback.h>
#include <filamat/Package.h>
#include <utils/BitmaskEnum.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
namespace utils {
class JobSystem;
}
namespace filamat {
struct MaterialInfo;
class ChunkContainer;
struct Variant;
class UTILS_PUBLIC MaterialBuilderBase {
public:
/**
* High-level hint that works in concert with TargetApi to determine the shader models (used to
* generate GLSL) and final output representations (spirv and/or text).
*/
enum class Platform {
DESKTOP,
MOBILE,
ALL
};
enum class TargetApi : uint8_t {
OPENGL = 0x01u,
VULKAN = 0x02u,
METAL = 0x04u,
ALL = OPENGL | VULKAN | METAL
};
enum class TargetLanguage {
GLSL, // GLSL with OpenGL semantics
SPIRV // GLSL with Vulkan semantics
};
enum class Optimization {
NONE,
PREPROCESSOR,
SIZE,
PERFORMANCE
};
/**
* Initialize MaterialBuilder.
*
* init must be called first before building any materials.
*/
static void init();
/**
* Release internal MaterialBuilder resources.
*
* Call shutdown when finished building materials to release all internal resources. After
* calling shutdown, another call to MaterialBuilder::init must precede another material build.
*/
static void shutdown();
protected:
// Looks at platform and target API, then decides on shader models and output formats.
void prepare(bool vulkanSemantics);
using ShaderModel = filament::backend::ShaderModel;
Platform mPlatform = Platform::DESKTOP;
TargetApi mTargetApi = (TargetApi) 0;
Optimization mOptimization = Optimization::PERFORMANCE;
bool mPrintShaders = false;
bool mGenerateDebugInfo = false;
utils::bitset32 mShaderModels;
struct CodeGenParams {
int shaderModel;
TargetApi targetApi;
TargetLanguage targetLanguage;
};
std::vector<CodeGenParams> mCodeGenPermutations;
// For finding properties and running semantic analysis, we always use the same code gen
// permutation. This is the first permutation generated with default arguments passed to matc.
const CodeGenParams mSemanticCodeGenParams = {
.shaderModel = (int) ShaderModel::GL_ES_30,
.targetApi = TargetApi::OPENGL,
.targetLanguage = TargetLanguage::SPIRV
};
uint8_t mVariantFilter = 0;
// Keeps track of how many times MaterialBuilder::init() has been called without a call to
// MaterialBuilder::shutdown(). Internally, glslang does something similar. We keep track for
// ourselves so we can inform the user if MaterialBuilder::init() hasn't been called before
// attempting to build a material.
static std::atomic<int> materialBuilderClients;
};
// Utility function that looks at an Engine backend to determine TargetApi
inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend(
filament::backend::Backend backend) noexcept {
using filament::backend::Backend;
using TargetApi = MaterialBuilderBase::TargetApi;
switch (backend) {
case Backend::DEFAULT: return TargetApi::ALL;
case Backend::OPENGL: return TargetApi::OPENGL;
case Backend::VULKAN: return TargetApi::VULKAN;
case Backend::METAL: return TargetApi::METAL;
case Backend::NOOP: return TargetApi::OPENGL;
}
}
/**
* MaterialBuilder builds Filament materials from shader code.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filamat/MaterialBuilder.h>
* using namespace filamat;
*
* // Must be called before any materials can be built.
* MaterialBuilder::init();
* MaterialBuilder builder;
* builder
* .name("My material")
* .material("void material (inout MaterialInputs material) {"
* " prepareMaterial(material);"
* " material.baseColor.rgb = float3(1.0, 0.0, 0.0);"
* "}")
* .shading(MaterialBuilder::Shading::LIT)
* .targetApi(MaterialBuilder::TargetApi::ALL)
* .platform(MaterialBuilder::Platform::ALL);
* Package package = builder.build();
* if (package.isValid()) {
* // success!
* }
* // Call when finished building all materials to release internal
* // MaterialBuilder resources.
* MaterialBuilder::shutdown();
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see filament::Material
*/
class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase {
public:
MaterialBuilder();
static constexpr size_t MATERIAL_VARIABLES_COUNT = 4;
enum class Variable : uint8_t {
CUSTOM0,
CUSTOM1,
CUSTOM2,
CUSTOM3
// when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT
};
using MaterialDomain = filament::MaterialDomain;
using RefractionMode = filament::RefractionMode;
using RefractionType = filament::RefractionType;
using ShaderQuality = filament::ShaderQuality;
using BlendingMode = filament::BlendingMode;
using Shading = filament::Shading;
using Interpolation = filament::Interpolation;
using VertexDomain = filament::VertexDomain;
using TransparencyMode = filament::TransparencyMode;
using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion;
using UniformType = filament::backend::UniformType;
using SamplerType = filament::backend::SamplerType;
using SubpassType = filament::backend::SubpassType;
using SamplerFormat = filament::backend::SamplerFormat;
using ParameterPrecision = filament::backend::Precision;
using CullingMode = filament::backend::CullingMode;
enum class VariableQualifier : uint8_t {
OUT
};
enum class OutputTarget : uint8_t {
COLOR,
DEPTH
};
enum class OutputType : uint8_t {
FLOAT,
FLOAT2,
FLOAT3,
FLOAT4
};
struct PreprocessorDefine {
std::string name;
std::string value;
PreprocessorDefine(const std::string& name, const std::string& value) :
name(name), value(value) {}
};
using PreprocessorDefineList = std::vector<PreprocessorDefine>;
//! Set the name of this material.
MaterialBuilder& name(const char* name) noexcept;
//! Set the file name of this material file. Used in error reporting.
MaterialBuilder& fileName(const char* name) noexcept;
//! Set the shading model.
MaterialBuilder& shading(Shading shading) noexcept;
//! Set the interpolation mode.
MaterialBuilder& interpolation(Interpolation interpolation) noexcept;
//! Add a parameter (i.e., a uniform) to this material.
MaterialBuilder& parameter(UniformType type, ParameterPrecision precision,
const char* name) noexcept;
//! Add a parameter (i.e., a uniform) to this material.
MaterialBuilder& parameter(UniformType type, const char* name) noexcept {
return parameter(type, ParameterPrecision::DEFAULT, name);
}
//! Add a parameter array to this material.
MaterialBuilder& parameter(UniformType type, size_t size,
ParameterPrecision precision, const char* name) noexcept;
//! Add a parameter array to this material.
MaterialBuilder& parameter(UniformType type, size_t size, const char* name) noexcept {
return parameter(type, size, ParameterPrecision::DEFAULT, name);
}
/**
* Add a sampler parameter to this material.
*
* When SamplerType::SAMPLER_EXTERNAL is specifed, format and precision are ignored.
*/
MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format,
ParameterPrecision precision, const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format,
const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, ParameterPrecision precision,
const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, const char* name) noexcept;
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
/**
* Require a specified attribute.
*
* position is always required and normal depends on the shading model.
*/
MaterialBuilder& require(filament::VertexAttribute attribute) noexcept;
//! Specify the domain that this material will operate in.
MaterialBuilder& materialDomain(MaterialDomain materialDomain) noexcept;
/**
* Set the code content of this material.
*
* Surface Domain
* --------------
*
* Materials in the SURFACE domain must declare a function:
* ~~~~~
* void material(inout MaterialInputs material) {
* prepareMaterial(material);
* material.baseColor.rgb = float3(1.0, 0.0, 0.0);
* }
* ~~~~~
* this function *must* call `prepareMaterial(material)` before it returns.
*
* Post-process Domain
* -------------------
*
* Materials in the POST_PROCESS domain must declare a function:
* ~~~~~
* void postProcess(inout PostProcessInputs postProcess) {
* postProcess.color = float4(1.0);
* }
* ~~~~~
*
* @param code The source code of the material.
* @param line The line number offset of the material, where 0 is the first line. Used for error
* reporting
*/
MaterialBuilder& material(const char* code, size_t line = 0) noexcept;
/**
* Set the callback used for resolving include directives.
* The default is no callback, which disallows all includes.
*/
MaterialBuilder& includeCallback(IncludeCallback callback) noexcept;
/**
* Set the vertex code content of this material.
*
* Surface Domain
* --------------
*
* Materials in the SURFACE domain must declare a function:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void materialVertex(inout MaterialVertexInputs material) {
*
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Post-process Domain
* -------------------
*
* Materials in the POST_PROCESS domain must declare a function:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void postProcessVertex(inout PostProcessVertexInputs postProcess) {
*
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* @param code The source code of the material.
* @param line The line number offset of the material, where 0 is the first line. Used for error
* reporting
*/
MaterialBuilder& materialVertex(const char* code, size_t line = 0) noexcept;
MaterialBuilder& quality(ShaderQuality quality) noexcept;
//! Set the blending mode for this material.
MaterialBuilder& blending(BlendingMode blending) noexcept;
/**
* Set the blending mode of the post-lighting color for this material.
* Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT.
* This setting requires the material property "postLightingColor" to be set.
*/
MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept;
//! Set the vertex domain for this material.
MaterialBuilder& vertexDomain(VertexDomain domain) noexcept;
/**
* How triangles are culled by default (doesn't affect points or lines, BACK by default).
* Material instances can override this.
*/
MaterialBuilder& culling(CullingMode culling) noexcept;
//! Enable / disable color-buffer write (enabled by default, material instances can override).
MaterialBuilder& colorWrite(bool enable) noexcept;
//! Enable / disable depth-buffer write (enabled by default for opaque, disabled for others, material instances can override).
MaterialBuilder& depthWrite(bool enable) noexcept;
//! Enable / disable depth based culling (enabled by default, material instances can override).
MaterialBuilder& depthCulling(bool enable) noexcept;
/**
* Double-sided materials don't cull faces, equivalent to culling(CullingMode::NONE).
* doubleSided() overrides culling() if called.
* When called with "false", this enables the capability for a run-time toggle.
*/
MaterialBuilder& doubleSided(bool doubleSided) noexcept;
/**
* Any fragment with an alpha below this threshold is clipped (MASKED blending mode only).
* The mask threshold can also be controlled by using the float material parameter called
* `_maskThreshold`, or by calling
* @ref filament::MaterialInstance::setMaskThreshold "MaterialInstance::setMaskThreshold".
*/
MaterialBuilder& maskThreshold(float threshold) noexcept;
//! The material output is multiplied by the shadowing factor (UNLIT model only).
MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept;
//! This material casts transparent shadows. The blending mode must be TRANSPARENT or FADE.
MaterialBuilder& transparentShadow(bool transparentShadow) noexcept;
/**
* Reduces specular aliasing for materials that have low roughness. Turning this feature on also
* helps preserve the shapes of specular highlights as an object moves away from the camera.
* When turned on, two float material parameters are added to control the effect:
* `_specularAAScreenSpaceVariance` and `_specularAAThreshold`. You can also use
* @ref filament::MaterialInstance::setSpecularAntiAliasingVariance
* "MaterialInstance::setSpecularAntiAliasingVariance" and
* @ref filament::MaterialInstance::setSpecularAntiAliasingThreshold
* "setSpecularAntiAliasingThreshold"
*
* Disabled by default.
*/
MaterialBuilder& specularAntiAliasing(bool specularAntiAliasing) noexcept;
/**
* Sets the screen-space variance of the filter kernel used when applying specular
* anti-aliasing. The default value is set to 0.15. The specified value should be between 0 and
* 1 and will be clamped if necessary.
*/
MaterialBuilder& specularAntiAliasingVariance(float screenSpaceVariance) noexcept;
/**
* Sets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0 and 1
* and will be clamped if necessary.
*/
MaterialBuilder& specularAntiAliasingThreshold(float threshold) noexcept;
/**
* Enables or disables the index of refraction (IoR) change caused by the clear coat layer when
* present. When the IoR changes, the base color is darkened. Disabling this feature preserves
* the base color as initially specified.
*
* Enabled by default.
*/
MaterialBuilder& clearCoatIorChange(bool clearCoatIorChange) noexcept;
//! Enable / disable flipping of the Y coordinate of UV attributes, enabled by default.
MaterialBuilder& flipUV(bool flipUV) noexcept;
//! Enable / disable multi-bounce ambient occlusion, disabled by default on mobile.
MaterialBuilder& multiBounceAmbientOcclusion(bool multiBounceAO) noexcept;
//! Set the specular ambient occlusion technique. Disabled by default on mobile.
MaterialBuilder& specularAmbientOcclusion(SpecularAmbientOcclusion specularAO) noexcept;
//! Specify the refraction
MaterialBuilder& refractionMode(RefractionMode refraction) noexcept;
//! Specify the refraction type
MaterialBuilder& refractionType(RefractionType refractionType) noexcept;
//! Specifies how transparent objects should be rendered (default is DEFAULT).
MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept;
/**
* Enable / disable custom surface shading. Custom surface shading requires the LIT
* shading model. In addition, the following function must be defined in the fragment
* block:
*
* ~~~~~
* vec3 surfaceShading(const MaterialInputs materialInputs,
* const ShadingData shadingData, const LightData lightData) {
*
* return vec3(1.0); // Compute surface shading with custom BRDF, etc.
* }
* ~~~~~
*
* This function is invoked once per light. Please refer to the materials documentation
* for more information about the different parameters.
*
* @param customSurfaceShading Enables or disables custom surface shading
*/
MaterialBuilder& customSurfaceShading(bool customSurfaceShading) noexcept;
/**
* Specifies desktop vs mobile; works in concert with TargetApi to determine the shader models
* (used to generate code) and final output representations (spirv and/or text).
*/
MaterialBuilder& platform(Platform platform) noexcept;
/**
* Specifies OpenGL, Vulkan, or Metal.
* This can be called repeatedly to build for multiple APIs.
* Works in concert with Platform to determine the shader models (used to generate code) and
* final output representations (spirv and/or text).
* If linking against filamat_lite, only `OPENGL` is allowed.
*/
MaterialBuilder& targetApi(TargetApi targetApi) noexcept;
/**
* Specifies the level of optimization to apply to the shaders (default is PERFORMANCE).
* If linking against filamat_lite, this _must_ be called with Optimization::NONE.
*/
MaterialBuilder& optimization(Optimization optimization) noexcept;
// TODO: this is present here for matc's "--print" flag, but ideally does not belong inside
// MaterialBuilder.
//! If true, will output the generated GLSL shader code to stdout.
MaterialBuilder& printShaders(bool printShaders) noexcept;
//! If true, will include debugging information in generated SPIRV.
MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept;
//! Specifies a list of variants that should be filtered out during code generation.
MaterialBuilder& variantFilter(uint8_t variantFilter) noexcept;
//! Adds a new preprocessor macro definition to the shader code. Can be called repeatedly.
MaterialBuilder& shaderDefine(const char* name, const char* value) noexcept;
//! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain.
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target,
OutputType type, const char* name, int location = -1) noexcept;
MaterialBuilder& enableFramebufferFetch() noexcept;
/**
* Build the material. If you are using the Filament engine with this library, you should use
* the job system provided by Engine.
*/
Package build(utils::JobSystem& jobSystem) noexcept;
public:
// The methods and types below are for internal use
/// @cond never
/**
* Add a subpass parameter to this material.
*/
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, ParameterPrecision
precision, const char* name) noexcept;
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, const char* name)
noexcept;
MaterialBuilder& parameter(SubpassType subpassType, ParameterPrecision precision,
const char* name) noexcept;
MaterialBuilder& parameter(SubpassType subpassType, const char* name) noexcept;
struct Parameter {
Parameter() noexcept : parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
: name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { }
// Subpass
Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { }
utils::CString name;
size_t size;
UniformType uniformType;
ParameterPrecision precision;
SamplerType samplerType;
SubpassType subpassType;
SamplerFormat format;
enum {
INVALID,
UNIFORM,
SAMPLER,
SUBPASS
} parameterType;
bool isSampler() const { return parameterType == SAMPLER; }
bool isUniform() const { return parameterType == UNIFORM; }
bool isSubpass() const { return parameterType == SUBPASS; }
};
struct Output {
Output() noexcept = default;
Output(const char* outputName, VariableQualifier qualifier, OutputTarget target,
OutputType type, int location) noexcept
: name(outputName), qualifier(qualifier), target(target), type(type),
location(location) { }
utils::CString name;
VariableQualifier qualifier;
OutputTarget target;
OutputType type;
int location;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
using PropertyList = bool[MATERIAL_PROPERTIES_COUNT];
using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT];
using OutputList = std::vector<Output>;
static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
static constexpr size_t MAX_DEPTH_OUTPUT = 1;
static_assert(MAX_COLOR_OUTPUT == 8,
"When updating MRT::TARGET_COUNT, manually update post_process_inputs.fs"
" and post_process.fs");
// Preview the first shader generated by the given CodeGenParams.
// This is used to run Static Code Analysis before generating a package.
const std::string peek(filament::backend::ShaderType type,
const CodeGenParams& params, const PropertyList& properties) noexcept;
// Returns true if any of the parameter samplers is of type samplerExternal
bool hasExternalSampler() const noexcept;
static constexpr size_t MAX_PARAMETERS_COUNT = 48;
static constexpr size_t MAX_SUBPASS_COUNT = 1;
using ParameterList = Parameter[MAX_PARAMETERS_COUNT];
// returns the number of parameters declared in this material
uint8_t getParameterCount() const noexcept { return mParameterCount; }
// returns a list of at least getParameterCount() parameters
const ParameterList& getParameters() const noexcept { return mParameters; }
uint8_t getVariantFilter() const { return mVariantFilter; }
/// @endcond
private:
void prepareToBuild(MaterialInfo& info) noexcept;
// Return true if the shader is syntactically and semantically valid.
// This method finds all the properties defined in the fragment and
// vertex shaders of the material.
bool findAllProperties() noexcept;
// Multiple calls to findProperties accumulate the property sets across fragment
// and vertex shaders in mProperties.
bool findProperties(filament::backend::ShaderType type,
MaterialBuilder::PropertyList& p) noexcept;
bool runSemanticAnalysis() noexcept;
bool checkLiteRequirements() noexcept;
void writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept;
void writeSurfaceChunks(ChunkContainer& container) const noexcept;
bool generateShaders(
utils::JobSystem& jobSystem,
const std::vector<Variant>& variants, ChunkContainer& container,
const MaterialInfo& info) const noexcept;
bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; }
utils::CString mMaterialName;
utils::CString mFileName;
class ShaderCode {
public:
void setLineOffset(size_t offset) noexcept { mLineOffset = offset; }
void setUnresolved(const utils::CString& code) noexcept {
mIncludesResolved = false;
mCode = code;
}
// Resolve all the #include directives, returns true if successful.
bool resolveIncludes(IncludeCallback callback, const utils::CString& fileName) noexcept;
const utils::CString& getResolved() const noexcept {
assert(mIncludesResolved);
return mCode;
}
size_t getLineOffset() const noexcept { return mLineOffset; }
private:
utils::CString mCode;
size_t mLineOffset = 0;
bool mIncludesResolved = false;
};
ShaderCode mMaterialCode;
ShaderCode mMaterialVertexCode;
IncludeCallback mIncludeCallback = nullptr;
PropertyList mProperties;
ParameterList mParameters;
VariableList mVariables;
OutputList mOutputs;
ShaderQuality mShaderQuality = ShaderQuality::DEFAULT;
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT;
CullingMode mCullingMode = CullingMode::BACK;
Shading mShading = Shading::LIT;
MaterialDomain mMaterialDomain = MaterialDomain::SURFACE;
RefractionMode mRefractionMode = RefractionMode::NONE;
RefractionType mRefractionType = RefractionType::SOLID;
Interpolation mInterpolation = Interpolation::SMOOTH;
VertexDomain mVertexDomain = VertexDomain::OBJECT;
TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT;
filament::AttributeBitset mRequiredAttributes;
float mMaskThreshold = 0.4f;
float mSpecularAntiAliasingVariance = 0.15f;
float mSpecularAntiAliasingThreshold = 0.2f;
bool mShadowMultiplier = false;
bool mTransparentShadow = false;
uint8_t mParameterCount = 0;
bool mDoubleSided = false;
bool mDoubleSidedCapability = false;
bool mColorWrite = true;
bool mDepthTest = true;
bool mDepthWrite = true;
bool mDepthWriteSet = false;
bool mSpecularAntiAliasing = false;
bool mClearCoatIorChange = true;
bool mFlipUV = true;
bool mMultiBounceAO = false;
bool mMultiBounceAOSet = false;
SpecularAmbientOcclusion mSpecularAO = SpecularAmbientOcclusion::NONE;
bool mSpecularAOSet = false;
bool mCustomSurfaceShading = false;
bool mEnableFramebufferFetch = false;
PreprocessorDefineList mDefines;
};
} // namespace filamat
template<> struct utils::EnableBitMaskOperators<filamat::MaterialBuilder::TargetApi>
: public std::true_type {};
#endif

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_PACKAGE_H
#define TNT_FILAMAT_PACKAGE_H
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <cstddef>
#include <functional>
#include <utils/compiler.h>
namespace filamat {
class UTILS_PUBLIC Package {
public:
Package() = default;
// Regular constructor
explicit Package(size_t size) : mSize(size) {
mPayload = new uint8_t[size];
}
Package(const void* src, size_t size) : Package(size) {
memcpy(mPayload, src, size);
}
// Move Constructor
Package(Package&& other) noexcept : mPayload(other.mPayload), mSize(other.mSize),
mValid(other.mValid) {
other.mPayload = nullptr;
other.mSize = 0;
other.mValid = false;
}
// Move assignment
Package& operator=(Package&& other) noexcept {
std::swap(mPayload, other.mPayload);
std::swap(mSize, other.mSize);
std::swap(mValid, other.mValid);
return *this;
}
// Copy assignment operator disallowed.
Package& operator=(const Package& other) = delete;
// Copy constructor disallowed.
Package(const Package& other) = delete;
~Package() {
delete[] mPayload;
}
uint8_t* getData() const noexcept {
return mPayload;
}
size_t getSize() const noexcept {
return mSize;
}
uint8_t* getEnd() const noexcept {
return mPayload + mSize;
}
void setValid(bool valid) noexcept {
mValid = valid;
}
bool isValid() const noexcept {
return mValid;
}
static Package invalidPackage() {
Package package(0);
package.setValid(false);
return package;
}
private:
uint8_t* mPayload = nullptr;
size_t mSize = 0;
bool mValid = true;
};
} // namespace filamat
#endif

View File

@@ -0,0 +1,242 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_IBL_PREFILTER_IBLPREFILTER_H
#define TNT_IBL_PREFILTER_IBLPREFILTER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <filament/Texture.h>
namespace filament {
class Engine;
class View;
class Scene;
class Renderer;
class Material;
class MaterialInstance;
class VertexBuffer;
class IndexBuffer;
class Camera;
class Texture;
} // namespace filament
/**
* IBLPrefilterContext creates and initializes GPU state common to all environment map filters
* supported. Typically, only one instance per filament Engine of this object needs to exist.
*
* Usage Example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
*
* IBLPrefilterContext context(engine);
* IBLPrefilterContext::SpecularFilter filter(context);
* Texture* texture = filter(environment_cubemap);
*
* IndirectLight* indirectLight = IndirectLight::Builder()
* .reflections(texture)
* .build(engine);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC IBLPrefilterContext {
public:
/**
* Creates an IBLPrefilter context.
* @param engine filament engine to use
*/
IBLPrefilterContext(filament::Engine& engine);
/**
* Destroys all GPU resources created during initialization.
*/
~IBLPrefilterContext() noexcept;
// not copyable
IBLPrefilterContext(IBLPrefilterContext const&) = delete;
IBLPrefilterContext& operator=(IBLPrefilterContext const&) = delete;
// movable
IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept;
IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs);
// -------------------------------------------------------------------------------------------
/**
* EquirectangularToCubemap is use to convert an equirectangluar image to a cubemap.
*/
class EquirectangularToCubemap {
public:
/**
* Creates a EquirectangularToCubemap processor.
* @param context IBLPrefilterContext to use
*/
explicit EquirectangularToCubemap(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~EquirectangularToCubemap() noexcept;
EquirectangularToCubemap(EquirectangularToCubemap const&) = delete;
EquirectangularToCubemap& operator=(EquirectangularToCubemap const&) = delete;
EquirectangularToCubemap(EquirectangularToCubemap&& rhs) noexcept;
EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs);
/**
* Converts an equirectangular image to a cubemap.
* @param equirectangular Texture to convert to a cubemap.
* - Can't be null.
* - Must be a 2d texture
* - Must have equirectangular geometry, that is width == 2*height.
* - Must be allocated with all mip levels.
* - Must be SAMPLEABLE
* @param outCubemap Output cubemap. If null the texture is automatically created
* with default parameters (size of 256 with 5 levels).
* - Must be a cubemap
* - Must have SAMPLEABLE and COLOR_ATTACHMENT usage bits
* @return returns outCubemap
*/
filament::Texture* operator()(
filament::Texture const* equirectangular,
filament::Texture* outCubemap = nullptr);
private:
IBLPrefilterContext& mContext;
filament::Material* mEquirectMaterial = nullptr;
};
/**
* SpecularFilter is a GPU based implementation of the specular probe pre-integration filter.
* An instance of SpecularFilter is needed per filter configuration. A filter configuration
* contains the filter's kernel and sample count.
*/
class SpecularFilter {
public:
enum class Kernel : uint8_t {
D_GGX, // Trowbridge-reitz distribution
};
/**
* Filter configuration.
*/
struct Config {
uint16_t sampleCount = 1024u; //!< filter sample count (max 2048)
uint8_t levelCount = 5u; //!< number of roughness levels
Kernel kernel = Kernel::D_GGX; //!< filter kernel
};
/**
* Filtering options for the current environment.
*/
struct Options {
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
float lodOffset = 1.0f; //!< Good values are 1.0 or 2.0. Higher values help with heavily HDR inputs.
bool generateMipmap = true; //!< set to false if the environment map already has mipmaps
};
/**
* Creates a SpecularFilter processor.
* @param context IBLPrefilterContext to use
* @param config Configuration of the filter
*/
SpecularFilter(IBLPrefilterContext& context, Config config);
/**
* Creates a filter with the default configuration.
* @param context IBLPrefilterContext to use
*/
explicit SpecularFilter(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~SpecularFilter() noexcept;
SpecularFilter(SpecularFilter const&) = delete;
SpecularFilter& operator=(SpecularFilter const&) = delete;
SpecularFilter(SpecularFilter&& rhs) noexcept;
SpecularFilter& operator=(SpecularFilter&& rhs);
/**
* Generates a prefiltered cubemap.
* @param options Options for this environment
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outReflectionsTexture Output prefiltered texture or, if null, it is
* automatically created with some default parameters.
* outReflectionsTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages and at
* least the same number of levels than requested by Config.
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(Options options,
filament::Texture const* environmentCubemap,
filament::Texture* outReflectionsTexture = nullptr);
/**
* Generates a prefiltered cubemap.
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. All mipmap levels will be overwritten.
* @param outReflectionsTexture Output prefiltered texture or, if null, it is
* automatically created with some default parameters.
* outReflectionsTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages and at
* least the same number of levels than requested by Config.
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(
filament::Texture const* environmentCubemap,
filament::Texture* outReflectionsTexture = nullptr);
// TODO: option for progressive filtering
// TODO: add a callback for when the processing is done?
private:
filament::Texture* createReflectionsTexture();
IBLPrefilterContext& mContext;
filament::Material* mKernelMaterial = nullptr;
filament::Texture* mKernelTexture = nullptr;
uint32_t mSampleCount = 0u;
uint8_t mLevelCount = 1u;
};
private:
friend class Filter;
filament::Engine& mEngine;
filament::Renderer* mRenderer{};
filament::Scene* mScene{};
filament::VertexBuffer* mVertexBuffer{};
filament::IndexBuffer* mIndexBuffer{};
filament::Camera* mCamera{};
utils::Entity mFullScreenQuadEntity{};
utils::Entity mCameraEntity{};
filament::View* mView{};
filament::Material* mIntegrationMaterial{};
};
#endif //TNT_IBL_PREFILTER_IBLPREFILTER_H

198
ios/include/filament/Box.h Normal file
View File

@@ -0,0 +1,198 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BOX_H
#define TNT_FILAMENT_BOX_H
#include <utils/compiler.h>
#include <limits>
#include <math/mat4.h>
#include <math/vec3.h>
namespace filament {
/**
* An axis aligned 3D box represented by its center and half-extent.
*/
class UTILS_PUBLIC Box {
public:
/** Center of the 3D box */
math::float3 center = {};
/** Half extent from the center on all 3 axis */
math::float3 halfExtent = {};
/**
* Whether the box is empty, i.e.: it's volume is null.
* @return true if the volume of the box is null
*/
constexpr bool isEmpty() const noexcept {
return length2(halfExtent) == 0;
}
/**
* Computes the lowest coordinates corner of the box.
* @return center - halfExtent
*/
constexpr math::float3 getMin() const noexcept {
return center - halfExtent;
}
/**
* Computes the largest coordinates corner of the box.
* @return center + halfExtent
*/
constexpr math::float3 getMax() const noexcept {
return center + halfExtent;
}
/**
* Initializes the 3D box from its min / max coordinates on each axis
* @param min lowest coordinates corner of the box
* @param max largest coordinates corner of the box
* @return This bounding box
*/
Box& set(const math::float3& min, const math::float3& max) noexcept {
// float3 ctor needed for visual studio
center = (max + min) * math::float3(0.5f);
halfExtent = (max - min) * math::float3(0.5f);
return *this;
}
/**
* Computes the bounding box of the union of two boxes
* @param box The box to be combined with
* @return The bounding box of the union of *this and box
*/
Box& unionSelf(const Box& box) noexcept {
set(min(getMin(), box.getMin()), max(getMax(), box.getMax()));
return *this;
}
/**
* Translates the box *to* a given center position
* @param tr position to translate the box to
* @return A box centered in \p tr with the same extent than *this
*/
constexpr Box translateTo(const math::float3& tr) const noexcept {
return Box{ tr, halfExtent };
}
/**
* Computes the smallest bounding sphere of the box.
* @return The smallest sphere defined by its center (.xyz) and radius (.w) that contains *this
*/
math::float4 getBoundingSphere() const noexcept {
return { center, length(halfExtent) };
}
/**
* Computes the bounding box of a box transformed by a rigid transform
* @param box the box to transform
* @param m a 4x4 matrix that must be a rigid transform
* @return the bounding box of the transformed box.
* Result is undefined if \p m is not a rigid transform
*/
friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept;
/**
* Computes the bounding box of a box transformed by a rigid transform
* @param box the box to transform
* @param m a 3x3 matrix that must be a rigid transform
* @return the bounding box of the transformed box.
* Result is undefined if \p m is not a rigid transform
*/
friend Box rigidTransform(Box const& box, const math::mat3f& m) noexcept;
};
/**
* An axis aligned box represented by its min and max coordinates
*/
struct UTILS_PUBLIC Aabb {
/** min coordinates */
math::float3 min = std::numeric_limits<float>::max();
/** max coordinates */
math::float3 max = std::numeric_limits<float>::lowest();
/**
* Computes the center of the box.
* @return (max + min)/2
*/
math::float3 center() const noexcept {
// float3 ctor needed for visual studio
return (max + min) * math::float3(0.5f);
}
/**
* Computes the half-extent of the box.
* @return (max - min)/2
*/
math::float3 extent() const noexcept {
// float3 ctor needed for visual studio
return (max - min) * math::float3(0.5f);
}
/**
* Whether the box is empty, i.e.: it's volume is null or negative.
* @return true if min >= max, i.e: the volume of the box is null or negative
*/
bool isEmpty() const noexcept {
return any(greaterThanEqual(min, max));
}
struct Corners {
using value_type = math::float3;
value_type const* begin() const { return vertices; }
value_type const* end() const { return vertices + 8; }
value_type * begin() { return vertices; }
value_type * end() { return vertices + 8; }
value_type const* data() const { return vertices; }
value_type * data() { return vertices; }
size_t size() const { return 8; }
value_type vertices[8];
};
/**
* Returns the 8 corner vertices of the AABB.
*/
Corners getCorners() const;
/**
* Returns whether the box contains a given point.
*
* @param p the point to test
* @return the maximum signed distance to the box. Negative if p is in the box
*/
float contains(math::float3 p) const noexcept;
/**
* Applies an affine transformation to the AABB.
*
* @param m the 4x4 transformation to apply
* @return the transformed box
*/
Aabb transform(const math::mat4f& m) const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_BOX_H

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BUFFEROBJECT_H
#define TNT_FILAMENT_BUFFEROBJECT_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
namespace filament {
class FBufferObject;
class Engine;
/**
* A generic GPU buffer containing data.
*
* Usage of this BufferObject is optional. For simple use cases it is not necessary. It is useful
* only when you need to share data between multiple VertexBuffer instances. It also allows you to
* efficiently swap-out the buffers in VertexBuffer.
*
* NOTE: For now this is only used for vertex data, but in the future we may use it for other things
* (e.g. compute).
*
* @see VertexBuffer
*/
class UTILS_PUBLIC BufferObject : public FilamentAPI {
struct BuilderDetails;
public:
using BufferDescriptor = backend::BufferDescriptor;
using BindingType = backend::BufferObjectBinding;
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Size of the buffer in bytes.
* @param byteCount Maximum number of bytes the BufferObject can hold.
* @return A reference to this Builder for chaining calls.
*/
Builder& size(uint32_t byteCount) noexcept;
/**
* The binding type for this buffer object. (defaults to VERTEX)
* @param BindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX.
* @return A reference to this Builder for chaining calls.
*/
Builder& bindingType(BindingType bindingType) noexcept;
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
*
* @param engine Reference to the filament::Engine to associate this BufferObject with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*
* @see IndexBuffer::setBuffer
*/
BufferObject* build(Engine& engine);
private:
friend class FBufferObject;
};
/**
* Asynchronously copy-initializes a region of this BufferObject from the data provided.
*
* @param engine Reference to the filament::Engine associated with this BufferObject.
* @param buffer A BufferDescriptor representing the data used to initialize the BufferObject.
* @param byteOffset Offset in bytes into the BufferObject
*/
void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0);
/**
* Returns the size of this BufferObject in elements.
* @return The maximum capacity of the BufferObject.
*/
size_t getByteCount() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_BUFFEROBJECT_H

View File

@@ -0,0 +1,516 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_CAMERA_H
#define TNT_FILAMENT_CAMERA_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
#include <math/vec2.h>
#include <math/vec4.h>
namespace utils {
class Entity;
} // namespace utils
namespace filament {
/**
* Camera represents the eye through which the scene is viewed.
*
* A Camera has a position and orientation and controls the projection and exposure parameters.
*
* Creation and destruction
* ========================
*
* In Filament, Camera is a component that must be associated with an entity. To do so,
* use Engine::createCamera(Entity). A Camera component is destroyed using
* Engine::destroyCameraComponent(Entity).
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* utils::Entity myCameraEntity = utils::EntityManager::get().create();
* filament::Camera* myCamera = engine->createCamera(myCameraEntity);
* myCamera->setProjection(45, 16.0/9.0, 0.1, 1.0);
* myCamera->lookAt({0, 1.60, 1}, {0, 0, 0});
* engine->destroyCameraComponent(myCamera);
* ~~~~~~~~~~~
*
*
* Coordinate system
* =================
*
* The camera coordinate system defines the *view space*. The camera points towards its -z axis
* and is oriented such that its top side is in the direction of +y, and its right side in the
* direction of +x.
*
* @note
* Since the *near* and *far* planes are defined by the distance from the camera,
* their respective coordinates are -\p distance(near) and -\p distance(far).
*
* Clipping planes
* ===============
*
* The camera defines six *clipping planes* which together create a *clipping volume*. The
* geometry outside this volume is clipped.
*
* The clipping volume can either be a box or a frustum depending on which projection is used,
* respectively Projection.ORTHO or Projection.PERSPECTIVE. The six planes are specified either
* directly or indirectly using setProjection().
*
* The six planes are:
* - left
* - right
* - bottom
* - top
* - near
* - far
*
* @note
* To increase the depth-buffer precision, the *far* clipping plane is always assumed to be at
* infinity for rendering. That is, it is not used to clip geometry during rendering.
* However, it is used during the culling phase (objects entirely behind the *far*
* plane are culled).
*
*
* Choosing the *near* plane distance
* ==================================
*
* The *near* plane distance greatly affects the depth-buffer resolution.
*
* Example: Precision at 1m, 10m, 100m and 1Km for various near distances assuming a 32-bit float
* depth-buffer
*
* near (m) | 1 m | 10 m | 100 m | 1 Km
* -----------:|:------:|:-------:|:--------:|:--------:
* 0.001 | 7.2e-5 | 0.0043 | 0.4624 | 48.58
* 0.01 | 6.9e-6 | 0.0001 | 0.0430 | 4.62
* 0.1 | 3.6e-7 | 7.0e-5 | 0.0072 | 0.43
* 1.0 | 0 | 3.8e-6 | 0.0007 | 0.07
*
*
* As can be seen in the table above, the depth-buffer precision drops rapidly with the
* distance to the camera.
* Make sure to pick the highest *near* plane distance possible.
*
*
* Exposure
* ========
*
* The Camera is also used to set the scene's exposure, just like with a real camera. The lights
* intensity and the Camera exposure interact to produce the final scene's brightness.
*
*
*
* \see Frustum, View
*/
class UTILS_PUBLIC Camera : public FilamentAPI {
public:
//! Denotes the projection type used by this camera. \see setProjection
enum class Projection : int {
PERSPECTIVE, //!< perspective projection, objects get smaller as they are farther
ORTHO //!< orthonormal projection, preserves distances
};
//! Denotes a field-of-view direction. \see setProjection
enum class Fov : int {
VERTICAL, //!< the field-of-view angle is defined on the vertical axis
HORIZONTAL //!< the field-of-view angle is defined on the horizontal axis
};
/** Sets the projection matrix from a frustum defined by six planes.
*
* @param projection type of #Projection to use.
*
* @param left distance in world units from the camera to the left plane,
* at the near plane.
* Precondition: \p left != \p right.
*
* @param right distance in world units from the camera to the right plane,
* at the near plane.
* Precondition: \p left != \p right.
*
* @param bottom distance in world units from the camera to the bottom plane,
* at the near plane.
* Precondition: \p bottom != \p top.
*
* @param top distance in world units from the camera to the top plane,
* at the near plane.
* Precondition: \p left != \p right.
*
* @param near distance in world units from the camera to the near plane. The near plane's
* position in view space is z = -\p near.
* Precondition: \p near > 0 for PROJECTION::PERSPECTIVE or
* \p near != far for PROJECTION::ORTHO
*
* @param far distance in world units from the camera to the far plane. The far plane's
* position in view space is z = -\p far.
* Precondition: \p far > near for PROJECTION::PERSPECTIVE or
* \p far != near for PROJECTION::ORTHO
*
* @attention these parameters are silently modified to meet the preconditions above.
*
* @see Projection, Frustum
*/
void setProjection(Projection projection,
double left, double right,
double bottom, double top,
double near, double far) noexcept;
/** Sets the projection matrix from the field-of-view.
*
* @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
* @param direction direction of the \p fovInDegrees parameter.
*
* @see Fov.
*/
void setProjection(double fovInDegrees, double aspect, double near, double far,
Fov direction = Fov::VERTICAL) noexcept;
/** Sets the projection matrix from the focal length.
*
* @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
void setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept;
/** Sets a custom projection matrix.
*
* The projection matrix must be of one of the following form:
* a 0 tx 0 a 0 0 tx
* 0 b ty 0 0 b 0 ty
* 0 0 tz c 0 0 c tz
* 0 0 -1 0 0 0 0 1
*
* The projection matrix must define an NDC system that must match the OpenGL convention,
* that is all 3 axis are mapped to [-1, 1].
*
* @param projection custom projection matrix used for rendering and culling
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept;
/** Sets the projection matrix.
*
* The projection matrices must be of one of the following form:
* a 0 tx 0 a 0 0 tx
* 0 b ty 0 0 b 0 ty
* 0 0 tz c 0 0 c tz
* 0 0 -1 0 0 0 0 1
*
* The projection matrices must define an NDC system that must match the OpenGL convention,
* that is all 3 axis are mapped to [-1, 1].
*
* @param projection custom projection matrix used for rendering
* @param projectionForCulling custom projection matrix used for culling
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling,
double near, double far) noexcept;
/** Sets an additional matrix that scales the projection matrix.
*
* This is useful to adjust the aspect ratio of the camera independent from its projection.
* First, pass an aspect of 1.0 to setProjection. Then set the scaling with the desired aspect
* ratio:
*
* const double aspect = width / height;
*
* // with Fov::HORIZONTAL passed to setProjection:
* camera->setScaling(double4 {1.0, aspect});
*
* // with Fov::VERTICAL passed to setProjection:
* camera->setScaling(double4 {1.0 / aspect, 1.0});
*
*
* By default, this is an identity matrix.
*
* @param scaling diagonal of the 2x2 scaling matrix to be applied after the projection matrix.
*
* @see setProjection, setLensProjection, setCustomProjection
*/
void setScaling(math::double2 scaling) noexcept;
/**
* Sets an additional matrix that shifts the projection matrix.
* By default, this is an identity matrix.
*
* @param shift x and y translation added to the projection matrix, specified in NDC
* coordinates, that is, if the translation must be specified in pixels,
* shift must be scaled by 1.0 / { viewport.width, viewport.height }.
*
* @see setProjection, setLensProjection, setCustomProjection
*/
void setShift(math::double2 shift) noexcept;
/** Returns the scaling amount used to scale the projection matrix.
*
* @return the diagonal of the scaling matrix applied after the projection matrix.
*
* @see setScaling
*/
math::double4 getScaling() const noexcept;
/** Returns the shift amount used to translate the projection matrix.
*
* @return the 2D translation x and y offsets applied after the projection matrix.
*
* @see setShift
*/
math::double2 getShift() const noexcept;
/** Returns the projection matrix used for rendering.
*
* The projection matrix used for rendering always has its far plane set to infinity. This
* is why it may differ from the matrix set through setProjection() or setLensProjection().
*
* @return The projection matrix used for rendering
*
* @see setProjection, setLensProjection, setCustomProjection, getCullingProjectionMatrix
*/
math::mat4 getProjectionMatrix() const noexcept;
/** Returns the projection matrix used for culling (far plane is finite).
*
* @return The projection matrix set by setProjection or setLensProjection.
*
* @see setProjection, setLensProjection, getProjectionMatrix
*/
math::mat4 getCullingProjectionMatrix() const noexcept;
//! Returns the frustum's near plane
float getNear() const noexcept;
//! Returns the frustum's far plane used for culling
float getCullingFar() const noexcept;
/** Sets the camera's view matrix.
*
* Helper method to set the camera's entity transform component.
* It has the same effect as calling:
*
* ~~~~~~~~~~~{.cpp}
* engine.getTransformManager().setTransform(
* engine.getTransformManager().getInstance(camera->getEntity()), view);
* ~~~~~~~~~~~
*
* @param view The camera position and orientation provided as a rigid transform matrix.
*
* @note The Camera "looks" towards its -z axis
*
* @warning \p view must be a rigid transform
*/
void setModelMatrix(const math::mat4& view) noexcept;
void setModelMatrix(const math::mat4f& view) noexcept; //!< \overload
/** Sets the camera's view matrix
*
* @param eye The position of the camera in world space.
* @param center The point in world space the camera is looking at.
* @param up A unit vector denoting the camera's "up" direction.
*/
void lookAt(const math::float3& eye,
const math::float3& center,
const math::float3& up) noexcept;
/** Sets the camera's view matrix, assuming up is along the y axis
*
* @param eye The position of the camera in world space.
* @param center The point in world space the camera is looking at.
*/
void lookAt(const math::float3& eye,
const math::float3& center) noexcept;
/** Returns the camera's model matrix
*
* Helper method to return the camera's entity transform component.
* It has the same effect as calling:
*
* ~~~~~~~~~~~{.cpp}
* engine.getTransformManager().getWorldTransform(
* engine.getTransformManager().getInstance(camera->getEntity()));
* ~~~~~~~~~~~
*
* @return The camera's pose in world space as a rigid transform. Parent transforms, if any,
* are taken into account.
*/
math::mat4 getModelMatrix() const noexcept;
//! Returns the camera's view matrix (inverse of the model matrix)
math::mat4 getViewMatrix() const noexcept;
//! Returns the camera's position in world space
math::float3 getPosition() const noexcept;
//! Returns the camera's normalized left vector
math::float3 getLeftVector() const noexcept;
//! Returns the camera's normalized up vector
math::float3 getUpVector() const noexcept;
//! Returns the camera's forward vector
math::float3 getForwardVector() const noexcept;
//! Returns the camera's field of view in degrees
float getFieldOfViewInDegrees(Fov direction) const noexcept;
//! Returns a Frustum object in world space
class Frustum getFrustum() const noexcept;
//! Returns the entity representing this camera
utils::Entity getEntity() const noexcept;
/** Sets this camera's exposure (default is f/16, 1/125s, 100 ISO)
*
* The exposure ultimately controls the scene's brightness, just like with a real camera.
* The default values provide adequate exposure for a camera placed outdoors on a sunny day
* with the sun at the zenith.
*
* @param aperture Aperture in f-stops, clamped between 0.5 and 64.
* A lower \p aperture value *increases* the exposure, leading to
* a brighter scene. Realistic values are between 0.95 and 32.
*
* @param shutterSpeed Shutter speed in seconds, clamped between 1/25,000 and 60.
* A lower shutter speed increases the exposure. Realistic values are
* between 1/8000 and 30.
*
* @param sensitivity Sensitivity in ISO, clamped between 10 and 204,800.
* A higher \p sensitivity increases the exposure. Realistic values are
* between 50 and 25600.
*
* @note
* With the default parameters, the scene must contain at least one Light of intensity
* similar to the sun (e.g.: a 100,000 lux directional light).
*
* @see LightManager, Exposure
*/
void setExposure(float aperture, float shutterSpeed, float sensitivity) noexcept;
/** Sets this camera's exposure directly. Calling this method will set the aperture
* to 1.0, the shutter speed to 1.2 and the sensitivity will be computed to match
* the requested exposure (for a desired exposure of 1.0, the sensitivity will be
* set to 100 ISO).
*
* This method is useful when trying to match the lighting of other engines or tools.
* Many engines/tools use unit-less light intensities, which can be matched by setting
* the exposure manually. This can be typically achieved by setting the exposure to
* 1.0.
*/
void setExposure(float exposure) noexcept {
setExposure(1.0f, 1.2f, 100.0f * (1.0f / exposure));
}
//! returns this camera's aperture in f-stops
float getAperture() const noexcept;
//! returns this camera's shutter speed in seconds
float getShutterSpeed() const noexcept;
//! returns this camera's sensitivity in ISO
float getSensitivity() const noexcept;
//! returns the focal length in meters [m] for a 35mm camera
double getFocalLength() const noexcept;
/**
* Sets the camera focus distance. This is used by the Depth-of-field PostProcessing effect.
* @param distance Distance from the camera to the plane of focus in world units.
* Must be positive and larger than the near clipping plane.
*/
void setFocusDistance(float distance) noexcept;
//! Returns the focus distance in world units
float getFocusDistance() const noexcept;
/**
* Returns the inverse of a projection matrix.
*
* \param p the projection matrix to inverse
* \returns the inverse of the projection matrix \p p
*
* \warning the projection matrix to invert must have one of the form below:
* - perspective projection
*
* \f$
* \left(
* \begin{array}{cccc}
* a & 0 & tx & 0 \\
* 0 & b & ty & 0 \\
* 0 & 0 & tz & c \\
* 0 & 0 & -1 & 0 \\
* \end{array}
* \right)
* \f$
*
* - orthographic projection
*
* \f$
* \left(
* \begin{array}{cccc}
* a & 0 & 0 & tx \\
* 0 & b & 0 & ty \\
* 0 & 0 & c & tz \\
* 0 & 0 & 0 & 1 \\
* \end{array}
* \right)
* \f$
*/
static math::mat4 inverseProjection(const math::mat4& p) noexcept;
/**
* Returns the inverse of a projection matrix.
* @see inverseProjection(const math::mat4&)
*/
static math::mat4f inverseProjection(const math::mat4f& p) noexcept;
/**
* Helper to compute the effective focal length taking into account the focus distance
*
* @param focalLength focal length in any unit (e.g. [m] or [mm])
* @param focusDistance focus distance in same unit as focalLength
* @return the effective focal length in same unit as focalLength
*/
static double computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept;
/**
* Helper to compute the effective field-of-view taking into account the focus distance
*
* @param fovInDegrees full field of view in degrees
* @param focusDistance focus distance in meters [m]
* @return effective full field of view in degrees
*/
static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_CAMERA_H

View File

@@ -0,0 +1,210 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_COLOR_H
#define TNT_FILAMENT_COLOR_H
#include <utils/compiler.h>
#include <math/vec3.h>
#include <math/vec4.h>
namespace filament {
//! RGB color in linear space
using LinearColor = math::float3;
//! RGB color in sRGB space
using sRGBColor = math::float3;
//! RGBA color in linear space, with alpha
using LinearColorA = math::float4;
//! RGBA color in sRGB space, with alpha
using sRGBColorA = math::float4;
//! types of RGB colors
enum class RgbType : uint8_t {
sRGB, //!< the color is defined in sRGB space
LINEAR, //!< the color is defined in linear space
};
//! types of RGBA colors
enum class RgbaType : uint8_t {
/**
* the color is defined in sRGB space and the RGB values
* have not been premultiplied by the alpha (for instance, a 50%
* transparent red is <1,0,0,0.5>)
*/
sRGB,
/**
* the color is defined in linear space and the RGB values
* have not been premultiplied by the alpha (for instance, a 50%
* transparent red is <1,0,0,0.5>)
*/
LINEAR,
/**
* the color is defined in sRGB space and the RGB values
* have been premultiplied by the alpha (for instance, a 50%
* transparent red is <0.5,0,0,0.5>)
*/
PREMULTIPLIED_sRGB,
/**
* the color is defined in linear space and the RGB values
* have been premultiplied by the alpha (for instance, a 50%
* transparent red is <0.5,0,0,0.5>)
*/
PREMULTIPLIED_LINEAR
};
//! type of color conversion to use when converting to/from sRGB and linear spaces
enum ColorConversion {
ACCURATE, //!< accurate conversion using the sRGB standard
FAST //!< fast conversion using a simple gamma 2.2 curve
};
/**
* Utilities to manipulate and convert colors
*/
class UTILS_PUBLIC Color {
public:
//! converts an RGB color to linear space, the conversion depends on the specified type
static LinearColor toLinear(RgbType type, math::float3 color);
//! converts an RGBA color to linear space, the conversion depends on the specified type
static LinearColorA toLinear(RgbaType type, math::float4 color);
//! converts an RGB color in sRGB space to an RGB color in linear space
template<ColorConversion = ACCURATE>
static LinearColor toLinear(sRGBColor const& color);
//! converts an RGB color in linear space to an RGB color in sRGB space
template<ColorConversion = ACCURATE>
static sRGBColor toSRGB(LinearColor const& color);
/**
* converts an RGBA color in sRGB space to an RGBA color in linear space
* the alpha component is left unmodified
*/
template<ColorConversion = ACCURATE>
static LinearColorA toLinear(sRGBColorA const& color);
/**
* converts an RGBA color in linear space to an RGBA color in sRGB space
* the alpha component is left unmodified
*/
template<ColorConversion = ACCURATE>
static sRGBColorA toSRGB(LinearColorA const& color);
/**
* converts a correlated color temperature to a linear RGB color in sRGB
* space the temperature must be expressed in kelvin and must be in the
* range 1,000K to 15,000K
*/
static LinearColor cct(float K);
/**
* converts a CIE standard illuminant series D to a linear RGB color in
* sRGB space the temperature must be expressed in kelvin and must be in
* the range 4,000K to 25,000K
*/
static LinearColor illuminantD(float K);
/**
* computes the Beer-Lambert absorption coefficients from the specified
* transmittance color and distance. The computed absorption will guarantee
* the white light will become the specified color at the specified distance.
* The output of this function can be used as the absorption parameter of
* materials that use refraction.
*
* @param color the desired linear RGB color in sRGB space
* @param distance the distance at which white light should become the specified color
*
* @return absorption coefficients for the Beer-Lambert law
*/
static math::float3 absorptionAtDistance(LinearColor const& color, float distance);
private:
static math::float3 sRGBToLinear(math::float3 color) noexcept;
static math::float3 linearToSRGB(math::float3 color) noexcept;
};
// Use the default implementation from the header
template<>
inline LinearColor Color::toLinear<FAST>(sRGBColor const& color) {
return pow(color, 2.2f);
}
template<>
inline LinearColorA Color::toLinear<FAST>(sRGBColorA const& color) {
return LinearColorA{pow(color.rgb, 2.2f), color.a};
}
template<>
inline LinearColor Color::toLinear<ACCURATE>(sRGBColor const& color) {
return sRGBToLinear(color);
}
template<>
inline LinearColorA Color::toLinear<ACCURATE>(sRGBColorA const& color) {
return LinearColorA{sRGBToLinear(color.rgb), color.a};
}
// Use the default implementation from the header
template<>
inline sRGBColor Color::toSRGB<FAST>(LinearColor const& color) {
return pow(color, 1.0f / 2.2f);
}
template<>
inline sRGBColorA Color::toSRGB<FAST>(LinearColorA const& color) {
return sRGBColorA{pow(color.rgb, 1.0f / 2.2f), color.a};
}
template<>
inline sRGBColor Color::toSRGB<ACCURATE>(LinearColor const& color) {
return linearToSRGB(color);
}
template<>
inline sRGBColorA Color::toSRGB<ACCURATE>(LinearColorA const& color) {
return sRGBColorA{linearToSRGB(color.rgb), color.a};
}
inline LinearColor Color::toLinear(RgbType type, math::float3 color) {
return (type == RgbType::LINEAR) ? color : Color::toLinear<ACCURATE>(color);
}
// converts an RGBA color to linear space
// the conversion depends on the specified type
inline LinearColorA Color::toLinear(RgbaType type, math::float4 color) {
switch (type) {
case RgbaType::sRGB:
return Color::toLinear<ACCURATE>(color) * math::float4{color.a, color.a, color.a, 1.0f};
case RgbaType::LINEAR:
return color * math::float4{color.a, color.a, color.a, 1.0f};
case RgbaType::PREMULTIPLIED_sRGB:
return Color::toLinear<ACCURATE>(color);
case RgbaType::PREMULTIPLIED_LINEAR:
return color;
}
}
} // namespace filament
#endif // TNT_FILAMENT_COLOR_H

View File

@@ -0,0 +1,405 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_COLOR_GRADING_H
#define TNT_FILAMENT_COLOR_GRADING_H
#include <filament/FilamentAPI.h>
#include <filament/ToneMapper.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
namespace filament {
class Engine;
class FColorGrading;
/**
* ColorGrading is used to transform (either to modify or correct) the colors of the HDR buffer
* rendered by Filament. Color grading transforms are applied after lighting, and after any lens
* effects (bloom for instance), and include tone mapping.
*
* Creation, usage and destruction
* ===============================
*
* A ColorGrading object is created using the ColorGrading::Builder and destroyed by calling
* Engine::destroy(const ColorGrading*). A ColorGrading object is meant to be set on a View.
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* filament::ColorGrading* colorGrading = filament::ColorGrading::Builder()
* .toneMapping(filament::ColorGrading::ToneMapping::ACES)
* .build(*engine);
*
* myView->setColorGrading(colorGrading);
*
* engine->destroy(colorGrading);
* ~~~~~~~~~~~
*
* Performance
* ===========
*
* Creating a new ColorGrading object may be more expensive than other Filament objects as a
* 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
* the CPU.
*
* Ordering
* ========
*
* The various transforms held by ColorGrading are applied in the following order:
* - Exposure
* - White balance
* - Channel mixer
* - Shadows/mid-tones/highlights
* - Slope/offset/power (CDL)
* - Contrast
* - Vibrance
* - Saturation
* - Curves
* - Tone mapping
* - Luminance scaling
*
* Defaults
* ========
*
* Here are the default color grading options:
* - Exposure: 0.0
* - White balance: temperature 0, and tint 0
* - Channel mixer: red {1,0,0}, green {0,1,0}, blue {0,0,1}
* - Shadows/mid-tones/highlights: shadows {1,1,1,0}, mid-tones {1,1,1,0}, highlights {1,1,1,0},
* ranges {0,0.333,0.550,1}
* - Slope/offset/power: slope 1.0, offset 0.0, and power 1.0
* - Contrast: 1.0
* - Vibrance: 1.0
* - Saturation: 1.0
* - Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1}
* - Tone mapping: ACESLegacyToneMapper
* - Luminance scaling: false
*
* @see View
*/
class UTILS_PUBLIC ColorGrading : public FilamentAPI {
struct BuilderDetails;
public:
enum class QualityLevel : uint8_t {
LOW,
MEDIUM,
HIGH,
ULTRA
};
/**
* List of available tone-mapping operators.
*
* @deprecated Use Builder::toneMapper(ToneMapper*) instead
*/
enum class UTILS_DEPRECATED ToneMapping : uint8_t {
LINEAR = 0, //!< Linear tone mapping (i.e. no tone mapping)
ACES_LEGACY = 1, //!< ACES tone mapping, with a brightness modifier to match Filament's legacy tone mapper
ACES = 2, //!< ACES tone mapping
FILMIC = 3, //!< Filmic tone mapping, modelled after ACES but applied in sRGB space
DISPLAY_RANGE = 4, //!< Tone mapping used to validate/debug scene exposure
};
//! Use Builder to construct a ColorGrading object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Sets the quality level of the color grading. When color grading is implemented using
* a 3D LUT, the quality level may impact the resolution and bit depth of the backing
* 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
*
* The default quality is medium.
*
* @param qualityLevel The desired quality of the color grading process
*
* @return This Builder, for chaining calls
*/
Builder& quality(QualityLevel qualityLevel) noexcept;
/**
* Selects the tone mapping operator to apply to the HDR color buffer as the last
* operation of the color grading post-processing step.
*
* The default tone mapping operator is ACESLegacyToneMapper.
*
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
* this builder. Since the build(Engine&) method is synchronous, it is safe to
* delete the tone mapper object after that finishes executing.
*
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
*
* @return This Builder, for chaining calls
*/
Builder& toneMapper(const ToneMapper* toneMapper) noexcept;
/**
* Selects the tone mapping operator to apply to the HDR color buffer as the last
* operation of the color grading post-processing step.
*
* The default tone mapping operator is ACES_LEGACY.
*
* @param toneMapping The tone mapping operator to apply to the HDR color buffer
*
* @return This Builder, for chaining calls
*
* @deprecated Use toneMapper(ToneMapper*) instead
*/
UTILS_DEPRECATED
Builder& toneMapping(ToneMapping toneMapping) noexcept;
/**
* Enables or disables the luminance scaling component (LICH) from the exposure value
* invariant luminance system (EVILS). When this setting is enabled, pixels with high
* chromatic values will roll-off to white to offer a more natural rendering. This step
* also helps avoid undesirable hue skews caused by out of gamut colors clipped
* to the destination color gamut.
*
* When luminance scaling is enabled, tone mapping is performed on the luminance of each
* pixel instead of per-channel.
*
* @param luminanceScaling Enables or disables EVILS post-tone mapping
*
* @return This Builder, for chaining calls
*/
Builder& luminanceScaling(bool luminanceScaling) noexcept;
/**
* Adjusts the exposure of this image. The exposure is specified in stops:
* each stop brightens (positive values) or darkens (negative values) the image by
* a factor of 2. This means that an exposure of 3 will brighten the image 8 times
* more than an exposure of 0 (2^3 = 8 and 2^0 = 1). Contrary to the camera's exposure,
* this setting is applied after all post-processing (bloom, etc.) are applied.
*
* @param exposure Value in EV stops. Can be negative, 0, or positive.
*
* @return This Builder, for chaining calls
*/
Builder& exposure(float exposure) noexcept;
/**
* Adjusts the while balance of the image. This can be used to remove color casts
* and correct the appearance of the white point in the scene, or to alter the
* overall chromaticity of the image for artistic reasons (to make the image appear
* cooler or warmer for instance).
*
* The while balance adjustment is defined with two values:
* - Temperature, to modify the color temperature. This value will modify the colors
* on a blue/yellow axis. Lower values apply a cool color temperature, and higher
* values apply a warm color temperature. The lowest value, -1.0f, is equivalent to
* a temperature of 50,000K. The highest value, 1.0f, is equivalent to a temperature
* of 2,000K.
* - Tint, to modify the colors on a green/magenta axis. The lowest value, -1.0f, will
* apply a strong green cast, and the highest value, 1.0f, will apply a strong magenta
* cast.
*
* Both values are expected to be in the range [-1.0..+1.0]. Values outside of that
* range will be clipped to that range.
*
* @param temperature Modification on the blue/yellow axis, as a value between -1.0 and +1.0.
* @param tint Modification on the green/magenta axis, as a value between -1.0 and +1.0.
*
* @return This Builder, for chaining calls
*/
Builder& whiteBalance(float temperature, float tint) noexcept;
/**
* The channel mixer adjustment modifies each output color channel using the specified
* mix of the source color channels.
*
* By default each output color channel is set to use 100% of the corresponding source
* channel and 0% of the other channels. For instance, the output red channel is set to
* {1.0, 0.0, 1.0} or 100% red, 0% green and 0% blue.
*
* Each output channel can add or subtract data from the source channel by using values
* in the range [-2.0..+2.0]. Values outside of that range will be clipped to that range.
*
* Using the channel mixer adjustment you can for instance create a monochrome output
* by setting all 3 output channels to the same mix. For instance: {0.4, 0.4, 0.2} for
* all 3 output channels(40% red, 40% green and 20% blue).
*
* More complex mixes can be used to create more complex effects. For instance, here is
* a mix that creates a sepia tone effect:
* - outRed = {0.255, 0.858, 0.087}
* - outGreen = {0.213, 0.715, 0.072}
* - outBlue = {0.170, 0.572, 0.058}
*
* @param outRed The mix of source RGB for the output red channel, between -2.0 and +2.0
* @param outGreen The mix of source RGB for the output green channel, between -2.0 and +2.0
* @param outBlue The mix of source RGB for the output blue channel, between -2.0 and +2.0
*
* @return This Builder, for chaining calls
*/
Builder& channelMixer(
math::float3 outRed, math::float3 outGreen, math::float3 outBlue) noexcept;
/**
* Adjusts the colors separately in 3 distinct tonal ranges or zones: shadows, mid-tones,
* and highlights.
*
* The tonal zones are by the ranges parameter: the x and y components define the beginning
* and end of the transition from shadows to mid-tones, and the z and w components define
* the beginning and end of the transition from mid-tones to highlights.
*
* A smooth transition is applied between the zones which means for instance that the
* correction color of the shadows range will partially apply to the mid-tones, and the
* other way around. This ensure smooth visual transitions in the final image.
*
* Each correction color is defined as a linear RGB color and a weight. The weight is a
* value (which may be positive or negative) that is added to the linear RGB color before
* mixing. This can be used to darken or brighten the selected tonal range.
*
* Shadows/mid-tones/highlights adjustment are performed linear space.
*
* @param shadows Linear RGB color (.rgb) and weight (.w) to apply to the shadows
* @param midtones Linear RGB color (.rgb) and weight (.w) to apply to the mid-tones
* @param highlights Linear RGB color (.rgb) and weight (.w) to apply to the highlights
* @param ranges Range of the shadows (x and y), and range of the highlights (z and w)
*
* @return This Builder, for chaining calls
*/
Builder& shadowsMidtonesHighlights(
math::float4 shadows, math::float4 midtones, math::float4 highlights,
math::float4 ranges) noexcept;
/**
* Applies a slope, offset, and power, as defined by the ASC CDL (American Society of
* Cinematographers Color Decision List) to the image. The CDL can be used to adjust the
* colors of different tonal ranges in the image.
*
* The ASC CDL is similar to the lift/gamma/gain controls found in many color grading tools.
* Lift is equivalent to a combination of offset and slope, gain is equivalent to slope,
* and gamma is equivalent to power.
*
* The slope and power values must be strictly positive. Values less than or equal to 0 will
* be clamped to a small positive value, offset can be any positive or negative value.
*
* Version 1.2 of the ASC CDL adds saturation control, which is here provided as a separate
* API. See the saturation() method for more information.
*
* Slope/offset/power adjustments are performed in log space.
*
* @param slope Multiplier of the input color, must be a strictly positive number
* @param offset Added to the input color, can be a negative or positive number, including 0
* @param power Power exponent of the input color, must be a strictly positive number
*
* @return This Builder, for chaining calls
*/
Builder& slopeOffsetPower(math::float3 slope, math::float3 offset, math::float3 power) noexcept;
/**
* Adjusts the contrast of the image. Lower values decrease the contrast of the image
* (the tonal range is narrowed), and higher values increase the contrast of the image
* (the tonal range is widened). A value of 1.0 has no effect.
*
* The contrast is defined as a value in the range [0.0...2.0]. Values outside of that
* range will be clipped to that range.
*
* Contrast adjustment is performed in log space.
*
* @param contrast Contrast expansion, between 0.0 and 2.0. 1.0 leaves contrast unaffected
*
* @return This Builder, for chaining calls
*/
Builder& contrast(float contrast) noexcept;
/**
* Adjusts the saturation of the image based on the input color's saturation level.
* Colors with a high level of saturation are less affected than colors with low saturation
* levels.
*
* Lower vibrance values decrease intensity of the colors present in the image, and
* higher values increase the intensity of the colors in the image. A value of 1.0 has
* no effect.
*
* The vibrance is defined as a value in the range [0.0...2.0]. Values outside of that
* range will be clipped to that range.
*
* Vibrance adjustment is performed in linear space.
*
* @param vibrance Vibrance, between 0.0 and 2.0. 1.0 leaves vibrance unaffected
*
* @return This Builder, for chaining calls
*/
Builder& vibrance(float vibrance) noexcept;
/**
* Adjusts the saturation of the image. Lower values decrease intensity of the colors
* present in the image, and higher values increase the intensity of the colors in the
* image. A value of 1.0 has no effect.
*
* The saturation is defined as a value in the range [0.0...2.0]. Values outside of that
* range will be clipped to that range.
*
* Saturation adjustment is performed in linear space.
*
* @param saturation Saturation, between 0.0 and 2.0. 1.0 leaves saturation unaffected
*
* @return This Builder, for chaining calls
*/
Builder& saturation(float saturation) noexcept;
/**
* Applies a curve to each RGB channel of the image. Each curve is defined by 3 values:
* a gamma value applied to the shadows only, a mid-point indicating where shadows stop
* and highlights start, and a scale factor for the highlights.
*
* The gamma and mid-point must be strictly positive values. If they are not, they will be
* clamped to a small positive value. The scale can be any negative of positive value.
*
* Curves are applied in linear space.
*
* @param shadowGamma Power value to apply to the shadows, must be strictly positive
* @param midPoint Mid-point defining where shadows stop and highlights start, must be strictly positive
* @param highlightScale Scale factor for the highlights, can be any negative or positive value
*
* @return This Builder, for chaining calls
*/
Builder& curves(math::float3 shadowGamma, math::float3 midPoint, math::float3 highlightScale) noexcept;
/**
* Creates the ColorGrading object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this ColorGrading with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*/
ColorGrading* build(Engine& engine);
private:
friend class FColorGrading;
};
};
} // namespace filament
#endif // TNT_FILAMENT_COLOR_GRADING_H

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_DEBUG_H
#define TNT_FILAMENT_DEBUG_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
#include <stdint.h>
namespace filament {
/**
* A registry of runtime properties used exclusively for debugging
*
* Filament exposes a few properties that can be queried and set, which control certain debugging
* features of the engine. These properties can be set at runtime at anytime.
*
*/
class UTILS_PUBLIC DebugRegistry : public FilamentAPI {
public:
/**
* Type of a property
*/
enum Type {
BOOL, INT, FLOAT, FLOAT2, FLOAT3, FLOAT4
};
/**
* Information about a property
*/
struct Property {
const char* name; //!< property name
Type type; //!< property type
};
struct PropertyArray {
Property const* array;
size_t size;
};
/**
* Queries the list of all available properties.
*
* @return A pair containing a pointer to a Property array and the size of this array.
*/
PropertyArray getProperties() const noexcept;
/**
* Queries whether a property exists
* @param name The name of the property to query
* @return true if the property exists, false otherwise
*/
bool hasProperty(const char* name) const noexcept;
/**
* Queries the address of a property's data from its name
* @param name Name of the property we want the data address of
* @return Address of the data of the \p name property
* @{
*/
void* getPropertyAddress(const char* name) noexcept;
template<typename T>
inline T* getPropertyAddress(const char* name) noexcept {
return static_cast<T*>(getPropertyAddress(name));
}
template<typename T>
inline bool getPropertyAddress(const char* name, T** p) noexcept {
*p = getPropertyAddress<T>(name);
return *p != nullptr;
}
/** @}*/
/**
* Set the value of a property
* @param name Name of the property to set the value of
* @param v Value to set
* @return true if the operation was successful, false otherwise.
* @{
*/
bool setProperty(const char* name, bool v) noexcept;
bool setProperty(const char* name, int v) noexcept;
bool setProperty(const char* name, float v) noexcept;
bool setProperty(const char* name, math::float2 v) noexcept;
bool setProperty(const char* name, math::float3 v) noexcept;
bool setProperty(const char* name, math::float4 v) noexcept;
/** @}*/
/**
* Get the value of a property
* @param name Name of the property to get the value of
* @param v A pointer to a variable which will hold the result
* @return true if the call was successful and \p v was updated
* @{
*/
bool getProperty(const char* name, bool* v) const noexcept;
bool getProperty(const char* name, int* v) const noexcept;
bool getProperty(const char* name, float* v) const noexcept;
bool getProperty(const char* name, math::float2* v) const noexcept;
bool getProperty(const char* name, math::float3* v) const noexcept;
bool getProperty(const char* name, math::float4* v) const noexcept;
/** @}*/
};
} // namespace filament
#endif /* TNT_FILAMENT_DEBUG_H */

View File

@@ -0,0 +1,543 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_ENGINE_H
#define TNT_FILAMENT_ENGINE_H
#include <backend/Platform.h>
#include <utils/compiler.h>
namespace utils {
class Entity;
class EntityManager;
class JobSystem;
} // namespace utils
namespace filament {
class BufferObject;
class Camera;
class ColorGrading;
class DebugRegistry;
class Fence;
class IndexBuffer;
class IndirectLight;
class Material;
class MaterialInstance;
class Renderer;
class RenderTarget;
class Scene;
class Skybox;
class Stream;
class SwapChain;
class Texture;
class VertexBuffer;
class View;
class LightManager;
class RenderableManager;
class TransformManager;
/**
* Engine is filament's main entry-point.
*
* An Engine instance main function is to keep track of all resources created by the user and
* manage the rendering thread as well as the hardware renderer.
*
* To use filament, an Engine instance must be created first:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Engine essentially represents (or is associated to) a hardware context
* (e.g. an OpenGL ES context).
*
* Rendering typically happens in an operating system's window (which can be full screen), such
* window is managed by a filament.Renderer.
*
* A typical filament render loop looks like this:
*
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* #include <filament/Renderer.h>
* #include <filament/Scene.h>
* #include <filament/View.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
* SwapChain* swapChain = engine->createSwapChain(nativeWindow);
* Renderer* renderer = engine->createRenderer();
* Scene* scene = engine->createScene();
* View* view = engine->createView();
*
* view->setScene(scene);
*
* do {
* // typically we wait for VSYNC and user input events
* if (renderer->beginFrame(swapChain)) {
* renderer->render(view);
* renderer->endFrame();
* }
* } while (!quit);
*
* engine->destroy(view);
* engine->destroy(scene);
* engine->destroy(renderer);
* engine->destroy(swapChain);
* Engine::destroy(&engine); // clears engine*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Resource Tracking
* =================
*
* Each Engine instance keeps track of all objects created by the user, such as vertex and index
* buffers, lights, cameras, etc...
* The user is expected to free those resources, however, leaked resources are freed when the
* engine instance is destroyed and a warning is emitted in the console.
*
* Thread safety
* =============
*
* An Engine instance is not thread-safe. The implementation makes no attempt to synchronize
* calls to an Engine instance methods.
* If multi-threading is needed, synchronization must be external.
*
* Multi-threading
* ===============
*
* When created, the Engine instance starts a render thread as well as multiple worker threads,
* these threads have an elevated priority appropriate for rendering, based on the platform's
* best practices. The number of worker threads depends on the platform and is automatically
* chosen for best performance.
*
* On platforms with asymmetric cores (e.g. ARM's Big.Little), Engine makes some educated guesses
* as to which cores to use for the render thread and worker threads. For example, it'll try to
* keep an OpenGL ES thread on a Big core.
*
* Swap Chains
* ===========
*
* A swap chain represents an Operating System's *native* renderable surface. Typically it's a window
* or a view. Because a SwapChain is initialized from a native object, it is given to filament
* as a `void*`, which must be of the proper type for each platform filament is running on.
*
* @see SwapChain
*
*
* @see Renderer
*/
class UTILS_PUBLIC Engine {
public:
using Platform = backend::Platform;
using Backend = backend::Backend;
/**
* Creates an instance of Engine
*
* @param backend Which driver backend to use.
*
* @param platform A pointer to an object that implements Platform. If this is
* provided, then this object is used to create the hardware context
* and expose platform features to it.
*
* If not provided (or nullptr is used), an appropriate Platform
* is created automatically.
*
* All methods of this interface are called from filament's
* render thread, which is different from the main thread.
*
* The lifetime of \p platform must exceed the lifetime of
* the Engine object.
*
* @param sharedGLContext A platform-dependant OpenGL context used as a shared context
* when creating filament's internal context.
* Setting this parameter will force filament to use the OpenGL
* implementation (instead of Vulkan for instance).
*
*
* @return A pointer to the newly created Engine, or nullptr if the Engine couldn't be created.
*
* nullptr if the GPU driver couldn't be initialized, for instance if it doesn't
* support the right version of OpenGL or OpenGL ES.
*
* @exception utils::PostConditionPanic can be thrown if there isn't enough memory to
* allocate the command buffer. If exceptions are disabled, this condition if fatal and
* this function will abort.
*
* \remark
* This method is thread-safe.
*/
static Engine* create(Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedGLContext = nullptr);
#if UTILS_HAS_THREADING
/**
* A callback used with Engine::createAsync() called once the engine is initialized and it is
* safe to call Engine::getEngine(token). This callback is invoked from an arbitrary worker
* thread. Engine::getEngine() CANNOT be called from that thread, instead it must be called
* from the same thread than Engine::createAsync() was called from.
*
* @param user User provided parameter given in createAsync().
*
* @param token An opaque token used to call Engine::getEngine().
*/
using CreateCallback = void(void* user, void* token);
/**
* Creates an instance of Engine asynchronously
*
* @param callback Callback called once the engine is initialized and it is safe to
* call Engine::getEngine.
*
* @param user A user provided pointer that is given back to callback unmodified.
*
* @param backend Which driver backend to use.
*
* @param platform A pointer to an object that implements Platform. If this is
* provided, then this object is used to create the hardware context
* and expose platform features to it.
*
* If not provided (or nullptr is used), an appropriate Platform
* is created automatically.
*
* All methods of this interface are called from filament's
* render thread, which is different from the main thread.
*
* The lifetime of \p platform must exceed the lifetime of
* the Engine object.
*
* @param sharedGLContext A platform-dependant OpenGL context used as a shared context
* when creating filament's internal context.
* Setting this parameter will force filament to use the OpenGL
* implementation (instead of Vulkan for instance).
*/
static void createAsync(CreateCallback callback, void* user,
Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedGLContext = nullptr);
/**
* Retrieve an Engine* from createAsync(). This must be called from the same thread than
* Engine::createAsync() was called from.
*
* @param token An opaque token given in the createAsync() callback function.
*
* @return A pointer to the newly created Engine, or nullptr if the Engine couldn't be created.
*
* @exception utils::PostConditionPanic can be thrown if there isn't enough memory to
* allocate the command buffer. If exceptions are disabled, this condition if fatal and
* this function will abort.
*/
static Engine* getEngine(void* token);
#endif
/**
* Destroy the Engine instance and all associated resources.
*
* Engine.destroy() should be called last and after all other resources have been destroyed,
* it ensures all filament resources are freed.
*
* Destroy performs the following tasks:
* 1. Destroy all internal software and hardware resources.
* 2. Free all user allocated resources that are not already destroyed and logs a warning.
* This indicates a "leak" in the user's code.
* 3. Terminate the rendering engine's thread.
*
* @param engine A pointer to the filament.Engine* to be destroyed.
* \p engine is cleared upon return.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
* Engine::destroy(&engine); // clears engine*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* \remark
* This method is thread-safe.
*/
static void destroy(Engine** engine);
/**
* Destroy the Engine instance and all associated resources.
*
* Engine.destroy() should be called last and after all other resources have been destroyed,
* it ensures all filament resources are freed.
*
* Destroy performs the following tasks:
* 1. Destroy all internal software and hardware resources.
* 2. Free all user allocated resources that are not already destroyed and logs a warning.
* This indicates a "leak" in the user's code.
* 3. Terminate the rendering engine's thread.
*
* @param engine A pointer to the filament.Engine to be destroyed.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
* Engine::destroy(engine);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* \remark
* This method is thread-safe.
*/
static void destroy(Engine* engine);
/**
* @return EntityManager used by filament
*/
utils::EntityManager& getEntityManager() noexcept;
/**
* @return RenderableManager reference
*/
RenderableManager& getRenderableManager() noexcept;
/**
* @return LightManager reference
*/
LightManager& getLightManager() noexcept;
/**
* @return TransformManager reference
*/
TransformManager& getTransformManager() noexcept;
/**
* Helper to enable accurate translations.
* If you need this Engine to handle a very large world space, one way to achieve this
* automatically is to enable accurate translations in the TransformManager. This helper
* provides a convenient way of doing that.
* This is typically called once just after creating the Engine.
*/
void enableAccurateTranslations() noexcept;
/**
* Creates a SwapChain from the given Operating System's native window handle.
*
* @param nativeWindow An opaque native window handle. e.g.: on Android this is an
* `ANativeWindow*`.
* @param flags One or more configuration flags as defined in `SwapChain`.
*
* @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
*
* @see Renderer.beginFrame()
*/
SwapChain* createSwapChain(void* nativeWindow, uint64_t flags = 0) noexcept;
/**
* Creates a headless SwapChain.
*
* @param width Width of the drawing buffer in pixels.
* @param height Height of the drawing buffer in pixels.
* @param flags One or more configuration flags as defined in `SwapChain`.
*
* @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
*
* @see Renderer.beginFrame()
*/
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept;
/**
* Creates a renderer associated to this engine.
*
* A Renderer is intended to map to a *window* on screen.
*
* @return A pointer to the newly created Renderer or nullptr if it couldn't be created.
*/
Renderer* createRenderer() noexcept;
/**
* Creates a View.
*
* @return A pointer to the newly created View or nullptr if it couldn't be created.
*/
View* createView() noexcept;
/**
* Creates a Scene.
*
* @return A pointer to the newly created Scene or nullptr if it couldn't be created.
*/
Scene* createScene() noexcept;
/**
* Creates a Camera component.
*
* @param entity Entity to add the camera component to.
* @return A pointer to the newly created Camera or nullptr if it couldn't be created.
*/
Camera* createCamera(utils::Entity entity) noexcept;
/**
* Returns the Camera component of the given entity.
*
* @param entity An entity.
* @return A pointer to the Camera component for this entity or nullptr if the entity didn't
* have a Camera component. The pointer is valid until destroyCameraComponent()
* is called or the entity itself is destroyed.
*/
Camera* getCameraComponent(utils::Entity entity) noexcept;
/**
* Destroys the Camera component associated with the given entity.
*
* @param entity An entity.
*/
void destroyCameraComponent(utils::Entity entity) noexcept;
/**
* Creates a Fence.
*
* @return A pointer to the newly created Fence or nullptr if it couldn't be created.
*/
Fence* createFence() noexcept;
bool destroy(const BufferObject* p); //!< Destroys a BufferObject object.
bool destroy(const VertexBuffer* p); //!< Destroys an VertexBuffer object.
bool destroy(const Fence* p); //!< Destroys a Fence object.
bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object.
bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object.
/**
* Destroys a Material object
* @param p the material object to destroy
* @attention All MaterialInstance of the specified material must be destroyed before
* destroying it.
* @exception utils::PreConditionPanic is thrown if some MaterialInstances remain.
* no-op if exceptions are disabled and some MaterialInstances remain.
*/
bool destroy(const Material* p);
bool destroy(const MaterialInstance* p); //!< Destroys a MaterialInstance object.
bool destroy(const Renderer* p); //!< Destroys a Renderer object.
bool destroy(const Scene* p); //!< Destroys a Scene object.
bool destroy(const Skybox* p); //!< Destroys a SkyBox object.
bool destroy(const ColorGrading* p); //!< Destroys a ColorGrading object.
bool destroy(const SwapChain* p); //!< Destroys a SwapChain object.
bool destroy(const Stream* p); //!< Destroys a Stream object.
bool destroy(const Texture* p); //!< Destroys a Texture object.
bool destroy(const RenderTarget* p); //!< Destroys a RenderTarget object.
bool destroy(const View* p); //!< Destroys a View object.
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that this doesn't guarantee that the
* hardware is actually finished.
*
* <p>This is typically used right after destroying the <code>SwapChain</code>,
* in cases where a guarantee about the <code>SwapChain</code> destruction is needed in a
* timely fashion, such as when responding to Android's
* <code>android.view.SurfaceHolder.Callback.surfaceDestroyed</code></p>
*/
void flushAndWait();
void flush();
/**
* Returns the default Material.
*
* The default material is 80% white and uses the Material.Shading.LIT shading.
*
* @return A pointer to the default Material instance (a singleton).
*/
const Material* getDefaultMaterial() const noexcept;
/**
* Returns the resolved backend.
*/
Backend getBackend() const noexcept;
/**
* Returns the Platform object that belongs to this Engine.
*
* When Engine::create is called with no platform argument, Filament creates an appropriate
* Platform subclass automatically. The specific subclass created depends on the backend and
* OS. For example, when the OpenGL backend is used, the Platform object will be a descendant of
* OpenGLPlatform.
*
* dynamic_cast should be used to cast the returned Platform object into a specific subclass.
* Note that RTTI must be available to use dynamic_cast.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Platform* platform = engine->getPlatform();
* // static_cast also works, but more dangerous.
* SpecificPlatform* specificPlatform = dynamic_cast<SpecificPlatform*>(platform);
* specificPlatform->platformSpecificMethod();
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* When a custom Platform is passed to Engine::create, Filament will use it instead, and this
* method will return it.
*
* @return A pointer to the Platform object that was provided to Engine::create, or the
* Filament-created one.
*/
Platform* getPlatform() const noexcept;
/**
* Allocate a small amount of memory directly in the command stream. The allocated memory is
* guaranteed to be preserved until the current command buffer is executed
*
* @param size size to allocate in bytes. This should be small (e.g. < 1 KB)
* @param alignment alignment requested
* @return a pointer to the allocated buffer or nullptr if no memory was available.
*
* @note there is no need to destroy this buffer, it will be freed automatically when
* the current command buffer is executed.
*/
void* streamAlloc(size_t size, size_t alignment = alignof(double)) noexcept;
/**
* Invokes one iteration of the render loop, used only on single-threaded platforms.
*
* This should be called every time the windowing system needs to paint (e.g. at 60 Hz).
*/
void execute();
/**
* Retrieves the job system that the Engine has ownership over.
*
* @return JobSystem used by filament
*/
utils::JobSystem& getJobSystem() noexcept;
DebugRegistry& getDebugRegistry() noexcept;
protected:
//! \privatesection
Engine() noexcept = default;
~Engine() = default;
public:
//! \privatesection
Engine(Engine const&) = delete;
Engine(Engine&&) = delete;
Engine& operator=(Engine const&) = delete;
Engine& operator=(Engine&&) = delete;
};
} // namespace filament
#endif // TNT_FILAMENT_ENGINE_H

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_EXPOSURE_H
#define TNT_FILAMENT_EXPOSURE_H
#include <utils/compiler.h>
namespace filament {
class Camera;
/**
* A series of utilities to compute exposure, exposure value at ISO 100 (EV100),
* luminance and illuminance using a physically-based camera model.
*/
namespace Exposure {
/**
* Returns the exposure value (EV at ISO 100) of the specified camera.
*/
UTILS_PUBLIC
float ev100(const Camera& camera) noexcept;
/**
* Returns the exposure value (EV at ISO 100) of the specified exposure parameters.
*/
UTILS_PUBLIC
float ev100(float aperture, float shutterSpeed, float sensitivity) noexcept;
/**
* Returns the exposure value (EV at ISO 100) for the given average luminance (in @f$ \frac{cd}{m^2} @f$).
*/
UTILS_PUBLIC
float ev100FromLuminance(float luminance) noexcept;
/**
* Returns the exposure value (EV at ISO 100) for the given illuminance (in lux).
*/
UTILS_PUBLIC
float ev100FromIlluminance(float illuminance) noexcept;
/**
* Returns the photometric exposure for the specified camera.
*/
UTILS_PUBLIC
float exposure(const Camera& camera) noexcept;
/**
* Returns the photometric exposure for the specified exposure parameters.
* This function is equivalent to calling `exposure(ev100(aperture, shutterSpeed, sensitivity))`
* but is slightly faster and offers higher precision.
*/
UTILS_PUBLIC
float exposure(float aperture, float shutterSpeed, float sensitivity) noexcept;
/**
* Returns the photometric exposure for the given EV100.
*/
UTILS_PUBLIC
float exposure(float ev100) noexcept;
/**
* Returns the incident luminance in @f$ \frac{cd}{m^2} @f$ for the specified camera acting as a spot meter.
*/
UTILS_PUBLIC
float luminance(const Camera& camera) noexcept;
/**
* Returns the incident luminance in @f$ \frac{cd}{m^2} @f$ for the specified exposure parameters of
* a camera acting as a spot meter.
* This function is equivalent to calling `luminance(ev100(aperture, shutterSpeed, sensitivity))`
* but is slightly faster and offers higher precision.
*/
UTILS_PUBLIC
float luminance(float aperture, float shutterSpeed, float sensitivity) noexcept;
/**
* Converts the specified EV100 to luminance in @f$ \frac{cd}{m^2} @f$.
* EV100 is not a measure of luminance, but an EV100 can be used to denote a
* luminance for which a camera would use said EV100 to obtain the nominally
* correct exposure
*/
UTILS_PUBLIC
float luminance(float ev100) noexcept;
/**
* Returns the illuminance in lux for the specified camera acting as an incident light meter.
*/
UTILS_PUBLIC
float illuminance(const Camera& camera) noexcept;
/**
* Returns the illuminance in lux for the specified exposure parameters of
* a camera acting as an incident light meter.
* This function is equivalent to calling `illuminance(ev100(aperture, shutterSpeed, sensitivity))`
* but is slightly faster and offers higher precision.
*/
UTILS_PUBLIC
float illuminance(float aperture, float shutterSpeed, float sensitivity) noexcept;
/**
* Converts the specified EV100 to illuminance in lux.
* EV100 is not a measure of illuminance, but an EV100 can be used to denote an
* illuminance for which a camera would use said EV100 to obtain the nominally
* correct exposure.
*/
UTILS_PUBLIC
float illuminance(float ev100) noexcept;
} // namespace exposure
} // namespace filament
#endif // TNT_FILAMENT_EXPOSURE_H

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_FENCE_H
#define TNT_FILAMENT_FENCE_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
namespace filament {
/**
* Fence is used to synchronize rendering operations together, with the CPU or with compute.
*
* \note
* Currently Fence only provide client-side synchronization.
*
*/
class UTILS_PUBLIC Fence : public FilamentAPI {
public:
//! Special \p timeout value to disable wait()'s timeout.
static constexpr uint64_t FENCE_WAIT_FOR_EVER = backend::FENCE_WAIT_FOR_EVER;
//! Error codes for Fence::wait()
using FenceStatus = backend::FenceStatus;
/** Mode controls the behavior of the command stream when calling wait()
*
* @attention
* It would be unwise to call `wait(..., Mode::DONT_FLUSH)` from the same thread
* the Fence was created, as it would most certainly create a dead-lock.
*/
enum class Mode : uint8_t {
FLUSH, //!< The command stream is flushed
DONT_FLUSH //!< The command stream is not flushed
};
/**
* Client-side wait on the Fence.
*
* Blocks the current thread until the Fence signals.
*
* @param mode Whether the command stream is flushed before waiting or not.
* @param timeout Wait time out. Using a \p timeout of 0 is a way to query the state of the fence.
* A \p timeout value of FENCE_WAIT_FOR_EVER is used to disable the timeout.
* @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::TIMEOUT_EXPIRED if the time out expired or
* FenceStatus::ERROR in other cases.
* @see #Mode
*/
FenceStatus wait(Mode mode = Mode::FLUSH, uint64_t timeout = FENCE_WAIT_FOR_EVER);
/**
* Client-side wait on a Fence and destroy the Fence.
*
* @param fence Fence object to wait on.
*
* @param mode Whether the command stream is flushed before waiting or not.
*
* @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::ERROR otherwise.
*/
static FenceStatus waitAndDestroy(Fence* fence, Mode mode = Mode::FLUSH);
};
} // namespace filament
#endif // TNT_FILAMENT_FENCE_H

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_FILAMENT_API_H
#define TNT_FILAMENT_FILAMENT_API_H
#include <utils/compiler.h>
#include <stddef.h>
namespace filament {
/**
* \privatesection
* FilamentAPI is used to define an API in filament.
* It ensures the class defining the API can't be created, destroyed
* or copied by the caller.
*/
class UTILS_PUBLIC FilamentAPI {
protected:
// disallow creation on the stack
FilamentAPI() noexcept = default;
~FilamentAPI() = default;
public:
// disallow copy and assignment
FilamentAPI(FilamentAPI const&) = delete;
FilamentAPI(FilamentAPI&&) = delete;
FilamentAPI& operator=(FilamentAPI const&) = delete;
FilamentAPI& operator=(FilamentAPI&&) = delete;
// allow placement-new allocation, don't use "noexcept", to avoid compiler null check
static void *operator new (size_t, void* p) { return p; }
// prevent heap allocation
static void *operator new (size_t) = delete;
static void *operator new[] (size_t) = delete;
static void operator delete (void*) = delete;
static void operator delete[](void*) = delete;
};
/**
* \privatesection
* BuilderBase is used to hide the implementation details of builders and ensure a higher
* level of backward binary compatibility.
* The actual implementation is in src/FilamentAPI-impl.h"
*/
template <typename T>
class BuilderBase {
public:
// none of these methods must be implemented inline because it's important that their
// implementation be hidden from the public headers.
template<typename ... ARGS>
explicit BuilderBase(ARGS&& ...) noexcept;
BuilderBase() noexcept;
~BuilderBase() noexcept;
BuilderBase(BuilderBase const& rhs) noexcept;
BuilderBase& operator = (BuilderBase const& rhs) noexcept;
// move ctor and copy operator can be implemented inline and don't need to be exported
BuilderBase(BuilderBase&& rhs) noexcept : mImpl(rhs.mImpl) { rhs.mImpl = nullptr; }
BuilderBase& operator = (BuilderBase&& rhs) noexcept {
auto temp = mImpl;
mImpl = rhs.mImpl;
rhs.mImpl = temp;
return *this;
}
protected:
T* mImpl = nullptr;
inline T* operator->() noexcept { return mImpl; }
inline T const* operator->() const noexcept { return mImpl; }
};
} // namespace filament
#endif // TNT_FILAMENT_FILAMENT_API_H

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_FRUSTUM_H
#define TNT_FILAMENT_FRUSTUM_H
#include <utils/compiler.h>
#include <math/mat4.h>
#include <math/vec3.h>
#include <utils/unwindows.h> // Because we define NEAR and FAR in the Plane enum.
namespace filament {
class Box;
class Culler;
/**
* A frustum defined by six planes
*/
class UTILS_PUBLIC Frustum {
public:
enum class Plane : uint8_t {
LEFT,
RIGHT,
BOTTOM,
TOP,
FAR,
NEAR
};
Frustum() = default;
Frustum(const Frustum& rhs) = default;
Frustum(Frustum&& rhs) noexcept = default;
Frustum& operator=(const Frustum& rhs) = default;
Frustum& operator=(Frustum&& rhs) noexcept = default;
/**
* Creates a frustum from a projection matrix (usually the projection * view matrix)
* @param pv a 4x4 projection matrix
*/
explicit Frustum(const math::mat4f& pv);
/**
* Creates a frustum from 8 corner coordinates.
* @param corners the corners of the frustum
*
* The corners should be specified in this order:
* 0. far bottom left
* 1. far bottom right
* 2. far top left
* 3. far top right
* 4. near bottom left
* 5. near bottom right
* 6. near top left
* 7. near top right
*
* 2----3
* /| /|
* 6----7 |
* | 0--|-1 far
* |/ |/ /
* 4----5 near
*
*/
explicit Frustum(const math::float3 corners[8]);
/**
* Sets the frustum from the given projection matrix
* @param pv a 4x4 projection matrix
*/
void setProjection(const math::mat4f& pv);
/**
* Returns the plane equation parameters with normalized normals
* @param plane Identifier of the plane to retrieve the equation of
* @return A plane equation encoded a float4 R such as R.x*x + R.y*y + R.z*z + R.w = 0
*/
math::float4 getNormalizedPlane(Plane plane) const noexcept;
/**
* Returns a copy of all six frustum planes in left, right, bottom, top, far, near order
* @param planes six plane equations encoded as in getNormalizedPlane() in
* left, right, bottom, top, far, near order
*/
void getNormalizedPlanes(math::float4 planes[6]) const noexcept;
/**
* Returns all six frustum planes in left, right, bottom, top, far, near order
* @return six plane equations encoded as in getNormalizedPlane() in
* left, right, bottom, top, far, near order
*/
math::float4 const* getNormalizedPlanes() const noexcept { return mPlanes; }
/**
* Returns whether a box intersects the frustum (i.e. is visible)
* @param box The box to test against the frustum
* @return true if the box may intersects the frustum, false otherwise. In some situations
* a box that doesn't intersect the frustum might be reported as though it does. However,
* a box that does intersect the frustum is always reported correctly (true).
*/
bool intersects(const Box& box) const noexcept;
/**
* Returns whether a sphere intersects the frustum (i.e. is visible)
* @param sphere A sphere encoded as a center + radius.
* @return true if the sphere may intersects the frustum, false otherwise. In some situations
* a sphere that doesn't intersect the frustum might be reported as though it does. However,
* a sphere that does intersect the frustum is always reported correctly (true).
*/
bool intersects(const math::float4& sphere) const noexcept;
/**
* Returns whether the frustum contains a given point.
* @param p the point to test
* @return the maximum signed distance to the frustum. Negative if p is inside.
*/
float contains(math::float3 p) const noexcept;
private:
friend class Culler;
math::float4 mPlanes[6];
};
} // namespace filament
#endif // TNT_FILAMENT_FRUSTUM_H

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_INDEXBUFFER_H
#define TNT_FILAMENT_INDEXBUFFER_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <stddef.h>
namespace filament {
class FIndexBuffer;
class Engine;
/**
* A buffer containing vertex indices into a VertexBuffer. Indices can be 16 or 32 bit.
* The buffer itself is a GPU resource, therefore mutating the data can be relatively slow.
* Typically these buffers are constant.
*
* It is possible, and even encouraged, to use a single index buffer for several Renderables.
*
* @see VertexBuffer, RenderableManager
*/
class UTILS_PUBLIC IndexBuffer : public FilamentAPI {
struct BuilderDetails;
public:
using BufferDescriptor = backend::BufferDescriptor;
/**
* Type of the index buffer
*/
enum class IndexType : uint8_t {
USHORT = uint8_t(backend::ElementType::USHORT), //!< 16-bit indices
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
};
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Size of the index buffer in elements.
* @param indexCount Number of indices the IndexBuffer can hold.
* @return A reference to this Builder for chaining calls.
*/
Builder& indexCount(uint32_t indexCount) noexcept;
/**
* Type of the index buffer, 16-bit or 32-bit.
* @param indexType Type of indices stored in the IndexBuffer.
* @return A reference to this Builder for chaining calls.
*/
Builder& bufferType(IndexType indexType) noexcept;
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
*
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*
* @see IndexBuffer::setBuffer
*/
IndexBuffer* build(Engine& engine);
private:
friend class FIndexBuffer;
};
/**
* Asynchronously copy-initializes a region of this IndexBuffer from the data provided.
*
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
* @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer.
* BufferDescriptor points to raw, untyped data that will be interpreted as
* either 16-bit or 32-bits indices based on the Type of this IndexBuffer.
* @param byteOffset Offset in *bytes* into the IndexBuffer
*/
void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0);
/**
* Returns the size of this IndexBuffer in elements.
* @return The number of indices the IndexBuffer holds.
*/
size_t getIndexCount() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_INDEXBUFFER_H

View File

@@ -0,0 +1,349 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_INDIRECT_LIGHT_H
#define TNT_FILAMENT_INDIRECT_LIGHT_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
namespace filament {
class Engine;
class Texture;
class FIndirectLight;
/**
* IndirectLight is used to simulate environment lighting, a form of global illumination.
*
* Environment lighting has a two components:
* 1. irradiance
* 2. reflections (specular component)
*
* Environments are usually captured as high-resolution HDR equirectangular images and processed
* by the **cmgen** tool to generate the data needed by IndirectLight.
*
* @note
* Currently IndirectLight is intended to be used for "distant probes", that is, to represent
* global illumination from a distant (i.e. at infinity) environment, such as the sky or distant
* mountains. Only a single IndirectLight can be used in a Scene. This limitation will be lifted
* in the future.
*
* Creation and destruction
* ========================
*
* An IndirectLight object is created using the IndirectLight::Builder and destroyed by calling
* Engine::destroy(const IndirectLight*).
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* filament::IndirectLight* environment = filament::IndirectLight::Builder()
* .reflections(cubemap)
* .build(*engine);
*
* engine->destroy(environment);
* ~~~~~~~~~~~
*
*
* Irradiance
* ==========
*
* The irradiance represents the light that comes from the environment and shines an
* object's surface.
*
* The irradiance is calculated automatically from the Reflections (see below), and generally
* doesn't need to be provided explicitly. However, it can be provided separately from the
* Reflections as
* [spherical harmonics](https://en.wikipedia.org/wiki/Spherical_harmonics) (SH) of 1, 2 or
* 3 bands, respectively 1, 4 or 9 coefficients.
*
* @note
* Use the **cmgen** tool to generate the `SH` for a given environment.
*
* Reflections
* ===========
*
* The reflections on object surfaces (specular component) is calculated from a specially
* filtered cubemap pyramid generated by the **cmgen** tool.
*
*
* @see Scene, Light, Texture, Skybox
*/
class UTILS_PUBLIC IndirectLight : public FilamentAPI {
struct BuilderDetails;
public:
//! Use Builder to construct an IndirectLight object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Set the reflections cubemap mipmap chain.
*
* @param cubemap A mip-mapped cubemap generated by **cmgen**. Each cubemap level
* encodes a the irradiance for a roughness level.
*
* @return This Builder, for chaining calls.
*
*/
Builder& reflections(Texture const* cubemap) noexcept;
/**
* Sets the irradiance as Spherical Harmonics.
*
* The irradiance must be pre-convolved by \f$ \langle n \cdot l \rangle \f$ and
* pre-multiplied by the Lambertian diffuse BRDF \f$ \frac{1}{\pi} \f$ and
* specified as Spherical Harmonics coefficients.
*
* Additionally, these Spherical Harmonics coefficients must be pre-scaled by the
* reconstruction factors \f$ A_{l}^{m} \f$ below.
*
* The final coefficients can be generated using the `cmgen` tool.
*
* The index in the \p sh array is given by:
*
* `index(l, m) = l * (l + 1) + m`
*
* \f$ sh[index(l,m)] = L_{l}^{m} \frac{1}{\pi} A_{l}^{m} \hat{C_{l}} \f$
*
* index | l | m | \f$ A_{l}^{m} \f$ | \f$ \hat{C_{l}} \f$ | \f$ \frac{1}{\pi} A_{l}^{m}\hat{C_{l}} \f$ |
* :-----:|:---:|:---:|:------------------:|:---------------------:|:--------------------------------------------:
* 0 | 0 | 0 | 0.282095 | 3.1415926 | 0.282095
* 1 | 1 | -1 | -0.488602 | 2.0943951 | -0.325735
* 2 | ^ | 0 | 0.488602 | ^ | 0.325735
* 3 | ^ | 1 | -0.488602 | ^ | -0.325735
* 4 | 2 | -2 | 1.092548 | 0.785398 | 0.273137
* 5 | ^ | -1 | -1.092548 | ^ | -0.273137
* 6 | ^ | 0 | 0.315392 | ^ | 0.078848
* 7 | ^ | 1 | -1.092548 | ^ | -0.273137
* 8 | ^ | 2 | 0.546274 | ^ | 0.136569
*
*
* Only 1, 2 or 3 bands are allowed.
*
* @param bands Number of spherical harmonics bands. Must be 1, 2 or 3.
* @param sh Array containing the spherical harmonics coefficients.
* The size of the array must be \f$ bands^{2} \f$.
* (i.e. 1, 4 or 9 coefficients respectively).
*
* @return This Builder, for chaining calls.
*
* @note
* Because the coefficients are pre-scaled, `sh[0]` is the environment's
* average irradiance.
*/
Builder& irradiance(uint8_t bands, math::float3 const* sh) noexcept;
/**
* Sets the irradiance from the radiance expressed as Spherical Harmonics.
*
* The radiance must be specified as Spherical Harmonics coefficients \f$ L_{l}^{m} \f$
*
* The index in the \p sh array is given by:
*
* `index(l, m) = l * (l + 1) + m`
*
* \f$ sh[index(l,m)] = L_{l}^{m} \f$
*
* index | l | m
* :-----:|:---:|:---:
* 0 | 0 | 0
* 1 | 1 | -1
* 2 | ^ | 0
* 3 | ^ | 1
* 4 | 2 | -2
* 5 | ^ | -1
* 6 | ^ | 0
* 7 | ^ | 1
* 8 | ^ | 2
*
* @param bands Number of spherical harmonics bands. Must be 1, 2 or 3.
* @param sh Array containing the spherical harmonics coefficients.
* The size of the array must be \f$ bands^{2} \f$.
* (i.e. 1, 4 or 9 coefficients respectively).
*
* @return This Builder, for chaining calls.
*/
Builder& radiance(uint8_t bands, math::float3 const* sh) noexcept;
/**
* Sets the irradiance as a cubemap.
*
* The irradiance can alternatively be specified as a cubemap instead of Spherical
* Harmonics coefficients. It may or may not be more efficient, depending on your
* hardware (essentially, it's trading ALU for bandwidth).
*
* @param cubemap Cubemap representing the Irradiance pre-convolved by
* \f$ \langle n \cdot l \rangle \f$.
*
* @return This Builder, for chaining calls.
*
* @note
* This irradiance cubemap can be generated with the **cmgen** tool.
*
* @see irradiance(uint8_t bands, math::float3 const* sh)
*/
Builder& irradiance(Texture const* cubemap) noexcept;
/**
* (optional) Environment intensity.
*
* Because the environment is encoded usually relative to some reference, the
* range can be adjusted with this method.
*
* @param envIntensity Scale factor applied to the environment and irradiance such that
* the result is in lux, or lumen/m^2 (default = 30000)
*
* @return This Builder, for chaining calls.
*/
Builder& intensity(float envIntensity) noexcept;
/**
* Specifies the rigid-body transformation to apply to the IBL.
*
* @param rotation 3x3 rotation matrix. Must be a rigid-body transform.
*
* @return This Builder, for chaining calls.
*/
Builder& rotation(math::mat3f const& rotation) noexcept;
/**
* Creates the IndirectLight object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this IndirectLight with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
IndirectLight* build(Engine& engine);
private:
friend class FIndirectLight;
};
/**
* Sets the environment's intensity.
*
* Because the environment is encoded usually relative to some reference, the
* range can be adjusted with this method.
*
* @param intensity Scale factor applied to the environment and irradiance such that
* the result is in lux, or <i>lumen/m^2(default = 30000)
*/
void setIntensity(float intensity) noexcept;
/**
* Returns the environment's intensity in <i>lux</i>, or <i>lumen/m^2</i>.
*/
float getIntensity() const noexcept;
/**
* Sets the rigid-body transformation to apply to the IBL.
*
* @param rotation 3x3 rotation matrix. Must be a rigid-body transform.
*/
void setRotation(math::mat3f const& rotation) noexcept;
/**
* Returns the rigid-body transformation applied to the IBL.
*/
const math::mat3f& getRotation() const noexcept;
/**
* Returns the associated reflection map, or null if it does not exist.
*/
Texture const* getReflectionsTexture() const noexcept;
/**
* Returns the associated irradiance map, or null if it does not exist.
*/
Texture const* getIrradianceTexture() const noexcept;
/**
* Helper to estimate the direction of the dominant light in the environment represented by
* spherical harmonics.
*
* This assumes that there is only a single dominant light (such as the sun in outdoors
* environments), if it's not the case the direction returned will be an average of the
* various lights based on their intensity.
*
* If there are no clear dominant light, as is often the case with low dynamic range (LDR)
* environments, this method may return a wrong or unexpected direction.
*
* The dominant light direction can be used to set a directional light's direction,
* for instance to produce shadows that match the environment.
*
* @param sh 3-band spherical harmonics
*
* @return A unit vector representing the direction of the dominant light
*
* @see LightManager::Builder::direction()
* @see getColorEstimate()
*/
static math::float3 getDirectionEstimate(const math::float3 sh[9]) noexcept;
/**
* Helper to estimate the color and relative intensity of the environment represented by
* spherical harmonics in a given direction.
*
* This can be used to set the color and intensity of a directional light. In this case
* make sure to multiply this relative intensity by the the intensity of this indirect light.
*
* @param sh 3-band spherical harmonics
* @param direction a unit vector representing the direction of the light to estimate the
* color of. Typically this the value returned by getDirectionEstimate().
*
* @return A vector of 4 floats where the first 3 components represent the linear color and
* the 4th component represents the intensity of the dominant light
*
* @see LightManager::Builder::color()
* @see LightManager::Builder::intensity()
* @see getDirectionEstimate, getIntensity, setIntensity
*/
static math::float4 getColorEstimate(const math::float3 sh[9], math::float3 direction) noexcept;
/** @deprecated use static versions instead */
UTILS_DEPRECATED
math::float3 getDirectionEstimate() const noexcept;
/** @deprecated use static versions instead */
UTILS_DEPRECATED
math::float4 getColorEstimate(math::float3 direction) const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_INDIRECT_LIGHT_H

View File

@@ -0,0 +1,938 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_LIGHTMANAGER_H
#define TNT_FILAMENT_LIGHTMANAGER_H
#include <filament/FilamentAPI.h>
#include <filament/Color.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <math/mathfwd.h>
namespace utils {
class Entity;
} // namespace utils
namespace filament {
class Engine;
class FEngine;
class FLightManager;
/**
* LightManager allows to create a light source in the scene, such as a sun or street lights.
*
* At least one light must be added to a scene in order to see anything
* (unless the Material.Shading.UNLIT is used).
*
*
* Creation and destruction
* ========================
*
* A Light component is created using the LightManager::Builder and destroyed by calling
* LightManager::destroy(utils::Entity).
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
* utils::Entity sun = utils::EntityManager.get().create();
*
* filament::LightManager::Builder(Type::SUN)
* .castShadows(true)
* .build(*engine, sun);
*
* engine->getLightManager().destroy(sun);
* ~~~~~~~~~~~
*
*
* Light types
* ===========
*
* Lights come in three flavors:
* - directional lights
* - point lights
* - spot lights
*
*
* Directional lights
* ------------------
*
* Directional lights have a direction, but don't have a position. All light rays are
* parallel and come from infinitely far away and from everywhere. Typically a directional light
* is used to simulate the sun.
*
* Directional lights and spot lights are able to cast shadows.
*
* To create a directional light use Type.DIRECTIONAL or Type.SUN, both are similar, but the later
* also draws a sun's disk in the sky and its reflection on glossy objects.
*
* @warning Currently, only a single directional light is supported. If several directional lights
* are added to the scene, the dominant one will be used.
*
* @see Builder.direction(), Builder.sunAngularRadius()
*
* Point lights
* ------------
*
* Unlike directional lights, point lights have a position but emit light in all directions.
* The intensity of the light diminishes with the inverse square of the distance to the light.
* Builder.falloff() controls distance beyond which the light has no more influence.
*
* A scene can have multiple point lights.
*
* @see Builder.position(), Builder.falloff()
*
* Spot lights
* -----------
*
* Spot lights are similar to point lights but the light it emits is limited to a cone defined by
* Builder.spotLightCone() and the light's direction.
*
* A spot light is therefore defined by a position, a direction and inner and outer cones. The
* spot light's influence is limited to inside the outer cone. The inner cone defines the light's
* falloff attenuation.
*
* A physically correct spot light is a little difficult to use because changing the outer angle
* of the cone changes the illumination levels, as the same amount of light is spread over a
* changing volume. The coupling of illumination and the outer cone means that an artist cannot
* tweak the influence cone of a spot light without also changing the perceived illumination.
* It therefore makes sense to provide artists with a parameter to disable this coupling. This
* is the difference between Type.FOCUSED_SPOT and Type.SPOT.
*
* @see Builder.position(), Builder.direction(), Builder.falloff(), Builder.spotLightCone()
*
* Performance considerations
* ==========================
*
* Generally, adding lights to the scene hurts performance, however filament is designed to be
* able to handle hundreds of lights in a scene under certain conditions. Here are some tips
* to keep performances high.
*
* 1. Prefer spot lights to point lights and use the smallest outer cone angle possible.
*
* 2. Use the smallest possible falloff distance for point and spot lights.
* Performance is very sensitive to overlapping lights. The falloff distance essentially
* defines a sphere of influence for the light, so try to position point and spot lights
* such that they don't overlap too much.
*
* On the other hand, a scene can contain hundreds of non overlapping lights without
* incurring a significant overhead.
*
*/
class UTILS_PUBLIC LightManager : public FilamentAPI {
struct BuilderDetails;
public:
using Instance = utils::EntityInstance<LightManager>;
/**
* Returns the number of component in the LightManager, not that component are not
* guaranteed to be active. Use the EntityManager::isAlive() before use if needed.
*
* @return number of component in the LightManager
*/
size_t getComponentCount() const noexcept;
/**
* Returns the list of Entity for all components. Use getComponentCount() to know the size
* of the list.
* @return a pointer to Entity
*/
utils::Entity const* getEntities() const noexcept;
/**
* Returns whether a particular Entity is associated with a component of this LightManager
* @param e An Entity.
* @return true if this Entity has a component associated with this manager.
*/
bool hasComponent(utils::Entity e) const noexcept;
/**
* Gets an Instance representing the Light component associated with the given Entity.
* @param e An Entity.
* @return An Instance object, which represents the Light component associated with the Entity e.
* @note Use Instance::isValid() to make sure the component exists.
* @see hasComponent()
*/
Instance getInstance(utils::Entity e) const noexcept;
// destroys this component from the given entity
void destroy(utils::Entity e) noexcept;
//! Denotes the type of the light being created.
enum class Type : uint8_t {
SUN, //!< Directional light that also draws a sun's disk in the sky.
DIRECTIONAL, //!< Directional light, emits light in a given direction.
POINT, //!< Point light, emits light from a position, in all directions.
FOCUSED_SPOT, //!< Physically correct spot light.
SPOT, //!< Spot light with coupling of outer cone and illumination disabled.
};
/**
* Control the quality / performance of the shadow map associated to this light
*/
struct ShadowOptions {
/** Size of the shadow map in texels. Must be a power-of-two and larger or equal to 8. */
uint32_t mapSize = 1024;
/**
* Number of shadow cascades to use for this light. Must be between 1 and 4 (inclusive).
* A value greater than 1 turns on cascaded shadow mapping (CSM).
* Only applicable to Type.SUN or Type.DIRECTIONAL lights.
*
* When using shadow cascades, cascadeSplitPositions must also be set.
*
* @see ShadowOptions::cascadeSplitPositions
*/
uint8_t shadowCascades = 1;
/**
* The split positions for shadow cascades.
*
* Cascaded shadow mapping (CSM) partitions the camera frustum into cascades. These values
* determine the planes along the camera's Z axis to split the frustum. The camera near
* plane is represented by 0.0f and the far plane represented by 1.0f.
*
* For example, if using 4 cascades, these values would set a uniform split scheme:
* { 0.25f, 0.50f, 0.75f }
*
* For N cascades, N - 1 split positions will be read from this array.
*
* Filament provides utility methods inside LightManager::ShadowCascades to help set these
* values. For example, to use a uniform split scheme:
*
* ~~~~~~~~~~~{.cpp}
* LightManager::ShadowCascades::computeUniformSplits(options.splitPositions, 4);
* ~~~~~~~~~~~
*
* @see ShadowCascades::computeUniformSplits
* @see ShadowCascades::computeLogSplits
* @see ShadowCascades::computePracticalSplits
*/
float cascadeSplitPositions[3] = { 0.25f, 0.50f, 0.75f };
/** Constant bias in world units (e.g. meters) by which shadows are moved away from the
* light. 1mm by default.
* This is ignored when the View's ShadowType is set to VSM.
*/
float constantBias = 0.001f;
/** Amount by which the maximum sampling error is scaled. The resulting value is used
* to move the shadow away from the fragment normal. Should be 1.0.
* This is ignored when the View's ShadowType is set to VSM.
*/
float normalBias = 1.0f;
/** Distance from the camera after which shadows are clipped. This is used to clip
* shadows that are too far and wouldn't contribute to the scene much, improving
* performance and quality. This value is always positive.
* Use 0.0f to use the camera far distance.
*/
float shadowFar = 0.0f;
/** Optimize the quality of shadows from this distance from the camera. Shadows will
* be rendered in front of this distance, but the quality may not be optimal.
* This value is always positive. Use 0.0f to use the camera near distance.
* The default of 1m works well with many scenes. The quality of shadows may drop
* rapidly when this value decreases.
*/
float shadowNearHint = 1.0f;
/** Optimize the quality of shadows in front of this distance from the camera. Shadows
* will be rendered behind this distance, but the quality may not be optimal.
* This value is always positive. Use std::numerical_limits<float>::infinity() to
* use the camera far distance.
*/
float shadowFarHint = 100.0f;
/**
* Controls whether the shadow map should be optimized for resolution or stability.
* When set to true, all resolution enhancing features that can affect stability are
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
*/
bool stable = false;
/**
* Constant bias in depth-resolution units by which shadows are moved away from the
* light. The default value of 0.5 is used to round depth values up.
* Generally this value shouldn't be changed or at least be small and positive.
*/
float polygonOffsetConstant = 0.5f;
/**
* Bias based on the change in depth in depth-resolution units by which shadows are moved
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
* Generally this value is between 0.5 and the size in texel of the PCF filter.
* Setting this value correctly is essential for LISPSM shadow-maps.
*/
float polygonOffsetSlope = 2.0f;
/**
* Whether screen-space contact shadows are used. This applies regardless of whether a
* Renderable is a shadow caster.
* Screen-space contact shadows are typically useful in large scenes.
* (off by default)
*/
bool screenSpaceContactShadows = false;
/**
* Number of ray-marching steps for screen-space contact shadows (8 by default).
*
* CAUTION: this parameter is ignored for all lights except the directional/sun light,
* all other lights use the same value set for the directional/sun light.
*
*/
uint8_t stepCount = 8;
/**
* Maximum shadow-occluder distance for screen-space contact shadows (world units).
* (30 cm by default)
*
* CAUTION: this parameter is ignored for all lights except the directional/sun light,
* all other lights use the same value set for the directional/sun light.
*
*/
float maxShadowDistance = 0.3f;
/**
* Options available when the View's ShadowType is set to VSM.
*
* @warning This API is still experimental and subject to change.
* @see View::setShadowType
*/
struct Vsm {
/**
* The number of MSAA samples to use when rendering VSM shadow maps.
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
* off MSAA.
* Higher values may not be available depending on the underlying hardware.
*/
uint8_t msaaSamples = 1;
/**
* Blur width for the VSM blur. Zero do disable.
* The maximum value is 125.
*/
float blurWidth = 0.0f;
} vsm;
};
struct ShadowCascades {
/**
* Utility method to compute ShadowOptions::cascadeSplitPositions according to a uniform
* split scheme.
*
* @param splitPositions a float array of at least size (cascades - 1) to write the split
* positions into
* @param cascades the number of shadow cascades, at most 4
*/
static void computeUniformSplits(float* splitPositions, uint8_t cascades);
/**
* Utility method to compute ShadowOptions::cascadeSplitPositions according to a logarithmic
* split scheme.
*
* @param splitPositions a float array of at least size (cascades - 1) to write the split
* positions into
* @param cascades the number of shadow cascades, at most 4
* @param near the camera near plane
* @param far the camera far plane
*/
static void computeLogSplits(float* splitPositions, uint8_t cascades,
float near, float far);
/**
* Utility method to compute ShadowOptions::cascadeSplitPositions according to a practical
* split scheme.
*
* The practical split scheme uses uses a lambda value to interpolate between the logarithmic
* and uniform split schemes. Start with a lambda value of 0.5f and adjust for your scene.
*
* See: Zhang et al 2006, "Parallel-split shadow maps for large-scale virtual environments"
*
* @param splitPositions a float array of at least size (cascades - 1) to write the split
* positions into
* @param cascades the number of shadow cascades, at most 4
* @param near the camera near plane
* @param far the camera far plane
* @param lambda a float in the range [0, 1] that interpolates between log and
* uniform split schemes
*/
static void computePracticalSplits(float* splitPositions, uint8_t cascades,
float near, float far, float lambda);
};
//! Use Builder to construct a Light object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
/**
* Creates a light builder and set the light's #Type.
*
* @param type #Type of Light object to create.
*/
explicit Builder(Type type) noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Enables or disables a light channel. Light channel 0 is enabled by default.
*
* @param channel Light channel to enable or disable, between 0 and 7.
* @param enable Whether to enable or disable the light channel.
* @return This Builder, for chaining calls.
*/
Builder& lightChannel(unsigned int channel, bool enable = true) noexcept;
/**
* Whether this Light casts shadows (disabled by default)
*
* @param enable Enables or disables casting shadows from this Light.
*
* @return This Builder, for chaining calls.
*
* @warning
* - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows
*/
Builder& castShadows(bool enable) noexcept;
/**
* Sets the shadow-map options for this light.
*
* @return This Builder, for chaining calls.
*/
Builder& shadowOptions(const ShadowOptions& options) noexcept;
/**
* Whether this light casts light (enabled by default)
*
* @param enable Enables or disables lighting from this Light.
*
* @return This Builder, for chaining calls.
*
* @note
* In some situations it can be useful to have a light in the scene that doesn't
* actually emit light, but does cast shadows.
*/
Builder& castLight(bool enable) noexcept;
/**
* Sets the initial position of the light in world space.
*
* @param position Light's position in world space. The default is at the origin.
*
* @return This Builder, for chaining calls.
*
* @note
* The Light's position is ignored for directional lights (Type.DIRECTIONAL or Type.SUN)
*/
Builder& position(const math::float3& position) noexcept;
/**
* Sets the initial direction of a light in world space.
*
* @param direction Light's direction in world space. Should be a unit vector.
* The default is {0,-1,0}.
*
* @return This Builder, for chaining calls.
*
* @note
* The Light's direction is ignored for Type.POINT lights.
*/
Builder& direction(const math::float3& direction) noexcept;
/**
* Sets the initial color of a light.
*
* @param color Color of the light specified in the linear sRGB color-space.
* The default is white {1,1,1}.
*
* @return This Builder, for chaining calls.
*/
Builder& color(const LinearColor& color) noexcept;
/**
* Sets the initial intensity of a light.
* @param intensity This parameter depends on the Light.Type:
* - For directional lights, it specifies the illuminance in *lux*
* (or *lumen/m^2*).
* - For point lights and spot lights, it specifies the luminous power
* in *lumen*.
*
* @return This Builder, for chaining calls.
*
* For example, the sun's illuminance is about 100,000 lux.
*
* This method overrides any prior calls to intensity or intensityCandela.
*
*/
Builder& intensity(float intensity) noexcept;
/**
* Sets the initial intensity of a spot or point light in candela.
*
* @param intensity Luminous intensity in *candela*.
*
* @return This Builder, for chaining calls.
*
* @note
* This method is equivalent to calling intensity(float intensity) for directional lights
* (Type.DIRECTIONAL or Type.SUN).
*
* This method overrides any prior calls to intensity or intensityCandela.
*/
Builder& intensityCandela(float intensity) noexcept;
/**
* Sets the initial intensity of a light in watts.
*
* @param watts Energy consumed by a lightbulb. It is related to the energy produced
* and ultimately the brightness by the \p efficiency parameter.
* This value is often available on the packaging of commercial
* lightbulbs.
*
* @param efficiency Efficiency in percent. This depends on the type of lightbulb used.
*
* Lightbulb type | Efficiency
* ----------------:|-----------:
* Incandescent | 2.2%
* Halogen | 7.0%
* LED | 8.7%
* Fluorescent | 10.7%
*
* @return This Builder, for chaining calls.
*
*
* @note
* This call is equivalent to `Builder::intensity(efficiency * 683 * watts);`
*
* This method overrides any prior calls to intensity or intensityCandela.
*/
Builder& intensity(float watts, float efficiency) noexcept;
/**
* Set the falloff distance for point lights and spot lights.
*
* At the falloff distance, the light has no more effect on objects.
*
* The falloff distance essentially defines a *sphere of influence* around the light, and
* therefore has an impact on performance. Larger falloffs might reduce performance
* significantly, especially when many lights are used.
*
* Try to avoid having a large number of light's spheres of influence overlap.
*
* @param radius Falloff distance in world units. Default is 1 meter.
*
* @return This Builder, for chaining calls.
*
* @note
* The Light's falloff is ignored for directional lights (Type.DIRECTIONAL or Type.SUN)
*/
Builder& falloff(float radius) noexcept;
/**
* Defines a spot light'st angular falloff attenuation.
*
* A spot light is defined by a position, a direction and two cones, \p inner and \p outer.
* These two cones are used to define the angular falloff attenuation of the spot light
* and are defined by the angle from the center axis to where the falloff begins (i.e.
* cones are defined by their half-angle).
*
* @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$
*
* @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$
*
* @return This Builder, for chaining calls.
*
* @note
* The spot light cone is ignored for directional and point lights.
*
* @see Type.SPOT, Type.FOCUSED_SPOT
*/
Builder& spotLightCone(float inner, float outer) noexcept;
/**
* Defines the angular radius of the sun, in degrees, between 0.25° and 20.0°
*
* The Sun as seen from Earth has an angular size of 0.526° to 0.545°
*
* @param angularRadius sun's radius in degree. Default is 0.545°.
*
* @return This Builder, for chaining calls.
*/
Builder& sunAngularRadius(float angularRadius) noexcept;
/**
* Defines the halo radius of the sun. The radius of the halo is defined as a
* multiplier of the sun angular radius.
*
* @param haloSize radius multiplier. Default is 10.0.
*
* @return This Builder, for chaining calls.
*/
Builder& sunHaloSize(float haloSize) noexcept;
/**
* Defines the halo falloff of the sun. The falloff is a dimensionless number
* used as an exponent.
*
* @param haloFalloff halo falloff. Default is 80.0.
*
* @return This Builder, for chaining calls.
*/
Builder& sunHaloFalloff(float haloFalloff) noexcept;
enum Result { Error = -1, Success = 0 };
/**
* Adds the Light component to an entity.
*
* @param engine Reference to the filament::Engine to associate this light with.
* @param entity Entity to add the light component to.
* @return Success if the component was created successfully, Error otherwise.
*
* If exceptions are disabled and an error occurs, this function is a no-op.
* Success can be checked by looking at the return value.
*
* If this component already exists on the given entity, it is first destroyed as if
* destroy(utils::Entity e) was called.
*
* @warning
* Currently, only 2048 lights can be created on a given Engine.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
Result build(Engine& engine, utils::Entity entity);
private:
friend class FEngine;
friend class FLightManager;
};
static constexpr float EFFICIENCY_INCANDESCENT = 0.0220f; //!< Typical efficiency of an incandescent light bulb (2.2%)
static constexpr float EFFICIENCY_HALOGEN = 0.0707f; //!< Typical efficiency of an halogen light bulb (7.0%)
static constexpr float EFFICIENCY_FLUORESCENT = 0.0878f; //!< Typical efficiency of a fluorescent light bulb (8.7%)
static constexpr float EFFICIENCY_LED = 0.1171f; //!< Typical efficiency of a LED light bulb (11.7%)
Type getType(Instance i) const noexcept;
/**
* Helper function that returns if a light is a directional light
*
* @param i Instance of the component obtained from getInstance().
* @return true is this light is a type of directional light
*/
inline bool isDirectional(Instance i) const noexcept {
Type type = getType(i);
return type == Type::DIRECTIONAL || type == Type::SUN;
}
/**
* Helper function that returns if a light is a point light
*
* @param i Instance of the component obtained from getInstance().
* @return true is this light is a type of point light
*/
inline bool isPointLight(Instance i) const noexcept {
return getType(i) == Type::POINT;
}
/**
* Helper function that returns if a light is a spot light
*
* @param i Instance of the component obtained from getInstance().
* @return true is this light is a type of spot light
*/
inline bool isSpotLight(Instance i) const noexcept {
Type type = getType(i);
return type == Type::SPOT || type == Type::FOCUSED_SPOT;
}
/**
* Enables or disables a light channel. Light channel 0 is enabled by default.
* @param channel light channel to enable or disable, between 0 and 7.
* @param enable whether to enable (true) or disable (false) the specified light channel.
*/
void setLightChannel(Instance i, unsigned int channel, bool enable = true) noexcept;
/**
* Returns whether a light channel is enabled on a specified light.
* @param i Instance of the component obtained from getInstance().
* @param channel Light channel to query
* @return true if the light channel is enabled, false otherwise
*/
bool getLightChannel(Instance i, unsigned int channel) const noexcept;
/**
* Dynamically updates the light's position.
*
* @param i Instance of the component obtained from getInstance().
* @param position Light's position in world space. The default is at the origin.
*
* @see Builder.position()
*/
void setPosition(Instance i, const math::float3& position) noexcept;
//! returns the light's position in world space
const math::float3& getPosition(Instance i) const noexcept;
/**
* Dynamically updates the light's direction
*
* @param i Instance of the component obtained from getInstance().
* @param direction Light's direction in world space. Should be a unit vector.
* The default is {0,-1,0}.
*
* @see Builder.direction()
*/
void setDirection(Instance i, const math::float3& direction) noexcept;
//! returns the light's direction in world space
const math::float3& getDirection(Instance i) const noexcept;
/**
* Dynamically updates the light's hue as linear sRGB
*
* @param i Instance of the component obtained from getInstance().
* @param color Color of the light specified in the linear sRGB color-space.
* The default is white {1,1,1}.
*
* @see Builder.color(), getInstance()
*/
void setColor(Instance i, const LinearColor& color) noexcept;
/**
* @param i Instance of the component obtained from getInstance().
* @return the light's color in linear sRGB
*/
const math::float3& getColor(Instance i) const noexcept;
/**
* Dynamically updates the light's intensity. The intensity can be negative.
*
* @param i Instance of the component obtained from getInstance().
* @param intensity This parameter depends on the Light.Type:
* - For directional lights, it specifies the illuminance in *lux*
* (or *lumen/m^2*).
* - For point lights and spot lights, it specifies the luminous power
* in *lumen*.
*
* @see Builder.intensity()
*/
void setIntensity(Instance i, float intensity) noexcept;
/**
* Dynamically updates the light's intensity. The intensity can be negative.
*
* @param i Instance of the component obtained from getInstance().
* @param watts Energy consumed by a lightbulb. It is related to the energy produced
* and ultimately the brightness by the \p efficiency parameter.
* This value is often available on the packaging of commercial
* lightbulbs.
* @param efficiency Efficiency in percent. This depends on the type of lightbulb used.
*
* Lightbulb type | Efficiency
* ----------------:|-----------:
* Incandescent | 2.2%
* Halogen | 7.0%
* LED | 8.7%
* Fluorescent | 10.7%
*
* @see Builder.intensity(float watts, float efficiency)
*/
void setIntensity(Instance i, float watts, float efficiency) noexcept {
setIntensity(i, watts * 683.0f * efficiency);
}
/**
* Dynamically updates the light's intensity in candela. The intensity can be negative.
*
* @param i Instance of the component obtained from getInstance().
* @param intensity Luminous intensity in *candela*.
*
* @note
* This method is equivalent to calling setIntensity(float intensity) for directional lights
* (Type.DIRECTIONAL or Type.SUN).
*
* @see Builder.intensityCandela(float intensity)
*/
void setIntensityCandela(Instance i, float intensity) noexcept;
/**
* returns the light's luminous intensity in candela.
*
* @param i Instance of the component obtained from getInstance().
*
* @note for Type.FOCUSED_SPOT lights, the returned value depends on the \p outer cone angle.
*
* @return luminous intensity in candela.
*/
float getIntensity(Instance i) const noexcept;
/**
* Set the falloff distance for point lights and spot lights.
*
* @param i Instance of the component obtained from getInstance().
* @param radius falloff distance in world units. Default is 1 meter.
*
* @see Builder.falloff()
*/
void setFalloff(Instance i, float radius) noexcept;
/**
* returns the falloff distance of this light.
* @param i Instance of the component obtained from getInstance().
* @return the falloff distance of this light.
*/
float getFalloff(Instance i) const noexcept;
/**
* Dynamically updates a spot light's cone as angles
*
* @param i Instance of the component obtained from getInstance().
* @param inner inner cone angle in *radians* between 0 and pi/2
* @param outer outer cone angle in *radians* between inner and pi/2
*
* @see Builder.spotLightCone()
*/
void setSpotLightCone(Instance i, float inner, float outer) noexcept;
/**
* returns the outer cone angle in *radians* between inner and pi/2.
* @param i Instance of the component obtained from getInstance().
* @return the outer cone angle of this light.
*/
float getSpotLightOuterCone(Instance i) const noexcept;
/**
* returns the inner cone angle in *radians* between 0 and pi/2.
*
* The value is recomputed from the initial values, thus is not precisely
* the same as the one passed to setSpotLightCone() or Builder.spotLightCone().
*
* @param i Instance of the component obtained from getInstance().
* @return the inner cone angle of this light.
*/
float getSpotLightInnerCone(Instance i) const noexcept;
/**
* Dynamically updates the angular radius of a Type.SUN light
*
* The Sun as seen from Earth has an angular size of 0.526° to 0.545°
*
* @param i Instance of the component obtained from getInstance().
* @param angularRadius sun's radius in degrees. Default is 0.545°.
*/
void setSunAngularRadius(Instance i, float angularRadius) noexcept;
/**
* returns the angular radius if the sun in degrees.
* @param i Instance of the component obtained from getInstance().
* @return the angular radius if the sun in degrees.
*/
float getSunAngularRadius(Instance i) const noexcept;
/**
* Dynamically updates the halo radius of a Type.SUN light. The radius
* of the halo is defined as a multiplier of the sun angular radius.
*
* @param i Instance of the component obtained from getInstance().
* @param haloSize radius multiplier. Default is 10.0.
*/
void setSunHaloSize(Instance i, float haloSize) noexcept;
/**
* returns the halo size of a Type.SUN light as a multiplier of the
* sun angular radius.
* @param i Instance of the component obtained from getInstance().
* @return the halo size
*/
float getSunHaloSize(Instance i) const noexcept;
/**
* Dynamically updates the halo falloff of a Type.SUN light. The falloff
* is a dimensionless number used as an exponent.
*
* @param i Instance of the component obtained from getInstance().
* @param haloFalloff halo falloff. Default is 80.0.
*/
void setSunHaloFalloff(Instance i, float haloFalloff) noexcept;
/**
* returns the halo falloff of a Type.SUN light as a dimensionless value.
* @param i Instance of the component obtained from getInstance().
* @return the halo falloff
*/
float getSunHaloFalloff(Instance i) const noexcept;
/**
* returns the shadow-map options for a given light
* @param i Instance of the component obtained from getInstance().
* @return A ShadowOption structure
*/
ShadowOptions const& getShadowOptions(Instance i) const noexcept;
/**
* sets the shadow-map options for a given light
* @param i Instance of the component obtained from getInstance().
* @param options A ShadowOption structure
*/
void setShadowOptions(Instance i, ShadowOptions const& options) noexcept;
/**
* Whether this Light casts shadows (disabled by default)
*
* @param i Instance of the component obtained from getInstance().
* @param shadowCaster Enables or disables casting shadows from this Light.
*
* @warning
* - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows
*/
void setShadowCaster(Instance i, bool shadowCaster) noexcept;
/**
* returns whether this light casts shadows.
* @param i Instance of the component obtained from getInstance().
*/
bool isShadowCaster(Instance i) const noexcept;
/**
* Helper to process all components with a given function
* @tparam F a void(Entity entity, Instance instance)
* @param func a function of type F
*/
template<typename F>
void forEachComponent(F func) noexcept {
utils::Entity const* const pEntity = getEntities();
for (size_t i = 0, c = getComponentCount(); i < c; i++) {
// Instance 0 is the invalid instance
func(pEntity[i], Instance(i + 1));
}
}
};
} // namespace filament
#endif // TNT_FILAMENT_LIGHTMANAGER_H

View File

@@ -0,0 +1,284 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_MATERIAL_H
#define TNT_FILAMENT_MATERIAL_H
#include <filament/Color.h>
#include <filament/FilamentAPI.h>
#include <filament/MaterialEnums.h>
#include <filament/MaterialInstance.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
#include <stdint.h>
namespace utils {
class CString;
} // namespace utils
namespace filament {
class Texture;
class TextureSampler;
class FEngine;
class FMaterial;
class Engine;
class UTILS_PUBLIC Material : public FilamentAPI {
struct BuilderDetails;
public:
using BlendingMode = BlendingMode;
using Shading = Shading;
using Interpolation = Interpolation;
using VertexDomain = VertexDomain;
using TransparencyMode = TransparencyMode;
using ParameterType = backend::UniformType;
using Precision = backend::Precision;
using SamplerType = backend::SamplerType;
using SamplerFormat = backend::SamplerFormat;
using CullingMode = backend::CullingMode;
using ShaderModel = backend::ShaderModel;
using SubpassType = backend::SubpassType;
/**
* Holds information about a material parameter.
*/
struct ParameterInfo {
//! Name of the parameter.
const char* name;
//! Whether the parameter is a sampler (texture).
bool isSampler;
//! Whether the parameter is a subpass type.
bool isSubpass;
union {
//! Type of the parameter if the parameter is not a sampler.
ParameterType type;
//! Type of the parameter if the parameter is a sampler.
SamplerType samplerType;
//! Type of the parameter if the parameter is a subpass.
SubpassType subpassType;
};
//! Size of the parameter when the parameter is an array.
uint32_t count;
//! Requested precision of the parameter.
Precision precision;
};
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Specifies the material data. The material data is a binary blob produced by
* libfilamat or by matc.
*
* @param payload Pointer to the material data, must stay valid until build() is called.
* @param size Size of the material data pointed to by "payload" in bytes.
*/
Builder& package(const void* payload, size_t size);
/**
* Creates the Material object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this Material with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
Material* build(Engine& engine);
private:
friend class FMaterial;
};
/**
* Creates a new instance of this material. Material instances should be freed using
* Engine::destroy(const MaterialInstance*).
*
* @param name Optional name to associate with the given material instance. If this is null,
* then the instance inherits the material's name.
*
* @return A pointer to the new instance.
*/
MaterialInstance* createInstance(const char* name = nullptr) const noexcept;
//! Returns the name of this material as a null-terminated string.
const char* getName() const noexcept;
//! Returns the shading model of this material.
Shading getShading() const noexcept;
//! Returns the interpolation mode of this material. This affects how variables are interpolated.
Interpolation getInterpolation() const noexcept;
//! Returns the blending mode of this material.
BlendingMode getBlendingMode() const noexcept;
//! Returns the vertex domain of this material.
VertexDomain getVertexDomain() const noexcept;
//! Returns the material domain of this material.
//! The material domain determines how the material is used.
MaterialDomain getMaterialDomain() const noexcept;
//! Returns the default culling mode of this material.
CullingMode getCullingMode() const noexcept;
//! Returns the transparency mode of this material.
//! This value only makes sense when the blending mode is transparent or fade.
TransparencyMode getTransparencyMode() const noexcept;
//! Indicates whether instances of this material will, by default, write to the color buffer.
bool isColorWriteEnabled() const noexcept;
//! Indicates whether instances of this material will, by default, write to the depth buffer.
bool isDepthWriteEnabled() const noexcept;
//! Indicates whether instances of this material will, by default, use depth testing.
bool isDepthCullingEnabled() const noexcept;
//! Indicates whether this material is double-sided.
bool isDoubleSided() const noexcept;
//! Returns the alpha mask threshold used when the blending mode is set to masked.
float getMaskThreshold() const noexcept;
//! Indicates whether this material uses the shadowing factor as a color multiplier.
//! This values only makes sense when the shading mode is unlit.
bool hasShadowMultiplier() const noexcept;
//! Indicates whether this material has specular anti-aliasing enabled
bool hasSpecularAntiAliasing() const noexcept;
//! Returns the screen-space variance for specular-antialiasing, this value is between 0 and 1.
float getSpecularAntiAliasingVariance() const noexcept;
//! Returns the clamping threshold for specular-antialiasing, this value is between 0 and 1.
float getSpecularAntiAliasingThreshold() const noexcept;
//! Returns the list of vertex attributes required by this material.
AttributeBitset getRequiredAttributes() const noexcept;
//! Returns the refraction mode used by this material.
RefractionMode getRefractionMode() const noexcept;
// Return the refraction type used by this material.
RefractionType getRefractionType() const noexcept;
/**
* Returns the number of parameters declared by this material.
* The returned value can be 0.
*/
size_t getParameterCount() const noexcept;
/**
* Gets information about this material's parameters.
*
* @param parameters A pointer to a list of ParameterInfo.
* The list must be at least "count" large
* @param count The number of parameters to retrieve. Must be >= 0 and can be > count.
*
* @return The number of parameters written to the parameters pointer.
*/
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
//! Indicates whether a parameter of the given name exists on this material.
bool hasParameter(const char* name) const noexcept;
//! Indicates whether an existing parameter is a sampler or not.
bool isSampler(const char* name) const noexcept;
/**
* Sets the value of the given parameter on this material's default instance.
*
* @param name The name of the material parameter
* @param value The value of the material parameter
*
* @see getDefaultInstance()
*/
template <typename T>
void setDefaultParameter(const char* name, T value) noexcept {
getDefaultInstance()->setParameter(name, value);
}
/**
* Sets a texture and sampler parameters on this material's default instance.
*
* @param name The name of the material texture parameter
* @param texture The texture to set as parameter
* @param sampler The sampler to be used with this texture
*
* @see getDefaultInstance()
*/
void setDefaultParameter(const char* name, Texture const* texture,
TextureSampler const& sampler) noexcept {
getDefaultInstance()->setParameter(name, texture, sampler);
}
/**
* Sets the color of the given parameter on this material's default instance.
*
* @param name The name of the material color parameter
* @param type Whether the color is specified in the linear or sRGB space
* @param color The color as a floating point red, green, blue tuple
*
* @see getDefaultInstance()
*/
void setDefaultParameter(const char* name, RgbType type, math::float3 color) noexcept {
getDefaultInstance()->setParameter(name, type, color);
}
/**
* Sets the color of the given parameter on this material's default instance.
*
* @param name The name of the material color parameter
* @param type Whether the color is specified in the linear or sRGB space
* @param color The color as a floating point red, green, blue, alpha tuple
*
* @see getDefaultInstance()
*/
void setDefaultParameter(const char* name, RgbaType type, math::float4 color) noexcept {
getDefaultInstance()->setParameter(name, type, color);
}
//! Returns this material's default instance.
MaterialInstance* getDefaultInstance() noexcept;
//! Returns this material's default instance.
MaterialInstance const* getDefaultInstance() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_MATERIAL_H

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_MATERIAL_CHUNK_TYPES_H
#define TNT_FILAMAT_MATERIAL_CHUNK_TYPES_H
#include <stdint.h>
#include <utils/compiler.h>
namespace filamat {
// Pack an eight character string into a 64 bit integer.
constexpr inline uint64_t charTo64bitNum(const char str[9]) noexcept {
return
( (static_cast<uint64_t >(str[0]) << 56))
| ((static_cast<uint64_t >(str[1]) << 48) & 0x00FF000000000000U)
| ((static_cast<uint64_t >(str[2]) << 40) & 0x0000FF0000000000U)
| ((static_cast<uint64_t >(str[3]) << 32) & 0x000000FF00000000U)
| ((static_cast<uint64_t >(str[4]) << 24) & 0x00000000FF000000U)
| ((static_cast<uint64_t >(str[5]) << 16) & 0x0000000000FF0000U)
| ((static_cast<uint64_t >(str[6]) << 8) & 0x000000000000FF00U)
| ( static_cast<uint64_t >(str[7]) & 0x00000000000000FFU);
}
enum UTILS_PUBLIC ChunkType : uint64_t {
Unknown = charTo64bitNum("UNKNOWN "),
MaterialUib = charTo64bitNum("MAT_UIB "),
MaterialSib = charTo64bitNum("MAT_SIB "),
MaterialSubpass = charTo64bitNum("MAT_SUB "),
MaterialGlsl = charTo64bitNum("MAT_GLSL"),
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
MaterialMetal = charTo64bitNum("MAT_METL"),
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), // no longer used
MaterialProperties = charTo64bitNum("MAT_PROP"),
MaterialName = charTo64bitNum("MAT_NAME"),
MaterialVersion = charTo64bitNum("MAT_VERS"),
MaterialShading = charTo64bitNum("MAT_SHAD"),
MaterialBlendingMode = charTo64bitNum("MAT_BLEN"),
MaterialTransparencyMode = charTo64bitNum("MAT_TRMD"),
MaterialMaskThreshold = charTo64bitNum("MAT_THRS"),
MaterialShadowMultiplier = charTo64bitNum("MAT_SHML"),
MaterialSpecularAntiAliasing = charTo64bitNum("MAT_SPAA"),
MaterialSpecularAntiAliasingVariance = charTo64bitNum("MAT_SVAR"),
MaterialSpecularAntiAliasingThreshold = charTo64bitNum("MAT_STHR"),
MaterialClearCoatIorChange = charTo64bitNum("MAT_CIOR"),
MaterialDomain = charTo64bitNum("MAT_DOMN"),
MaterialRefraction = charTo64bitNum("MAT_REFM"),
MaterialRefractionType = charTo64bitNum("MAT_REFT"),
MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"),
MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"),
MaterialDoubleSidedSet = charTo64bitNum("MAT_DOSS"),
MaterialDoubleSided = charTo64bitNum("MAT_DOSI"),
MaterialColorWrite = charTo64bitNum("MAT_CWRIT"),
MaterialDepthWrite = charTo64bitNum("MAT_DWRIT"),
MaterialDepthTest = charTo64bitNum("MAT_DTEST"),
MaterialCullingMode = charTo64bitNum("MAT_CUMO"),
MaterialHasCustomDepthShader =charTo64bitNum("MAT_CSDP"),
MaterialVertexDomain = charTo64bitNum("MAT_VEDO"),
MaterialInterpolation = charTo64bitNum("MAT_INTR"),
DictionaryText = charTo64bitNum("DIC_TEXT"),
DictionarySpirv = charTo64bitNum("DIC_SPIR"),
};
} // namespace filamat
#endif // TNT_FILAMAT_MATERIAL_CHUNK_TYPES_H

View File

@@ -0,0 +1,226 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_MATERIAL_ENUM_H
#define TNT_FILAMENT_MATERIAL_ENUM_H
#include <utils/bitset.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 12;
/**
* Supported shading models
*/
enum class Shading : uint8_t {
UNLIT, //!< no lighting applied, emissive possible
LIT, //!< default, standard lighting
SUBSURFACE, //!< subsurface lighting model
CLOTH, //!< cloth lighting model
SPECULAR_GLOSSINESS, //!< legacy lighting model
};
/**
* Attribute interpolation types in the fragment shader
*/
enum class Interpolation : uint8_t {
SMOOTH, //!< default, smooth interpolation
FLAT //!< flat interpolation
};
/**
* Shader quality, affect some global quality parameters
*/
enum class ShaderQuality : int8_t {
DEFAULT = -1, // LOW on mobile, HIGH on desktop
LOW = 0, // enable optimizations that can slightly affect correctness
NORMAL = 1, // normal quality, correctness honored
HIGH = 2 // higher quality (e.g. better upscaling, etc...)
};
/**
* Supported blending modes
*/
enum class BlendingMode : uint8_t {
//! material is opaque
OPAQUE,
//! material is transparent and color is alpha-pre-multiplied, affects diffuse lighting only
TRANSPARENT,
//! material is additive (e.g.: hologram)
ADD,
//! material is masked (i.e. alpha tested)
MASKED,
/**
* material is transparent and color is alpha-pre-multiplied, affects specular lighting
* when adding more entries, change the size of FRenderer::CommandKey::blending
*/
FADE,
//! material darkens what's behind it
MULTIPLY,
//! material brightens what's behind it
SCREEN,
};
/**
* How transparent objects are handled
*/
enum class TransparencyMode : uint8_t {
//! the transparent object is drawn honoring the raster state
DEFAULT,
/**
* the transparent object is first drawn in the depth buffer,
* then in the color buffer, honoring the culling mode, but ignoring the depth test function
*/
TWO_PASSES_ONE_SIDE,
/**
* the transparent object is drawn twice in the color buffer,
* first with back faces only, then with front faces; the culling
* mode is ignored. Can be combined with two-sided lighting
*/
TWO_PASSES_TWO_SIDES
};
/**
* Supported types of vertex domains.
*/
enum class VertexDomain : uint8_t {
OBJECT, //!< vertices are in object space, default
WORLD, //!< vertices are in world space
VIEW, //!< vertices are in view space
DEVICE //!< vertices are in normalized device space
// when adding more entries, make sure to update VERTEX_DOMAIN_COUNT
};
/**
* Vertex attribute types
*/
enum VertexAttribute : uint8_t {
// Update hasIntegerTarget() in VertexBuffer when adding an attribute that will
// be read as integers in the shaders
POSITION = 0, //!< XYZ position (float3)
TANGENTS = 1, //!< tangent, bitangent and normal, encoded as a quaternion (float4)
COLOR = 2, //!< vertex color (float4)
UV0 = 3, //!< texture coordinates (float2)
UV1 = 4, //!< texture coordinates (float2)
BONE_INDICES = 5, //!< indices of 4 bones, as unsigned integers (uvec4)
BONE_WEIGHTS = 6, //!< weights of the 4 bones (normalized float4)
// -- we have 1 unused slot here --
CUSTOM0 = 8,
CUSTOM1 = 9,
CUSTOM2 = 10,
CUSTOM3 = 11,
CUSTOM4 = 12,
CUSTOM5 = 13,
CUSTOM6 = 14,
CUSTOM7 = 15,
// Aliases for vertex morphing.
MORPH_POSITION_0 = CUSTOM0,
MORPH_POSITION_1 = CUSTOM1,
MORPH_POSITION_2 = CUSTOM2,
MORPH_POSITION_3 = CUSTOM3,
MORPH_TANGENTS_0 = CUSTOM4,
MORPH_TANGENTS_1 = CUSTOM5,
MORPH_TANGENTS_2 = CUSTOM6,
MORPH_TANGENTS_3 = CUSTOM7,
// this is limited by driver::MAX_VERTEX_ATTRIBUTE_COUNT
};
static constexpr size_t MAX_MORPH_TARGETS = 4;
static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8;
/**
* Material domains
*/
enum class MaterialDomain : uint8_t {
SURFACE = 0, //!< shaders applied to renderables
POST_PROCESS = 1, //!< shaders applied to rendered buffers
};
/**
* Specular occlusion
*/
enum class SpecularAmbientOcclusion : uint8_t {
NONE = 0, //!< no specular occlusion
SIMPLE = 1, //!< simple specular occlusion
BENT_NORMALS = 2, //!< more accurate specular occlusion, requires bent normals
};
/**
* Refraction
*/
enum class RefractionMode : uint8_t {
NONE = 0, //!< no refraction
CUBEMAP = 1, //!< refracted rays go to the ibl cubemap
SCREEN_SPACE = 2, //!< refracted rays go to screen space
};
/**
* Refraction type
*/
enum class RefractionType : uint8_t {
SOLID = 0, //!< refraction through solid objects (e.g. a sphere)
THIN = 1, //!< refraction through thin objects (e.g. window)
};
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
using AttributeBitset = utils::bitset32;
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 26;
enum class Property : uint8_t {
BASE_COLOR, //!< float4, all shading models
ROUGHNESS, //!< float, lit shading models only
METALLIC, //!< float, all shading models, except unlit and cloth
REFLECTANCE, //!< float, all shading models, except unlit and cloth
AMBIENT_OCCLUSION, //!< float, lit shading models only, except subsurface and cloth
CLEAR_COAT, //!< float, lit shading models only, except subsurface and cloth
CLEAR_COAT_ROUGHNESS, //!< float, lit shading models only, except subsurface and cloth
CLEAR_COAT_NORMAL, //!< float, lit shading models only, except subsurface and cloth
ANISOTROPY, //!< float, lit shading models only, except subsurface and cloth
ANISOTROPY_DIRECTION, //!< float3, lit shading models only, except subsurface and cloth
THICKNESS, //!< float, subsurface shading model only
SUBSURFACE_POWER, //!< float, subsurface shading model only
SUBSURFACE_COLOR, //!< float3, subsurface and cloth shading models only
SHEEN_COLOR, //!< float3, lit shading models only, except subsurface
SHEEN_ROUGHNESS, //!< float3, lit shading models only, except subsurface and cloth
SPECULAR_COLOR, //!< float3, specular-glossiness shading model only
GLOSSINESS, //!< float, specular-glossiness shading model only
EMISSIVE, //!< float4, all shading models
NORMAL, //!< float3, all shading models only, except unlit
POST_LIGHTING_COLOR, //!< float4, all shading models
CLIP_SPACE_TRANSFORM, //!< mat4, vertex shader only
ABSORPTION, //!< float3, how much light is absorbed by the material
TRANSMISSION, //!< float, how much light is refracted through the material
IOR, //!< float, material's index of refraction
MICRO_THICKNESS, //!< float, thickness of the thin layer
BENT_NORMAL, //!< float3, all shading models only, except unlit
// when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT
};
} // namespace filament
#endif

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_MATERIALINSTANCE_H
#define TNT_FILAMENT_MATERIALINSTANCE_H
#include <filament/FilamentAPI.h>
#include <filament/Color.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
namespace filament {
class Material;
class Texture;
class TextureSampler;
class UniformBuffer;
class UniformInterfaceBlock;
class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
public:
using CullingMode = filament::backend::CullingMode;
template<typename T>
using is_supported_parameter_t = typename std::enable_if<
std::is_same<float, T>::value ||
std::is_same<int32_t, T>::value ||
std::is_same<uint32_t, T>::value ||
std::is_same<math::int2, T>::value ||
std::is_same<math::int3, T>::value ||
std::is_same<math::int4, T>::value ||
std::is_same<math::uint2, T>::value ||
std::is_same<math::uint3, T>::value ||
std::is_same<math::uint4, T>::value ||
std::is_same<math::float2, T>::value ||
std::is_same<math::float3, T>::value ||
std::is_same<math::float4, T>::value ||
std::is_same<math::mat4f, T>::value ||
// these types are slower as they need a layout conversion
std::is_same<bool, T>::value ||
std::is_same<math::bool2, T>::value ||
std::is_same<math::bool3, T>::value ||
std::is_same<math::bool4, T>::value ||
std::is_same<math::mat3f, T>::value
>::type;
/**
* Creates a new MaterialInstance using another MaterialInstance as a template for initialization.
* The new MaterialInstance is an instance of the same Material of the template instance and
* must be destroyed just like any other MaterialInstance.
*
* @param other A MaterialInstance to use as a template for initializing a new instance
* @param name A name for the new MaterialInstance or nullptr to use the template's name
* @return A new MaterialInstance
*/
static MaterialInstance* duplicate(MaterialInstance const* other, const char* name = nullptr) noexcept;
/**
* @return the Material associated with this instance
*/
Material const* getMaterial() const noexcept;
/**
* @return the name associated with this instance
*/
const char* getName() const noexcept;
/**
* Set a uniform by name
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param value Value of the parameter to set.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(const char* name, T const& value) noexcept;
/**
* Set a uniform array by name
*
* @param name Name of the parameter array as defined by Material. Cannot be nullptr.
* @param values Array of values to set to the named parameter array.
* @param count Size of the array to set.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(const char* name, const T* values, size_t count) noexcept;
/**
* Set a texture as the named parameter
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param texture Non nullptr Texture object pointer.
* @param sampler Sampler parameters.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name,
Texture const* texture, TextureSampler const& sampler) noexcept;
/**
* Set an RGB color as the named parameter.
* A conversion might occur depending on the specified type
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param type Whether the color value is encoded as Linear or sRGB.
* @param color Array of read, green, blue channels values.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name, RgbType type, math::float3 color) noexcept;
/**
* Set an RGBA color as the named parameter.
* A conversion might occur depending on the specified type
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param type Whether the color value is encoded as Linear or sRGB/A.
* @param color Array of read, green, blue and alpha channels values.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name, RgbaType type, math::float4 color) noexcept;
/**
* Set up a custom scissor rectangle; by default this encompasses the View.
*
* @param left left coordinate of the scissor box
* @param bottom bottom coordinate of the scissor box
* @param width width of the scissor box
* @param height height of the scissor box
*/
void setScissor(uint32_t left, uint32_t bottom, uint32_t width, uint32_t height) noexcept;
/**
* Returns the scissor rectangle to its default setting, which encompasses the View.
*/
void unsetScissor() noexcept;
/**
* Sets a polygon offset that will be applied to all renderables drawn with this material
* instance.
*
* The value of the offset is scale * dz + r * constant, where dz is the change in depth
* relative to the screen area of the triangle, and r is the smallest value that is guaranteed
* to produce a resolvable offset for a given implementation. This offset is added before the
* depth test.
*
* @warning using a polygon offset other than zero has a significant negative performance
* impact, as most implementations have to disable early depth culling. DO NOT USE unless
* absolutely necessary.
*
* @param scale scale factor used to create a variable depth offset for each triangle
* @param constant scale factor used to create a constant depth offset for each triangle
*/
void setPolygonOffset(float scale, float constant) noexcept;
/**
* Overrides the minimum alpha value a fragment must have to not be discarded when the blend
* mode is MASKED. Defaults to 0.4 if it has not been set in the parent Material. The specified
* value should be between 0 and 1 and will be clamped if necessary.
*/
void setMaskThreshold(float threshold) noexcept;
/**
* Sets the screen space variance of the filter kernel used when applying specular
* anti-aliasing. The default value is set to 0.15. The specified value should be between
* 0 and 1 and will be clamped if necessary.
*/
void setSpecularAntiAliasingVariance(float variance) noexcept;
/**
* Sets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0
* and 1 and will be clamped if necessary.
*/
void setSpecularAntiAliasingThreshold(float threshold) noexcept;
/**
* Enables or disables double-sided lighting if the parent Material has double-sided capability,
* otherwise prints a warning. If double-sided lighting is enabled, backface culling is
* automatically disabled.
*/
void setDoubleSided(bool doubleSided) noexcept;
/**
* Overrides the default triangle culling state that was set on the material.
*/
void setCullingMode(CullingMode culling) noexcept;
/**
* Overrides the default color-buffer write state that was set on the material.
*/
void setColorWrite(bool enable) noexcept;
/**
* Overrides the default depth-buffer write state that was set on the material.
*/
void setDepthWrite(bool enable) noexcept;
/**
* Overrides the default depth testing state that was set on the material.
*/
void setDepthCulling(bool enable) noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_MATERIALINSTANCE_H

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_RENDERTARGET_H
#define TNT_FILAMENT_RENDERTARGET_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <stddef.h>
namespace filament {
class FRenderTarget;
class Engine;
class Texture;
/**
* An offscreen render target that can be associated with a View and contains
* weak references to a set of attached Texture objects.
*
* Clients are responsible for the lifetime of all associated Texture attachments.
*
* @see View
*/
class UTILS_PUBLIC RenderTarget : public FilamentAPI {
struct BuilderDetails;
public:
using CubemapFace = backend::TextureCubemapFace;
/** Minimum number of color attachment supported */
static constexpr uint8_t MIN_SUPPORTED_COLOR_ATTACHMENTS_COUNT =
backend::MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT;
/** Maximum number of color attachment supported */
static constexpr uint8_t MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT =
backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
/**
* Attachment identifiers
*/
enum class AttachmentPoint : uint8_t {
COLOR0 = 0, //!< identifies the 1st color attachment
COLOR1 = 1, //!< identifies the 2nd color attachment
COLOR2 = 2, //!< identifies the 3rd color attachment
COLOR3 = 3, //!< identifies the 4th color attachment
COLOR4 = 4, //!< identifies the 5th color attachment
COLOR5 = 5, //!< identifies the 6th color attachment
COLOR6 = 6, //!< identifies the 7th color attachment
COLOR7 = 7, //!< identifies the 8th color attachment
DEPTH = MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT, //!< identifies the depth attachment
COLOR = COLOR0, //!< identifies the 1st color attachment
};
//! Use Builder to construct a RenderTarget object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Sets a texture to a given attachment point.
*
* All RenderTargets must have a non-null COLOR attachment.
*
* @param attachment The attachment point of the texture.
* @param texture The associated texture object.
* @return A reference to this Builder for chaining calls.
*/
Builder& texture(AttachmentPoint attachment, Texture* texture) noexcept;
/**
* Sets the mipmap level for a given attachment point.
*
* @param attachment The attachment point of the texture.
* @param level The associated mipmap level, 0 by default.
* @return A reference to this Builder for chaining calls.
*/
Builder& mipLevel(AttachmentPoint attachment, uint8_t level) noexcept;
/**
* Sets the cubemap face for a given attachment point.
*
* @param attachment The attachment point.
* @param face The associated cubemap face.
* @return A reference to this Builder for chaining calls.
*/
Builder& face(AttachmentPoint attachment, CubemapFace face) noexcept;
/**
* Sets the layer for a given attachment point (for 3D textures).
*
* @param attachment The attachment point.
* @param layer The associated cubemap layer.
* @return A reference to this Builder for chaining calls.
*/
Builder& layer(AttachmentPoint attachment, uint32_t layer) noexcept;
/**
* Creates the RenderTarget object and returns a pointer to it.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*/
RenderTarget* build(Engine& engine);
private:
friend class FRenderTarget;
};
/**
* Gets the texture set on the given attachment point
* @param attachment Attachment point
* @return A Texture object or nullptr if no texture is set for this attachment point
*/
Texture* getTexture(AttachmentPoint attachment) const noexcept;
/**
* Returns the mipmap level set on the given attachment point
* @param attachment Attachment point
* @return the mipmap level set on the given attachment point
*/
uint8_t getMipLevel(AttachmentPoint attachment) const noexcept;
/**
* Returns the face of a cubemap set on the given attachment point
* @param attachment Attachment point
* @return A cubemap face identifier. This is only relevant if the attachment's texture is
* a cubemap.
*/
CubemapFace getFace(AttachmentPoint attachment) const noexcept;
/**
* Returns the texture-layer set on the given attachment point
* @param attachment Attachment point
* @return A texture layer. This is only relevant if the attachment's texture is a 3D texture.
*/
uint32_t getLayer(AttachmentPoint attachment) const noexcept;
/**
* Returns the number of color attachments usable by this instance of Engine. This method is
* guaranteed to return at least MIN_SUPPORTED_COLOR_ATTACHMENTS_COUNT and at most
* MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT.
* @return Number of color attachments usable in a render target.
*/
uint8_t getSupportedColorAttachmentsCount() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_RENDERTARGET_H

View File

@@ -0,0 +1,585 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_RENDERABLECOMPONENTMANAGER_H
#define TNT_FILAMENT_RENDERABLECOMPONENTMANAGER_H
#include <filament/Box.h>
#include <filament/FilamentAPI.h>
#include <filament/MaterialEnums.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/EntityInstance.h>
#include <math/mathfwd.h>
#include <type_traits>
namespace utils {
class Entity;
} // namespace utils
namespace filament {
class BufferObject;
class Engine;
class IndexBuffer;
class Material;
class MaterialInstance;
class Renderer;
class SkinningBuffer;
class VertexBuffer;
class FEngine;
class FRenderPrimitive;
class FRenderableManager;
/**
* Factory and manager for \em renderables, which are entities that can be drawn.
*
* Renderables are bundles of \em primitives, each of which has its own geometry and material. All
* primitives in a particular renderable share a set of rendering attributes, such as whether they
* cast shadows or use vertex skinning.
*
* Usage example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto renderable = utils::EntityManager::get().create();
*
* RenderableManager::Builder(1)
* .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
* .material(0, matInstance)
* .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vertBuffer, indBuffer, 0, 3)
* .receiveShadows(false)
* .build(engine, renderable);
*
* scene->addEntity(renderable);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To modify the state of an existing renderable, clients should first use RenderableManager
* to get a temporary handle called an \em instance. The instance can then be used to get or set
* the renderable's state. Please note that instances are ephemeral; clients should store entities,
* not instances.
*
* - For details about constructing renderables, see RenderableManager::Builder.
* - To associate a 4x4 transform with an entity, see TransformManager.
* - To associate a human-readable label with an entity, see utils::NameComponentManager.
*/
class UTILS_PUBLIC RenderableManager : public FilamentAPI {
struct BuilderDetails;
public:
using Instance = utils::EntityInstance<RenderableManager>;
using PrimitiveType = backend::PrimitiveType;
/**
* Checks if the given entity already has a renderable component.
*/
bool hasComponent(utils::Entity e) const noexcept;
/**
* Gets a temporary handle that can be used to access the renderable state.
*
* @return Non-zero handle if the entity has a renderable component, 0 otherwise.
*/
Instance getInstance(utils::Entity e) const noexcept;
/**
* The transformation associated with a skinning joint.
*
* Clients can specify bones either using this quat-vec3 pair, or by using 4x4 matrices.
*/
struct Bone {
math::quatf unitQuaternion = { 1, 0, 0, 0 };
math::float3 translation = { 0, 0, 0 };
float reserved = 0;
};
/**
* Adds renderable components to entities using a builder pattern.
*/
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
enum Result { Error = -1, Success = 0 };
/**
* Creates a builder for renderable components.
*
* @param count the number of primitives that will be supplied to the builder
*
* Note that builders typically do not have a long lifetime since clients should discard
* them after calling build(). For a usage example, see RenderableManager.
*/
explicit Builder(size_t count) noexcept;
/*! \cond PRIVATE */
Builder(Builder const& rhs) = delete;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder& rhs) = delete;
Builder& operator=(Builder&& rhs) noexcept;
/*! \endcond */
/**
* Specifies the geometry data for a primitive.
*
* Filament primitives must have an associated VertexBuffer and IndexBuffer. Typically, each
* primitive is specified with a pair of daisy-chained calls: \c geometry(...) and \c
* material(...).
*
* @param index zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param type specifies the topology of the primitive (e.g., \c RenderableManager::PrimitiveType::TRIANGLES)
* @param vertices specifies the vertex buffer, which in turn specifies a set of attributes
* @param indices specifies the index buffer (either u16 or u32)
* @param offset specifies where in the index buffer to start reading (expressed as a number of indices)
* @param minIndex specifies the minimum index contained in the index buffer
* @param maxIndex specifies the maximum index contained in the index buffer
* @param count number of indices to read (for triangles, this should be a multiple of 3)
*/
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept;
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t count) noexcept; //!< \overload
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices) noexcept; //!< \overload
/**
* Binds a material instance to the specified primitive.
*
* If no material is specified for a given primitive, Filament will fall back to a basic default material.
*
* @param index zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param materialInstance the material to bind
*/
Builder& material(size_t index, MaterialInstance const* materialInstance) noexcept;
/**
* The axis-aligned bounding box of the renderable.
*
* This is an object-space AABB used for frustum culling. For skinning and morphing, this
* should encompass all possible vertex positions. It is mandatory unless culling is
* disabled for the renderable.
*
* \see computeAABB()
*/
Builder& boundingBox(const Box& axisAlignedBoundingBox) noexcept;
/**
* Sets bits in a visibility mask. By default, this is 0x1.
*
* This feature provides a simple mechanism for hiding and showing groups of renderables
* in a Scene. See View::setVisibleLayers().
*
* For example, to set bit 1 and reset bits 0 and 2 while leaving all other bits unaffected,
* do: `builder.layerMask(7, 2)`.
*
* To change this at run time, see RenderableManager::setLayerMask.
*
* @param select the set of bits to affect
* @param values the replacement values for the affected bits
*/
Builder& layerMask(uint8_t select, uint8_t values) noexcept;
/**
* Provides coarse-grained control over draw order.
*
* In general Filament reserves the right to re-order renderables to allow for efficient
* rendering. However clients can control ordering at a coarse level using \em priority.
*
* For example, this could be used to draw a semitransparent HUD, if a client wishes to
* avoid using a separate View for the HUD. Note that priority is completely orthogonal to
* Builder::layerMask, which merely controls visibility.
*
* \see Builder::blendOrder()
*
* The priority is clamped to the range [0..7], defaults to 4; 7 is lowest priority
* (rendered last).
*/
Builder& priority(uint8_t priority) noexcept;
/**
* Controls frustum culling, true by default.
*
* \note Do not confuse frustum culling with backface culling. The latter is controlled via
* the material.
*/
Builder& culling(bool enable) noexcept;
/**
* Enables or disables a light channel. Light channel 0 is enabled by default.
*
* @param channel Light channel to enable or disable, between 0 and 7.
* @param enable Whether to enable or disable the light channel.
*/
Builder& lightChannel(unsigned int channel, bool enable = true) noexcept;
/**
* Controls if this renderable casts shadows, false by default.
*
* If the View's shadow type is set to ShadowType::VSM, castShadows should only be disabled
* if either is true:
* - receiveShadows is also disabled
* - the object is guaranteed to not cast shadows on itself or other objects (for example,
* a ground plane)
*/
Builder& castShadows(bool enable) noexcept;
/**
* Controls if this renderable receives shadows, true by default.
*/
Builder& receiveShadows(bool enable) noexcept;
/**
* Controls if this renderable uses screen-space contact shadows. This is more
* expensive but can improve the quality of shadows, especially in large scenes.
* (off by default).
*/
Builder& screenSpaceContactShadows(bool enable) noexcept;
/**
* Allows bones to be swapped out and shared using SkinningBuffer.
*
* If skinning buffer mode is enabled, clients must call setSkinningBuffer() rather than
* setBones(). This allows sharing of data between renderables.
*
* @param enabled If true, enables buffer object mode. False by default.
*/
Builder& enableSkinningBuffers(bool enabled = true) noexcept;
/**
* Enables GPU vertex skinning for up to 255 bones, 0 by default.
*
* Skinning Buffer mode must be enabled.
*
* Each vertex can be affected by up to 4 bones simultaneously. The attached
* VertexBuffer must provide data in the \c BONE_INDICES slot (uvec4) and the
* \c BONE_WEIGHTS slot (float4).
*
* See also RenderableManager::setSkinningBuffer() or SkinningBuffer::setBones(),
* which can be called on a per-frame basis to advance the animation.
*
* @param skinningBuffer nullptr to disable, otherwise the SkinningBuffer to use
* @param count 0 to disable, otherwise the number of bone transforms (up to 255)
* @param offset offset in the SkinningBuffer
*/
Builder& skinning(SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept;
/**
* Enables GPU vertex skinning for up to 255 bones, 0 by default.
*
* Skinning Buffer mode must be disabled.
*
* Each vertex can be affected by up to 4 bones simultaneously. The attached
* VertexBuffer must provide data in the \c BONE_INDICES slot (uvec4) and the
* \c BONE_WEIGHTS slot (float4).
*
* See also RenderableManager::setBones(), which can be called on a per-frame basis
* to advance the animation.
*
* @param boneCount 0 to disable, otherwise the number of bone transforms (up to 255)
* @param transforms the initial set of transforms (one for each bone)
*/
Builder& skinning(size_t boneCount, math::mat4f const* transforms) noexcept;
Builder& skinning(size_t boneCount, Bone const* bones) noexcept; //!< \overload
Builder& skinning(size_t boneCount) noexcept; //!< \overload
/**
* Controls if the renderable has vertex morphing targets, false by default.
*
* This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer
* must provide data in the appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc).
*
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
Builder& morphing(bool enable) noexcept;
/**
* Sets an ordering index for blended primitives that all live at the same Z value.
*
* @param primitiveIndex the primitive of interest
* @param order draw order number (0 by default). Only the lowest 15 bits are used.
*/
Builder& blendOrder(size_t primitiveIndex, uint16_t order) noexcept;
/**
* Adds the Renderable component to an entity.
*
* @param engine Reference to the filament::Engine to associate this Renderable with.
* @param entity Entity to add the Renderable component to.
* @return Success if the component was created successfully, Error otherwise.
*
* If exceptions are disabled and an error occurs, this function is a no-op.
* Success can be checked by looking at the return value.
*
* If this component already exists on the given entity and the construction is successful,
* it is first destroyed as if destroy(utils::Entity e) was called. In case of error,
* the existing component is unmodified.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
Result build(Engine& engine, utils::Entity entity);
private:
friend class FEngine;
friend class FRenderPrimitive;
friend class FRenderableManager;
struct Entry {
VertexBuffer* vertices = nullptr;
IndexBuffer* indices = nullptr;
size_t offset = 0;
size_t minIndex = 0;
size_t maxIndex = 0;
size_t count = 0;
MaterialInstance const* materialInstance = nullptr;
PrimitiveType type = PrimitiveType::TRIANGLES;
uint16_t blendOrder = 0;
};
};
/**
* Destroys the renderable component in the given entity.
*/
void destroy(utils::Entity e) noexcept;
/**
* Changes the bounding box used for frustum culling.
*
* \see Builder::boundingBox()
* \see RenderableManager::getAxisAlignedBoundingBox()
*/
void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept;
/**
* Changes the visibility bits.
*
* \see Builder::layerMask()
* \see View::setVisibleLayers().
* \see RenderableManager::getLayerMask()
*/
void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept;
/**
* Changes the coarse-level draw ordering.
*
* \see Builder::priority().
*/
void setPriority(Instance instance, uint8_t priority) noexcept;
/**
* Changes whether or not frustum culling is on.
*
* \see Builder::culling()
*/
void setCulling(Instance instance, bool enable) noexcept;
/**
* Enables or disables a light channel.
* Light channel 0 is enabled by default.
*
* \see Builder::lightChannel()
*/
void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept;
/**
* Returns whether a light channel is enabled on a specified renderable.
* @param instance Instance of the component obtained from getInstance().
* @param channel Light channel to query
* @return true if the light channel is enabled, false otherwise
*/
bool getLightChannel(Instance instance, unsigned int channel) const noexcept;
/**
* Changes whether or not the renderable casts shadows.
*
* \see Builder::castShadows()
*/
void setCastShadows(Instance instance, bool enable) noexcept;
/**
* Changes whether or not the renderable can receive shadows.
*
* \see Builder::receiveShadows()
*/
void setReceiveShadows(Instance instance, bool enable) noexcept;
/**
* Changes whether or not the renderable can use screen-space contact shadows.
*
* \see Builder::screenSpaceContactShadows()
*/
void setScreenSpaceContactShadows(Instance instance, bool enable) noexcept;
/**
* Checks if the renderable can cast shadows.
*
* \see Builder::castShadows().
*/
bool isShadowCaster(Instance instance) const noexcept;
/**
* Checks if the renderable can receive shadows.
*
* \see Builder::receiveShadows().
*/
bool isShadowReceiver(Instance instance) const noexcept;
/**
* Updates the bone transforms in the range [offset, offset + boneCount).
* The bones must be pre-allocated using Builder::skinning().
*/
void setBones(Instance instance, Bone const* transforms, size_t boneCount = 1, size_t offset = 0) noexcept;
void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0) noexcept; //!< \overload
/**
* Associates a SkinningBuffer to a renderable instance
*/
void setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer,
size_t count, size_t offset) noexcept;
/**
* Updates the vertex morphing weights on a renderable, all zeroes by default.
*
* This is specified using a 4-tuple, one float per morph target. If the renderable has fewer
* than 4 morph targets, then clients should fill the unused components with zeroes.
*
* The renderable must be built with morphing enabled, see Builder::morphing().
*/
void setMorphWeights(Instance instance, math::float4 const& weights) noexcept;
/**
* Gets the bounding box used for frustum culling.
*
* \see Builder::boundingBox()
* \see RenderableManager::setAxisAlignedBoundingBox()
*/
const Box& getAxisAlignedBoundingBox(Instance instance) const noexcept;
/**
* Get the visibility bits.
*
* \see Builder::layerMask()
* \see View::setVisibleLayers().
* \see RenderableManager::getLayerMask()
*/
uint8_t getLayerMask(Instance instance) const noexcept;
/**
* Gets the immutable number of primitives in the given renderable.
*/
size_t getPrimitiveCount(Instance instance) const noexcept;
/**
* Changes the material instance binding for the given primitive.
*
* \see Builder::material()
*/
void setMaterialInstanceAt(Instance instance,
size_t primitiveIndex, MaterialInstance const* materialInstance) noexcept;
/**
* Retrieves the material instance that is bound to the given primitive.
*/
MaterialInstance* getMaterialInstanceAt(Instance instance, size_t primitiveIndex) const noexcept;
/**
* Changes the geometry for the given primitive.
*
* \see Builder::geometry()
*/
void setGeometryAt(Instance instance, size_t primitiveIndex,
PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices,
size_t offset, size_t count) noexcept;
/**
* Changes the active range of indices or topology for the given primitive.
*
* \see Builder::geometry()
*/
void setGeometryAt(Instance instance, size_t primitiveIndex,
PrimitiveType type, size_t offset, size_t count) noexcept;
/**
* Changes the ordering index for blended primitives that all live at the same Z value.
*
* \see Builder::blendOrder()
*
* @param instance the renderable of interest
* @param primitiveIndex the primitive of interest
* @param order draw order number (0 by default). Only the lowest 15 bits are used.
*/
void setBlendOrderAt(Instance instance, size_t primitiveIndex, uint16_t order) noexcept;
/**
* Retrieves the set of enabled attribute slots in the given primitive's VertexBuffer.
*/
AttributeBitset getEnabledAttributesAt(Instance instance, size_t primitiveIndex) const noexcept;
/*! \cond PRIVATE */
template<typename T>
struct is_supported_vector_type {
using type = typename std::enable_if<
std::is_same<math::float4, T>::value ||
std::is_same<math::half4, T>::value ||
std::is_same<math::float3, T>::value ||
std::is_same<math::half3, T>::value
>::type;
};
template<typename T>
struct is_supported_index_type {
using type = typename std::enable_if<
std::is_same<uint16_t, T>::value ||
std::is_same<uint32_t, T>::value
>::type;
};
/*! \endcond */
/**
* Utility method that computes the axis-aligned bounding box from a set of vertices.
*
* - The index type must be \c uint16_t or \c uint32_t.
* - The vertex type must be \c float4, \c half4, \c float3, or \c half3.
* - For 4-component vertices, the w component is ignored (implicitly replaced with 1.0).
*/
template<typename VECTOR, typename INDEX,
typename = typename is_supported_vector_type<VECTOR>::type,
typename = typename is_supported_index_type<INDEX>::type>
static Box computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
size_t stride = sizeof(VECTOR)) noexcept;
};
template<typename VECTOR, typename INDEX, typename, typename>
Box RenderableManager::computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
size_t stride) noexcept {
math::float3 bmin(std::numeric_limits<float>::max());
math::float3 bmax(std::numeric_limits<float>::lowest());
for (size_t i = 0; i < count; ++i) {
VECTOR const* p = reinterpret_cast<VECTOR const*>(
(char const*)vertices + indices[i] * stride);
const math::float3 v(p->x, p->y, p->z);
bmin = min(bmin, v);
bmax = max(bmax, v);
}
return Box().set(bmin, bmax);
}
} // namespace filament
#endif // TNT_FILAMENT_RENDERABLECOMPONENTMANAGER_H

View File

@@ -0,0 +1,540 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_RENDERER_H
#define TNT_FILAMENT_RENDERER_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <backend/PresentCallable.h>
#include <backend/DriverEnums.h>
#include <math/vec4.h>
#include <stdint.h>
namespace filament {
class Engine;
class RenderTarget;
class SwapChain;
class View;
class Viewport;
namespace backend {
class PixelBufferDescriptor;
} // namespace backend
/**
* A Renderer instance represents an operating system's window.
*
* Typically, applications create a Renderer per window. The Renderer generates drawing commands
* for the render thread and manages frame latency.
*
* A Renderer generates drawing commands from a View, itself containing a Scene description.
*
* Creation and Destruction
* ========================
*
* A Renderer is created using Engine.createRenderer() and destroyed using
* Engine.destroy(const Renderer*).
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Renderer.h>
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
*
* Renderer* renderer = engine->createRenderer();
* engine->destroy(&renderer);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see Engine, View
*/
class UTILS_PUBLIC Renderer : public FilamentAPI {
public:
/**
* Use DisplayInfo to set important Display properties. This is used to achieve correct
* frame pacing and dynamic resolution scaling.
*/
struct DisplayInfo {
// refresh-rate of the display in Hz. set to 0 for offscreen or turn off frame-pacing.
float refreshRate = 60.0f;
// how far in advance a buffer must be queued for presentation at a given time in ns
uint64_t presentationDeadlineNanos = 0;
// offset by which vsyncSteadyClockTimeNano provided in beginFrame() is offset in ns
uint64_t vsyncOffsetNanos = 0;
};
/**
* Use FrameRateOptions to set the desired frame rate and control how quickly the system
* reacts to GPU load changes.
*
* interval: desired frame interval in multiple of the refresh period, set in DisplayInfo
* (as 1 / DisplayInfo::refreshRate)
*
* The parameters below are relevant when some Views are using dynamic resolution scaling:
*
* headRoomRatio: additional headroom for the GPU as a ratio of the targetFrameTime.
* Useful for taking into account constant costs like post-processing or
* GPU drivers on different platforms.
* history: History size. higher values, tend to filter more (clamped to 30)
* scaleRate: rate at which the gpu load is adjusted to reach the target frame rate
* This value can be computed as 1 / N, where N is the number of frames
* needed to reach 64% of the target scale factor.
* Higher values make the dynamic resolution react faster.
*
* @see View::DynamicResolutionOptions
* @see Renderer::DisplayInfo
*
*/
struct FrameRateOptions {
float headRoomRatio = 0.0f; //!< additional headroom for the GPU
float scaleRate = 0.125f; //!< rate at which the system reacts to load changes
uint8_t history = 3; //!< history size
uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate
};
/**
* ClearOptions are used at the beginning of a frame to clear or retain the SwapChain content.
*/
struct ClearOptions {
/** Color to use to clear the SwapChain */
math::float4 clearColor = {};
/**
* Whether the SwapChain should be cleared using the clearColor. Use this if translucent
* View will be drawn, for instance.
*/
bool clear = false;
/**
* Whether the SwapChain content should be discarded. clear implies discard. Set this
* to false (along with clear to false as well) if the SwapChain already has content that
* needs to be preserved
*/
bool discard = true;
};
/**
* Information about the display this Renderer is associated to. This information is needed
* to accurately compute dynamic-resolution scaling and for frame-pacing.
*/
void setDisplayInfo(const DisplayInfo& info) noexcept;
/**
* Set options controlling the desired frame-rate.
*/
void setFrameRateOptions(FrameRateOptions const& options) noexcept;
/**
* Set ClearOptions which are used at the beginning of a frame to clear or retain the
* SwapChain content.
*/
void setClearOptions(const ClearOptions& options);
/**
* Get the Engine that created this Renderer.
*
* @return A pointer to the Engine instance this Renderer is associated to.
*/
Engine* getEngine() noexcept;
/**
* Get the Engine that created this Renderer.
*
* @return A constant pointer to the Engine instance this Renderer is associated to.
*/
inline Engine const* getEngine() const noexcept {
return const_cast<Renderer *>(this)->getEngine();
}
/**
* Flags used to configure the behavior of copyFrame().
*
* @see
* copyFrame()
*/
using CopyFrameFlag = uint32_t;
/**
* Indicates that the dstSwapChain passed into copyFrame() should be
* committed after the frame has been copied.
*
* @see
* copyFrame()
*/
static constexpr CopyFrameFlag COMMIT = 0x1;
/**
* Indicates that the presentation time should be set on the dstSwapChain
* passed into copyFrame to the monotonic clock time when the frame is
* copied.
*
* @see
* copyFrame()
*/
static constexpr CopyFrameFlag SET_PRESENTATION_TIME = 0x2;
/**
* Indicates that the dstSwapChain passed into copyFrame() should be
* cleared to black before the frame is copied into the specified viewport.
*
* @see
* copyFrame()
*/
static constexpr CopyFrameFlag CLEAR = 0x4;
/**
* Set-up a frame for this Renderer.
*
* beginFrame() manages frame pacing, and returns whether or not a frame should be drawn. The
* goal of this is to skip frames when the GPU falls behind in order to keep the frame
* latency low.
*
* If a given frame takes too much time in the GPU, the CPU will get ahead of the GPU. The
* display will draw the same frame twice producing a stutter. At this point, the CPU is
* ahead of the GPU and depending on how many frames are buffered, latency increases.
*
* beginFrame() attempts to detect this situation and returns false in that case, indicating
* to the caller to skip the current frame.
*
* When beginFrame() returns true, it is mandatory to render the frame and call endFrame().
* However, when beginFrame() returns false, the caller has the choice to either skip the
* frame and not call endFrame(), or proceed as though true was returned.
*
* @param vsyncSteadyClockTimeNano The time in nanosecond of when the current frame started,
* or 0 if unknown. This value should be the timestamp of
* the last h/w vsync. It is expressed in the
* std::chrono::steady_clock time base.
* @param swapChain A pointer to the SwapChain instance to use.
*
* @return
* *false* the current frame should be skipped,
* *true* the current frame must be drawn and endFrame() must be called.
*
* @remark
* When skipping a frame, the whole frame is canceled, and endFrame() must not be called.
*
* @note
* All calls to render() must happen *after* beginFrame().
*
* @see
* endFrame()
*/
bool beginFrame(SwapChain* swapChain,
uint64_t vsyncSteadyClockTimeNano = 0u);
/**
* Render a View into this renderer's window.
*
* This is filament main rendering method, most of the CPU-side heavy lifting is performed
* here. render() main function is to generate render commands which are asynchronously
* executed by the Engine's render thread.
*
* render() generates commands for each of the following stages:
*
* 1. Shadow map pass, if needed (currently only a single shadow map is supported).
* 2. Depth pre-pass.
* 3. Color pass.
* 4. Post-processing pass.
*
* A typical render loop looks like this:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Renderer.h>
* #include <filament/View.h>
* using namespace filament;
*
* void renderLoop(Renderer* renderer, SwapChain* swapChain) {
* do {
* // typically we wait for VSYNC and user input events
* if (renderer->beginFrame(swapChain)) {
* renderer->render(mView);
* renderer->endFrame();
* }
* } while (!quit());
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*
* @param view A pointer to the view to render.
*
* @attention
* render() must be called *after* beginFrame() and *before* endFrame().
*
* @note
* render() must be called from the Engine's main thread (or external synchronization
* must be provided). In particular, calls to render() on different Renderer instances
* **must** be synchronized.
*
* @remark
* render() perform potentially heavy computations and cannot be multi-threaded. However,
* internally, render() is highly multi-threaded to both improve performance in mitigate
* the call's latency.
*
* @remark
* render() is typically called once per frame (but not necessarily).
*
* @see
* beginFrame(), endFrame(), View
*
*/
void render(View const* view);
/**
* Copy the currently rendered view to the indicated swap chain, using the
* indicated source and destination rectangle.
*
* @param dstSwapChain The swap chain into which the frame should be copied.
* @param dstViewport The destination rectangle in which to draw the view.
* @param srcViewport The source rectangle to be copied.
* @param flags One or more CopyFrameFlag behavior configuration flags.
*
* @remark
* copyFrame() should be called after a frame is rendered using render()
* but before endFrame() is called.
*/
void copyFrame(SwapChain* dstSwapChain, Viewport const& dstViewport,
Viewport const& srcViewport, uint32_t flags = 0);
/**
* Reads back the content of the SwapChain associated with this Renderer.
*
* @param xoffset Left offset of the sub-region to read back.
* @param yoffset Bottom offset of the sub-region to read back.
* @param width Width of the sub-region to read back.
* @param height Height of the sub-region to read back.
* @param buffer Client-side buffer where the read-back will be written.
*
* The following formats are always supported:
* - PixelBufferDescriptor::PixelDataFormat::RGBA
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
*
* The following types are always supported:
* - PixelBufferDescriptor::PixelDataType::UBYTE
* - PixelBufferDescriptor::PixelDataType::UINT
* - PixelBufferDescriptor::PixelDataType::INT
* - PixelBufferDescriptor::PixelDataType::FLOAT
*
* Other combinations of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.
*
*
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
* screen
*
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
*
* Typically readPixels() will be called after render() and before endFrame().
*
* After issuing this method, the callback associated with `buffer` will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen
* after multiple calls to beginFrame(), render(), endFrame().
*
* It is also possible to use a Fence to wait for the read-back.
*
* @remark
* readPixels() is intended for debugging and testing. It will impact performance significantly.
*
*/
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
/**
* Finishes the current frame and schedules it for display.
*
* endFrame() schedules the current frame to be displayed on the Renderer's window.
*
* @note
* All calls to render() must happen *before* endFrame(). endFrame() must be called if
* beginFrame() returned true, otherwise, endFrame() must not be called unless the caller
* ignored beginFrame()'s return value.
*
* @see
* beginFrame()
*/
void endFrame();
/**
* Reads back the content of the provided RenderTarget.
*
* @param renderTarget RenderTarget to read back from.
* @param xoffset Left offset of the sub-region to read back.
* @param yoffset Bottom offset of the sub-region to read back.
* @param width Width of the sub-region to read back.
* @param height Height of the sub-region to read back.
* @param buffer Client-side buffer where the read-back will be written.
*
* The following formats are always supported:
* - PixelBufferDescriptor::PixelDataFormat::RGBA
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
*
* The following types are always supported:
* - PixelBufferDescriptor::PixelDataType::UBYTE
* - PixelBufferDescriptor::PixelDataType::UINT
* - PixelBufferDescriptor::PixelDataType::INT
* - PixelBufferDescriptor::PixelDataType::FLOAT
*
* Other combinations of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.
*
*
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
* screen
*
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
*
* Typically readPixels() will be called after render() and before endFrame().
*
* After issuing this method, the callback associated with `buffer` will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen
* after multiple calls to beginFrame(), render(), endFrame().
*
* It is also possible to use a Fence to wait for the read-back.
*
* @remark
* readPixels() is intended for debugging and testing. It will impact performance significantly.
*
*/
void readPixels(RenderTarget* renderTarget,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
/**
* Render a standalone View into its associated RenderTarget
*
* This call is mostly equivalent to calling render(View*) inside a
* beginFrame / endFrame block, but incurs less overhead. It can be used
* as a poor man's compute API.
*
* @param view A pointer to the view to render. This View must have a RenderTarget associated
* to it.
*
* @attention
* renderStandaloneView() must be called outside of beginFrame() / endFrame().
*
* @note
* renderStandaloneView() must be called from the Engine's main thread
* (or external synchronization must be provided). In particular, calls to
* renderStandaloneView() on different Renderer instances **must** be synchronized.
*
* @remark
* renderStandaloneView() perform potentially heavy computations and cannot be multi-threaded.
* However, internally, renderStandaloneView() is highly multi-threaded to both improve
* performance in mitigate the call's latency.
*/
void renderStandaloneView(View const* view);
/**
* Returns the time in second of the last call to beginFrame(). This value is constant for all
* views rendered during a frame. The epoch is set with resetUserTime().
*
* In materials, this value can be queried using `vec4 getUserTime()`. The value returned
* is a highp vec4 encoded as follows:
*
* time.x = (float)Renderer.getUserTime();
* time.y = Renderer.getUserTime() - time.x;
*
* It follows that the following invariants are true:
*
* (double)time.x + (double)time.y == Renderer.getUserTime()
* time.x == (float)Renderer.getUserTime()
*
* This encoding allows the shader code to perform high precision (i.e. double) time
* calculations when needed despite the lack of double precision in the shader, for e.g.:
*
* To compute (double)time * vertex in the material, use the following construct:
*
* vec3 result = time.x * vertex + time.y * vertex;
*
*
* Most of the time, high precision computations are not required, but be aware that the
* precision of time.x rapidly diminishes as time passes:
*
* time | precision
* --------+----------
* 16.7s | us
* 4h39 | ms
* 77h | 1/60s
*
*
* In other words, it only possible to get microsecond accuracy for about 16s or millisecond
* accuracy for just under 5h.
*
* This problem can be mitigated by calling resetUserTime(), or using high precision time as
* described above.
*
* @return The time is seconds since resetUserTime() was last called.
*
* @see
* resetUserTime()
*/
double getUserTime() const;
/**
* Sets the user time epoch to now, i.e. resets the user time to zero.
*
* Use this method used to keep the precision of time high in materials, in practice it should
* be called at least when the application is paused, e.g. Activity.onPause() in Android.
*
* @see
* getUserTime()
*/
void resetUserTime();
};
} // namespace filament
#endif // TNT_FILAMENT_RENDERER_H

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_SCENE_H
#define TNT_FILAMENT_SCENE_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
namespace utils {
class Entity;
} // namespace utils
namespace filament {
class IndirectLight;
class Skybox;
/**
* A Scene is a flat container of Renderable and Light instances.
*
* A Scene doesn't provide a hierarchy of Renderable objects, i.e.: it's not a scene-graph.
* However, it manages the list of objects to render and the list of lights. Renderable
* and Light objects can be added or removed from a Scene at any time.
*
* A Renderable *must* be added to a Scene in order to be rendered, and the Scene must be
* provided to a View.
*
*
* Creation and Destruction
* ========================
*
* A Scene is created using Engine.createScene() and destroyed using
* Engine.destroy(const Scene*).
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Scene.h>
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
*
* Scene* scene = engine->createScene();
* engine->destroy(&scene);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see View, Renderable, Light
*/
class UTILS_PUBLIC Scene : public FilamentAPI {
public:
/**
* Sets the Skybox.
*
* The Skybox is drawn last and covers all pixels not touched by geometry.
*
* @param skybox The Skybox to use to fill untouched pixels, or nullptr to unset the Skybox.
*/
void setSkybox(Skybox* skybox) noexcept;
/**
* Returns the Skybox associated with the Scene.
*
* @return The associated Skybox, or nullptr if there is none.
*/
Skybox* getSkybox() noexcept;
/**
* Returns an immutable Skybox associated with the Scene.
*
* @return The associated Skybox, or nullptr if there is none.
*/
Skybox const* getSkybox() const noexcept;
/**
* Set the IndirectLight to use when rendering the Scene.
*
* Currently, a Scene may only have a single IndirectLight. This call replaces the current
* IndirectLight.
*
* @param ibl The IndirectLight to use when rendering the Scene or nullptr to unset.
*/
void setIndirectLight(IndirectLight const* ibl) noexcept;
/**
* Adds an Entity to the Scene.
*
* @param entity The entity is ignored if it doesn't have a Renderable or Light component.
*
* \attention
* A given Entity object can only be added once to a Scene.
*
*/
void addEntity(utils::Entity entity);
/**
* Adds a list of entities to the Scene.
*
* @param entities Array containing entities to add to the scene.
* @param count Size of the entity array.
*/
void addEntities(const utils::Entity* entities, size_t count);
/**
* Removes the Renderable from the Scene.
*
* @param entity The Entity to remove from the Scene. If the specified
* \p entity doesn't exist, this call is ignored.
*/
void remove(utils::Entity entity);
/**
* Removes a list of entities to the Scene.
*
* This is equivalent to calling remove in a loop.
* If any of the specified entities do not exist in the scene, they are skipped.
*
* @param entities Array containing entities to remove from the scene.
* @param count Size of the entity array.
*/
void removeEntities(const utils::Entity* entities, size_t count);
/**
* Returns the number of Renderable objects in the Scene.
*
* @return number of Renderable objects in the Scene.
*/
size_t getRenderableCount() const noexcept;
/**
* Returns the total number of Light objects in the Scene.
*
* @return The total number of Light objects in the Scene.
*/
size_t getLightCount() const noexcept;
/**
* Returns true if the given entity is in the Scene.
*
* @return Whether the given entity is in the Scene.
*/
bool hasEntity(utils::Entity entity) const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_SCENE_H

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_SKINNINGBUFFER_H
#define TNT_FILAMENT_SKINNINGBUFFER_H
#include <filament/FilamentAPI.h>
#include <filament/RenderableManager.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
#include <stddef.h>
namespace filament {
/**
* SkinningBuffer is used to hold skinning data (bones). It is a simple wraper around
* a structured UBO.
*/
class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Size of the skinning buffer in bones.
*
* Due to limitation in the GLSL, the SkinningBuffer must always by a multiple of
* 256, this adjustment is done automatically, but can cause
* some memory overhead. This memory overhead can be mitigated by using the same
* SkinningBuffer to store the bone information for multiple RenderPrimitives.
*
* @param boneCount Number of bones the skinning buffer can hold.
* @return A reference to this Builder for chaining calls.
*/
Builder& boneCount(uint32_t boneCount) noexcept;
/**
* The new buffer is created with identity bones
* @param initialize true to initializing the buffer, false to not.
* @return A reference to this Builder for chaining calls.
*/
Builder& initialize(bool initialize = true) noexcept;
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this SkinningBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*
* @see SkinningBuffer::setBones
*/
SkinningBuffer* build(Engine& engine);
private:
friend class FSkinningBuffer;
};
/**
* Updates the bone transforms in the range [offset, offset + count).
* @param engine Reference to the filament::Engine to associate this SkinningBuffer with.
* @param transforms pointer to at least count Bone
* @param count number of Bone elements in transforms
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
*/
void setBones(Engine& engine, RenderableManager::Bone const* transforms,
size_t count, size_t offset = 0);
/**
* Updates the bone transforms in the range [offset, offset + count).
* @param engine Reference to the filament::Engine to associate this SkinningBuffer with.
* @param transforms pointer to at least count mat4f
* @param count number of mat4f elements in transforms
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
*/
void setBones(Engine& engine, math::mat4f const* transforms,
size_t count, size_t offset = 0);
/**
* Returns the size of this SkinningBuffer in elements.
* @return The number of bones the SkinningBuffer holds.
*/
size_t getBoneCount() const noexcept;
};
} // namespace filament
#endif //TNT_FILAMENT_SKINNINGBUFFER_H

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_SKYBOX_H
#define TNT_FILAMENT_SKYBOX_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <stdint.h>
#include <math/mathfwd.h>
namespace filament {
class FSkybox;
class Engine;
class Texture;
/**
* Skybox
*
* When added to a Scene, the Skybox fills all untouched pixels.
*
* Creation and destruction
* ========================
*
* A Skybox object is created using the Skybox::Builder and destroyed by calling
* Engine::destroy(const Skybox*).
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* filament::IndirectLight* skybox = filament::Skybox::Builder()
* .environment(cubemap)
* .build(*engine);
*
* engine->destroy(skybox);
* ~~~~~~~~~~~
*
*
* @note
* Currently only Texture based sky boxes are supported.
*
* @see Scene, IndirectLight
*/
class UTILS_PUBLIC Skybox : public FilamentAPI {
struct BuilderDetails;
public:
//! Use Builder to construct an Skybox object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Set the environment map (i.e. the skybox content).
*
* The Skybox is rendered as though it were an infinitely large cube with the camera
* inside it. This means that the cubemap which is mapped onto the cube's exterior
* will appear mirrored. This follows the OpenGL conventions.
*
* The cmgen tool generates reflection maps by default which are therefore ideal to use
* as skyboxes.
*
* @param cubemap This Texture must be a cube map.
*
* @return This Builder, for chaining calls.
*
* @see Texture
*/
Builder& environment(Texture* cubemap) noexcept;
/**
* Indicates whether the sun should be rendered. The sun can only be
* rendered if there is at least one light of type SUN in the scene.
* The default value is false.
*
* @param show True if the sun should be rendered, false otherwise
*
* @return This Builder, for chaining calls.
*/
Builder& showSun(bool show) noexcept;
/**
* Skybox intensity when no IndirectLight is set on the Scene.
*
* This call is ignored when an IndirectLight is set on the Scene, and the intensity
* of the IndirectLight is used instead.
*
* @param envIntensity Scale factor applied to the skybox texel values such that
* the result is in lux, or lumen/m^2 (default = 30000)
*
* @return This Builder, for chaining calls.
*
* @see IndirectLight::Builder::intensity
*/
Builder& intensity(float envIntensity) noexcept;
/**
* Sets the skybox to a constant color. Default is opaque black.
*
* Ignored if an environment is set.
*
* @param color
*
* @return This Builder, for chaining calls.
*/
Builder& color(math::float4 color) noexcept;
/**
* Creates the Skybox object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this Skybox with.
*
* @return pointer to the newly created object, or nullptr if the light couldn't be created.
*/
Skybox* build(Engine& engine);
private:
friend class FSkybox;
};
void setColor(math::float4 color) noexcept;
/**
* Sets bits in a visibility mask. By default, this is 0x1.
*
* This provides a simple mechanism for hiding or showing this Skybox in a Scene.
*
* @see View::setVisibleLayers().
*
* For example, to set bit 1 and reset bits 0 and 2 while leaving all other bits unaffected,
* call: `setLayerMask(7, 2)`.
*
* @param select the set of bits to affect
* @param values the replacement values for the affected bits
*/
void setLayerMask(uint8_t select, uint8_t values) noexcept;
/**
* @return the visibility mask bits
*/
uint8_t getLayerMask() const noexcept;
/**
* Returns the skybox's intensity in lux, or lumen/m^2.
*/
float getIntensity() const noexcept;
/**
* @return the associated texture, or null if it does not exist
*/
Texture const* getTexture() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_SKYBOX_H

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_STREAM_H
#define TNT_FILAMENT_STREAM_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <utils/compiler.h>
namespace filament {
class FStream;
class Engine;
/**
* Stream is used to attach a video stream to a Filament `Texture`.
*
* Note that the `Stream` class is fairly Android centric. It supports three different
* configurations:
*
* - TEXTURE_ID...takes an OpenGL texture ID and incurs a copy
* - ACQUIRED.....connects to an Android AHardwareBuffer
* - NATIVE.......connects to an Android SurfaceTexture
*
* Before explaining these different configurations, let's review the high-level structure of an AR
* or video application that uses Filament:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* while (true) {
*
* // Misc application work occurs here, such as:
* // - Writing the image data for a video frame into a Stream
* // - Moving the Filament Camera
*
* if (renderer->beginFrame(swapChain)) {
* renderer->render(view);
* renderer->endFrame();
* }
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Let's say that the video image data at the time of a particular invocation of `beginFrame`
* becomes visible to users at time A. The 3D scene state (including the camera) at the time of
* that same invocation becomes apparent to users at time B.
*
* - If time A matches time B, we say that the stream is \em{synchronized}.
* - Filament invokes low-level graphics commands on the \em{driver thread}.
* - The thread that calls `beginFrame` is called the \em{main thread}.
*
* The TEXTURE_ID configuration achieves synchronization automatically. In this mode, Filament
* performs a copy on the main thread during `beginFrame` by blitting the external image into
* an internal round-robin queue of images. This copy has a run-time cost.
*
* For ACQUIRED streams, there is no need to perform the copy because Filament explictly acquires
* the stream, then releases it later via a callback function. This configuration is especially
* useful when the Vulkan backend is enabled.
*
* For NATIVE streams, Filament does not make any synchronization guarantee. However they are simple
* to use and do not incur a copy. These are often appropriate in video applications.
*
* Please see `sample-stream-test` and `sample-hello-camera` for usage examples.
*
* @see backend::StreamType
* @see Texture#setExternalStream
* @see Engine#destroyStream
*/
class UTILS_PUBLIC Stream : public FilamentAPI {
struct BuilderDetails;
public:
using Callback = backend::StreamCallback;
using StreamType = backend::StreamType;
/**
* Constructs a Stream object instance.
*
* By default, Stream objects are ACQUIRED and must have external images pushed to them via
* <pre>Stream::setAcquiredImage</pre>.
*
* To create a NATIVE or TEXTURE_ID stream, call one of the <pre>stream</pre> methods
* on the builder.
*/
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Creates a NATIVE stream. Native streams can sample data directly from an
* opaque platform object such as a SurfaceTexture on Android.
*
* @param stream An opaque native stream handle. e.g.: on Android this is an
* `android/graphics/SurfaceTexture` JNI jobject. The wrap mode must
* be CLAMP_TO_EDGE.
*
* @return This Builder, for chaining calls.
*/
Builder& stream(void* stream) noexcept;
/**
* Creates a TEXTURE_ID stream. This will sample data from the supplied
* external texture and copy it into an internal private texture.
*
* @param externalTextureId An opaque texture id (typically a GLuint created with glGenTextures)
* In a context shared with filament. In that case this texture's
* target must be GL_TEXTURE_EXTERNAL_OES and the wrap mode must
* be CLAMP_TO_EDGE.
*
* @return This Builder, for chaining calls.
*
* @see Texture::setExternalStream()
* @deprecated this method existed only for ARCore which doesn't need this anymore, use Texture::import() instead.
*/
UTILS_DEPRECATED
Builder& stream(intptr_t externalTextureId) noexcept;
/**
*
* @param width initial width of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
* Builder::stream(long externalTextureId).
*
* @return This Builder, for chaining calls.
*/
Builder& width(uint32_t width) noexcept;
/**
*
* @param height initial height of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
* Builder::stream(long externalTextureId).
*
* @return This Builder, for chaining calls.
*/
Builder& height(uint32_t height) noexcept;
/**
* Creates the Stream object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this Stream with.
*
* @return pointer to the newly created object, or nullptr if the stream couldn't be created.
*/
Stream* build(Engine& engine);
private:
friend class FStream;
};
/**
* Indicates whether this stream is a NATIVE stream, TEXTURE_ID stream, or ACQUIRED stream.
*/
StreamType getStreamType() const noexcept;
/**
* Updates an ACQUIRED stream with an image that is guaranteed to be used in the next frame.
*
* This method tells Filament to immediately "acquire" the image and trigger a callback
* when it is done with it. This should be called by the user outside of beginFrame / endFrame,
* and should be called only once per frame. If the user pushes images to the same stream
* multiple times in a single frame, only the final image is honored, but all callbacks are
* invoked.
*
* This method should be called on the same thread that calls Renderer::beginFrame, which is
* also where the callback is invoked. This method can only be used for streams that were
* constructed without calling the `stream` method on the builder.
*
* @see Stream for more information about NATIVE, TEXTURE_ID, and ACQUIRED configurations.
*
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
* @param callback This is triggered by Filament when it wishes to release the image.
* It callback tales two arguments: the AHardwareBuffer and the userdata.
* @param userdata Optional closure data. Filament will pass this into the callback when it
* releases the image.
*/
void setAcquiredImage(void* image, Callback callback, void* userdata) noexcept;
/**
* Updates the size of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
* Builder::stream(long externalTextureId).
*
* @param width new width of the incoming stream
* @param height new height of the incoming stream
*/
void setDimensions(uint32_t width, uint32_t height) noexcept;
/**
* Read-back the content of the last frame of a Stream since the last call to
* Renderer.beginFrame().
*
* The Stream must be of type externalTextureId. This function is a no-op otherwise.
*
* @param xoffset Left offset of the sub-region to read back.
* @param yoffset Bottom offset of the sub-region to read back.
* @param width Width of the sub-region to read back.
* @param height Height of the sub-region to read back.
* @param buffer Client-side buffer where the read-back will be written.
*
* The following format are always supported:
* - PixelBufferDescriptor::PixelDataFormat::RGBA
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
*
* The following types are always supported:
* - PixelBufferDescriptor::PixelDataType::UBYTE
* - PixelBufferDescriptor::PixelDataType::UINT
* - PixelBufferDescriptor::PixelDataType::INT
* - PixelBufferDescriptor::PixelDataType::FLOAT
*
* Other combination of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.
*
* Stream buffer User buffer (PixelBufferDescriptor&)
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
* Typically readPixels() will be called after Renderer.beginFrame().
*
* After issuing this method, the callback associated with `buffer` will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen
* after multiple calls to beginFrame(), render(), endFrame().
*
* It is also possible to use a Fence to wait for the read-back.
*
* @remark
* readPixels() is intended for debugging and testing. It will impact performance significantly.
*/
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer) noexcept;
/**
* Returns the presentation time of the currently displayed frame in nanosecond.
*
* This value can change at any time.
*
* @return timestamp in nanosecond.
*/
int64_t getTimestamp() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_STREAM_H

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_SWAPCHAIN_H
#define TNT_FILAMENT_SWAPCHAIN_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/PresentCallable.h>
#include <utils/compiler.h>
namespace filament {
/**
* A swap chain represents an Operating System's *native* renderable surface.
*
* Typically it's a native window or a view. Because a SwapChain is initialized from a
* native object, it is given to filament as a `void *`, which must be of the proper type
* for each platform filament is running on.
*
* \code
* SwapChain* swapChain = engine->createSwapChain(nativeWindow);
* \endcode
*
* When Engine::create() is used without specifying a Platform, the `nativeWindow`
* parameter above must be of type:
*
* Platform | nativeWindow type
* :---------------|:----------------------------:
* Android | ANativeWindow*
* macOS - OpenGL | NSView*
* macOS - Metal | CAMetalLayer*
* iOS - OpenGL | CAEAGLLayer*
* iOS - Metal | CAMetalLayer*
* X11 | Window
* Windows | HWND
*
* Otherwise, the `nativeWindow` is defined by the concrete implementation of Platform.
*
*
* Examples:
*
* Android
* -------
*
* On Android, an `ANativeWindow*` can be obtained from a Java `Surface` object using:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <android/native_window_jni.h>
* // parameters
* // env: JNIEnv*
* // surface: jobject
* ANativeWindow* win = ANativeWindow_fromSurface(env, surface);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* \warning
* Don't use reflection to access the `mNativeObject` field, it won't work.
*
* A `Surface` can be retrieved from a `SurfaceView` or `SurfaceHolder` easily using
* `SurfaceHolder.getSurface()` and/or `SurfaceView.getHolder()`.
*
* \note
* To use a `TextureView` as a SwapChain, it is necessary to first get its `SurfaceTexture`,
* for instance using `TextureView.SurfaceTextureListener` and then create a `Surface`:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
* // using a TextureView.SurfaceTextureListener:
* public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
* mSurface = new Surface(surfaceTexture);
* // mSurface can now be used in JNI to create an ANativeWindow.
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Linux
* -----
*
* Example using SDL:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* SDL_SysWMinfo wmi;
* SDL_VERSION(&wmi.version);
* SDL_GetWindowWMInfo(sdlWindow, &wmi);
* Window nativeWindow = (Window) wmi.info.x11.window;
*
* using namespace filament;
* Engine* engine = Engine::create();
* SwapChain* swapChain = engine->createSwapChain((void*) nativeWindow);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Windows
* -------
*
* Example using SDL:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* SDL_SysWMinfo wmi;
* SDL_VERSION(&wmi.version);
* ASSERT_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi), "SDL version unsupported!");
* HDC nativeWindow = (HDC) wmi.info.win.hdc;
*
* using namespace filament;
* Engine* engine = Engine::create();
* SwapChain* swapChain = engine->createSwapChain((void*) nativeWindow);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* OSX
* ---
*
* On OSX, any `NSView` can be used *directly* as a `nativeWindow` with createSwapChain().
*
* Example using SDL/Objective-C:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.mm}
* #include <filament/Engine.h>
*
* #include <Cocoa/Cocoa.h>
* #include <SDL_syswm.h>
*
* SDL_SysWMinfo wmi;
* SDL_VERSION(&wmi.version);
* NSWindow* win = (NSWindow*) wmi.info.cocoa.window;
* NSView* view = [win contentView];
* void* nativeWindow = view;
*
* using namespace filament;
* Engine* engine = Engine::create();
* SwapChain* swapChain = engine->createSwapChain(nativeWindow);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see Engine
*/
class UTILS_PUBLIC SwapChain : public FilamentAPI {
public:
using FrameScheduledCallback = backend::FrameScheduledCallback;
using FrameCompletedCallback = backend::FrameCompletedCallback;
static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
/**
* This flag indicates that the swap chain may be used as a source surface
* for reading back render results. This config must be set when creating
* any swap chain that will be used as the source for a blit operation.
*
* @see
* Renderer.copyFrame()
*/
static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
/**
* Indicates that the native window is a CVPixelBufferRef.
*
* This is only supported by the Metal backend. The CVPixelBuffer must be in the
* kCVPixelFormatType_32BGRA format.
*
* It is not necessary to add an additional retain call before passing the pixel buffer to
* Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and
* CVPixelBufferRelease when the swap chain is destroyed.
*/
static const uint64_t CONFIG_APPLE_CVPIXELBUFFER =
backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER;
void* getNativeWindow() const noexcept;
/**
* FrameScheduledCallback is a callback function that notifies an application when Filament has
* completed processing a frame and that frame is ready to be scheduled for presentation.
*
* Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain.
* If a SwapChain::FrameScheduledCallback is set, however, the application bares the
* responsibility of scheduling a frame for presentation by calling the backend::PresentCallable
* passed to the callback function. Currently this functionality is only supported by the Metal
* backend.
*
* A FrameScheduledCallback can be set on an individual SwapChain through
* SwapChain::setFrameScheduledCallback. If the callback is set, then the SwapChain will *not*
* automatically schedule itself for presentation. Instead, the application must call the
* PresentCallable passed to the FrameScheduledCallback.
*
* @param callback A callback, or nullptr to unset.
* @param user An optional pointer to user data passed to the callback function.
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
*
* @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread.
*
* @see PresentCallable
*/
void setFrameScheduledCallback(FrameScheduledCallback callback, void* user = nullptr);
/**
* FrameCompletedCallback is a callback function that notifies an application when a frame's
* contents have completed rendering on the GPU.
*
* Use SwapChain::setFrameCompletedCallback to set a callback on an individual SwapChain. Each
* time a frame completes GPU rendering, the callback will be called with optional user data.
*
* The FrameCompletedCallback is guaranteed to be called on the main Filament thread.
*
* @param callback A callback, or nullptr to unset.
* @param user An optional pointer to user data passed to the callback function.
*
* @remark Only Filament's Metal backend supports frame callbacks. Other backends ignore the
* callback (which will never be called) and proceed normally.
*/
void setFrameCompletedCallback(FrameCompletedCallback callback, void* user = nullptr);
};
} // namespace filament
#endif // TNT_FILAMENT_SWAPCHAIN_H

View File

@@ -0,0 +1,517 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_TEXTURE_H
#define TNT_FILAMENT_TEXTURE_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <utils/compiler.h>
#include <stddef.h>
namespace filament {
class FTexture;
class Engine;
class Stream;
/**
* Texture
*
* The Texture class supports:
* - 2D textures
* - 3D textures
* - Cube maps
* - mip mapping
*
*
* Creation and destruction
* ========================
*
* A Texture object is created using the Texture::Builder and destroyed by calling
* Engine::destroy(const Texture*).
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* filament::Texture* texture = filament::Texture::Builder()
* .width(64)
* .height(64)
* .build(*engine);
*
* engine->destroy(texture);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
class UTILS_PUBLIC Texture : public FilamentAPI {
struct BuilderDetails;
public:
static constexpr const size_t BASE_LEVEL = 0;
using PixelBufferDescriptor = backend::PixelBufferDescriptor; //!< Geometry of a pixel buffer
using Sampler = backend::SamplerType; //!< Type of sampler
using InternalFormat = backend::TextureFormat; //!< Internal texel format
using CubemapFace = backend::TextureCubemapFace; //!< Cube map faces
using Format = backend::PixelDataFormat; //!< Pixel color format
using Type = backend::PixelDataType; //!< Pixel data format
using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format
using FaceOffsets = backend::FaceOffsets; //!< Cube map faces offsets
using Usage = backend::TextureUsage; //!< Usage affects texel layout
using Swizzle = backend::TextureSwizzle; //!< Texture swizzle
/** @return whether a backend supports a particular format. */
static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept;
/** @return whether a backend supports texture swizzling. */
static bool isTextureSwizzleSupported(Engine& engine) noexcept;
static size_t computeTextureDataSize(Texture::Format format, Texture::Type type,
size_t stride, size_t height, size_t alignment) noexcept;
/**
* Options for environment prefiltering into reflection map
*
* @see generatePrefilterMipmap()
*/
struct PrefilterOptions {
uint16_t sampleCount = 8; //!< sample count used for filtering
bool mirror = true; //!< whether the environment must be mirrored
private:
UTILS_UNUSED uintptr_t reserved[3] = {};
};
//! Use Builder to construct a Texture object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Specifies the width in texels of the texture. Doesn't need to be a power-of-two.
* @param width Width of the texture in texels (default: 1).
* @return This Builder, for chaining calls.
*/
Builder& width(uint32_t width) noexcept;
/**
* Specifies the height in texels of the texture. Doesn't need to be a power-of-two.
* @param height Height of the texture in texels (default: 1).
* @return This Builder, for chaining calls.
*/
Builder& height(uint32_t height) noexcept;
/**
* Specifies the depth in texels of the texture. Doesn't need to be a power-of-two.
* The depth controls the number of layers in a 2D array texture. Values greater than 1
* effectively create a 3D texture.
* @param depth Depth of the texture in texels (default: 1).
* @return This Builder, for chaining calls.
* @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_ARRAY or it has no effect.
*/
Builder& depth(uint32_t depth) noexcept;
/**
* Specifies the numbers of mip map levels.
* This creates a mip-map pyramid. The maximum number of levels a texture can have is
* such that max(width, height, level) / 2^MAX_LEVELS = 1
* @param levels Number of mipmap levels for this texture.
* @return This Builder, for chaining calls.
*/
Builder& levels(uint8_t levels) noexcept;
/**
* Specifies the type of sampler to use.
* @param target Sampler type
* @return This Builder, for chaining calls.
* @see Sampler
*/
Builder& sampler(Sampler target) noexcept;
/**
* Specifies the *internal* format of this texture.
*
* The internal format specifies how texels are stored (which may be different from how
* they're specified in setImage()). InternalFormat specifies both the color components
* and the data type used.
*
* @param format Format of the texture's texel.
* @return This Builder, for chaining calls.
* @see InternalFormat, setImage
*/
Builder& format(InternalFormat format) noexcept;
/**
* Specifies if the texture will be used as a render target attachment.
*
* If the texture is potentially rendered into, it may require a different memory layout,
* which needs to be known during construction.
*
* @param usage Defaults to Texture::Usage::DEFAULT; c.f. Texture::Usage::COLOR_ATTACHMENT.
* @return This Builder, for chaining calls.
*/
Builder& usage(Usage usage) noexcept;
/**
* Specifies how a texture's channels map to color components
*
* Texture Swizzle is only supported is isTextureSwizzleSupported() returns true.
*
* @param r texture channel for red component
* @param g texture channel for green component
* @param b texture channel for blue component
* @param a texture channel for alpha component
* @return This Builder, for chaining calls.
* @see Texture::isTextureSwizzleSupported()
*/
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
/**
* Creates the Texture object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this Texture with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
Texture* build(Engine& engine);
/* no user serviceable parts below */
/**
* Specify a native texture to import as a Filament texture.
*
* The texture id is backend-specific:
* - OpenGL: GLuint texture ID
* - Metal: id<MTLTexture>
*
* With Metal, the id<MTLTexture> object should be cast to an intptr_t using
* CFBridgingRetain to transfer ownership to Filament. Filament will release ownership of
* the texture object when the Filament texture is destroyed.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
* id <MTLTexture> metalTexture = ...
* filamentTexture->import((intptr_t) CFBridgingRetain(metalTexture));
* // free to release metalTexture
*
* // after using texture:
* engine->destroy(filamentTexture); // metalTexture is released
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @warning This method should be used as a last resort. This API is subject to change or
* removal.
*
* @param id a backend specific texture identifier
*
* @return This Builder, for chaining calls.
*/
Builder& import(intptr_t id) noexcept;
private:
friend class FTexture;
};
/**
* Returns the width of a 2D or 3D texture level
* @param level texture level.
* @return Width in texel of the specified \p level, clamped to 1.
* @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension
* of the texture are unknown and this method always returns whatever was set on the Builder.
*/
size_t getWidth(size_t level = BASE_LEVEL) const noexcept;
/**
* Returns the height of a 2D or 3D texture level
* @param level texture level.
* @return Height in texel of the specified \p level, clamped to 1.
* @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension
* of the texture are unknown and this method always returns whatever was set on the Builder.
*/
size_t getHeight(size_t level = BASE_LEVEL) const noexcept;
/**
* Returns the depth of a 3D texture level
* @param level texture level.
* @return Depth in texel of the specified \p level, clamped to 1.
* @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension
* of the texture are unknown and this method always returns whatever was set on the Builder.
*/
size_t getDepth(size_t level = BASE_LEVEL) const noexcept;
/**
* Returns the maximum number of levels this texture can have.
* @return maximum number of levels this texture can have.
* @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension
* of the texture are unknown and this method always returns whatever was set on the Builder.
*/
size_t getLevels() const noexcept;
/**
* Return this texture Sampler as set by Builder::sampler().
* @return this texture Sampler as set by Builder::sampler()
*/
Sampler getTarget() const noexcept;
/**
* Return this texture InternalFormat as set by Builder::format().
* @return this texture InternalFormat as set by Builder::format().
*/
InternalFormat getFormat() const noexcept;
/**
* Specify the image of a 2D texture for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_2D or
* Sampler::SAMPLER_EXTERNAL. IF the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with driver::SamplerType::SAMPLER_2D.
*
* @note
* This is equivalent to calling:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 2D texture for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param xoffset Left offset of the sub-region to update.
* @param yoffset Bottom offset of the sub-region to update.
* @param width Width of the sub-region to update.
* @param height Height of the sub-region to update.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_2D or
* Sampler::SAMPLER_EXTERNAL. IF the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with Sampler::SAMPLER_2D.
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 3D texture or 2D texture array for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param xoffset Left offset of the sub-region to update.
* @param yoffset Bottom offset of the sub-region to update.
* @param zoffset Depth offset of the sub-region to update.
* @param width Width of the sub-region to update.
* @param height Height of the sub-region to update.
* @param depth Depth of the sub-region to update.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_array.
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& buffer) const;
/**
* Specify all six images of a cube map level.
*
* This method follows exactly the OpenGL conventions.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param buffer Client-side buffer containing the images to set.
* @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets
* are specified in the following order: +x, -x, +y, -y, +z, -z
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_CUBEMAP or it has no effect
*
* @see Texture::CubemapFace, Builder::sampler()
*/
void setImage(Engine& engine, size_t level,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
/**
* Specify the external image to associate with this Texture. Typically the external
* image is OS specific, and can be a video or camera frame.
* There are many restrictions when using an external image as a texture, such as:
* - only the level of detail (lod) 0 can be specified
* - only nearest or linear filtering is supported
* - the size and format of the texture is defined by the external image
* - only the CLAMP_TO_EDGE wrap mode is supported
*
* @param engine Engine this texture is associated to.
* @param image An opaque handle to a platform specific image. Supported types are
* eglImageOES on Android and CVPixelBufferRef on iOS.
*
* On iOS the following pixel formats are supported:
* - kCVPixelFormatType_32BGRA
* - kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL or it has no effect
*
* @see Builder::sampler()
*
*/
void setExternalImage(Engine& engine, void* image) noexcept;
/**
* Specify the external image and plane to associate with this Texture. Typically the external
* image is OS specific, and can be a video or camera frame. When using this method, the
* external image must be a planar type (such as a YUV camera frame). The plane parameter
* selects which image plane is bound to this texture.
*
* A single external image can be bound to different Filament textures, with each texture
* associated with a separate plane:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* textureA->setExternalImage(engine, image, 0);
* textureB->setExternalImage(engine, image, 1);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* There are many restrictions when using an external image as a texture, such as:
* - only the level of detail (lod) 0 can be specified
* - only nearest or linear filtering is supported
* - the size and format of the texture is defined by the external image
* - only the CLAMP_TO_EDGE wrap mode is supported
*
* @param engine Engine this texture is associated to.
* @param image An opaque handle to a platform specific image. Supported types are
* eglImageOES on Android and CVPixelBufferRef on iOS.
* @param plane The plane index of the external image to associate with this texture.
*
* This method is only meaningful on iOS with
* kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms
* other than iOS, this method is a no-op.
*/
void setExternalImage(Engine& engine, void* image, size_t plane) noexcept;
/**
* Specify the external stream to associate with this Texture. Typically the external
* stream is OS specific, and can be a video or camera stream.
* There are many restrictions when using an external stream as a texture, such as:
* - only the level of detail (lod) 0 can be specified
* - only nearest or linear filtering is supported
* - the size and format of the texture is defined by the external stream
*
* @param engine Engine this texture is associated to.
* @param stream A Stream object
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL or it has no effect
*
* @see Builder::sampler(), Stream
*
*/
void setExternalStream(Engine& engine, Stream* stream) noexcept;
/**
* Generates all the mipmap levels automatically. This requires the texture to have a
* color-renderable format.
*
* @param engine Engine this texture is associated to.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention This Texture instance must NOT use Sampler::SAMPLER_CUBEMAP or it has no effect
*/
void generateMipmaps(Engine& engine) const noexcept;
/**
* Creates a reflection map from an environment map.
*
* This is a utility function that replaces calls to Texture::setImage().
* The provided environment map is processed and all mipmap levels are populated. The
* processing is similar to the offline tool `cmgen` as a lower quality setting.
*
* This function is intended to be used when the environment cannot be processed offline,
* for instance if it's generated at runtime.
*
* The source data must obey to some constraints:
* - the data type must be PixelDataFormat::RGB
* - the data format must be one of
* - PixelDataType::FLOAT
* - PixelDataType::HALF
*
* The current texture must be a cubemap
*
* The reflections cubemap's internal format cannot be a compressed format.
*
* The reflections cubemap's dimension must be a power-of-two.
*
* @warning This operation is computationally intensive, especially with large environments and
* is currently synchronous. Expect about 1ms for a 16x16 cubemap.
*
* @param engine Reference to the filament::Engine to associate this IndirectLight with.
* @param buffer Client-side buffer containing the images to set.
* @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets
* are specified in the following order: +x, -x, +y, -y, +z, -z
* @param options Optional parameter to controlling user-specified quality and options.
*
* @exception utils::PreConditionPanic if the source data constraints are not respected.
*
*/
void generatePrefilterMipmap(Engine& engine,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
PrefilterOptions const* options = nullptr);
};
} // namespace filament
#endif // TNT_FILAMENT_TEXTURE_H

View File

@@ -0,0 +1,205 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_TEXTURESAMPLER_H
#define TNT_FILAMENT_TEXTURESAMPLER_H
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <math.h>
namespace filament {
/**
* TextureSampler defines how a texture is accessed.
*/
class UTILS_PUBLIC TextureSampler {
public:
using WrapMode = backend::SamplerWrapMode;
using MinFilter = backend::SamplerMinFilter;
using MagFilter = backend::SamplerMagFilter;
using CompareMode = backend::SamplerCompareMode;
using CompareFunc = backend::SamplerCompareFunc;
/**
* Creates a default sampler.
* The default parameters are:
* - filterMag : NEAREST
* - filterMin : NEAREST
* - wrapS : CLAMP_TO_EDGE
* - wrapT : CLAMP_TO_EDGE
* - wrapR : CLAMP_TO_EDGE
* - compareMode : NONE
* - compareFunc : Less or equal
* - no anisotropic filtering
*/
TextureSampler() noexcept = default;
TextureSampler(const TextureSampler& rhs) noexcept = default;
TextureSampler& operator=(const TextureSampler& rhs) noexcept = default;
/**
* Creates a TextureSampler with the default parameters but setting the filtering and wrap modes.
* @param minMag filtering for both minification and magnification
* @param str wrapping mode for all texture coordinate axes
*/
explicit TextureSampler(MagFilter minMag, WrapMode str = WrapMode::CLAMP_TO_EDGE) noexcept {
mSamplerParams.filterMin = MinFilter(minMag);
mSamplerParams.filterMag = minMag;
mSamplerParams.wrapS = str;
mSamplerParams.wrapT = str;
mSamplerParams.wrapR = str;
}
/**
* Creates a TextureSampler with the default parameters but setting the filtering and wrap modes.
* @param min filtering for minification
* @param mag filtering for magnification
* @param str wrapping mode for all texture coordinate axes
*/
TextureSampler(MinFilter min, MagFilter mag, WrapMode str = WrapMode::CLAMP_TO_EDGE) noexcept {
mSamplerParams.filterMin = min;
mSamplerParams.filterMag = mag;
mSamplerParams.wrapS = str;
mSamplerParams.wrapT = str;
mSamplerParams.wrapR = str;
}
/**
* Creates a TextureSampler with the default parameters but setting the filtering and wrap modes.
* @param min filtering for minification
* @param mag filtering for magnification
* @param s wrap mode for the s (horizontal)texture coordinate
* @param t wrap mode for the t (vertical) texture coordinate
* @param r wrap mode for the r (depth) texture coordinate
*/
TextureSampler(MinFilter min, MagFilter mag, WrapMode s, WrapMode t, WrapMode r) noexcept {
mSamplerParams.filterMin = min;
mSamplerParams.filterMag = mag;
mSamplerParams.wrapS = s;
mSamplerParams.wrapT = t;
mSamplerParams.wrapR = r;
}
/**
* Creates a TextureSampler with the default parameters but setting the compare mode and function
* @param mode Compare mode
* @param func Compare function
*/
explicit TextureSampler(CompareMode mode, CompareFunc func = CompareFunc::LE) noexcept {
mSamplerParams.compareMode = mode;
mSamplerParams.compareFunc = func;
}
/**
* Sets the minification filter
* @param v Minification filter
*/
void setMinFilter(MinFilter v) noexcept {
mSamplerParams.filterMin = v;
}
/**
* Sets the magnification filter
* @param v Magnification filter
*/
void setMagFilter(MagFilter v) noexcept {
mSamplerParams.filterMag = v;
}
/**
* Sets the wrap mode for the s (horizontal) texture coordinate
* @param v wrap mode
*/
void setWrapModeS(WrapMode v) noexcept {
mSamplerParams.wrapS = v;
}
/**
* Sets the wrap mode for the t (vertical) texture coordinate
* @param v wrap mode
*/
void setWrapModeT(WrapMode v) noexcept {
mSamplerParams.wrapT = v;
}
/**
* Sets the wrap mode for the r (depth, for 3D textures) texture coordinate
* @param v wrap mode
*/
void setWrapModeR(WrapMode v) noexcept {
mSamplerParams.wrapR = v;
}
/**
* This controls anisotropic filtering.
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0.
* The maximum permissible value is 7.
*/
void setAnisotropy(float anisotropy) noexcept {
const int log2 = ilogbf(anisotropy > 0 ? anisotropy : -anisotropy);
mSamplerParams.anisotropyLog2 = uint8_t(log2 < 7 ? log2 : 7);
}
/**
* Sets the compare mode and function.
* @param mode Compare mode
* @param func Compare function
*/
void setCompareMode(CompareMode mode, CompareFunc func = CompareFunc::LE) noexcept {
mSamplerParams.compareMode = mode;
mSamplerParams.compareFunc = func;
}
//! returns the minification filter value
MinFilter getMinFilter() const noexcept { return mSamplerParams.filterMin; }
//! returns the magnification filter value
MagFilter getMagFilter() const noexcept { return mSamplerParams.filterMag; }
//! returns the s-coordinate wrap mode (horizontal)
WrapMode getWrapModeS() const noexcept { return mSamplerParams.wrapS; }
//! returns the t-coordinate wrap mode (vertical)
WrapMode getWrapModeT() const noexcept { return mSamplerParams.wrapT; }
//! returns the r-coordinate wrap mode (depth)
WrapMode getWrapModeR() const noexcept { return mSamplerParams.wrapR; }
//! returns the anisotropy value
float getAnisotropy() const noexcept { return float(1u << mSamplerParams.anisotropyLog2); }
//! returns the compare mode
CompareMode getCompareMode() const noexcept { return mSamplerParams.compareMode; }
//! returns the compare function
CompareFunc getCompareFunc() const noexcept { return mSamplerParams.compareFunc; }
// no user-serviceable parts below...
backend::SamplerParams getSamplerParams() const noexcept { return mSamplerParams; }
private:
backend::SamplerParams mSamplerParams{};
};
} // namespace filament
#endif // TNT_FILAMENT_TEXTURESAMPLER_H

View File

@@ -0,0 +1,235 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_TONE_MAPPER_H
#define TNT_FILAMENT_TONE_MAPPER_H
#include <utils/compiler.h>
#include <math/mathfwd.h>
namespace filament {
/**
* Interface for tone mapping operators. A tone mapping operator, or tone mapper,
* is responsible for compressing the dynamic range of the rendered scene to a
* dynamic range suitable for display.
*
* In Filament, tone mapping is a color grading step. ToneMapper instances are
* created and passed to the ColorGrading::Builder to produce a 3D LUT that will
* be used during post-processing to prepare the final color buffer for display.
*
* Filament provides several default tone mapping operators that fall into three
* categories:
*
* - Configurable tone mapping operators
* - GenericToneMapper
* - Fixed-aesthetic tone mapping operators
* - ACESToneMapper
* - ACESLegacyToneMapper
* - FilmicToneMapper
* - Debug/validation tone mapping operators
* - LinearToneMapper
* - DisplayRangeToneMapper
*
* You can create custom tone mapping operators by subclassing ToneMapper.
*/
struct UTILS_PUBLIC ToneMapper {
ToneMapper() noexcept;
virtual ~ToneMapper() noexcept;
/**
* Maps an open domain (or "scene referred" values) color value to display
* domain (or "display referred") color value. Both the input and output
* color values are defined in the Rec.2020 color space, with no transfer
* function applied ("linear Rec.2020").
*
* @param c Input color to tone map, in the Rec.2020 color space with no
* transfer function applied ("linear")
*
* @return A tone mapped color in the Rec.2020 color space, with no transfer
* function applied ("linear")
*/
virtual math::float3 operator()(math::float3 c) const noexcept = 0;
};
/**
* Linear tone mapping operator that returns the input color but clamped to
* the 0..1 range. This operator is mostly useful for debugging.
*/
struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
LinearToneMapper() noexcept;
~LinearToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept;
};
/**
* ACES tone mapping operator. This operator is an implementation of the
* ACES Reference Rendering Transform (RRT) combined with the Output Device
* Transform (ODT) for sRGB monitors (dim surround, 100 nits).
*/
struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
ACESToneMapper() noexcept;
~ACESToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept;
};
/**
* ACES tone mapping operator, modified to match the perceived brightness
* of FilmicToneMapper. This operator is the same as ACESToneMapper but
* applies a brightness multiplier of ~1.6 to the input color value to
* target brighter viewing environments.
*/
struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
ACESLegacyToneMapper() noexcept;
~ACESLegacyToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept;
};
/**
* "Filmic" tone mapping operator. This tone mapper was designed to
* approximate the aesthetics of the ACES RRT + ODT for Rec.709
* and historically Filament's default tone mapping operator. It exists
* only for backward compatibility purposes and is not otherwise recommended.
*/
struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
FilmicToneMapper() noexcept;
~FilmicToneMapper() noexcept final;
math::float3 operator()(math::float3 x) const noexcept;
};
/**
* Generic tone mapping operator that gives control over the tone mapping
* curve. This operator can be used to control the aesthetics of the final
* image. This operator also allows to control the dynamic range of the
* scene referred values.
*
* The tone mapping curve is defined by 5 parameters:
* - contrast: controls the contrast of the curve
* - shoulder: controls the shoulder of the curve, i.e. how quickly scene
* referred values map to output white
* - midGrayIn: sets the input middle gray
* - midGrayOut: sets the output middle gray
* - hdrMax: defines the maximum input value that will be mapped to
* output white
*/
struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
/**
* Builds a new generic tone mapper. The default values of the
* constructor parameters approximate an ACES tone mapping curve
* and the maximum input value is set to 10.0.
*
* @param contrast: controls the contrast of the curve, must be > 0.0, values
* in the range 0.5..2.0 are recommended.
* @param shoulder: controls the shoulder of the curve, i.e. how quickly scene
* referred values map to output white, between 0.0 and 1.0.
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
* @param hdrMax: defines the maximum input value that will be mapped to
* output white. Must be >= 1.0.
*/
GenericToneMapper(
float contrast = 1.585f,
float shoulder = 0.5f,
float midGrayIn = 0.18f,
float midGrayOut = 0.268f,
float hdrMax = 10.0f
) noexcept;
~GenericToneMapper() noexcept final;
GenericToneMapper(GenericToneMapper const&) = delete;
GenericToneMapper& operator=(GenericToneMapper const&) = delete;
GenericToneMapper(GenericToneMapper&& rhs) noexcept;
GenericToneMapper& operator=(GenericToneMapper& rhs) noexcept;
math::float3 operator()(math::float3 x) const noexcept;
/** Returns the contrast of the curve as a strictly positive value. */
float getContrast() const noexcept;
/** Returns how fast scene referred values map to output white as a value between 0.0 and 1.0. */
float getShoulder() const noexcept;
/** Returns the middle gray point for input values as a value between 0.0 and 1.0. */
float getMidGrayIn() const noexcept;
/** Returns the middle gray point for output values as a value between 0.0 and 1.0. */
float getMidGrayOut() const noexcept;
/** Returns the maximum input value that will map to output white, as a value >= 1.0. */
float getHdrMax() const noexcept;
/** Sets the contrast of the curve, must be > 0.0, values in the range 0.5..2.0 are recommended. */
void setContrast(float contrast) noexcept;
/** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */
void setShoulder(float shoulder) noexcept;
/** Sets the input middle gray, between 0.0 and 1.0. */
void setMidGrayIn(float midGrayIn) noexcept;
/** Sets the output middle gray, between 0.0 and 1.0. */
void setMidGrayOut(float midGrayOut) noexcept;
/** Defines the maximum input value that will be mapped to output white. Must be >= 1.0. */
void setHdrMax(float hdrMax) noexcept;
private:
struct Options;
Options* mOptions;
};
/**
* A tone mapper that converts the input HDR RGB color into one of 16 debug colors
* that represent the pixel's exposure. When the output is cyan, the input color
* represents middle gray (18% exposure). Every exposure stop above or below middle
* gray causes a color shift.
*
* The relationship between exposures and colors is:
*
* - -5EV black
* - -4EV darkest blue
* - -3EV darker blue
* - -2EV dark blue
* - -1EV blue
* - OEV cyan
* - +1EV dark green
* - +2EV green
* - +3EV yellow
* - +4EV yellow-orange
* - +5EV orange
* - +6EV bright red
* - +7EV red
* - +8EV magenta
* - +9EV purple
* - +10EV white
*
* This tone mapper is useful to validate and tweak scene lighting.
*/
struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper {
DisplayRangeToneMapper() noexcept;
~DisplayRangeToneMapper() noexcept;
math::float3 operator()(math::float3 c) const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_TONE_MAPPER_H

View File

@@ -0,0 +1,310 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_TRANSFORMMANAGER_H
#define TNT_FILAMENT_TRANSFORMMANAGER_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <utils/EntityInstance.h>
#include <math/mathfwd.h>
#include <iterator>
namespace utils {
class Entity;
} // namespace utils
namespace filament {
class FTransformManager;
/**
* TransformManager is used to add transform components to entities.
*
* A Transform component gives an entity a position and orientation in space in the coordinate
* space of its parent transform. The TransformManager takes care of computing the world-space
* transform of each component (i.e. its transform relative to the root).
*
* Creation and destruction
* ========================
*
* A transform component is created using TransformManager::create() and destroyed by calling
* TransformManager::destroy().
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
* utils::Entity object = utils::EntityManager.get().create();
*
* auto& tcm = engine->getTransformManager();
*
* // create the transform component
* tcm.create(object);
*
* // set its transform
* auto i = tcm.getInstance(object);
* tcm.setTransform(i, mat4f::translation({ 0, 0, -1 }));
*
* // destroy the transform component
* tcm.destroy(object);
* ~~~~~~~~~~~
*
*/
class UTILS_PUBLIC TransformManager : public FilamentAPI {
public:
using Instance = utils::EntityInstance<TransformManager>;
class children_iterator : std::iterator<std::forward_iterator_tag, Instance> {
friend class FTransformManager;
TransformManager const& mManager;
Instance mInstance;
children_iterator(TransformManager const& mgr, Instance instance) noexcept
: mManager(mgr), mInstance(instance) { }
public:
children_iterator& operator++();
children_iterator operator++(int) { // NOLINT
children_iterator ret(*this);
++(*this);
return ret;
}
bool operator == (const children_iterator& other) const noexcept {
return mInstance == other.mInstance;
}
bool operator != (const children_iterator& other) const noexcept {
return mInstance != other.mInstance;
}
value_type operator*() const { return mInstance; }
};
/**
* Returns whether a particular Entity is associated with a component of this TransformManager
* @param e An Entity.
* @return true if this Entity has a component associated with this manager.
*/
bool hasComponent(utils::Entity e) const noexcept;
/**
* Gets an Instance representing the transform component associated with the given Entity.
* @param e An Entity.
* @return An Instance object, which represents the transform component associated with the Entity e.
* @note Use Instance::isValid() to make sure the component exists.
* @see hasComponent()
*/
Instance getInstance(utils::Entity e) const noexcept;
/**
* Enables or disable the accurate translation mode. Disabled by default.
*
* When accurate translation mode is active, the translation component of all transforms is
* maintained at double precision. This is only useful if the mat4 version of setTransform()
* is used, as well as getTransformAccurate().
*
* @param enable true to enable the accurate translation mode, false to disable.
*
* @see isAccurateTranslationsEnabled
* @see create(utils::Entity, Instance, const math::mat4&);
* @see setTransform(Instance, const math::mat4&)
* @see getTransformAccurate
* @see getWorldTransformAccurate
*/
void setAccurateTranslationsEnabled(bool enable) noexcept;
/**
* Returns whether the high precision translation mode is active.
* @return true if accurate translations mode is active, false otherwise
* @see setAccurateTranslationsEnabled
*/
bool isAccurateTranslationsEnabled() const noexcept;
/**
* Creates a transform component and associate it with the given entity.
* @param entity An Entity to associate a transform component to.
* @param parent The Instance of the parent transform, or Instance{} if no parent.
* @param localTransform The transform to initialize the transform component with.
* This is always relative to the parent.
*
* If this component already exists on the given entity, it is first destroyed as if
* destroy(utils::Entity e) was called.
*
* @see destroy()
*/
void create(utils::Entity entity, Instance parent, const math::mat4f& localTransform);
void create(utils::Entity entity, Instance parent, const math::mat4& localTransform); //!< \overload
void create(utils::Entity entity, Instance parent = {}); //!< \overload
/**
* Destroys this component from the given entity, children are orphaned.
* @param e An entity.
*
* @note If this transform had children, these are orphaned, which means their local
* transform becomes a world transform. Usually it's nonsensical. It's recommended to make
* sure that a destroyed transform doesn't have children.
*
* @see create()
*/
void destroy(utils::Entity e) noexcept;
/**
* Re-parents an entity to a new one.
* @param i The instance of the transform component to re-parent
* @param newParent The instance of the new parent transform
* @attention It is an error to re-parent an entity to a descendant and will cause undefined behaviour.
* @see getInstance()
*/
void setParent(Instance i, Instance newParent) noexcept;
/**
* Returns the parent of a transform component, or the null entity if it is a root.
* @param i The instance of the transform component to query.
*/
utils::Entity getParent(Instance i) const noexcept;
/**
* Returns the number of children of a transform component.
* @param i The instance of the transform component to query.
* @return The number of children of the queried component.
*/
size_t getChildCount(Instance i) const noexcept;
/**
* Gets a list of children for a transform component.
*
* @param i The instance of the transform component to query.
* @param children Pointer to array-of-Entity. The array must have at least "count" elements.
* @param count The maximum number of children to retrieve.
* @return The number of children written to the pointer.
*/
size_t getChildren(Instance i, utils::Entity* children, size_t count) const noexcept;
/**
* Returns an iterator to the Instance of the first child of the given parent.
*
* @param parent Instance of the parent
* @return A forward iterator pointing to the first child of the given parent.
*
* A child_iterator can only safely be dereferenced if it's different from getChildrenEnd(parent)
*/
children_iterator getChildrenBegin(Instance parent) const noexcept;
/**
* Returns an undreferencable iterator representing the end of the children list
*
* @param parent Instance of the parent
* @return A forward iterator.
*
* This iterator cannot be dereferenced
*/
children_iterator getChildrenEnd(Instance parent) const noexcept;
/**
* Sets a local transform of a transform component.
* @param ci The instance of the transform component to set the local transform to.
* @param localTransform The local transform (i.e. relative to the parent).
* @see getTransform()
* @attention This operation can be slow if the hierarchy of transform is too deep, and this
* will be particularly bad when updating a lot of transforms. In that case,
* consider using openLocalTransformTransaction() / commitLocalTransformTransaction().
*/
void setTransform(Instance ci, const math::mat4f& localTransform) noexcept;
/**
* Sets a local transform of a transform component and keeps double precision translation.
* All other values of the transform are stored at single precision.
* @param ci The instance of the transform component to set the local transform to.
* @param localTransform The local transform (i.e. relative to the parent).
* @see getTransform()
* @attention This operation can be slow if the hierarchy of transform is too deep, and this
* will be particularly bad when updating a lot of transforms. In that case,
* consider using openLocalTransformTransaction() / commitLocalTransformTransaction().
*/
void setTransform(Instance ci, const math::mat4& localTransform) noexcept;
/**
* Returns the local transform of a transform component.
* @param ci The instance of the transform component to query the local transform from.
* @return The local transform of the component (i.e. relative to the parent). This always
* returns the value set by setTransform().
* @see setTransform()
*/
const math::mat4f& getTransform(Instance ci) const noexcept;
/**
* Returns the local transform of a transform component.
* @param ci The instance of the transform component to query the local transform from.
* @return The local transform of the component (i.e. relative to the parent). This always
* returns the value set by setTransform().
* @see setTransform()
*/
const math::mat4 getTransformAccurate(Instance ci) const noexcept;
/**
* Return the world transform of a transform component.
* @param ci The instance of the transform component to query the world transform from.
* @return The world transform of the component (i.e. relative to the root). This is the
* composition of this component's local transform with its parent's world transform.
* @see setTransform()
*/
const math::mat4f& getWorldTransform(Instance ci) const noexcept;
/**
* Return the world transform of a transform component.
* @param ci The instance of the transform component to query the world transform from.
* @return The world transform of the component (i.e. relative to the root). This is the
* composition of this component's local transform with its parent's world transform.
* @see setTransform()
*/
const math::mat4 getWorldTransformAccurate(Instance ci) const noexcept;
/**
* Opens a local transform transaction. During a transaction, getWorldTransform() can
* return an invalid transform until commitLocalTransformTransaction() is called. However,
* setTransform() will perform significantly better and in constant time.
*
* This is useful when updating many transforms and the transform hierarchy is deep (say more
* than 4 or 5 levels).
*
* @note If the local transform transaction is already open, this is a no-op.
*
* @see commitLocalTransformTransaction(), setTransform()
*/
void openLocalTransformTransaction() noexcept;
/**
* Commits the currently open local transform transaction. When this returns, calls
* to getWorldTransform() will return the proper value.
*
* @attention failing to call this method when done updating the local transform will cause
* a lot of rendering problems. The system never closes the transaction
* automatically.
*
* @note If the local transform transaction is not open, this is a no-op.
*
* @see openLocalTransformTransaction(), setTransform()
*/
void commitLocalTransformTransaction() noexcept;
};
} // namespace filament
#endif // TNT_TRANSFORMMANAGER_H

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_VERTEXBUFFER_H
#define TNT_FILAMENT_VERTEXBUFFER_H
#include <filament/FilamentAPI.h>
#include <filament/MaterialEnums.h>
#include <backend/BufferDescriptor.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
namespace filament {
class FVertexBuffer;
class BufferObject;
class Engine;
/**
* Holds a set of buffers that define the geometry of a Renderable.
*
* The geometry of the Renderable itself is defined by a set of vertex attributes such as
* position, color, normals, tangents, etc...
*
* There is no need to have a 1-to-1 mapping between attributes and buffer. A buffer can hold the
* data of several attributes -- attributes are then referred as being "interleaved".
*
* The buffers themselves are GPU resources, therefore mutating their data can be relatively slow.
* For this reason, it is best to separate the constant data from the dynamic data into multiple
* buffers.
*
* It is possible, and even encouraged, to use a single vertex buffer for several Renderables.
*
* @see IndexBuffer, RenderableManager
*/
class UTILS_PUBLIC VertexBuffer : public FilamentAPI {
struct BuilderDetails;
public:
using AttributeType = backend::ElementType;
using BufferDescriptor = backend::BufferDescriptor;
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Defines how many buffers will be created in this vertex buffer set. These buffers are
* later referenced by index from 0 to \p bufferCount - 1.
*
* This call is mandatory. The default is 0.
*
* @param bufferCount Number of buffers in this vertex buffer set. The maximum value is 8.
* @return A reference to this Builder for chaining calls.
*/
Builder& bufferCount(uint8_t bufferCount) noexcept;
/**
* Size of each buffer in the set in vertex.
*
* @param vertexCount Number of vertices in each buffer in this set.
* @return A reference to this Builder for chaining calls.
*/
Builder& vertexCount(uint32_t vertexCount) noexcept;
/**
* Allows buffers to be swapped out and shared using BufferObject.
*
* If buffer objects mode is enabled, clients must call setBufferObjectAt rather than
* setBufferAt. This allows sharing of data between VertexBuffer objects, but it may
* slightly increase the memory footprint of Filament's internal bookkeeping.
*
* @param enabled If true, enables buffer object mode. False by default.
*/
Builder& enableBufferObjects(bool enabled = true) noexcept;
/**
* Sets up an attribute for this vertex buffer set.
*
* Using \p byteOffset and \p byteStride, attributes can be interleaved in the same buffer.
*
* @param attribute The attribute to set up.
* @param bufferIndex The index of the buffer containing the data for this attribute. Must
* be between 0 and bufferCount() - 1.
* @param attributeType The type of the attribute data (e.g. byte, float3, etc...)
* @param byteOffset Offset in *bytes* into the buffer \p bufferIndex
* @param byteStride Stride in *bytes* to the next element of this attribute. When set to
* zero the attribute size, as defined by \p attributeType is used.
*
* @return A reference to this Builder for chaining calls.
*
* @warning VertexAttribute::TANGENTS must be specified as a quaternion and is how normals
* are specified.
*
* @warning Not all backends support 3-component attributes that are not floats. For help
* with conversion, see geometry::Transcoder.
*
* @see VertexAttribute
*
* This is a no-op if the \p attribute is an invalid enum.
* This is a no-op if the \p bufferIndex is out of bounds.
*
*/
Builder& attribute(VertexAttribute attribute, uint8_t bufferIndex,
AttributeType attributeType,
uint32_t byteOffset = 0, uint8_t byteStride = 0) noexcept;
/**
* Sets whether a given attribute should be normalized. By default attributes are not
* normalized. A normalized attribute is mapped between 0 and 1 in the shader. This applies
* only to integer types.
*
* @param attribute Enum of the attribute to set the normalization flag to.
* @param normalize true to automatically normalize the given attribute.
* @return A reference to this Builder for chaining calls.
*
* This is a no-op if the \p attribute is an invalid enum.
*/
Builder& normalized(VertexAttribute attribute, bool normalize = true) noexcept;
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
VertexBuffer* build(Engine& engine);
private:
friend class FVertexBuffer;
};
/**
* Returns the vertex count.
* @return Number of vertices in this vertex buffer set.
*/
size_t getVertexCount() const noexcept;
/**
* Asynchronously copy-initializes the specified buffer from the given buffer data.
*
* Do not use this if you called enableBufferObjects() on the Builder.
*
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
* @param bufferIndex Index of the buffer to initialize. Must be between 0
* and Builder::bufferCount() - 1.
* @param buffer A BufferDescriptor representing the data used to initialize the buffer at
* index \p bufferIndex. BufferDescriptor points to raw, untyped data that will
* be copied as-is into the buffer.
* @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex
* buffer set.
*/
void setBufferAt(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer,
uint32_t byteOffset = 0);
/**
* Swaps in the given buffer object.
*
* To use this, you must first call enableBufferObjects() on the Builder.
*
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
* @param bufferIndex Index of the buffer to initialize. Must be between 0
* and Builder::bufferCount() - 1.
* @param bufferObject The handle to the GPU data that will be used in this buffer slot.
*/
void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* bufferObject);
};
} // namespace filament
#endif // TNT_FILAMENT_VERTEXBUFFER_H

911
ios/include/filament/View.h Normal file
View File

@@ -0,0 +1,911 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_VIEW_H
#define TNT_FILAMENT_VIEW_H
#include <filament/Color.h>
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
namespace filament {
class Camera;
class ColorGrading;
class MaterialInstance;
class RenderTarget;
class Scene;
class Texture;
class Viewport;
/**
* A View encompasses all the state needed for rendering a Scene.
*
* Renderer::render() operates on View objects. These View objects specify important parameters
* such as:
* - The Scene
* - The Camera
* - The Viewport
* - Some rendering parameters
*
* \note
* View instances are heavy objects that internally cache a lot of data needed for rendering.
* It is not advised for an application to use many View objects.
*
* For example, in a game, a View could be used for the main scene and another one for the
* game's user interface. More View instances could be used for creating special effects (e.g.
* a View is akin to a rendering pass).
*
*
* @see Renderer, Scene, Camera, RenderTarget
*/
class UTILS_PUBLIC View : public FilamentAPI {
public:
enum class QualityLevel : uint8_t {
LOW,
MEDIUM,
HIGH,
ULTRA
};
enum class BlendMode : uint8_t {
OPAQUE,
TRANSLUCENT
};
/**
* Dynamic resolution can be used to either reach a desired target frame rate
* by lowering the resolution of a View, or to increase the quality when the
* rendering is faster than the target frame rate.
*
* This structure can be used to specify the minimum scale factor used when
* lowering the resolution of a View, and the maximum scale factor used when
* increasing the resolution for higher quality rendering. The scale factors
* can be controlled on each X and Y axis independently. By default, all scale
* factors are set to 1.0.
*
* enabled: enable or disables dynamic resolution on a View
*
* homogeneousScaling: by default the system scales the major axis first. Set this to true
* to force homogeneous scaling.
*
* minScale: the minimum scale in X and Y this View should use
*
* maxScale: the maximum scale in X and Y this View should use
*
* quality: upscaling quality.
* LOW: 1 bilinear tap, Medium: 4 bilinear taps, High: 9 bilinear taps (tent)
*
* \note
* Dynamic resolution is only supported on platforms where the time to render
* a frame can be measured accurately. Dynamic resolution is currently only
* supported on Android.
*
* @see Renderer::FrameRateOptions
*
*/
struct DynamicResolutionOptions {
math::float2 minScale = math::float2(0.5f); //!< minimum scale factors in x and y
math::float2 maxScale = math::float2(1.0f); //!< maximum scale factors in x and y
bool enabled = false; //!< enable or disable dynamic resolution
bool homogeneousScaling = false; //!< set to true to force homogeneous scaling
QualityLevel quality = QualityLevel::LOW; //!< Upscaling quality
};
/**
* Options to control the bloom effect
*
* enabled: Enable or disable the bloom post-processing effect. Disabled by default.
*
* levels: Number of successive blurs to achieve the blur effect, the minimum is 3 and the
* maximum is 12. This value together with resolution influences the spread of the
* blur effect. This value can be silently reduced to accommodate the original
* image size.
*
* resolution: Resolution of bloom's minor axis. The minimum value is 2^levels and the
* the maximum is lower of the original resolution and 4096. This parameter is
* silently clamped to the minimum and maximum.
* It is highly recommended that this value be smaller than the target resolution
* after dynamic resolution is applied (horizontally and vertically).
*
* strength: how much of the bloom is added to the original image. Between 0 and 1.
*
* blendMode: Whether the bloom effect is purely additive (false) or mixed with the original
* image (true).
*
* anamorphism: Bloom's aspect ratio (x/y), for artistic purposes.
*
* threshold: When enabled, a threshold at 1.0 is applied on the source image, this is
* useful for artistic reasons and is usually needed when a dirt texture is used.
*
* dirt: A dirt/scratch/smudges texture (that can be RGB), which gets added to the
* bloom effect. Smudges are visible where bloom occurs. Threshold must be
* enabled for the dirt effect to work properly.
*
* dirtStrength: Strength of the dirt texture.
*/
struct BloomOptions {
enum class BlendMode : uint8_t {
ADD, //!< Bloom is modulated by the strength parameter and added to the scene
INTERPOLATE //!< Bloom is interpolated with the scene using the strength parameter
};
Texture* dirt = nullptr; //!< user provided dirt texture
float dirtStrength = 0.2f; //!< strength of the dirt texture
float strength = 0.10f; //!< bloom's strength between 0.0 and 1.0
uint32_t resolution = 360; //!< resolution of vertical axis (2^levels to 2048)
float anamorphism = 1.0f; //!< bloom x/y aspect-ratio (1/32 to 32)
uint8_t levels = 6; //!< number of blur levels (3 to 11)
BlendMode blendMode = BlendMode::ADD; //!< how the bloom effect is applied
bool threshold = true; //!< whether to threshold the source
bool enabled = false; //!< enable or disable bloom
float highlight = 1000.0f; //!< limit highlights to this value before bloom [10, +inf]
bool lensFlare = false; //!< enable screen-space lens flare
bool starburst = true; //!< enable starburst effect on lens flare
float chromaticAberration = 0.005f; //!< amount of chromatic aberration
uint8_t ghostCount = 4; //!< number of flare "ghosts"
float ghostSpacing = 0.6f; //!< spacing of the ghost in screen units [0, 1[
float ghostThreshold = 10.0f; //!< hdr threshold for the ghosts
float haloThickness = 0.1f; //!< thickness of halo in vertical screen units, 0 to disable
float haloRadius = 0.4f; //!< radius of halo in vertical screen units [0, 0.5]
float haloThreshold = 10.0f; //!< hdr threshold for the halo
};
/**
* Options to control fog in the scene
*/
struct FogOptions {
float distance = 0.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 )
float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1
float height = 0.0f; //!< fog's floor in world units
float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude
LinearColor color{0.5f}; //!< fog's color (linear), see fogColorFromIbl
float density = 0.1f; //!< fog's density at altitude given by 'height'
float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts
float inScatteringSize = -1.0f; //!< size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction.
bool enabled = false; //!< enable or disable fog
};
/**
* Options to control Depth of Field (DoF) effect in the scene.
*
* cocScale can be used to set the depth of field blur independently from the camera
* aperture, e.g. for artistic reasons. This can be achieved by setting:
* cocScale = cameraAperture / desiredDoFAperture
*
* @see Camera
*/
struct DepthOfFieldOptions {
enum class Filter : uint8_t {
NONE = 0,
MEDIAN = 2
};
float cocScale = 1.0f; //!< circle of confusion scale factor (amount of blur)
float maxApertureDiameter = 0.01f; //!< maximum aperture diameter in meters (zero to disable rotation)
bool enabled = false; //!< enable or disable depth of field effect
Filter filter = Filter::MEDIAN; //!< filter to use for filling gaps in the kernel
bool nativeResolution = false; //!< perform DoF processing at native resolution
/**
* Number of of rings used by the gather kernels. The number of rings affects quality
* and performance. The actual number of sample per pixel is defined
* as (ringCount * 2 - 1)^2. Here are a few commonly used values:
* 3 rings : 25 ( 5x 5 grid)
* 4 rings : 49 ( 7x 7 grid)
* 5 rings : 81 ( 9x 9 grid)
* 17 rings : 1089 (33x33 grid)
*
* With a maximum circle-of-confusion of 32, it is never necessary to use more than 17 rings.
*
* Usually all three settings below are set to the same value, however, it is often
* acceptable to use a lower ring count for the "fast tiles", which improves performance.
* Fast tiles are regions of the screen where every pixels have a similar
* circle-of-confusion radius.
*
* A value of 0 means default, which is 5 on desktop and 3 on mobile.
*
* @{
*/
uint8_t foregroundRingCount = 0; //!< number of kernel rings for foreground tiles
uint8_t backgroundRingCount = 0; //!< number of kernel rings for background tiles
uint8_t fastGatherRingCount = 0; //!< number of kernel rings for fast tiles
/** @}*/
/**
* maximum circle-of-confusion in pixels for the foreground, must be in [0, 32] range.
* A value of 0 means default, which is 32 on desktop and 24 on mobile.
*/
uint16_t maxForegroundCOC = 0;
/**
* maximum circle-of-confusion in pixels for the background, must be in [0, 32] range.
* A value of 0 means default, which is 32 on desktop and 24 on mobile.
*/
uint16_t maxBackgroundCOC = 0;
};
/**
* Options to control the vignetting effect.
*/
struct VignetteOptions {
float midPoint = 0.5f; //!< high values restrict the vignette closer to the corners, between 0 and 1
float roundness = 0.5f; //!< controls the shape of the vignette, from a rounded rectangle (0.0), to an oval (0.5), to a circle (1.0)
float feather = 0.5f; //!< softening amount of the vignette effect, between 0 and 1
LinearColorA color{0.0f, 0.0f, 0.0f, 1.0f}; //!< color of the vignette effect, alpha is currently ignored
bool enabled = false; //!< enables or disables the vignette effect
};
/**
* Structure used to set the precision of the color buffer and related quality settings.
*
* @see setRenderQuality, getRenderQuality
*/
struct RenderQuality {
/**
* Sets the quality of the HDR color buffer.
*
* A quality of HIGH or ULTRA means using an RGB16F or RGBA16F color buffer. This means
* colors in the LDR range (0..1) have a 10 bit precision. A quality of LOW or MEDIUM means
* using an R11G11B10F opaque color buffer or an RGBA16F transparent color buffer. With
* R11G11B10F colors in the LDR range have a precision of either 6 bits (red and green
* channels) or 5 bits (blue channel).
*/
QualityLevel hdrColorBuffer = QualityLevel::HIGH;
};
/**
* Options for screen space Ambient Occlusion (SSAO) and Screen Space Cone Tracing (SSCT)
* @see setAmbientOcclusionOptions()
*/
struct AmbientOcclusionOptions {
float radius = 0.3f; //!< Ambient Occlusion radius in meters, between 0 and ~10.
float power = 1.0f; //!< Controls ambient occlusion's contrast. Must be positive.
float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
float resolution = 0.5f;//!< How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0.
float intensity = 1.0f; //!< Strength of the Ambient Occlusion effect.
float bilateralThreshold = 0.05f; //!< depth distance that constitute an edge for filtering
QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO.
QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness
QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality
bool enabled = false; //!< enables or disables screen-space ambient occlusion
bool bentNormals = false; //!< enables bent normals computation from AO, and specular AO
float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider
/**
* Screen Space Cone Tracing (SSCT) options
* Ambient shadows from dominant light
*/
struct Ssct {
float lightConeRad = 1.0f; //!< full cone angle in radian, between 0 and pi/2
float shadowDistance = 0.3f; //!< how far shadows can be cast
float contactDistanceMax = 1.0f; //!< max distance for contact
float intensity = 0.8f; //!< intensity
math::float3 lightDirection{ 0, -1, 0 }; //!< light direction
float depthBias = 0.01f; //!< depth bias in world units (mitigate self shadowing)
float depthSlopeBias = 0.01f; //!< depth slope bias (mitigate self shadowing)
uint8_t sampleCount = 4; //!< tracing sample count, between 1 and 255
uint8_t rayCount = 1; //!< # of rays to trace, between 1 and 255
bool enabled = false; //!< enables or disables SSCT
} ssct;
};
/**
* Options for Temporal Anti-aliasing (TAA)
* @see setTemporalAntiAliasingOptions()
*/
struct TemporalAntiAliasingOptions {
float filterWidth = 1.0f; //!< reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
float feedback = 0.04f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
bool enabled = false; //!< enables or disables temporal anti-aliasing
};
/**
* List of available post-processing anti-aliasing techniques.
* @see setAntiAliasing, getAntiAliasing, setSampleCount
*/
enum class AntiAliasing : uint8_t {
NONE = 0, //!< no anti aliasing performed as part of post-processing
FXAA = 1 //!< FXAA is a low-quality but very efficient type of anti-aliasing. (default).
};
/**
* List of available post-processing dithering techniques.
*/
enum class Dithering : uint8_t {
NONE = 0, //!< No dithering
TEMPORAL = 1 //!< Temporal dithering (default)
};
/**
* List of available shadow mapping techniques.
* @see setShadowType
*/
enum class ShadowType : uint8_t {
PCF, //!< percentage-closer filtered shadows (default)
VSM //!< variance shadows
};
/**
* View-level options for VSM Shadowing.
* @see setVsmShadowOptions()
* @warning This API is still experimental and subject to change.
*/
struct VsmShadowOptions {
/**
* Sets the number of anisotropic samples to use when sampling a VSM shadow map. If greater
* than 0, mipmaps will automatically be generated each frame for all lights.
*
* The number of anisotropic samples = 2 ^ vsmAnisotropy.
*/
uint8_t anisotropy = 0;
/**
* Whether to generate mipmaps for all VSM shadow maps.
*/
bool mipmapping = false;
/**
* EVSM exponent.
* The maximum value permissible is 5.54 for a shadow map in fp16, or 42.0 for a
* shadow map in fp32. Currently the shadow map bit depth is always fp16.
*/
float exponent = 5.54f;
/**
* VSM minimum variance scale, must be positive.
*/
float minVarianceScale = 0.5f;
/**
* VSM light bleeding reduction amount, between 0 and 1.
*/
float lightBleedReduction = 0.15f;
};
/**
* Sets the View's name. Only useful for debugging.
* @param name Pointer to the View's name. The string is copied.
*/
void setName(const char* name) noexcept;
/**
* Returns the View's name
*
* @return a pointer owned by the View instance to the View's name.
*
* @attention Do *not* free the pointer or modify its content.
*/
const char* getName() const noexcept;
/**
* Set this View instance's Scene.
*
* @param scene Associate the specified Scene to this View. A Scene can be associated to
* several View instances.\n
* \p scene can be nullptr to dissociate the currently set Scene
* from this View.\n
* The View doesn't take ownership of the Scene pointer (which
* acts as a reference).
*
* @note
* There is no reference-counting.
* Make sure to dissociate a Scene from all Views before destroying it.
*/
void setScene(Scene* scene);
/**
* Returns the Scene currently associated with this View.
* @return A pointer to the Scene associated to this View. nullptr if no Scene is set.
*/
Scene* getScene() noexcept;
/**
* Returns the Scene currently associated with this View.
* @return A pointer to the Scene associated to this View. nullptr if no Scene is set.
*/
Scene const* getScene() const noexcept {
return const_cast<View*>(this)->getScene();
}
/**
* Specifies an offscreen render target to render into.
*
* By default, the view's associated render target is nullptr, which corresponds to the
* SwapChain associated with the engine.
*
* A view with a custom render target cannot rely on Renderer::ClearOptions, which only apply
* to the SwapChain. Such view can use a Skybox instead.
*
* @param renderTarget Render target associated with view, or nullptr for the swap chain.
*/
void setRenderTarget(RenderTarget* renderTarget) noexcept;
/**
* Gets the offscreen render target associated with this view.
*
* Returns nullptr if the render target is the swap chain (which is default).
*
* @see setRenderTarget
*/
RenderTarget* getRenderTarget() const noexcept;
/**
* Sets the rectangular region to render to.
*
* The viewport specifies where the content of the View (i.e. the Scene) is rendered in
* the render target. The Render target is automatically clipped to the Viewport.
*
* @param viewport The Viewport to render the Scene into. The Viewport is a value-type, it is
* therefore copied. The parameter can be discarded after this call returns.
*/
void setViewport(Viewport const& viewport) noexcept;
/**
* Returns the rectangular region that gets rendered to.
* @return A constant reference to View's viewport.
*/
Viewport const& getViewport() const noexcept;
/**
* Sets this View's Camera.
*
* @param camera Associate the specified Camera to this View. A Camera can be associated to
* several View instances.\n
* \p camera can be nullptr to dissociate the currently set Camera from this
* View.\n
* The View doesn't take ownership of the Camera pointer (which
* acts as a reference).
*
* @note
* There is no reference-counting.
* Make sure to dissociate a Camera from all Views before destroying it.
*/
void setCamera(Camera* camera) noexcept;
/**
* Returns the Camera currently associated with this View.
* @return A reference to the Camera associated to this View.
*/
Camera& getCamera() noexcept;
/**
* Returns the Camera currently associated with this View.
* @return A reference to the Camera associated to this View.
*/
Camera const& getCamera() const noexcept {
return const_cast<View*>(this)->getCamera();
}
/**
* Sets the blending mode used to draw the view into the SwapChain.
*
* @param blendMode either BlendMode::OPAQUE or BlendMode::TRANSLUCENT
* @see getBlendMode
*/
void setBlendMode(BlendMode blendMode) noexcept;
/**
*
* @return blending mode set by setBlendMode
* @see setBlendMode
*/
BlendMode getBlendMode() const noexcept;
/**
* Sets which layers are visible.
*
* Renderable objects can have one or several layers associated to them. Layers are
* represented with an 8-bits bitmask, where each bit corresponds to a layer.
* @see RenderableManager::setLayerMask().
*
* This call sets which of those layers are visible. Renderables in invisible layers won't be
* rendered.
*
* @param select a bitmask specifying which layer to set or clear using \p values.
* @param values a bitmask where each bit sets the visibility of the corresponding layer
* (1: visible, 0: invisible), only layers in \p select are affected.
*
* @note By default all layers are visible.
* @note This is a convenient way to quickly show or hide sets of Renderable objects.
*/
void setVisibleLayers(uint8_t select, uint8_t values) noexcept;
/**
* Get the visible layers.
*
* @see View::setVisibleLayers()
*/
uint8_t getVisibleLayers() const noexcept;
/**
* Enables or disables shadow mapping. Enabled by default.
*
* @param enabled true enables shadow mapping, false disables it.
*
* @see LightManager::Builder::castShadows(),
* RenderableManager::Builder::receiveShadows(),
* RenderableManager::Builder::castShadows(),
*/
void setShadowingEnabled(bool enabled) noexcept;
/**
* @return whether shadowing is enabled
*/
bool isShadowingEnabled() const noexcept;
/**
* Enables or disables screen space refraction. Enabled by default.
*
* @param enabled true enables screen space refraction, false disables it.
*/
void setScreenSpaceRefractionEnabled(bool enabled) noexcept;
/**
* @return whether screen space refraction is enabled
*/
bool isScreenSpaceRefractionEnabled() const noexcept;
/**
* Sets how many samples are to be used for MSAA in the post-process stage.
* Default is 1 and disables MSAA.
*
* @param count number of samples to use for multi-sampled anti-aliasing.\n
* 0: treated as 1
* 1: no anti-aliasing
* n: sample count. Effective sample could be different depending on the
* GPU capabilities.
*
* @note Anti-aliasing can also be performed in the post-processing stage, generally at lower
* cost. See setAntialiasing.
*
* @see setAntialiasing
*/
void setSampleCount(uint8_t count = 1) noexcept;
/**
* Returns the sample count set by setSampleCount(). Effective sample count could be different.
* A value of 0 or 1 means MSAA is disabled.
*
* @return value set by setSampleCount().
*/
uint8_t getSampleCount() const noexcept;
/**
* Enables or disables anti-aliasing in the post-processing stage. Enabled by default.
* MSAA can be enabled in addition, see setSampleCount().
*
* @param type FXAA for enabling, NONE for disabling anti-aliasing.
*
* @note For MSAA anti-aliasing, see setSamplerCount().
*
* @see setSampleCount
*/
void setAntiAliasing(AntiAliasing type) noexcept;
/**
* Queries whether anti-aliasing is enabled during the post-processing stage. To query
* whether MSAA is enabled, see getSampleCount().
*
* @return The post-processing anti-aliasing method.
*/
AntiAliasing getAntiAliasing() const noexcept;
/**
* Enables or disable temporal anti-aliasing (TAA). Disabled by default.
*
* @param options temporal anti-aliasing options
*/
void setTemporalAntiAliasingOptions(TemporalAntiAliasingOptions options) noexcept;
/**
* Returns temporal anti-aliasing options.
*
* @return temporal anti-aliasing options
*/
TemporalAntiAliasingOptions const& getTemporalAntiAliasingOptions() const noexcept;
/**
* Sets this View's color grading transforms.
*
* @param colorGrading Associate the specified ColorGrading to this View. A ColorGrading can be
* associated to several View instances.\n
* \p colorGrading can be nullptr to dissociate the currently set
* ColorGrading from this View. Doing so will revert to the use of the
* default color grading transforms.\n
* The View doesn't take ownership of the ColorGrading pointer (which
* acts as a reference).
*
* @note
* There is no reference-counting.
* Make sure to dissociate a ColorGrading from all Views before destroying it.
*/
void setColorGrading(ColorGrading* colorGrading) noexcept;
/**
* Returns the color grading transforms currently associated to this view.
* @return A pointer to the ColorGrading associated to this View.
*/
const ColorGrading* getColorGrading() const noexcept;
/**
* Sets ambient occlusion options.
*
* @param options Options for ambient occlusion.
*/
void setAmbientOcclusionOptions(AmbientOcclusionOptions const& options) noexcept;
/**
* Gets the ambient occlusion options.
*
* @return ambient occlusion options currently set.
*/
AmbientOcclusionOptions const& getAmbientOcclusionOptions() const noexcept;
/**
* Enables or disables bloom in the post-processing stage. Disabled by default.
*
* @param options options
*/
void setBloomOptions(BloomOptions options) noexcept;
/**
* Queries the bloom options.
*
* @return the current bloom options for this view.
*/
BloomOptions getBloomOptions() const noexcept;
/**
* Enables or disables fog. Disabled by default.
*
* @param options options
*/
void setFogOptions(FogOptions options) noexcept;
/**
* Queries the fog options.
*
* @return the current fog options for this view.
*/
FogOptions getFogOptions() const noexcept;
/**
* Enables or disables Depth of Field. Disabled by default.
*
* @param options options
*/
void setDepthOfFieldOptions(DepthOfFieldOptions options) noexcept;
/**
* Queries the depth of field options.
*
* @return the current depth of field options for this view.
*/
DepthOfFieldOptions getDepthOfFieldOptions() const noexcept;
/**
* Enables or disables the vignetted effect in the post-processing stage. Disabled by default.
*
* @param options options
*/
void setVignetteOptions(VignetteOptions options) noexcept;
/**
* Queries the vignette options.
*
* @return the current vignette options for this view.
*/
VignetteOptions getVignetteOptions() const noexcept;
/**
* Enables or disables dithering in the post-processing stage. Enabled by default.
*
* @param dithering dithering type
*/
void setDithering(Dithering dithering) noexcept;
/**
* Queries whether dithering is enabled during the post-processing stage.
*
* @return the current dithering type for this view.
*/
Dithering getDithering() const noexcept;
/**
* Sets the dynamic resolution options for this view. Dynamic resolution options
* controls whether dynamic resolution is enabled, and if it is, how it behaves.
*
* @param options The dynamic resolution options to use on this view
*/
void setDynamicResolutionOptions(DynamicResolutionOptions const& options) noexcept;
/**
* Returns the dynamic resolution options associated with this view.
* @return value set by setDynamicResolutionOptions().
*/
DynamicResolutionOptions getDynamicResolutionOptions() const noexcept;
/**
* Sets the rendering quality for this view. Refer to RenderQuality for more
* information about the different settings available.
*
* @param renderQuality The render quality to use on this view
*/
void setRenderQuality(RenderQuality const& renderQuality) noexcept;
/**
* Returns the render quality used by this view.
* @return value set by setRenderQuality().
*/
RenderQuality getRenderQuality() const noexcept;
/**
* Sets options relative to dynamic lighting for this view.
*
* @param zLightNear Distance from the camera where the lights are expected to shine.
* This parameter can affect performance and is useful because depending
* on the scene, lights that shine close to the camera may not be
* visible -- in this case, using a larger value can improve performance.
* e.g. when standing and looking straight, several meters of the ground
* isn't visible and if lights are expected to shine there, there is no
* point using a short zLightNear. (Default 5m).
*
* @param zLightFar Distance from the camera after which lights are not expected to be visible.
* Similarly to zLightNear, setting this value properly can improve
* performance. (Default 100m).
*
*
* Together zLightNear and zLightFar must be chosen so that the visible influence of lights
* is spread between these two values.
*
*/
void setDynamicLightingOptions(float zLightNear, float zLightFar) noexcept;
/*
* Set the shadow mapping technique this View uses.
*
* The ShadowType affects all the shadows seen within the View.
*
* ShadowType::VSM imposes a restriction on marking renderables as only shadow receivers (but
* not casters). To ensure correct shadowing with VSM, all shadow participant renderables should
* be marked as both receivers and casters. Objects that are guaranteed to not cast shadows on
* themselves or other objects (such as flat ground planes) can be set to not cast shadows,
* which might improve shadow quality.
*
* @warning This API is still experimental and subject to change.
*/
void setShadowType(ShadowType shadow) noexcept;
/**
* Sets VSM shadowing options that apply across the entire View.
*
* Additional light-specific VSM options can be set with LightManager::setShadowOptions.
*
* Only applicable when shadow type is set to ShadowType::VSM.
*
* @param options Options for shadowing.
*
* @see setShadowType
*
* @warning This API is still experimental and subject to change.
*/
void setVsmShadowOptions(VsmShadowOptions const& options) noexcept;
/**
* Returns the VSM shadowing options associated with this View.
*
* @return value set by setVsmShadowOptions().
*/
VsmShadowOptions getVsmShadowOptions() const noexcept;
/**
* Enables or disables post processing. Enabled by default.
*
* Post-processing includes:
* - Bloom
* - Tone-mapping & gamma encoding
* - Dithering
* - MSAA
* - FXAA
* - Dynamic scaling
*
* Disabling post-processing forgoes color correctness as well as anti-aliasing and
* should only be used experimentally (e.g., for UI overlays).
*
* @param enabled true enables post processing, false disables it.
*
* @see setBloomOptions, setColorGrading, setAntiAliasing, setDithering, setSampleCount
*/
void setPostProcessingEnabled(bool enabled) noexcept;
//! Returns true if post-processing is enabled. See setPostProcessingEnabled() for more info.
bool isPostProcessingEnabled() const noexcept;
/**
* Inverts the winding order of front faces. By default front faces use a counter-clockwise
* winding order. When the winding order is inverted, front faces are faces with a clockwise
* winding order.
*
* Changing the winding order will directly affect the culling mode in materials
* (see Material::getCullingMode()).
*
* Inverting the winding order of front faces is useful when rendering mirrored reflections
* (water, mirror surfaces, front camera in AR, etc.).
*
* @param inverted True to invert front faces, false otherwise.
*/
void setFrontFaceWindingInverted(bool inverted) noexcept;
/**
* Returns true if the winding order of front faces is inverted.
* See setFrontFaceWindingInverted() for more information.
*/
bool isFrontFaceWindingInverted() const noexcept;
// for debugging...
//! debugging: allows to entirely disable frustum culling. (culling enabled by default).
void setFrustumCullingEnabled(bool culling) noexcept;
//! debugging: returns whether frustum culling is enabled.
bool isFrustumCullingEnabled() const noexcept;
//! debugging: sets the Camera used for rendering. It may be different from the culling camera.
void setDebugCamera(Camera* camera) noexcept;
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
Camera const* getDirectionalLightCamera() const noexcept;
/**
* List of available ambient occlusion techniques
* @deprecated use AmbientOcclusionOptions::enabled instead
*/
enum class UTILS_DEPRECATED AmbientOcclusion : uint8_t {
NONE = 0, //!< No Ambient Occlusion
SSAO = 1 //!< Basic, sampling SSAO
};
/**
* Activates or deactivates ambient occlusion.
* @deprecated use setAmbientOcclusionOptions() instead
* @see setAmbientOcclusionOptions
*
* @param ambientOcclusion Type of ambient occlusion to use.
*/
UTILS_DEPRECATED
void setAmbientOcclusion(AmbientOcclusion ambientOcclusion) noexcept;
/**
* Queries the type of ambient occlusion active for this View.
* @deprecated use getAmbientOcclusionOptions() instead
* @see getAmbientOcclusionOptions
*
* @return ambient occlusion type.
*/
UTILS_DEPRECATED
AmbientOcclusion getAmbientOcclusion() const noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_VIEW_H

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_VIEWPORT_H
#define TNT_FILAMENT_VIEWPORT_H
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <math/scalar.h>
#include <math/mathfwd.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/**
* Viewport describes a view port in pixel coordinates
*
* A view port is represented by its left-bottom coordinate, width and height in pixels.
*/
class UTILS_PUBLIC Viewport : public backend::Viewport {
public:
/**
* Creates a Viewport of zero width and height at the origin.
*/
Viewport() noexcept : backend::Viewport{} {}
Viewport(const Viewport& viewport) noexcept = default;
Viewport(Viewport&& viewport) noexcept = default;
Viewport& operator=(const Viewport& viewport) noexcept = default;
Viewport& operator=(Viewport&& viewport) noexcept = default;
/**
* Creates a Viewport from its left-bottom coordinates, width and height in pixels
*
* @param left left coordinate in pixel
* @param bottom bottom coordinate in pixel
* @param width width in pixel
* @param height height in pixel
*/
Viewport(int32_t left, int32_t bottom, uint32_t width, uint32_t height) noexcept
: backend::Viewport{ left, bottom, width, height } {
}
/**
* Returns whether the area of the view port is null.
*
* @return true if either width or height is 0 pixel.
*/
bool empty() const noexcept { return !width || !height; }
/**
* Computes a new scaled Viewport
* @param s scaling factor on the x and y axes.
* @return A new scaled Viewport. The coordinates and dimensions of the new Viewport are
* rounded to the nearest integer value.
*/
Viewport scale(math::float2 s) const noexcept;
private:
/**
* Compares two Viewports for equality
* @param lhs reference to the left hand side Viewport
* @param rhs reference to the right hand side Viewport
* @return true if \p rhs and \p lhs are identical.
*/
friend bool operator==(Viewport const& lhs, Viewport const& rhs) noexcept {
return (&rhs == &lhs) ||
(rhs.left == lhs.left && rhs.bottom == lhs.bottom &&
rhs.width == lhs.width && rhs.height == lhs.height);
}
/**
* Compares two Viewports for inequality
* @param lhs reference to the left hand side Viewport
* @param rhs reference to the right hand side Viewport
* @return true if \p rhs and \p lhs are different.
*/
friend bool operator!=(Viewport const& lhs, Viewport const& rhs) noexcept {
return !(rhs == lhs);
}
};
} // namespace filament
#endif // TNT_FILAMENT_VIEWPORT_H

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_FILAMESHIO_MESHREADER_H
#define TNT_FILAMENT_FILAMESHIO_MESHREADER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/CString.h>
namespace filament {
class Engine;
class VertexBuffer;
class IndexBuffer;
class MaterialInstance;
}
namespace utils {
class Path;
}
namespace filamesh {
/**
* This API can be used to read meshes stored in the "filamesh" format produced
* by the command line tool of the same name. This file format is documented in
* "docs/filamesh.md" in the Filament distribution.
*/
class UTILS_PUBLIC MeshReader {
public:
using Callback = void(*)(void* buffer, size_t size, void* user);
// Class to track material instances
class MaterialRegistry {
public:
MaterialRegistry();
MaterialRegistry(const MaterialRegistry& rhs);
MaterialRegistry& operator=(const MaterialRegistry& rhs);
~MaterialRegistry();
MaterialRegistry(MaterialRegistry&&);
MaterialRegistry& operator=(MaterialRegistry&&);
filament::MaterialInstance* getMaterialInstance(const utils::CString& name);
void registerMaterialInstance(const utils::CString& name,
filament::MaterialInstance* materialInstance);
void unregisterMaterialInstance(const utils::CString& name);
void unregisterAll();
std::size_t numRegistered() const noexcept;
void getRegisteredMaterials(filament::MaterialInstance** materialList,
utils::CString* materialNameList) const;
void getRegisteredMaterials(filament::MaterialInstance** materialList) const;
void getRegisteredMaterialNames(utils::CString* materialNameList) const;
private:
struct MaterialRegistryImpl;
MaterialRegistryImpl* mImpl;
};
struct Mesh {
utils::Entity renderable;
filament::VertexBuffer* vertexBuffer = nullptr;
filament::IndexBuffer* indexBuffer = nullptr;
};
/**
* Loads a filamesh renderable from the specified file. The material registry
* can be used to provide named materials. If a material found in the filamesh
* file cannot be matched to a material in the registry, a default material is
* used instead. The default material can be overridden by adding a material
* named "DefaultMaterial" to the registry.
*/
static Mesh loadMeshFromFile(filament::Engine* engine,
const utils::Path& path,
MaterialRegistry& materials);
/**
* Loads a filamesh renderable from an in-memory buffer. The material registry
* can be used to provide named materials. If a material found in the filamesh
* file cannot be matched to a material in the registry, a default material is
* used instead. The default material can be overridden by adding a material
* named "DefaultMaterial" to the registry.
*/
static Mesh loadMeshFromBuffer(filament::Engine* engine,
void const* data, Callback destructor, void* user,
MaterialRegistry& materials);
/**
* Loads a filamesh renderable from an in-memory buffer. The material registry
* can be used to provide named materials. All the primitives of the decoded
* renderable are assigned the specified default material.
*/
static Mesh loadMeshFromBuffer(filament::Engine* engine,
void const* data, Callback destructor, void* user,
filament::MaterialInstance* defaultMaterial);
};
} // namespace filamesh
#endif // TNT_FILAMENT_FILAMESHIO_MESHREADER_H

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_GEOMETRY_SURFACEORIENTATION_H
#define TNT_GEOMETRY_SURFACEORIENTATION_H
#include <math/quat.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
namespace filament {
/**
* Mesh-related utilities.
*/
namespace geometry {
struct OrientationBuilderImpl;
struct OrientationImpl;
/**
* The surface orientation helper can be used to populate Filament-style TANGENTS buffers.
*/
class UTILS_PUBLIC SurfaceOrientation {
public:
/**
* The Builder is used to construct an immutable surface orientation helper.
*
* Clients provide pointers into their own data, which is synchronously consumed during build().
* At a minimum, clients must supply a vertex count. They can supply data in any of the
* following combinations:
*
* 1. normals only ........................... not recommended, selects arbitrary orientation
* 2. normals + tangents ..................... sign of W determines bitangent orientation
* 3. normals + uvs + positions + indices .... selects Lengyels Method
* 4. positions + indices .................... generates normals for flat shading only
*
* Additionally, the client-side data has the following type constraints:
*
* - Normals must be float3
* - Tangents must be float4
* - UVs must be float2
* - Positions must be float3
* - Triangles must be uint3 or ushort3
*
* Currently, mikktspace is not supported because it requires re-indexing the mesh. Instead
* we use the method described by Eric Lengyel in "Foundations of Game Engine Development"
* (Volume 2, Chapter 7).
*/
class Builder {
public:
Builder() noexcept;
~Builder() noexcept;
Builder(Builder&& that) noexcept;
Builder& operator=(Builder&& that) noexcept;
/**
* This attribute is required.
*/
Builder& vertexCount(size_t vertexCount) noexcept;
Builder& normals(const filament::math::float3*, size_t stride = 0) noexcept;
Builder& tangents(const filament::math::float4*, size_t stride = 0) noexcept;
Builder& uvs(const filament::math::float2*, size_t stride = 0) noexcept;
Builder& positions(const filament::math::float3*, size_t stride = 0) noexcept;
Builder& triangleCount(size_t triangleCount) noexcept;
Builder& triangles(const filament::math::uint3*) noexcept;
Builder& triangles(const filament::math::ushort3*) noexcept;
/**
* Generates quats or returns null if the submitted data is an incomplete combination.
*/
SurfaceOrientation* build();
private:
OrientationBuilderImpl* mImpl;
Builder(const Builder&) = delete;
Builder& operator=(const Builder&) = delete;
};
~SurfaceOrientation() noexcept;
SurfaceOrientation(SurfaceOrientation&& that) noexcept;
SurfaceOrientation& operator=(SurfaceOrientation&& that) noexcept;
/**
* Returns the vertex count.
*/
size_t getVertexCount() const noexcept;
/**
* Converts quaternions into the desired output format and writes up to "quatCount"
* to the given output pointer. Normally quatCount should be equal to the vertex count.
* The optional stride is the desired quat-to-quat stride in bytes.
* @{
*/
void getQuats(filament::math::quatf* out, size_t quatCount, size_t stride = 0) const noexcept;
void getQuats(filament::math::short4* out, size_t quatCount, size_t stride = 0) const noexcept;
void getQuats(filament::math::quath* out, size_t quatCount, size_t stride = 0) const noexcept;
/**
* @}
*/
private:
SurfaceOrientation(OrientationImpl*) noexcept;
SurfaceOrientation(const SurfaceOrientation&) = delete;
SurfaceOrientation& operator=(const SurfaceOrientation&) = delete;
OrientationImpl* mImpl;
friend struct OrientationBuilderImpl;
};
} // namespace geometry
} // namespace filament
#endif // TNT_GEOMETRY_SURFACEORIENTATION_H

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_GEOMETRY_TRANSCODER_H
#define TNT_GEOMETRY_TRANSCODER_H
#include <utils/compiler.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace geometry {
enum class ComponentType {
BYTE, //!< If normalization is enabled, this maps from [-127,127] to [-1,+1]
UBYTE, //!< If normalization is enabled, this maps from [0,255] to [0, +1]
SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1]
USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1]
HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits.
};
/**
* Creates a function object that can convert vertex attribute data into tightly packed floats.
*
* This is especially useful for 3-component formats which are not supported by all backends.
* e.g. The Vulkan minspec includes float3 but not short3.
*
* Usage Example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* using filament::geometry::Transcoder;
* using filament::geometry::ComponentType;
*
* Transcoder transcode({
* .componentType = ComponentType::BYTE,
* .normalized = true,
* .componentCount = 3,
* .inputStrideBytes = 0
* });
*
* transcode(outputPtr, inputPtr, count);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* The interpretation of signed normalized data is consistent with Vulkan and OpenGL ES 3.0+.
* Note that this slightly differs from earlier versions of OpenGL ES. For example, a signed byte
* value of -127 maps exactly to -1.0f under ES3 and VK rules, but not ES2.
*/
class UTILS_PUBLIC Transcoder {
public:
/**
* Describes the format of all input data that get passed to this transcoder object.
*/
struct Config {
ComponentType componentType;
bool normalized;
uint32_t componentCount;
uint32_t inputStrideBytes = 0; //!< If stride is 0, the transcoder assumes tight packing.
};
/**
* Creates an immutable function object with the specified configuration.
*
* The config is not passed by const reference to allow for type inference at the call site.
*/
Transcoder(Config config) noexcept : mConfig(config) {}
/**
* Converts arbitrary data into tightly packed 32-bit floating point values.
*
* If target is non-null, writes up to "count" items into target and returns the number of bytes
* actually written.
*
* If target is null, returns the number of bytes required.
*
* @param target Client owned area to write into, or null for a size query
* @param source Pointer to the data to read from (does not get retained)
* @param count The maximum number of items to write (i.e. number of float3 values, not bytes)
* @return Number of bytes required to contain "count" items after conversion to packed floats
*
*/
size_t operator()(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source,
size_t count) const noexcept;
private:
const Config mConfig;
};
} // namespace geometry
} // namespace filament
#endif // TNT_GEOMETRY_TRANSCODER_H

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_ANIMATOR_H
#define GLTFIO_ANIMATOR_H
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
namespace gltfio {
struct FFilamentAsset;
struct FFilamentInstance;
struct AnimatorImpl;
/**
* \class Animator Animator.h gltfio/Animator.h
* \brief Updates matrices according to glTF \c animation and \c skin definitions.
*
* Animator can be used for two things:
* - Updating matrices in filament::TransformManager components according to glTF \c animation definitions.
* - Updating bone matrices in filament::RenderableManager components according to glTF \c skin definitions.
*
* For a usage example, see the documentation for AssetLoader.
*/
class UTILS_PUBLIC Animator {
public:
/**
* Applies rotation, translation, and scale to entities that have been targeted by the given
* animation definition. Uses filament::TransformManager.
*
* @param animationIndex Zero-based index for the \c animation of interest.
* @param time Elapsed time of interest in seconds.
*/
void applyAnimation(size_t animationIndex, float time) const;
/**
* Computes root-to-node transforms for all bone nodes, then passes
* the results into filament::RenderableManager::setBones.
* Uses filament::TransformManager and filament::RenderableManager.
*
* NOTE: this operation is independent of \c animation.
*/
void updateBoneMatrices();
/** Returns the number of \c animation definitions in the glTF asset. */
size_t getAnimationCount() const;
/** Returns the duration of the specified glTF \c animation in seconds. */
float getAnimationDuration(size_t animationIndex) const;
/**
* Returns a weak reference to the string name of the specified \c animation, or an
* empty string if none was specified.
*/
const char* getAnimationName(size_t animationIndex) const;
// For internal use only.
void addInstance(FFilamentInstance* instance);
private:
/*! \cond PRIVATE */
friend struct FFilamentAsset;
friend struct FFilamentInstance;
/*! \endcond */
Animator(FFilamentAsset* asset, FFilamentInstance* instance);
~Animator();
AnimatorImpl* mImpl;
};
} // namespace gltfio
#endif // GLTFIO_ANIMATOR_H

View File

@@ -0,0 +1,244 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_ASSETLOADER_H
#define GLTFIO_ASSETLOADER_H
#include <filament/Engine.h>
#include <filament/Material.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/MaterialProvider.h>
#include <utils/compiler.h>
namespace utils {
class EntityManager;
class NameComponentManager;
}
/**
* Loader and pipeline for glTF 2.0 assets.
*/
namespace gltfio {
/**
* \struct AssetConfiguration AssetLoader.h gltfio/AssetLoader.h
* \brief Construction parameters for AssetLoader.
*/
struct AssetConfiguration {
//! The engine that the loader should pass to builder objects (e.g.
//! filament::VertexBuffer::Builder).
class filament::Engine* engine;
//! Controls whether the loader uses filamat to generate materials on the fly, or loads a small
//! set of precompiled ubershader materials. Deleting the MaterialProvider is the client's
//! responsibility. See createMaterialGenerator() and createUbershaderLoader().
MaterialProvider* materials;
//! Optional manager for associating string names with entities in the transform hierarchy.
utils::NameComponentManager* names = nullptr;
//! Overrides the factory used for creating entities in the transform hierarchy. If this is not
//! specified, AssetLoader will use the singleton EntityManager associated with the current
//! process.
utils::EntityManager* entities = nullptr;
//! Optional default node name for anonymous nodes
char* defaultNodeName = nullptr;
};
/**
* \class AssetLoader AssetLoader.h gltfio/AssetLoader.h
* \brief Consumes glTF content and produces FilamentAsset objects.
*
* AssetLoader consumes a blob of glTF 2.0 content (either JSON or GLB) and produces a FilamentAsset
* object, which is a bundle of Filament entities, material instances, textures, vertex buffers,
* and index buffers.
*
* Clients must use AssetLoader to create and destroy FilamentAsset objects.
*
* AssetLoader does not fetch external buffer data or create textures on its own. Clients can use
* ResourceLoader for this, which obtains the URI list from the asset. This is demonstrated in the
* code snippet below.
*
* AssetLoader also owns a cache of filament::Material objects that may be re-used across multiple
* loads.
*
* Example usage:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto engine = Engine::create();
* auto materials = createMaterialGenerator(engine);
* auto loader = AssetLoader::create({engine, materials});
*
* // Parse the glTF content and create Filament entities.
* std::vector<uint8_t> content(...);
* FilamentAsset* asset = loader->createAssetFromJson(content.data(), content.size());
* content.clear();
*
* // Load buffers and textures from disk.
* ResourceLoader({engine, ".", true}).loadResources(asset);
*
* // Obtain the simple animation interface.
* Animator* animator = asset->getAnimator();
*
* // Free the glTF hierarchy as it is no longer needed.
* asset->releaseSourceData();
*
* // Add renderables to the scene.
* scene->addEntities(asset->getEntities(), asset->getEntityCount());
*
* // Execute the render loop and play the first animation.
* do {
* animator->applyAnimation(0, time);
* animator->updateBoneMatrices();
* if (renderer->beginFrame(swapChain)) {
* renderer->render(view);
* renderer->endFrame();
* }
* } while (!quit);
*
* scene->removeEntities(asset->getEntities(), asset->getEntityCount());
* loader->destroyAsset(asset);
* materials->destroyMaterials();
* delete materials;
* AssetLoader::destroy(&loader);
* Engine::destroy(&engine);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC AssetLoader {
public:
/**
* Creates an asset loader for the given configuration, which specifies the Filament engine.
*
* The engine is held weakly, used only for the creation and destruction of Filament objects.
* The optional name component manager can be used to assign names to renderables.
* The material source specifies whether to use filamat to generate materials on the fly, or to
* load a small set of precompiled ubershader materials.
*/
static AssetLoader* create(const AssetConfiguration& config);
/**
* Frees the loader.
*
* This does not not automatically free the cache of materials, nor
* does it free the entities for created assets (see destroyAsset).
*/
static void destroy(AssetLoader** loader);
/**
* Takes a pointer to the contents of a JSON-based glTF 2.0 file and returns a bundle
* of Filament objects. Returns null on failure.
*/
FilamentAsset* createAssetFromJson(const uint8_t* bytes, uint32_t nbytes);
/**
* Takes a pointer to the contents of a GLB glTF 2.0 file and returns a bundle
* of Filament objects. Returns null on failure.
*/
FilamentAsset* createAssetFromBinary(const uint8_t* bytes, uint32_t nbytes);
/**
* Consumes the contents of a glTF 2.0 file and produces a primary asset with one or more
* instances. The primary asset has ownership over the instances.
*
* The returned instances share their textures, material instances, and vertex buffers with the
* primary asset. However each instance has its own unique set of entities, transform
* components, and renderable components. Instances are freed when the primary asset is freed.
*
* Light components are not instanced, they belong only to the primary asset.
*
* Clients must use ResourceLoader to load resources on the primary asset.
*
* The entity accessor and renderable stack API in the primary asset can be used to control the
* union of all instances. The individual FilamentInstance objects can be used to access each
* instance's partition of entities. Similarly, the Animator in the primary asset controls all
* instances. To animate instances individually, use FilamentInstance::getAnimator().
*
* @param bytes the contents of a glTF 2.0 file (JSON or GLB)
* @param numBytes the number of bytes in "bytes"
* @param instances destination pointer, to be populated by the requested number of instances
* @param numInstances requested number of instances
* @return the primary asset that has ownership over all instances
*/
FilamentAsset* createInstancedAsset(const uint8_t* bytes, uint32_t numBytes,
FilamentInstance** instances, size_t numInstances);
/**
* Adds a new instance to an instanced asset.
*
* Use this with caution. It is more efficient to pre-allocate a max number of instances, and
* gradually add them to the scene as needed. Instances can also be "recycled" by removing and
* re-adding them to the scene.
*
* NOTE: destroyInstance() does not exist because gltfio favors flat arrays for storage of
* entity lists and instance lists, which would be slow to shift. We also wish to discourage
* create/destroy churn, as noted above.
*
* This cannot be called after FilamentAsset::releaseSourceData().
* This cannot be called on a non-instanced asset.
* See also AssetLoader::createInstancedAsset().
*/
FilamentInstance* createInstance(FilamentAsset* primary);
/**
* Allows clients to enable diagnostic shading on newly-loaded assets.
*/
void enableDiagnostics(bool enable = true);
/**
* Destroys the given asset and all of its associated Filament objects.
*
* This destroys entities, components, material instances, vertex buffers, index buffers,
* and textures. This does not necessarily immediately free all source data, since
* texture decoding or GPU uploading might be underway.
*/
void destroyAsset(const FilamentAsset* asset);
/**
* Gets a weak reference to an array of cached materials, used internally to create material
* instances for assets.
*/
const filament::Material* const* getMaterials() const noexcept;
/**
* Gets the number of cached materials.
*/
size_t getMaterialsCount() const noexcept;
utils::NameComponentManager* getNames() const noexcept;
MaterialProvider* getMaterialProvider() const noexcept;
/*! \cond PRIVATE */
protected:
AssetLoader() noexcept = default;
~AssetLoader() = default;
public:
AssetLoader(AssetLoader const&) = delete;
AssetLoader(AssetLoader&&) = delete;
AssetLoader& operator=(AssetLoader const&) = delete;
AssetLoader& operator=(AssetLoader&&) = delete;
/*! \endcond */
};
} // namespace gltfio
#endif // GLTFIO_ASSETLOADER_H

View File

@@ -0,0 +1,251 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_FILAMENTASSET_H
#define GLTFIO_FILAMENTASSET_H
#include <filament/Box.h>
#include <filament/TextureSampler.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
namespace filament {
class Camera;
class Engine;
class MaterialInstance;
}
namespace gltfio {
class Animator;
class FilamentInstance;
/**
* \class FilamentAsset FilamentAsset.h gltfio/FilamentAsset.h
* \brief Owns a bundle of Filament objects that have been created by AssetLoader.
*
* For usage instructions, see the documentation for AssetLoader.
*
* This class owns a hierarchy of entities that have been loaded from a glTF asset. Every entity has
* a filament::TransformManager component, and some entities also have \c Name, \c Renderable,
* \c Light, or \c Camera components.
*
* In addition to the aforementioned entities, an asset has strong ownership over a list of
* filament::VertexBuffer, filament::IndexBuffer, filament::MaterialInstance, filament::Texture,
* and, optionally, a simple animation engine (gltfio::Animator).
*
* Clients must use ResourceLoader to create filament::Texture objects, compute tangent quaternions,
* and upload data into vertex buffers and index buffers.
*
* \todo Only the default glTF scene is loaded, other glTF scenes are ignored.
*/
class UTILS_PUBLIC FilamentAsset {
public:
/**
* Gets the list of entities, one for each glTF node. All of these have a Transform component.
* Some of the returned entities may also have a Renderable component and/or a Light component.
*/
const utils::Entity* getEntities() const noexcept;
/**
* Gets the number of entities returned by getEntities().
*/
size_t getEntityCount() const noexcept;
/**
* Gets the list of entities in the scene representing lights. All of these have a Light component.
*/
const utils::Entity* getLightEntities() const noexcept;
/**
* Gets the number of entities returned by getLightEntities().
*/
size_t getLightEntityCount() const noexcept;
/**
* Gets the list of entities in the scene representing cameras. All of these have a \c Camera
* component.
*
* Note about aspect ratios:
* gltfio always uses an aspect ratio of 1.0 when setting the projection matrix for perspective
* cameras. gltfio then sets the camera's scaling matrix with the aspect ratio specified in the
* glTF file (if present).
*
* The camera's scaling matrix allows clients to adjust the aspect ratio independently from the
* camera's projection.
*
* To change the aspect ratio of the glTF camera:
*
* camera->setScaling(double4 {1.0 / newAspectRatio, 1.0, 1.0, 1.0});
*
* @see filament::Camera::setScaling
*/
const utils::Entity* getCameraEntities() const noexcept;
/**
* Gets the number of entities returned by getCameraEntities().
*/
size_t getCameraEntityCount() const noexcept;
/**
* Gets the transform root for the asset, which has no matching glTF node.
*
* This node exists for convenience, allowing users to transform the entire asset. For instanced
* assets, this is a "super root" where each of its children is a root in a particular instance.
* This allows users to transform all instances en masse if they wish to do so.
*/
utils::Entity getRoot() const noexcept;
/**
* Pops a ready renderable off the queue, or returns 0 if no renderables have become ready.
*
* NOTE: To determine the progress percentage or completion status, please use
* ResourceLoader#asyncGetLoadProgress. To get the number of ready renderables,
* please use popRenderables().
*
* This method allows clients to progressively add the asset's renderables to the scene as
* textures gradually become ready through asynchronous loading. For example, on every frame
* progressive applications can do something like this:
*
* while (utils::Entity e = popRenderable()) { scene.addEntity(e); }
*
* \see ResourceLoader#asyncBeginLoad
* \see popRenderables()
*/
utils::Entity popRenderable() noexcept;
/**
* Pops up to "count" ready renderables off the queue, or returns the available number.
*
* The given pointer should either be null or point to memory that can hold up to count
* entities. If the pointer is null, returns the number of available renderables. Otherwise
* returns the number of entities that have been written.
*
* \see ResourceLoader#asyncBeginLoad
*/
size_t popRenderables(utils::Entity* entities, size_t count) noexcept;
/** Gets all material instances. These are already bound to renderables. */
const filament::MaterialInstance* const* getMaterialInstances() const noexcept;
/** Gets all material instances (non-const). These are already bound to renderables. */
filament::MaterialInstance* const* getMaterialInstances() noexcept;
/** Gets the number of materials returned by getMaterialInstances(). */
size_t getMaterialInstanceCount() const noexcept;
/** Gets resource URIs for all externally-referenced buffers. */
const char* const* getResourceUris() const noexcept;
/** Gets the number of resource URIs returned by getResourceUris(). */
size_t getResourceUriCount() const noexcept;
/** Gets the bounding box computed from the supplied min / max values in glTF accessors. */
filament::Aabb getBoundingBox() const noexcept;
/** Gets the NameComponentManager label for the given entity, if it exists. */
const char* getName(utils::Entity) const noexcept;
/** Returns the first entity with the given name, or 0 if none exist. */
utils::Entity getFirstEntityByName(const char* name) noexcept;
/**
* Gets a list of entities with the given name.
*
* @param name Null-terminated string to match.
* @param entities Pointer to an array to populate.
* @param maxCount Maximum number of entities to retrieve.
*
* @return If entities is non-null, the number of entities written to the entity pointer.
* Otherwise this returns the number of entities with the given name.
*/
size_t getEntitiesByName(const char* name, utils::Entity* entities,
size_t maxCount) const noexcept;
/**
* Gets a list of entities whose names start with the given prefix.
*
* @param prefix Null-terminated prefix string to match.
* @param entities Pointer to an array to populate.
* @param maxCount Maximum number of entities to retrieve.
*
* @return If entities is non-null, the number of entities written to the entity pointer.
* Otherwise this returns the number of entities with the given prefix.
*/
size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities,
size_t maxCount) const noexcept;
/** Gets the glTF extras string for a specific node, or for the asset, if it exists. */
const char* getExtras(utils::Entity entity = {}) const noexcept;
/**
* Lazily creates the animation engine or returns it from the cache.
*
* The animator is owned by the asset and should not be manually deleted.
* The first time this is called, it must be called before FilamentAsset::releaseSourceData().
* If the asset is instanced, this returns a "primary" animator that controls all instances.
* To animate each instance individually, use \see FilamentInstance.
*/
Animator* getAnimator() noexcept;
/**
* Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy
* for diagnostic purposes. The wireframe is owned by the asset so clients should not delete it.
*/
utils::Entity getWireframe() noexcept;
/**
* Returns the Filament engine associated with the AssetLoader that created this asset.
*/
filament::Engine* getEngine() const noexcept;
/**
* Reclaims CPU-side memory for URI strings, binding lists, and raw animation data.
*
* This should only be called after ResourceLoader::loadResources().
* If using Animator, this should be called after getAnimator().
* If this is an instanced asset, this prevents creation of new instances.
*/
void releaseSourceData() noexcept;
/**
* Returns a weak reference to the underlying cgltf hierarchy. This becomes invalid after
* calling releaseSourceData().
*/
const void* getSourceAsset() noexcept;
/*! \cond PRIVATE */
FilamentInstance** getAssetInstances() noexcept;
size_t getAssetInstanceCount() const noexcept;
protected:
FilamentAsset() noexcept = default;
~FilamentAsset() = default;
public:
FilamentAsset(FilamentAsset const&) = delete;
FilamentAsset(FilamentAsset&&) = delete;
FilamentAsset& operator=(FilamentAsset const&) = delete;
FilamentAsset& operator=(FilamentAsset&&) = delete;
/*! \endcond */
};
} // namespace gltfio
#endif // GLTFIO_FILAMENTASSET_H

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_FILAMENTINSTANCE_H
#define GLTFIO_FILAMENTINSTANCE_H
#include <utils/compiler.h>
#include <utils/Entity.h>
namespace gltfio {
class Animator;
class FilamentAsset;
/**
* \class FilamentInstance FilamentInstance.h gltfio/FilamentInstance.h
* \brief Provides access to a hierarchy of entities that have been instanced from a glTF asset.
*
* Every entity has a filament::TransformManager component, and some entities also have \c Name or
* \c Renderable components.
*
* \see AssetLoader::createInstancedAsset()
*/
class UTILS_PUBLIC FilamentInstance {
public:
/**
* Gets the owner of this instance.
*/
FilamentAsset* getAsset() const noexcept;
/**
* Gets the list of entities in this instance, one for each glTF node. All of these have a
* Transform component. Some of the returned entities may also have a Renderable component or
* Name component.
*/
const utils::Entity* getEntities() const noexcept;
/**
* Gets the number of entities returned by getEntities().
*/
size_t getEntityCount() const noexcept;
/** Gets the transform root for the instance, which has no matching glTF node. */
utils::Entity getRoot() const noexcept;
/**
* Lazily creates the animation engine for the instance, or returns it from the cache.
*
* Note that an animator can be obtained either from an individual instance, or from the
* originating FilamentAsset. In the latter case, the animation frame is shared amongst all
* instances. If individual control is desired, users must obtain the animator from the
* individual instances.
*
* The animator is owned by the asset and should not be manually deleted.
* The first time this is called, it must be called before FilamentAsset::releaseSourceData().
*/
Animator* getAnimator() noexcept;
};
} // namespace gltfio
#endif // GLTFIO_FILAMENTINSTANCE_H

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// gltfio supports PNG and JPEG, disable all other formats.
#define STBI_NO_BMP
#define STBI_NO_PSD
#define STBI_NO_TGA
#define STBI_NO_GIF
#define STBI_NO_HDR
#define STBI_NO_PIC
#define STBI_NO_PNM
// For emscripten and Android builds, we never load from the file
// system, so we-opt out of the stdio functionality in stb.
#if defined(__EMSCRIPTEN__) || defined(ANDROID)
#define STBI_NO_STDIO
#endif
#include <stb_image.h>

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_MATERIALPROVIDER_H
#define GLTFIO_MATERIALPROVIDER_H
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <utils/compiler.h>
#include <array>
#include <string>
namespace gltfio {
enum class AlphaMode : uint8_t {
OPAQUE,
MASK,
BLEND
};
/**
* \struct MaterialKey MaterialProvider.h gltfio/MaterialProvider.h
* \brief Small POD structure that specifies the requirements for a glTF material.
* \note This key is processed by MurmurHashFn so please make padding explicit.
*/
struct alignas(4) MaterialKey {
// -- 32 bit boundary --
bool doubleSided : 1;
bool unlit : 1;
bool hasVertexColors : 1;
bool hasBaseColorTexture : 1;
bool hasNormalTexture : 1;
bool hasOcclusionTexture : 1;
bool hasEmissiveTexture : 1;
bool useSpecularGlossiness : 1;
AlphaMode alphaMode : 4;
bool enableDiagnostics : 4;
union {
struct {
bool hasMetallicRoughnessTexture : 1;
uint8_t metallicRoughnessUV : 7;
};
struct {
bool hasSpecularGlossinessTexture : 1;
uint8_t specularGlossinessUV : 7;
};
};
uint8_t baseColorUV;
// -- 32 bit boundary --
bool hasClearCoatTexture : 1;
uint8_t clearCoatUV : 7;
bool hasClearCoatRoughnessTexture : 1;
uint8_t clearCoatRoughnessUV : 7;
bool hasClearCoatNormalTexture : 1;
uint8_t clearCoatNormalUV : 7;
bool hasClearCoat : 1;
bool hasTransmission : 1;
bool hasTextureTransforms : 6;
// -- 32 bit boundary --
uint8_t emissiveUV;
uint8_t aoUV;
uint8_t normalUV;
bool hasTransmissionTexture : 1;
uint8_t transmissionUV : 7;
// -- 32 bit boundary --
bool hasSheenColorTexture : 1;
uint8_t sheenColorUV : 7;
bool hasSheenRoughnessTexture : 1;
uint8_t sheenRoughnessUV : 7;
bool hasVolumeThicknessTexture : 1;
uint8_t volumeThicknessUV : 7;
bool hasSheen : 1;
bool hasIOR : 1;
bool hasVolume : 1;
};
static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected padding.");
bool operator==(const MaterialKey& k1, const MaterialKey& k2);
// Define a mapping from a uv set index in the source asset to one of Filament's uv sets.
enum UvSet : uint8_t { UNUSED, UV0, UV1 };
constexpr int UvMapSize = 8;
using UvMap = std::array<UvSet, UvMapSize>;
inline uint8_t getNumUvSets(const UvMap& uvmap) {
return std::max({
uvmap[0], uvmap[1], uvmap[2], uvmap[3],
uvmap[4], uvmap[5], uvmap[6], uvmap[7],
});
};
/**
* \class MaterialProvider MaterialProvider.h gltfio/MaterialProvider.h
* \brief Interface to a provider of glTF materials (has two implementations).
*
* - The \c MaterialGenerator implementation generates materials at run time (which can be slow) and
* requires the filamat library, but produces streamlined shaders. See createMaterialGenerator().
*
* - The \c UbershaderLoader implementation uses a small number of pre-built materials with complex
* fragment shaders, but does not require any run time work or usage of filamat. See
* createUbershaderLoader().
*
* Both implementations of MaterialProvider maintain a small cache of materials which must be
* explicitly freed using destroyMaterials(). These materials are not freed automatically when the
* MaterialProvider is destroyed, which allows clients to take ownership if desired.
*
*/
class UTILS_PUBLIC MaterialProvider {
public:
virtual ~MaterialProvider() {}
/**
* Creates or fetches a compiled Filament material, then creates an instance from it.
*
* @param config Specifies requirements; might be mutated due to resource constraints.
* @param uvmap Output argument that gets populated with a small table that maps from a glTF uv
* index to a Filament uv index.
* @param label Optional tag that is not a part of the cache key.
*/
virtual filament::MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label = "material") = 0;
/**
* Gets a weak reference to the array of cached materials.
*/
virtual const filament::Material* const* getMaterials() const noexcept = 0;
/**
* Gets the number of cached materials.
*/
virtual size_t getMaterialsCount() const noexcept = 0;
/**
* Destroys all cached materials.
*
* This is not called automatically when MaterialProvider is destroyed, which allows
* clients to take ownership of the cache if desired.
*/
virtual void destroyMaterials() = 0;
/**
* Returns true if the presence of the given vertex attribute is required.
*
* Some types of providers (e.g. ubershader) require dummy attribute values
* if the glTF model does not provide them.
*/
virtual bool needsDummyData(filament::VertexAttribute attrib) const noexcept = 0;
};
void constrainMaterial(MaterialKey* key, UvMap* uvmap);
void processShaderString(std::string* shader, const UvMap& uvmap,
const MaterialKey& config);
/**
* Creates a material provider that builds materials on the fly, composing GLSL at run time.
*
* @param optimizeShaders Optimizes shaders, but at significant cost to construction time.
* @return New material provider that can build materials at run time.
*
* Requires \c libfilamat to be linked in. Not available in \c libgltfio_core.
*
* @see createUbershaderLoader
*/
UTILS_PUBLIC
MaterialProvider* createMaterialGenerator(filament::Engine* engine, bool optimizeShaders = false);
/**
* Creates a material provider that loads a small set of pre-built materials.
*
* @return New material provider that can quickly load a material from a cache.
*
* Requires \c libgltfio_resources to be linked in.
*
* @see createMaterialGenerator
*/
UTILS_PUBLIC
MaterialProvider* createUbershaderLoader(filament::Engine* engine);
} // namespace gltfio
#endif // GLTFIO_MATERIALPROVIDER_H

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_RESOURCELOADER_H
#define GLTFIO_RESOURCELOADER_H
#include <gltfio/FilamentAsset.h>
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
namespace filament {
class Engine;
}
namespace gltfio {
struct FFilamentAsset;
class AssetPool;
/**
* \struct ResourceConfiguration ResourceLoader.h gltfio/ResourceLoader.h
* \brief Construction parameters for ResourceLoader.
*/
struct ResourceConfiguration {
//! The engine that the loader should pass to builder objects (e.g.
//! filament::Texture::Builder).
class filament::Engine* engine;
//! Optional path or URI that points to the base glTF file. This is used solely
//! to resolve relative paths. The string pointer is not retained.
const char* gltfPath;
//! If true, adjusts skinning weights to sum to 1. Well formed glTF files do not need this,
//! but it is useful for robustness.
bool normalizeSkinningWeights;
//! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files
//! do not need this, but it is useful for robustness.
bool recomputeBoundingBoxes;
};
/**
* \class ResourceLoader ResourceLoader.h gltfio/ResourceLoader.h
* \brief Prepares and uploads vertex buffers and textures to the GPU.
*
* For a usage example, see the documentation for AssetLoader.
*
* ResourceLoader must be destroyed on the same thread that calls filament::Renderer::render()
* because it listens to filament::backend::BufferDescriptor callbacks in order to determine when to
* free CPU-side data blobs.
*
* \todo If clients persist their ResourceLoader, Filament textures are currently re-created upon
* subsequent re-loads of the same asset. To fix this, we would need to enable shared ownership
* of Texture objects between ResourceLoader and FilamentAsset.
*/
class UTILS_PUBLIC ResourceLoader {
public:
using BufferDescriptor = filament::backend::BufferDescriptor;
ResourceLoader(const ResourceConfiguration& config);
~ResourceLoader();
/**
* Feeds the binary content of an external resource into the loader's URI cache.
*
* On some platforms, `ResourceLoader` does not know how to download external resources on its
* own (external resources might come from a filesystem, a database, or the internet) so this
* method allows clients to download external resources and push them to the loader.
*
* Every resource should be passed in before calling #loadResources or #asyncBeginLoad. See
* also FilamentAsset#getResourceUris.
*
* When loading GLB files (as opposed to JSON-based glTF files), clients typically do not
* need to call this method.
*/
void addResourceData(const char* uri, BufferDescriptor&& buffer);
/**
* Checks if the given resource has already been added to the URI cache.
*/
bool hasResourceData(const char* uri) const;
/**
* Frees memory by evicting the URI cache that was populated via addResourceData.
*
* This can be called only after a model is fully loaded or after loading has been cancelled.
*/
void evictResourceData();
/**
* Loads resources for the given asset from the filesystem or data cache and "finalizes" the
* asset by transforming the vertex data format if necessary, decoding image files, supplying
* tangent data, etc.
*
* Returns false if resources have already been loaded, or if one or more resources could not
* be loaded.
*
* Note: this method is synchronous and blocks until all textures have been decoded.
* For an asynchronous alternative, see #asyncBeginLoad.
*/
bool loadResources(FilamentAsset* asset);
/**
* Starts an asynchronous resource load.
*
* Returns false if the loading process was unable to start.
*
* This is an alternative to #loadResources and requires periodic calls to #asyncUpdateLoad.
* On multi-threaded systems this creates threads for texture decoding.
*/
bool asyncBeginLoad(FilamentAsset* asset);
/**
* Gets the status of an asynchronous resource load as a percentage in [0,1].
*/
float asyncGetLoadProgress() const;
/**
* Updates an asynchronous load by performing any pending work that must take place
* on the main thread.
*
* Clients must periodically call this until #asyncGetLoadProgress returns 100%.
* After progress reaches 100%, calling this is harmless; it just does nothing.
*/
void asyncUpdateLoad();
/**
* Cancels pending decoder jobs, frees all CPU-side texel data, and flushes the Engine.
*
* Calling this is only necessary if the asyncBeginLoad API was used
* and cancellation is required before progress reaches 100%.
*/
void asyncCancelLoad();
private:
bool loadResources(FFilamentAsset* asset, bool async);
void applySparseData(FFilamentAsset* asset) const;
void normalizeSkinningWeights(FFilamentAsset* asset) const;
void updateBoundingBoxes(FFilamentAsset* asset) const;
AssetPool* mPool;
struct Impl;
Impl* pImpl;
};
} // namespace gltfio
#endif // GLTFIO_RESOURCELOADER_H

View File

@@ -0,0 +1,46 @@
#ifndef GLTFRESOURCES_H_
#define GLTFRESOURCES_H_
#include <stdint.h>
extern "C" {
extern const uint8_t GLTFRESOURCES_PACKAGE[];
extern int GLTFRESOURCES_LIT_FADE_OFFSET;
extern int GLTFRESOURCES_LIT_FADE_SIZE;
extern int GLTFRESOURCES_LIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_LIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_LIT_MASKED_OFFSET;
extern int GLTFRESOURCES_LIT_MASKED_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_SIZE;
extern int GLTFRESOURCES_UNLIT_FADE_OFFSET;
extern int GLTFRESOURCES_UNLIT_FADE_SIZE;
extern int GLTFRESOURCES_UNLIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_UNLIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_UNLIT_MASKED_OFFSET;
extern int GLTFRESOURCES_UNLIT_MASKED_SIZE;
extern int GLTFRESOURCES_LIT_VOLUME_OFFSET;
extern int GLTFRESOURCES_LIT_VOLUME_SIZE;
extern int GLTFRESOURCES_LIT_TRANSMISSION_OFFSET;
extern int GLTFRESOURCES_LIT_TRANSMISSION_SIZE;
extern int GLTFRESOURCES_LIT_SHEEN_OFFSET;
extern int GLTFRESOURCES_LIT_SHEEN_SIZE;
}
#define GLTFRESOURCES_LIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_FADE_OFFSET)
#define GLTFRESOURCES_LIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_LIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_MASKED_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET)
#define GLTFRESOURCES_UNLIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_FADE_OFFSET)
#define GLTFRESOURCES_UNLIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_UNLIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_MASKED_OFFSET)
#define GLTFRESOURCES_LIT_VOLUME_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_VOLUME_OFFSET)
#define GLTFRESOURCES_LIT_TRANSMISSION_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_TRANSMISSION_OFFSET)
#define GLTFRESOURCES_LIT_SHEEN_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_SHEEN_OFFSET)
#endif

View File

@@ -0,0 +1,16 @@
#ifndef GLTFRESOURCES_LITE_H_
#define GLTFRESOURCES_LITE_H_
#include <stdint.h>
extern "C" {
extern const uint8_t GLTFRESOURCES_LITE_PACKAGE[];
extern int GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_LITE_LIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_LITE_LIT_FADE_OFFSET;
extern int GLTFRESOURCES_LITE_LIT_FADE_SIZE;
}
#define GLTFRESOURCES_LITE_LIT_OPAQUE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_LITE_LIT_FADE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_FADE_OFFSET)
#endif

199
ios/include/ibl/Cubemap.h Normal file
View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAP_H
#define IBL_CUBEMAP_H
#include <ibl/Image.h>
#include <utils/compiler.h>
#include <math/vec4.h>
#include <math/vec3.h>
#include <math/vec2.h>
#include <algorithm>
namespace filament {
namespace ibl {
/**
* Generic cubemap class. It handles writing / reading into the 6 faces of a cubemap.
*
* Seamless trilinear filtering is handled.
*
* This class doesn't own the face data, it's just a "view" on the 6 images.
*
* @see CubemapUtils
*
*/
class UTILS_PUBLIC Cubemap {
public:
/**
* Initialize the cubemap with a given size, but no face is set and no memory is allocated.
*
* Usually Cubemaps are created using CubemapUtils.
*
* @see CubemapUtils
*/
explicit Cubemap(size_t dim);
Cubemap(Cubemap&&) = default;
Cubemap& operator=(Cubemap&&) = default;
~Cubemap();
enum class Face : uint8_t {
PX = 0, // left +----+
NX, // right | PY |
PY, // bottom +----+----+----+----+
NY, // top | NX | PZ | PX | NZ |
PZ, // back +----+----+----+----+
NZ // front | NY |
// +----+
};
using Texel = filament::math::float3;
//! releases all images and reset the cubemap size
void resetDimensions(size_t dim);
//! assigns an image to a face.
void setImageForFace(Face face, const Image& image);
//! retrieves the image attached to a face
inline const Image& getImageForFace(Face face) const;
//! retrieves the image attached to a face
inline Image& getImageForFace(Face face);
//! computes the center of a pixel at coordinate x, y
static inline filament::math::float2 center(size_t x, size_t y);
//! computes a direction vector from a face and a location of the center of pixel in an Image
inline filament::math::float3 getDirectionFor(Face face, size_t x, size_t y) const;
//! computes a direction vector from a face and a location in pixel in an Image
inline filament::math::float3 getDirectionFor(Face face, float x, float y) const;
//! samples the cubemap at the given direction using nearest neighbor filtering
inline Texel const& sampleAt(const filament::math::float3& direction) const;
//! samples the cubemap at the given direction using bilinear filtering
inline Texel filterAt(const filament::math::float3& direction) const;
//! samples an image at the given location in pixel using bilinear filtering
static Texel filterAt(const Image& image, float x, float y);
static Texel filterAtCenter(const Image& image, size_t x, size_t y);
//! samples two cubemaps in a given direction and lerps the result by a given lerp factor
static Texel trilinearFilterAt(const Cubemap& c0, const Cubemap& c1, float lerp,
const filament::math::float3& direction);
//! reads a texel at a given address
inline static const Texel& sampleAt(void const* data) {
return *static_cast<Texel const*>(data);
}
//! writes a texel at a given address
inline static void writeAt(void* data, const Texel& texel) {
*static_cast<Texel*>(data) = texel;
}
//! returns the size of the cubemap in pixels
size_t getDimensions() const;
/**
* Prepares a cubemap for seamless access to its faces.
*
* @warning All faces of the cubemap must be backed-up by the same Image, and must already
* be spaced by 2 lines/rows.
*/
void makeSeamless();
struct Address {
Face face;
float s = 0;
float t = 0;
};
//! returns the face and texture coordinates of the given direction
static Address getAddressFor(const filament::math::float3& direction);
private:
size_t mDimensions = 0;
float mScale = 1;
float mUpperBound = 0;
Image mFaces[6];
};
// ------------------------------------------------------------------------------------------------
inline const Image& Cubemap::getImageForFace(Face face) const {
return mFaces[int(face)];
}
inline Image& Cubemap::getImageForFace(Face face) {
return mFaces[int(face)];
}
inline filament::math::float2 Cubemap::center(size_t x, size_t y) {
return { x + 0.5f, y + 0.5f };
}
inline filament::math::float3 Cubemap::getDirectionFor(Face face, size_t x, size_t y) const {
return getDirectionFor(face, x + 0.5f, y + 0.5f);
}
inline filament::math::float3 Cubemap::getDirectionFor(Face face, float x, float y) const {
// map [0, dim] to [-1,1] with (-1,-1) at bottom left
float cx = (x * mScale) - 1;
float cy = 1 - (y * mScale);
filament::math::float3 dir;
const float l = std::sqrt(cx * cx + cy * cy + 1);
switch (face) {
case Face::PX: dir = { 1, cy, -cx }; break;
case Face::NX: dir = { -1, cy, cx }; break;
case Face::PY: dir = { cx, 1, -cy }; break;
case Face::NY: dir = { cx, -1, cy }; break;
case Face::PZ: dir = { cx, cy, 1 }; break;
case Face::NZ: dir = { -cx, cy, -1 }; break;
}
return dir * (1 / l);
}
inline Cubemap::Texel const& Cubemap::sampleAt(const filament::math::float3& direction) const {
Cubemap::Address addr(getAddressFor(direction));
const size_t x = std::min(size_t(addr.s * mDimensions), mDimensions - 1);
const size_t y = std::min(size_t(addr.t * mDimensions), mDimensions - 1);
return sampleAt(getImageForFace(addr.face).getPixelRef(x, y));
}
inline Cubemap::Texel Cubemap::filterAt(const filament::math::float3& direction) const {
Cubemap::Address addr(getAddressFor(direction));
addr.s = std::min(addr.s * mDimensions, mUpperBound);
addr.t = std::min(addr.t * mDimensions, mUpperBound);
return filterAt(getImageForFace(addr.face), addr.s, addr.t);
}
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAP_H */

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAPIBL_H
#define IBL_CUBEMAPIBL_H
#include <math/vec3.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <vector>
#include <stdint.h>
#include <stddef.h>
namespace utils {
class JobSystem;
} // namespace utils
namespace filament {
namespace ibl {
class Cubemap;
class Image;
/**
* Generates cubemaps for the IBL.
*/
class UTILS_PUBLIC CubemapIBL {
public:
typedef void (*Progress)(size_t, float, void*);
/**
* Computes a roughness LOD using prefiltered importance sampling GGX
*
* @param dst the destination cubemap
* @param levels a list of prefiltered lods of the source environment
* @param linearRoughness roughness
* @param maxNumSamples number of samples for importance sampling
* @param updater a callback for the caller to track progress
*/
static void roughnessFilter(
utils::JobSystem& js, Cubemap& dst, const utils::Slice<Cubemap>& levels,
float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter,
Progress updater = nullptr, void* userdata = nullptr);
static void roughnessFilter(
utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter,
Progress updater = nullptr, void* userdata = nullptr);
//! Computes the "DFG" term of the "split-sum" approximation and stores it in a 2D image
static void DFG(utils::JobSystem& js, Image& dst, bool multiscatter, bool cloth);
/**
* Computes the diffuse irradiance using prefiltered importance sampling GGX
*
* @note Usually this is done using spherical harmonics instead.
*
* @param dst the destination cubemap
* @param levels a list of prefiltered lods of the source environment
* @param maxNumSamples number of samples for importance sampling
* @param updater a callback for the caller to track progress
*
* @see CubemapSH
*/
static void diffuseIrradiance(utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
size_t maxNumSamples = 1024, Progress updater = nullptr, void* userdata = nullptr);
// for debugging. ignore.
static void brdf(utils::JobSystem& js, Cubemap& dst, float linearRoughness);
};
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAPIBL_H */

125
ios/include/ibl/CubemapSH.h Normal file
View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAPSH_H
#define IBL_CUBEMAPSH_H
#include <utils/compiler.h>
#include <math/mat3.h>
#include <math/vec3.h>
#include <memory>
#include <vector>
namespace utils {
class JobSystem;
} // namespace utils
namespace filament {
namespace ibl {
class Cubemap;
/**
* Computes spherical harmonics
*/
class UTILS_PUBLIC CubemapSH {
public:
/**
* Spherical Harmonics decomposition of the given cubemap
* Optionally calculates irradiance by convolving with truncated cos.
*/
static std::unique_ptr<math::float3[]> computeSH(
utils::JobSystem& js, const Cubemap& cm, size_t numBands, bool irradiance);
/**
* Render given spherical harmonics into a cubemap
*/
static void renderSH(utils::JobSystem& js, Cubemap& cm,
const std::unique_ptr<math::float3[]>& sh, size_t numBands);
static void windowSH(std::unique_ptr<math::float3[]>& sh, size_t numBands, float cutoff);
/**
* Compute spherical harmonics of the irradiance of the given cubemap.
* The SH basis are pre-scaled for easier rendering by the shader. The resulting coefficients
* are not spherical harmonics (as they're scalled by various factors). In particular they
* cannot be rendered with renderSH() above. Instead use renderPreScaledSH3Bands() which
* is exactly the code ran by our shader.
*/
static void preprocessSHForShader(std::unique_ptr<math::float3[]>& sh);
/**
* Render pre-scaled irrandiance SH
*/
static void renderPreScaledSH3Bands(utils::JobSystem& js, Cubemap& cm,
const std::unique_ptr<math::float3[]>& sh);
static constexpr size_t getShIndex(ssize_t m, size_t l) {
return SHindex(m, l);
}
private:
class float5 {
float v[5];
public:
float5() = default;
constexpr float5(float a, float b, float c, float d, float e) : v{ a, b, c, d, e } {}
constexpr float operator[](size_t i) const { return v[i]; }
float& operator[](size_t i) { return v[i]; }
};
static inline const float5 multiply(const float5 M[5], float5 x) noexcept {
return float5{
M[0][0] * x[0] + M[1][0] * x[1] + M[2][0] * x[2] + M[3][0] * x[3] + M[4][0] * x[4],
M[0][1] * x[0] + M[1][1] * x[1] + M[2][1] * x[2] + M[3][1] * x[3] + M[4][1] * x[4],
M[0][2] * x[0] + M[1][2] * x[1] + M[2][2] * x[2] + M[3][2] * x[3] + M[4][2] * x[4],
M[0][3] * x[0] + M[1][3] * x[1] + M[2][3] * x[2] + M[3][3] * x[3] + M[4][3] * x[4],
M[0][4] * x[0] + M[1][4] * x[1] + M[2][4] * x[2] + M[3][4] * x[3] + M[4][4] * x[4]
};
};
static inline constexpr size_t SHindex(ssize_t m, size_t l) {
return l * (l + 1) + m;
}
static void computeShBasis(float* SHb, size_t numBands, const math::float3& s);
static float Kml(ssize_t m, size_t l);
static std::vector<float> Ki(size_t numBands);
static constexpr float computeTruncatedCosSh(size_t l);
static float sincWindow(size_t l, float w);
static math::float3 rotateShericalHarmonicBand1(math::float3 band1, math::mat3f const& M);
static float5 rotateShericalHarmonicBand2(float5 const& band2, math::mat3f const& M);
// debugging only...
static float Legendre(ssize_t l, ssize_t m, float x);
static float TSH(int l, int m, const math::float3& d);
static void printShBase(std::ostream& out, int l, int m);
};
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAPSH_H */

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAP_UTILS_H
#define IBL_CUBEMAP_UTILS_H
#include <ibl/Cubemap.h>
#include <ibl/Image.h>
#include <utils/compiler.h>
#include <functional>
namespace utils {
class JobSystem;
} // namespace utils
namespace filament {
namespace ibl {
class CubemapIBL;
/**
* Create and convert Cubemap formats
*/
class UTILS_PUBLIC CubemapUtils {
public:
//! Creates a cubemap object and its backing Image
static Cubemap create(Image& image, size_t dim, bool horizontal = true);
struct EmptyState {
};
template<typename STATE>
using ScanlineProc = std::function<
void(STATE& state, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t width)>;
template<typename STATE>
using ReduceProc = std::function<void(STATE& state)>;
//! process the cubemap using multithreading
template<typename STATE>
static void process(Cubemap& cm,
utils::JobSystem& js,
ScanlineProc<STATE> proc,
ReduceProc<STATE> reduce = [](STATE&) {},
const STATE& prototype = STATE());
//! process the cubemap
template<typename STATE>
static void processSingleThreaded(Cubemap& cm,
utils::JobSystem& js,
ScanlineProc<STATE> proc,
ReduceProc<STATE> reduce = [](STATE&) {},
const STATE& prototype = STATE());
//! clamps image to acceptable range
static void clamp(Image& src);
static void highlight(Image& src);
//! Downsamples a cubemap by helf in x and y using a box filter
static void downsampleCubemapLevelBoxFilter(utils::JobSystem& js, Cubemap& dst, const Cubemap& src);
//! Return the name of a face (suitable for a file name)
static const char* getFaceName(Cubemap::Face face);
//! computes the solid angle of a pixel of a face of a cubemap
static float solidAngle(size_t dim, size_t u, size_t v);
//! Sets a Cubemap faces from a cross image
static void setAllFacesFromCross(Cubemap& cm, const Image& image);
private:
//move these into cmgen?
static void setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image);
static Image createCubemapImage(size_t dim, bool horizontal = true);
#ifndef FILAMENT_IBL_LITE
public:
//! Converts horizontal or vertical cross Image to a Cubemap
static void crossToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts equirectangular Image to a Cubemap
static void equirectangularToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts a Cubemap to an equirectangular Image
static void cubemapToEquirectangular(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! Converts a Cubemap to an octahedron
static void cubemapToOctahedron(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! mirror the cubemap in the horizontal direction
static void mirrorCubemap(utils::JobSystem& js, Cubemap& dst, const Cubemap& src);
//! generates a UV grid in the cubemap -- useful for debugging.
static void generateUVGrid(utils::JobSystem& js, Cubemap& cml, size_t gridFrequencyX, size_t gridFrequencyY);
#endif
friend class CubemapIBL;
};
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAP_UTILS_H */

77
ios/include/ibl/Image.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_IMAGE_H
#define IBL_IMAGE_H
#include <math/scalar.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
#include <memory>
namespace filament {
namespace ibl {
class UTILS_PUBLIC Image {
public:
Image();
Image(size_t w, size_t h, size_t stride = 0);
void reset();
void set(Image const& image);
void subset(Image const& image, size_t x, size_t y, size_t w, size_t h);
bool isValid() const { return mData != nullptr; }
size_t getWidth() const { return mWidth; }
size_t getStride() const { return mBpr / getBytesPerPixel(); }
size_t getHeight() const { return mHeight; }
size_t getBytesPerRow() const { return mBpr; }
size_t getBytesPerPixel() const { return sizeof(math::float3); }
void* getData() const { return mData; }
size_t getSize() const { return mBpr * mHeight; }
void* getPixelRef(size_t x, size_t y) const;
std::unique_ptr<uint8_t[]> detach() { return std::move(mOwnedData); }
private:
size_t mBpr = 0;
size_t mWidth = 0;
size_t mHeight = 0;
std::unique_ptr<uint8_t[]> mOwnedData;
void* mData = nullptr;
};
inline void* Image::getPixelRef(size_t x, size_t y) const {
return static_cast<uint8_t*>(mData) + y * getBytesPerRow() + x * getBytesPerPixel();
}
} // namespace ibl
} // namespace filament
#endif /* IBL_IMAGE_H */

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_UTILITIES_H
#define IBL_UTILITIES_H
#include <math.h>
#include <math/vec2.h>
#include <math/vec3.h>
namespace filament {
namespace ibl {
template<typename T>
static inline constexpr T sq(T x) {
return x * x;
}
template<typename T>
static inline constexpr T log4(T x) {
// log2(x)/log2(4)
// log2(x)/2
return std::log2(x) * T(0.5);
}
inline bool isPOT(size_t x) {
return !(x & (x - 1));
}
inline filament::math::float2 hammersley(uint32_t i, float iN) {
constexpr float tof = 0.5f / 0x80000000U;
uint32_t bits = i;
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return { i * iN, bits * tof };
}
} // namespace ibl
} // namespace filament
#endif /* IBL_UTILITIES_H */

View File

@@ -0,0 +1,381 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_COLORTRANSFORM_H_
#define IMAGE_COLORTRANSFORM_H_
#include <image/LinearImage.h>
#include <utils/compiler.h>
#include <math/scalar.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/half.h>
#include <algorithm>
#include <memory>
namespace image {
template <typename T>
uint32_t linearToRGB_10_11_11_REV(const T& linear) {
using fp11 = filament::math::fp<0, 5, 6>;
using fp10 = filament::math::fp<0, 5, 5>;
// the max value for a RGB_11_11_10 is {65024, 65024, 64512} : (2 - 2^-M) * 2^(E-1)
// we clamp to the min of that
fp11 r = fp11::fromf(std::min(64512.0f, linear[0]));
fp11 g = fp11::fromf(std::min(64512.0f, linear[1]));
fp10 b = fp10::fromf(std::min(64512.0f, linear[2]));
uint32_t ir = r.bits & 0x7FF;
uint32_t ig = g.bits & 0x7FF;
uint32_t ib = b.bits & 0x3FF;
return (ib << 22) | (ig << 11) | ir;
}
template <typename T>
inline filament::math::float4 linearToRGBM(const T& linear) {
using filament::math::float4;
float4 RGBM(linear[0], linear[1], linear[2], 1.0f);
// Linear to gamma space
RGBM.rgb = sqrt(RGBM.rgb);
// Set the range
RGBM.rgb /= 16.0f;
float maxComponent = std::max(std::max(RGBM.r, RGBM.g), std::max(RGBM.b, 1e-6f));
// Don't let M go below 1 in the [0..16] range
RGBM.a = filament::math::clamp(maxComponent, 1.0f / 16.0f, 1.0f);
RGBM.a = std::ceil(RGBM.a * 255.0f) / 255.0f;
RGBM.rgb = saturate(RGBM.rgb / RGBM.a);
return RGBM;
}
template <typename T>
inline filament::math::float3 RGBMtoLinear(const T& rgbm) {
using filament::math::float3;
float3 linear(rgbm[0], rgbm[1], rgbm[2]);
linear *= rgbm.a * 16.0f;
// Gamma to linear space
return linear * linear;
}
template <typename T>
inline filament::math::float3 linearTosRGB(const T& linear) {
using filament::math::float3;
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 1 / 2.4f;
float3 sRGB;
for (size_t i=0 ; i<3 ; i++) {
if (linear[i] <= 0.0031308f) {
sRGB[i] = linear[i] * 12.92f;
} else {
sRGB[i] = a1 * std::pow(linear[i], p) - a;
}
}
return sRGB;
}
inline float linearTosRGB(float linear) {
if (linear <= 0.0031308f) {
return linear * 12.92f;
} else {
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 1 / 2.4f;
return a1 * std::pow(linear, p) - a;
}
}
template<typename T>
T sRGBToLinear(const T& sRGB);
template<>
inline filament::math::float3 sRGBToLinear(const filament::math::float3& sRGB) {
using filament::math::float3;
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 2.4f;
float3 linear;
for (size_t i=0 ; i<3 ; i++) {
if (sRGB[i] <= 0.04045f) {
linear[i] = sRGB[i] * (1.0f / 12.92f);
} else {
linear[i] = std::pow((sRGB[i] + a) / a1, p);
}
}
return linear;
}
template<>
inline filament::math::float4 sRGBToLinear(const filament::math::float4& sRGB) {
using filament::math::float4;
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 2.4f;
float4 linear;
for (size_t i=0 ; i<3 ; i++) {
if (sRGB[i] <= 0.04045f) {
linear[i] = sRGB[i] * (1.0f / 12.92f);
} else {
linear[i] = std::pow((sRGB[i] + a) / a1, p);
}
}
linear[3] = sRGB[3];
return linear;
}
template<typename T>
T linearToSRGB(const T& color);
template<>
inline filament::math::float3 linearToSRGB(const filament::math::float3& color) {
using filament::math::float3;
float3 sRGBColor{color};
#pragma nounroll
for (size_t i = 0; i < sRGBColor.size(); i++) {
sRGBColor[i] = (sRGBColor[i] <= 0.0031308f) ?
sRGBColor[i] * 12.92f : (powf(sRGBColor[i], 1.0f / 2.4f) * 1.055f) - 0.055f;
}
return sRGBColor;
}
// Creates a n-channel sRGB image from a linear floating-point image.
// The source image can have more than N channels, but only the first N are converted to sRGB.
template<typename T, int N = 3>
std::unique_ptr<uint8_t[]> fromLinearTosRGB(const LinearImage& image) {
const size_t w = image.getWidth();
const size_t h = image.getHeight();
const size_t nchan = image.getChannels();
assert(nchan >= N);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * N * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
float const* p = image.getPixelRef(0, y);
for (size_t x = 0; x < w; ++x, p += nchan, d += N) {
for (int n = 0; n < N; n++) {
float source = n < 3 ? linearTosRGB(p[n]) : p[n];
float target = filament::math::saturate(source) * std::numeric_limits<T>::max() + 0.5f;
d[n] = T(target);
}
}
}
return dst;
}
// Creates a N-channel RGB u8 image from a f32 image.
template<typename T, int N = 3>
std::unique_ptr<uint8_t[]> fromLinearToRGB(const LinearImage& image) {
size_t w = image.getWidth();
size_t h = image.getHeight();
size_t channels = image.getChannels();
assert(channels >= N);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * N * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
float const* p = image.getPixelRef(0, y);
for (size_t x = 0; x < w; ++x, p += channels, d += N) {
for (int n = 0; n < N; n++) {
float target = filament::math::saturate(p[n]) * std::numeric_limits<T>::max() + 0.5f;
d[n] = T(target);
}
}
}
return dst;
}
// Creates a 4-channel RGBM u8 image from a f32 image.
// The source image can have three or more channels, but only the first three are honored.
template <typename T>
std::unique_ptr<uint8_t[]> fromLinearToRGBM(const LinearImage& image) {
using namespace filament::math;
size_t w = image.getWidth();
size_t h = image.getHeight();
UTILS_UNUSED_IN_RELEASE size_t channels = image.getChannels();
assert(channels >= 3);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * 4 * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x, d += 4) {
auto src = image.get<float3>((uint32_t) x, (uint32_t) y);
float4 l(linearToRGBM(*src) * std::numeric_limits<T>::max() + 0.5f);
for (size_t i = 0; i < 4; i++) {
d[i] = T(l[i]);
}
}
}
return dst;
}
// Creates a 3-channel RGB_10_11_11_REV image from a f32 image.
// The source image can have three or more channels, but only the first three are honored.
inline std::unique_ptr<uint8_t[]> fromLinearToRGB_10_11_11_REV(const LinearImage& image) {
using namespace filament::math;
size_t w = image.getWidth();
size_t h = image.getHeight();
UTILS_UNUSED_IN_RELEASE size_t channels = image.getChannels();
assert(channels >= 3);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * sizeof(uint32_t)]);
uint8_t* d = dst.get();
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x, d += sizeof(uint32_t)) {
auto src = image.get<float3>((uint32_t)x, (uint32_t)y);
uint32_t v = linearToRGB_10_11_11_REV(*src);
*reinterpret_cast<uint32_t*>(d) = v;
}
}
return dst;
}
// Creates a packed single-channel integer-based image from a floating-point image.
// For example if T is uint8_t, then this performs a transformation from [0,1] to [0,255].
template <typename T>
std::unique_ptr<uint8_t[]> fromLinearToGrayscale(const LinearImage& image) {
const size_t w = image.getWidth();
const size_t h = image.getHeight();
assert(image.getChannels() == 1);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
float const* p = image.getPixelRef(0, y);
for (size_t x = 0; x < w; ++x, ++p, ++d) {
const float gray = filament::math::saturate(*p) * std::numeric_limits<T>::max() + 0.5f;
d[0] = T(gray);
}
}
return dst;
}
// Constructs a 3-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// The "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinear(size_t w, size_t h, size_t bpr,
const uint8_t* src, PROCESS proc, TRANSFORM transform) {
LinearImage result((uint32_t) w, (uint32_t) h, 3);
auto d = result.get< filament::math::float3>();
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 3) {
filament::math::float3 sRGB(proc(p[0]), proc(p[1]), proc(p[2]));
sRGB /= std::numeric_limits<T>::max();
*d++ = transform(sRGB);
}
}
return result;
}
// Constructs a 3-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// The "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinear(size_t w, size_t h, size_t bpr,
const std::unique_ptr<uint8_t[]>& src, PROCESS proc, TRANSFORM transform) {
return toLinear<T>(w, h, bpr, src.get(), proc, transform);
}
// Constructs a 4-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// the "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr,
const uint8_t* src, PROCESS proc, TRANSFORM transform) {
LinearImage result((uint32_t) w, (uint32_t) h, 4);
auto d = result.get< filament::math::float4>();
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 4) {
filament::math::float4 sRGB(proc(p[0]), proc(p[1]), proc(p[2]), proc(p[3]));
sRGB /= std::numeric_limits<T>::max();
*d++ = transform(sRGB);
}
}
return result;
}
// Constructs a 4-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// the "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr,
const std::unique_ptr<uint8_t[]>& src, PROCESS proc, TRANSFORM transform) {
return toLinearWithAlpha<T>(w, h, bpr, src.get(), proc, transform);
}
// Constructs a 3-channel LinearImage from RGBM data.
inline LinearImage toLinearFromRGBM( filament::math::float4 const* src, uint32_t w, uint32_t h) {
LinearImage result(w, h, 3);
auto dst = result.get< filament::math::float3>();
for (uint32_t row = 0; row < h; ++row) {
for (uint32_t col = 0; col < w; ++col, ++src, ++dst) {
*dst = RGBMtoLinear(*src);
}
}
return result;
}
inline LinearImage fromLinearToRGBM(const LinearImage& image) {
assert(image.getChannels() == 3);
const uint32_t w = image.getWidth(), h = image.getHeight();
LinearImage result(w, h, 4);
auto src = image.get< filament::math::float3>();
auto dst = result.get< filament::math::float4>();
for (uint32_t row = 0; row < h; ++row) {
for (uint32_t col = 0; col < w; ++col, ++src, ++dst) {
*dst = linearToRGBM(*src);
}
}
return result;
}
template<typename T>
static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, const uint8_t* src) {
LinearImage result(w, h, 4);
filament::math::float4* d = reinterpret_cast<filament::math::float4*>(result.getPixelRef(0, 0));
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 4) {
filament::math::float3 sRGB(p[0], p[1], p[2]);
sRGB /= std::numeric_limits<T>::max();
*d++ = filament::math::float4(sRGBToLinear(sRGB), 1.0f);
}
}
return result;
}
template<typename T>
static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) {
LinearImage result(w, h, 3);
filament::math::float3* d = reinterpret_cast<filament::math::float3*>(result.getPixelRef(0, 0));
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 3) {
filament::math::float3 sRGB(p[0], p[1], p[2]);
sRGB /= std::numeric_limits<T>::max();
*d++ = sRGBToLinear(sRGB);
}
}
return result;
}
} // namespace Image
#endif // IMAGE_COLORTRANSFORM_H_

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_IMAGEOPS_H
#define IMAGE_IMAGEOPS_H
#include <image/LinearImage.h>
#include <utils/compiler.h>
#include <cstddef>
#include <initializer_list>
namespace image {
// Concatenates images horizontally to create a filmstrip atlas, similar to numpy's hstack.
UTILS_PUBLIC LinearImage horizontalStack(std::initializer_list<LinearImage> images);
UTILS_PUBLIC LinearImage horizontalStack(LinearImage const* img, size_t count);
// Concatenates images vertically to create a filmstrip atlas, similar to numpy's vstack.
UTILS_PUBLIC LinearImage verticalStack(std::initializer_list<LinearImage> images);
UTILS_PUBLIC LinearImage verticalStack(LinearImage const* img, size_t count);
// Horizontally or vertically mirror the given image.
UTILS_PUBLIC LinearImage horizontalFlip(const LinearImage& image);
UTILS_PUBLIC LinearImage verticalFlip(const LinearImage& image);
// Transforms normals (components live in [-1,+1]) into colors (components live in [0,+1]).
UTILS_PUBLIC LinearImage vectorsToColors(const LinearImage& image);
UTILS_PUBLIC LinearImage colorsToVectors(const LinearImage& image);
// Creates a single-channel image by extracting the selected channel.
UTILS_PUBLIC LinearImage extractChannel(const LinearImage& image, uint32_t channel);
// Constructs a multi-channel image by copying data from a sequence of single-channel images.
UTILS_PUBLIC LinearImage combineChannels(std::initializer_list<LinearImage> images);
UTILS_PUBLIC LinearImage combineChannels(LinearImage const* img, size_t count);
// Generates a new image with rows & columns swapped.
UTILS_PUBLIC LinearImage transpose(const LinearImage& image);
// Extracts pixels by specifying a crop window where (0,0) is the top-left corner of the image.
// The boundary is specified as Left Top Right Bottom.
UTILS_PUBLIC
LinearImage cropRegion(const LinearImage& image, uint32_t l, uint32_t t, uint32_t r, uint32_t b);
// Lexicographically compares two images, similar to memcmp.
UTILS_PUBLIC int compare(const LinearImage& a, const LinearImage& b, float epsilon = 0.0f);
// Sets all pixels in all channels to the given value.
UTILS_PUBLIC void clearToValue(LinearImage& img, float value);
// Called by the coordinate field generator to query if a pixel is within the region of interest.
using PresenceCallback = bool(*)(const LinearImage& img, uint32_t col, uint32_t row, void* user);
// Generates a two-channel field of non-normalized coordinates that indicate the nearest pixel
// whose presence function returns true. This is the first step before generating a distance
// field or generalized Voronoi map.
UTILS_PUBLIC
LinearImage computeCoordField(const LinearImage& src, PresenceCallback presence, void* user);
// Generates a single-channel Euclidean distance field with positive values outside the region
// of interest in the source image, and zero values inside. If sqrt is false, the computed
// distances are squared. If signed distance (SDF) is desired, this function can be called a second
// time using an inverted source field.
UTILS_PUBLIC LinearImage edtFromCoordField(const LinearImage& coordField, bool sqrt);
// Dereferences the given coordinate field. Useful for creating Voronoi diagrams or dilated images.
UTILS_PUBLIC
LinearImage voronoiFromCoordField(const LinearImage& coordField, const LinearImage& src);
// Copies content of a source image into a target image. Requires width/height/channels to match.
UTILS_PUBLIC void blitImage(LinearImage& target, const LinearImage& source);
} // namespace image
#endif /* IMAGE_LINEARIMAGE_H */

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_IMAGESAMPLER_H
#define IMAGE_IMAGESAMPLER_H
#include <image/LinearImage.h>
#include <utils/compiler.h>
namespace image {
/**
* Value of a single point sample, allocated according to the number of image channels.
*/
struct UTILS_PUBLIC SingleSample {
float& operator[](int index) { return *(data + index); }
float* data = nullptr;
~SingleSample();
};
/**
* Controls the weighted average used across a window of source samples.
*/
enum class Filter {
DEFAULT, // Selects MITCHELL or LANCZOS dynamically.
BOX, // Computes the un-weighted average over the filter radius.
NEAREST, // Copies the source sample nearest to the center of the filter.
HERMITE, // Also known as "smoothstep", has some nice properties.
GAUSSIAN_SCALARS, // Standard Gaussian filter with sigma = 0.5
GAUSSIAN_NORMALS, // Same as GAUSSIAN_SCALARS, but interpolates unitized vectors.
MITCHELL, // Cubic resampling per Mitchell-Netravali, default for magnification.
LANCZOS, // Popular sinc-based filter, default for minification.
MINIMUM // Takes a min val rather than avg, perhaps useful for depth maps and SDF's.
};
/**
* Defines a viewport inside the texture such that (0,0) is at the top-left corner of the top-left
* pixel, and (1,1) is at the bottom-right corner of the bottom-corner pixel.
*/
struct Region {
float left;
float top;
float right;
float bottom;
};
/**
* Transforms the texel fetching operation when sampling from adjacent images.
*/
enum class Orientation {
STANDARD = 0,
FLIP_X = 1 << 0,
FLIP_Y = 1 << 1,
FLIP_XY = FLIP_X | FLIP_Y
};
/**
* Specifies how to generate samples that lie outside the boundaries of the source region.
*/
struct Boundary {
enum {
EXCLUDE, // Ignore the samples and renormalize the filter. This is probably what you want.
REGION, // Keep samples that are outside sourceRegion if they are still within the image.
CLAMP, // Pretend the edge pixel is repeated forever. Gives edge pixels more weight.
REPEAT, // Resample from the region, wrapping back to the front of the row or column.
MIRROR, // Resample from the region but assume that it has been flipped.
COLOR, // Use the specified constant color.
NEIGHBOR // Sample from an adjacent image.
} mode = EXCLUDE;
SingleSample color; // Used only if mode = COLOR
LinearImage* neighbor = nullptr; // Used only if mode = NEIGHBOR
Orientation orientation; // Used only if mode = NEIGHBOR
};
/**
* Configuration for the resampleImage function. Provides reasonable defaults.
*/
struct ImageSampler {
Filter horizontalFilter = Filter::DEFAULT;
Filter verticalFilter = Filter::DEFAULT;
Region sourceRegion = {0, 0, 1, 1};
float filterRadiusMultiplier = 1;
Boundary east;
Boundary north;
Boundary west;
Boundary south;
};
/**
* Resizes or blurs the given linear image, producing a new linear image with the given dimensions.
*/
UTILS_PUBLIC
LinearImage resampleImage(const LinearImage& source, uint32_t width, uint32_t height,
const ImageSampler& sampler);
/**
* Resizes the given linear image using a simplified API that takes target dimensions and filter.
*/
UTILS_PUBLIC
LinearImage resampleImage(const LinearImage& source, uint32_t width, uint32_t height,
Filter filter = Filter::DEFAULT);
/**
* Computes a single sample for the given texture coordinate and writes the resulting color
* components into the given output holder.
*
* For decent performance, do not call this across the entire image, instead call resampleImage.
* On the first call, pass in a default SingleSample to allocate the result holder. For example:
*
* SingleSample result;
* computeSingleSample(img, 0.5f, 0.5f, &result);
* printf("r g b = %f %f %f\n", result[0], result[1], result[2]);
* computeSingleSample(img, 0.9f, 0.1f, &result);
* printf("r g b = %f %f %f\n", result[0], result[1], result[2]);
*
* The x y coordinates live in "texture space" such that (0.0f, 0.0f) is the upper-left boundary of
* the top-left pixel and (+1.0f, +1.0f) is the lower-right boundary of the bottom-right pixel.
*/
UTILS_PUBLIC
void computeSingleSample(const LinearImage& source, float x, float y, SingleSample* result,
Filter filter = Filter::BOX);
/**
* Generates a sequence of miplevels using the requested filter. To determine the number of mips
* it would take to get down to 1x1, see getMipmapCount.
*
* Source image need not be power-of-two. In the result vector, the half-size image is returned at
* index 0, the quarter-size image is at index 1, etc. Please note that the original-sized image is
* not included.
*/
UTILS_PUBLIC
void generateMipmaps(const LinearImage& source, Filter, LinearImage* result, uint32_t mipCount);
/**
* Returns the number of miplevels it would take to downsample the given image down to 1x1. This
* number does not include the original image (i.e. mip 0).
*/
UTILS_PUBLIC
uint32_t getMipmapCount(const LinearImage& source);
/**
* Given the string name of a filter, converts it to uppercase and returns the corresponding
* enum value. If no corresponding enumerant exists, returns DEFAULT.
*/
UTILS_PUBLIC
Filter filterFromString(const char* name);
} // namespace image
#endif /* IMAGE_IMAGESAMPLER_H */

View File

@@ -0,0 +1,279 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_KTXBUNDLE_H
#define IMAGE_KTXBUNDLE_H
#include <math/vec3.h>
#include <utils/compiler.h>
#include <cstdint>
#include <memory>
namespace image {
struct KtxInfo {
uint32_t endianness;
uint32_t glType;
uint32_t glTypeSize;
uint32_t glFormat;
uint32_t glInternalFormat;
uint32_t glBaseInternalFormat;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
};
struct KtxBlobIndex {
uint32_t mipLevel;
uint32_t arrayIndex;
uint32_t cubeFace;
};
struct KtxBlobList;
struct KtxMetadata;
/**
* KtxBundle is a structured set of opaque data blobs that can be passed straight to the GPU, such
* that a single bundle corresponds to a single texture object. It is well suited for storing
* block-compressed texture data.
*
* One bundle may be comprised of several mipmap levels, cubemap faces, and array elements. The
* number of blobs is immutable, and is determined as follows.
*
* blob_count = mip_count * array_length * (cubemap ? 6 : 1)
*
* Bundles can be quickly serialized to a certain file format (see below link), but this class lives
* in the image lib rather than imageio because it has no dependencies, and does not support CPU
* decoding.
*
* https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
*/
class UTILS_PUBLIC KtxBundle {
public:
~KtxBundle();
/**
* Creates a hierarchy of empty texture blobs, to be filled later via setBlob().
*/
KtxBundle(uint32_t numMipLevels, uint32_t arrayLength, bool isCubemap);
/**
* Creates a new bundle by deserializing the given data.
*
* Typically, this constructor is used to consume the contents of a KTX file.
*/
KtxBundle(uint8_t const* bytes, uint32_t nbytes);
/**
* Serializes the bundle into the given target memory. Returns false if there's not enough
* memory.
*
* Typically, this method is used to write out the contents of a KTX file.
*/
bool serialize(uint8_t* destination, uint32_t numBytes) const;
/**
* Computes the size (in bytes) of the serialized bundle.
*/
uint32_t getSerializedLength() const;
/**
* Gets or sets information about the texture object, such as format and type.
*/
KtxInfo const& getInfo() const { return mInfo; }
KtxInfo& info() { return mInfo; }
/**
* Gets or sets key/value metadata.
*/
const char* getMetadata(const char* key, size_t* valueSize = nullptr) const;
void setMetadata(const char* key, const char* value);
/**
* Parses the key="sh" metadata and returns 3 bands of data.
*
* Assumes 3 bands for a total of 9 RGB coefficients.
* Returns true if successful.
*/
bool getSphericalHarmonics(filament::math::float3* result);
/**
* Gets the number of miplevels (this is never zero).
*/
uint32_t getNumMipLevels() const { return mNumMipLevels; }
/**
* Gets the number of array elements (this is never zero).
*/
uint32_t getArrayLength() const { return mArrayLength; }
/**
* Returns whether or not this is a cubemap.
*/
bool isCubemap() const { return mNumCubeFaces > 1; }
/**
* Retrieves a weak reference to a given data blob. Returns false if the given blob index is out
* of bounds, or if the blob at the given index is empty.
*/
bool getBlob(KtxBlobIndex index, uint8_t** data, uint32_t* size) const;
/**
* Copies the given data into the blob at the given index, replacing whatever is already there.
* Returns false if the given blob index is out of bounds.
*/
bool setBlob(KtxBlobIndex index, uint8_t const* data, uint32_t size);
/**
* Allocates the blob at the given index to the given number of bytes. This allows subsequent
* calls to setBlob to be thread-safe.
*/
bool allocateBlob(KtxBlobIndex index, uint32_t size);
// The following constants help clients populate the "info" struct. Most of them have corollary
// constants in the OpenGL headers.
static constexpr uint32_t R8 = 0x8229;
static constexpr uint32_t R8_SNORM = 0x8F94;
static constexpr uint32_t R8UI = 0x8232;
static constexpr uint32_t R8I = 0x8231;
static constexpr uint32_t STENCIL_INDEX8 = 0x8D48;
static constexpr uint32_t R16F = 0x822D;
static constexpr uint32_t R16UI = 0x8234;
static constexpr uint32_t R16I = 0x8233;
static constexpr uint32_t RG8 = 0x822B;
static constexpr uint32_t RG8_SNORM = 0x8F95;
static constexpr uint32_t RG8UI = 0x8238;
static constexpr uint32_t RG8I = 0x8237;
static constexpr uint32_t RGB565 = 0x8D62;
static constexpr uint32_t RGB5_A1 = 0x8057;
static constexpr uint32_t RGBA4 = 0x8056;
static constexpr uint32_t DEPTH_COMPONENT16 = 0x81A5;
static constexpr uint32_t RGB8 = 0x8051;
static constexpr uint32_t SRGB8 = 0x8C41;
static constexpr uint32_t RGB8_SNORM = 0x8F96;
static constexpr uint32_t RGB8UI = 0x8D7D;
static constexpr uint32_t RGB8I = 0x8D8F;
static constexpr uint32_t DEPTH_COMPONENT24 = 0x81A6;
static constexpr uint32_t R32F = 0x822E;
static constexpr uint32_t R32UI = 0x8236;
static constexpr uint32_t R32I = 0x8235;
static constexpr uint32_t RG16F = 0x822F;
static constexpr uint32_t RG16UI = 0x823A;
static constexpr uint32_t RG16I = 0x8239;
static constexpr uint32_t R11F_G11F_B10F = 0x8C3A;
static constexpr uint32_t RGB9_E5 = 0x8C3D;
static constexpr uint32_t RGBA8 = 0x8058;
static constexpr uint32_t SRGB8_ALPHA8 = 0x8C43;
static constexpr uint32_t RGBA8_SNORM = 0x8F97;
static constexpr uint32_t RGB10_A2 = 0x8059;
static constexpr uint32_t RGBA8UI = 0x8D7C;
static constexpr uint32_t RGBA8I = 0x8D8E;
static constexpr uint32_t DEPTH_COMPONENT32F = 0x8CAC;
static constexpr uint32_t DEPTH24_STENCIL8 = 0x88F0;
static constexpr uint32_t DEPTH32F_STENCIL8 = 0x8CAD;
static constexpr uint32_t RGB16F = 0x881B;
static constexpr uint32_t RGB16UI = 0x8D77;
static constexpr uint32_t RGB16I = 0x8D89;
static constexpr uint32_t RG32F = 0x8230;
static constexpr uint32_t RG32UI = 0x823C;
static constexpr uint32_t RG32I = 0x823B;
static constexpr uint32_t RGBA16F = 0x881A;
static constexpr uint32_t RGBA16UI = 0x8D76;
static constexpr uint32_t RGBA16I = 0x8D88;
static constexpr uint32_t RGB32F = 0x8815;
static constexpr uint32_t RGB32UI = 0x8D71;
static constexpr uint32_t RGB32I = 0x8D83;
static constexpr uint32_t RGBA32F = 0x8814;
static constexpr uint32_t RGBA32UI = 0x8D70;
static constexpr uint32_t RGBA32I = 0x8D82;
static constexpr uint32_t RED = 0x1903;
static constexpr uint32_t RG = 0x8227;
static constexpr uint32_t RGB = 0x1907;
static constexpr uint32_t RGBA = 0x1908;
static constexpr uint32_t BGR = 0x80E0;
static constexpr uint32_t BGRA = 0x80E1;
static constexpr uint32_t LUMINANCE = 0x1909;
static constexpr uint32_t LUMINANCE_ALPHA = 0x190A;
static constexpr uint32_t UNSIGNED_BYTE = 0x1401;
static constexpr uint32_t UNSIGNED_SHORT = 0x1403;
static constexpr uint32_t HALF_FLOAT = 0x140B;
static constexpr uint32_t FLOAT = 0x1406;
static constexpr uint32_t ENDIAN_DEFAULT = 0x04030201;
static constexpr uint32_t RGB_S3TC_DXT1 = 0x83F0;
static constexpr uint32_t RGBA_S3TC_DXT1 = 0x83F1;
static constexpr uint32_t RGBA_S3TC_DXT3 = 0x83F2;
static constexpr uint32_t RGBA_S3TC_DXT5 = 0x83F3;
static constexpr uint32_t RGBA_ASTC_4x4 = 0x93B0;
static constexpr uint32_t RGBA_ASTC_5x4 = 0x93B1;
static constexpr uint32_t RGBA_ASTC_5x5 = 0x93B2;
static constexpr uint32_t RGBA_ASTC_6x5 = 0x93B3;
static constexpr uint32_t RGBA_ASTC_6x6 = 0x93B4;
static constexpr uint32_t RGBA_ASTC_8x5 = 0x93B5;
static constexpr uint32_t RGBA_ASTC_8x6 = 0x93B6;
static constexpr uint32_t RGBA_ASTC_8x8 = 0x93B7;
static constexpr uint32_t RGBA_ASTC_10x5 = 0x93B8;
static constexpr uint32_t RGBA_ASTC_10x6 = 0x93B9;
static constexpr uint32_t RGBA_ASTC_10x8 = 0x93BA;
static constexpr uint32_t RGBA_ASTC_10x10 = 0x93BB;
static constexpr uint32_t RGBA_ASTC_12x10 = 0x93BC;
static constexpr uint32_t RGBA_ASTC_12x12 = 0x93BD;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_4x4 = 0x93D0;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_5x4 = 0x93D1;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_5x5 = 0x93D2;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_6x5 = 0x93D3;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_6x6 = 0x93D4;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x5 = 0x93D5;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x6 = 0x93D6;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x8 = 0x93D7;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x5 = 0x93D8;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x6 = 0x93D9;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x8 = 0x93DA;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x10 = 0x93DB;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_12x10 = 0x93DC;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_12x12 = 0x93DD;
static constexpr uint32_t R11_EAC = 0x9270;
static constexpr uint32_t SIGNED_R11_EAC = 0x9271;
static constexpr uint32_t RG11_EAC = 0x9272;
static constexpr uint32_t SIGNED_RG11_EAC = 0x9273;
static constexpr uint32_t RGB8_ETC2 = 0x9274;
static constexpr uint32_t SRGB8_ETC2 = 0x9275;
static constexpr uint32_t RGB8_ALPHA1_ETC2 = 0x9276;
static constexpr uint32_t SRGB8_ALPHA1_ETC = 0x9277;
static constexpr uint32_t RGBA8_ETC2_EAC = 0x9278;
static constexpr uint32_t SRGB8_ALPHA8_ETC2_EAC = 0x9279;
private:
image::KtxInfo mInfo = {};
uint32_t mNumMipLevels;
uint32_t mArrayLength;
uint32_t mNumCubeFaces;
std::unique_ptr<KtxBlobList> mBlobs;
std::unique_ptr<KtxMetadata> mMetadata;
};
} // namespace image
#endif /* IMAGE_KTXBUNDLE_H */

View File

@@ -0,0 +1,367 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_KTXUTILITY_H
#define IMAGE_KTXUTILITY_H
#include <filament/Engine.h>
#include <filament/Texture.h>
#include <image/KtxBundle.h>
namespace image {
/**
* Allows clients to create Filament textures from KtxBundle objects.
*
* Note that libimage does not have a dependency on libfilament, so for simplicity this is a
* header-only library with inlined functions.
*/
namespace ktx {
using Texture = filament::Texture;
using Engine = filament::Engine;
using TextureFormat = Texture::InternalFormat;
using CompressedPixelDataType = Texture::CompressedType;
using PixelDataType = Texture::Type;
using PixelDataFormat = Texture::Format;
using PixelBufferDescriptor = Texture::PixelBufferDescriptor;
using Callback = void(*)(void* userdata);
CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info);
PixelDataType toPixelDataType(const KtxInfo& info);
PixelDataFormat toPixelDataFormat(const KtxInfo& info);
bool isCompressed(const KtxInfo& info);
TextureFormat toTextureFormat(const KtxInfo& info);
TextureFormat toSrgbTextureFormat(TextureFormat tex);
/**
* Creates a Texture object from a KTX file and populates all of its faces and miplevels.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Forces the KTX-specified format into an SRGB format if possible
* @param callback Gets called after all texture data has been uploaded to the GPU
* @param userdata Passed into the callback
*/
inline Texture* createTexture(Engine* engine, const KtxBundle& ktx, bool srgb,
Callback callback, void* userdata) {
using Sampler = Texture::Sampler;
const auto& ktxinfo = ktx.getInfo();
const uint32_t nmips = ktx.getNumMipLevels();
const auto cdatatype = toCompressedPixelDataType(ktxinfo);
const auto datatype = toPixelDataType(ktxinfo);
const auto dataformat = toPixelDataFormat(ktxinfo);
auto texformat = toTextureFormat(ktxinfo);
if (srgb) {
texformat = toSrgbTextureFormat(texformat);
}
Texture* texture = Texture::Builder()
.width(ktxinfo.pixelWidth)
.height(ktxinfo.pixelHeight)
.levels(static_cast<uint8_t>(nmips))
.sampler(ktx.isCubemap() ? Sampler::SAMPLER_CUBEMAP : Sampler::SAMPLER_2D)
.format(texformat)
.build(*engine);
struct Userdata {
uint32_t remainingBuffers;
Callback callback;
void* userdata;
};
Userdata* cbuser = new Userdata({nmips, callback, userdata});
PixelBufferDescriptor::Callback cb = [](void*, size_t, void* cbuserptr) {
Userdata* cbuser = (Userdata*) cbuserptr;
if (--cbuser->remainingBuffers == 0) {
if (cbuser->callback) {
cbuser->callback(cbuser->userdata);
}
delete cbuser;
}
};
uint8_t* data;
uint32_t size;
if (isCompressed(ktxinfo)) {
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
}
return texture;
}
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
}
return texture;
}
/**
* Creates a Texture object from a KTX bundle, populates all of its faces and miplevels,
* and automatically destroys the bundle after all the texture data has been uploaded.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Forces the KTX-specified format into an SRGB format if possible
*/
inline Texture* createTexture(Engine* engine, KtxBundle* ktx, bool srgb) {
auto freeKtx = [] (void* userdata) {
KtxBundle* ktx = (KtxBundle*) userdata;
delete ktx;
};
return createTexture(engine, *ktx, srgb, freeKtx, ktx);
}
template<typename T>
T toCompressedFilamentEnum(uint32_t format) {
switch (format) {
case KtxBundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
case KtxBundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
case KtxBundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
case KtxBundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
case KtxBundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
case KtxBundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
case KtxBundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
case KtxBundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
case KtxBundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
case KtxBundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
case KtxBundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
case KtxBundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
case KtxBundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
case KtxBundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
case KtxBundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
case KtxBundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
case KtxBundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
case KtxBundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
case KtxBundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
case KtxBundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
case KtxBundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
case KtxBundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
case KtxBundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
case KtxBundle::R11_EAC: return T::EAC_R11;
case KtxBundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
case KtxBundle::RG11_EAC: return T::EAC_RG11;
case KtxBundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
case KtxBundle::RGB8_ETC2: return T::ETC2_RGB8;
case KtxBundle::SRGB8_ETC2: return T::ETC2_SRGB8;
case KtxBundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
case KtxBundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
case KtxBundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
case KtxBundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
}
return (T) 0xffff;
}
inline CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info) {
return toCompressedFilamentEnum<CompressedPixelDataType>(info.glInternalFormat);
}
inline PixelDataType toPixelDataType(const KtxInfo& info) {
switch (info.glType) {
case KtxBundle::UNSIGNED_BYTE: return PixelDataType::UBYTE;
case KtxBundle::UNSIGNED_SHORT: return PixelDataType::USHORT;
case KtxBundle::HALF_FLOAT: return PixelDataType::HALF;
case KtxBundle::FLOAT: return PixelDataType::FLOAT;
case KtxBundle::R11F_G11F_B10F: return PixelDataType::UINT_10F_11F_11F_REV;
}
return (PixelDataType) 0xff;
}
inline PixelDataFormat toPixelDataFormat(const KtxInfo& info) {
switch (info.glFormat) {
case KtxBundle::LUMINANCE:
case KtxBundle::RED: return PixelDataFormat::R;
case KtxBundle::RG: return PixelDataFormat::RG;
case KtxBundle::RGB: return PixelDataFormat::RGB;
case KtxBundle::RGBA: return PixelDataFormat::RGBA;
// glFormat should NOT be a sized format according to the spec
// however cmgen was generating incorrect files until after Filament 1.8.0
// so we keep this line here to preserve compatibility with older assets
case KtxBundle::R11F_G11F_B10F: return PixelDataFormat::RGB;
}
return (PixelDataFormat) 0xff;
}
inline bool isCompressed(const KtxInfo& info) {
return info.glFormat == 0;
}
inline TextureFormat toSrgbTextureFormat(TextureFormat format) {
switch(format) {
// Non-compressed
case Texture::InternalFormat::RGB8:
return Texture::InternalFormat::SRGB8;
case Texture::InternalFormat::RGBA8:
return Texture::InternalFormat::SRGB8_A8;
// ASTC
case Texture::InternalFormat::RGBA_ASTC_4x4:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_4x4;
case Texture::InternalFormat::RGBA_ASTC_5x4:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x4;
case Texture::InternalFormat::RGBA_ASTC_5x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x5;
case Texture::InternalFormat::RGBA_ASTC_6x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x5;
case Texture::InternalFormat::RGBA_ASTC_6x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x6;
case Texture::InternalFormat::RGBA_ASTC_8x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x5;
case Texture::InternalFormat::RGBA_ASTC_8x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x6;
case Texture::InternalFormat::RGBA_ASTC_8x8:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x8;
case Texture::InternalFormat::RGBA_ASTC_10x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x5;
case Texture::InternalFormat::RGBA_ASTC_10x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x6;
case Texture::InternalFormat::RGBA_ASTC_10x8:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x8;
case Texture::InternalFormat::RGBA_ASTC_10x10:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x10;
case Texture::InternalFormat::RGBA_ASTC_12x10:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x10;
case Texture::InternalFormat::RGBA_ASTC_12x12:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x12;
// ETC2
case Texture::InternalFormat::ETC2_RGB8:
return Texture::InternalFormat::ETC2_SRGB8;
case Texture::InternalFormat::ETC2_RGB8_A1:
return Texture::InternalFormat::ETC2_SRGB8_A1;
case Texture::InternalFormat::ETC2_EAC_RGBA8:
return Texture::InternalFormat::ETC2_EAC_SRGBA8;
// DXT
case Texture::InternalFormat::DXT1_RGB:
return Texture::InternalFormat::DXT1_SRGB;
case Texture::InternalFormat::DXT1_RGBA:
return Texture::InternalFormat::DXT1_SRGBA;
case Texture::InternalFormat::DXT3_RGBA:
return Texture::InternalFormat::DXT3_SRGBA;
case Texture::InternalFormat::DXT5_RGBA:
return Texture::InternalFormat::DXT5_SRGBA;
default:
return format;
}
}
inline TextureFormat toTextureFormat(const KtxInfo& info) {
switch (info.glInternalFormat) {
case KtxBundle::RED: return TextureFormat::R8;
case KtxBundle::RG: return TextureFormat::RG8;
case KtxBundle::RGB: return TextureFormat::RGB8;
case KtxBundle::RGBA: return TextureFormat::RGBA8;
case KtxBundle::LUMINANCE: return TextureFormat::R8;
case KtxBundle::LUMINANCE_ALPHA: return TextureFormat::RG8;
case KtxBundle::R8: return TextureFormat::R8;
case KtxBundle::R8_SNORM: return TextureFormat::R8_SNORM;
case KtxBundle::R8UI: return TextureFormat::R8UI;
case KtxBundle::R8I: return TextureFormat::R8I;
case KtxBundle::STENCIL_INDEX8: return TextureFormat::STENCIL8;
case KtxBundle::R16F: return TextureFormat::R16F;
case KtxBundle::R16UI: return TextureFormat::R16UI;
case KtxBundle::R16I: return TextureFormat::R16I;
case KtxBundle::RG8: return TextureFormat::RG8;
case KtxBundle::RG8_SNORM: return TextureFormat::RG8_SNORM;
case KtxBundle::RG8UI: return TextureFormat::RG8UI;
case KtxBundle::RG8I: return TextureFormat::RG8I;
case KtxBundle::RGB565: return TextureFormat::RGB565;
case KtxBundle::RGB9_E5: return TextureFormat::RGB9_E5;
case KtxBundle::RGB5_A1: return TextureFormat::RGB5_A1;
case KtxBundle::RGBA4: return TextureFormat::RGBA4;
case KtxBundle::DEPTH_COMPONENT16: return TextureFormat::DEPTH16;
case KtxBundle::RGB8: return TextureFormat::RGB8;
case KtxBundle::SRGB8: return TextureFormat::SRGB8;
case KtxBundle::RGB8_SNORM: return TextureFormat::RGB8_SNORM;
case KtxBundle::RGB8UI: return TextureFormat::RGB8UI;
case KtxBundle::RGB8I: return TextureFormat::RGB8I;
case KtxBundle::R32F: return TextureFormat::R32F;
case KtxBundle::R32UI: return TextureFormat::R32UI;
case KtxBundle::R32I: return TextureFormat::R32I;
case KtxBundle::RG16F: return TextureFormat::RG16F;
case KtxBundle::RG16UI: return TextureFormat::RG16UI;
case KtxBundle::RG16I: return TextureFormat::RG16I;
case KtxBundle::R11F_G11F_B10F: return TextureFormat::R11F_G11F_B10F;
case KtxBundle::RGBA8: return TextureFormat::RGBA8;
case KtxBundle::SRGB8_ALPHA8: return TextureFormat::SRGB8_A8;
case KtxBundle::RGBA8_SNORM: return TextureFormat::RGBA8_SNORM;
case KtxBundle::RGB10_A2: return TextureFormat::RGB10_A2;
case KtxBundle::RGBA8UI: return TextureFormat::RGBA8UI;
case KtxBundle::RGBA8I: return TextureFormat::RGBA8I;
case KtxBundle::DEPTH24_STENCIL8: return TextureFormat::DEPTH24_STENCIL8;
case KtxBundle::DEPTH32F_STENCIL8: return TextureFormat::DEPTH32F_STENCIL8;
case KtxBundle::RGB16F: return TextureFormat::RGB16F;
case KtxBundle::RGB16UI: return TextureFormat::RGB16UI;
case KtxBundle::RGB16I: return TextureFormat::RGB16I;
case KtxBundle::RG32F: return TextureFormat::RG32F;
case KtxBundle::RG32UI: return TextureFormat::RG32UI;
case KtxBundle::RG32I: return TextureFormat::RG32I;
case KtxBundle::RGBA16F: return TextureFormat::RGBA16F;
case KtxBundle::RGBA16UI: return TextureFormat::RGBA16UI;
case KtxBundle::RGBA16I: return TextureFormat::RGBA16I;
case KtxBundle::RGB32F: return TextureFormat::RGB32F;
case KtxBundle::RGB32UI: return TextureFormat::RGB32UI;
case KtxBundle::RGB32I: return TextureFormat::RGB32I;
case KtxBundle::RGBA32F: return TextureFormat::RGBA32F;
case KtxBundle::RGBA32UI: return TextureFormat::RGBA32UI;
case KtxBundle::RGBA32I: return TextureFormat::RGBA32I;
}
return toCompressedFilamentEnum<TextureFormat>(info.glInternalFormat);
}
} // namespace ktx
} // namespace image
#endif

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_LINEARIMAGE_H
#define IMAGE_LINEARIMAGE_H
#include <utils/compiler.h>
#include <cstdint>
/**
* Types and free functions for the Filament core imaging library, primarily used for offline tools,
* but with minimal dependencies to support potential use by the renderer.
*/
namespace image {
/**
* LinearImage is a handle to packed floating point data arranged into a row-major grid.
*
* We use this object as input/output for core algorithms that wish to be agnostic of source and
* destination formats. The number of channels is arbitrary (1 or more) but we often use 3-channel
* images to represent color data.
*
* The underlying pixel data has shared ownership semantics to allow clients to easily pass around
* the image object without incurring a deep copy. Shared access to pixels is not thread safe.
*
* By convention, we do not use channel major order (i.e. planar). However we provide a free
* function in ImageOps to combine planar data. Pixels are stored such that the row stride is simply
* width * channels * sizeof(float).
*/
class UTILS_PUBLIC LinearImage {
public:
~LinearImage();
/**
* Allocates a zeroed-out image.
*/
LinearImage(uint32_t width, uint32_t height, uint32_t channels);
/**
* Makes a shallow copy with shared pixel data.
*/
LinearImage(const LinearImage& that);
LinearImage& operator=(const LinearImage& that);
/**
* Creates an empty (invalid) image.
*/
LinearImage() : mDataRef(nullptr), mData(nullptr), mWidth(0), mHeight(0), mChannels(0) {}
operator bool() const { return mData != nullptr; }
/**
* Gets a pointer to the underlying pixel data.
*/
float* getPixelRef() { return mData; }
template<typename T> T* get() { return reinterpret_cast<T*>(mData); }
/**
* Gets a pointer to immutable pixel data.
*/
float const* getPixelRef() const { return mData; }
template<typename T> T const* get() const { return reinterpret_cast<T const*>(mData); }
/**
* Gets a pointer to the pixel data at the given column and row. (not bounds checked)
*/
float* getPixelRef(uint32_t column, uint32_t row) {
return mData + (column + row * mWidth) * mChannels;
}
template<typename T>
T* get(uint32_t column, uint32_t row) {
return reinterpret_cast<T*>(getPixelRef(column, row));
}
/**
* Gets a pointer to the immutable pixel data at the given column and row. (not bounds checked)
*/
float const* getPixelRef(uint32_t column, uint32_t row) const {
return mData + (column + row * mWidth) * mChannels;
}
template<typename T>
T const* get(uint32_t column, uint32_t row) const {
return reinterpret_cast<T const*>(getPixelRef(column, row));
}
uint32_t getWidth() const { return mWidth; }
uint32_t getHeight() const { return mHeight; }
uint32_t getChannels() const { return mChannels; }
void reset() { *this = LinearImage(); }
bool isValid() const { return mData; }
private:
struct SharedReference;
SharedReference* mDataRef = nullptr;
float* mData;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mChannels;
};
} // namespace image
#endif /* IMAGE_LINEARIMAGE_H */

View File

@@ -0,0 +1,807 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_TMATHELPERS_H_
#define MATH_TMATHELPERS_H_
#include <math/compiler.h>
#include <math/quat.h>
#include <math/TVecHelpers.h>
#include <algorithm> // for std::swap
#include <cmath> // for std:: namespace
#include <math.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
namespace details {
// -------------------------------------------------------------------------------------
/*
* No user serviceable parts here.
*
* Don't use this file directly, instead include math/mat*.h
*/
/*
* Matrix utilities
*/
namespace matrix {
/*
* Matrix inversion
*/
template<typename MATRIX>
constexpr MATRIX MATH_PURE gaussJordanInverse(MATRIX src) {
typedef typename MATRIX::value_type T;
constexpr unsigned int N = MATRIX::NUM_ROWS;
MATRIX inverted;
for (size_t i = 0; i < N; ++i) {
// look for largest element in i'th column
size_t swap = i;
T t = src[i][i] < 0 ? -src[i][i] : src[i][i];
for (size_t j = i + 1; j < N; ++j) {
const T t2 = src[j][i] < 0 ? -src[j][i] : src[j][i];
if (t2 > t) {
swap = j;
t = t2;
}
}
if (swap != i) {
// swap columns.
std::swap(src[i], src[swap]);
std::swap(inverted[i], inverted[swap]);
}
const T denom(src[i][i]);
for (size_t k = 0; k < N; ++k) {
src[i][k] /= denom;
inverted[i][k] /= denom;
}
// Factor out the lower triangle
for (size_t j = 0; j < N; ++j) {
if (j != i) {
const T t = src[j][i];
for (size_t k = 0; k < N; ++k) {
src[j][k] -= src[i][k] * t;
inverted[j][k] -= inverted[i][k] * t;
}
}
}
}
return inverted;
}
//------------------------------------------------------------------------------
// 2x2 matrix inverse is easy.
template<typename MATRIX>
constexpr MATRIX MATH_PURE fastInverse2(const MATRIX& x) {
typedef typename MATRIX::value_type T;
// Assuming the input matrix is:
// | a b |
// | c d |
//
// The analytic inverse is
// | d -b |
// | -c a | / (a d - b c)
//
// Importantly, our matrices are column-major!
MATRIX inverted{};
const T a = x[0][0];
const T c = x[0][1];
const T b = x[1][0];
const T d = x[1][1];
const T det((a * d) - (b * c));
inverted[0][0] = d / det;
inverted[0][1] = -c / det;
inverted[1][0] = -b / det;
inverted[1][1] = a / det;
return inverted;
}
//------------------------------------------------------------------------------
// From the Wikipedia article on matrix inversion's section on fast 3x3
// matrix inversion:
// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
template<typename MATRIX>
constexpr MATRIX MATH_PURE fastInverse3(const MATRIX& x) {
typedef typename MATRIX::value_type T;
// Assuming the input matrix is:
// | a b c |
// | d e f |
// | g h i |
//
// The analytic inverse is
// | A B C |^T
// | D E F |
// | G H I | / determinant
//
// Which is
// | A D G |
// | B E H |
// | C F I | / determinant
//
// Where:
// A = (ei - fh), B = (fg - di), C = (dh - eg)
// D = (ch - bi), E = (ai - cg), F = (bg - ah)
// G = (bf - ce), H = (cd - af), I = (ae - bd)
//
// and the determinant is a*A + b*B + c*C (The rule of Sarrus)
//
// Importantly, our matrices are column-major!
MATRIX inverted{};
const T a = x[0][0];
const T b = x[1][0];
const T c = x[2][0];
const T d = x[0][1];
const T e = x[1][1];
const T f = x[2][1];
const T g = x[0][2];
const T h = x[1][2];
const T i = x[2][2];
// Do the full analytic inverse
const T A = e * i - f * h;
const T B = f * g - d * i;
const T C = d * h - e * g;
inverted[0][0] = A; // A
inverted[0][1] = B; // B
inverted[0][2] = C; // C
inverted[1][0] = c * h - b * i; // D
inverted[1][1] = a * i - c * g; // E
inverted[1][2] = b * g - a * h; // F
inverted[2][0] = b * f - c * e; // G
inverted[2][1] = c * d - a * f; // H
inverted[2][2] = a * e - b * d; // I
const T det(a * A + b * B + c * C);
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
inverted[col][row] /= det;
}
}
return inverted;
}
//------------------------------------------------------------------------------
// Determinant and cofactor
// this is just a dummy matrix helper
template<typename T, size_t ORDER>
class Matrix {
T m[ORDER][ORDER];
public:
constexpr auto operator[](size_t i) const noexcept { return m[i]; }
constexpr auto& operator[](size_t i) noexcept { return m[i]; }
static constexpr Matrix<T, ORDER - 1> submatrix(Matrix in, size_t row, size_t col) noexcept {
size_t colCount = 0, rowCount = 0;
Matrix<T, ORDER - 1> dest{};
for (size_t i = 0; i < ORDER; i++) {
if (i != row) {
colCount = 0;
for (size_t j = 0; j < ORDER; j++) {
if (j != col) {
dest[rowCount][colCount] = in[i][j];
colCount++;
}
}
rowCount++;
}
}
return dest;
}
};
template<typename T, size_t O>
struct Determinant {
static constexpr T determinant(Matrix<T, O> in) {
T det = {};
for (size_t i = 0; i < O; i++) {
T m = Determinant<T, O - 1>::determinant(Matrix<T, O>::submatrix(in, 0, i));
T factor = (i % 2 == 1) ? T(-1) : T(1);
det += factor * in[0][i] * m;
}
return det;
}
};
template<typename T>
struct Determinant<T, 3> {
static constexpr T determinant(Matrix<T, 3> in) {
return
in[0][0] * in[1][1] * in[2][2] +
in[1][0] * in[2][1] * in[0][2] +
in[2][0] * in[0][1] * in[1][2] -
in[2][0] * in[1][1] * in[0][2] -
in[1][0] * in[0][1] * in[2][2] -
in[0][0] * in[2][1] * in[1][2];
}
};
template<typename T>
struct Determinant<T, 2> {
static constexpr T determinant(Matrix<T, 2> in) {
return in[0][0] * in[1][1] - in[0][1] * in[1][0];
}
};
template<typename T>
struct Determinant<T, 1> {
static constexpr T determinant(Matrix<T, 1> in) { return in[0][0]; }
};
template<typename MATRIX>
constexpr MATRIX MATH_PURE cofactor(const MATRIX& m) {
typedef typename MATRIX::value_type T;
MATRIX out;
constexpr size_t order = MATRIX::NUM_COLS;
Matrix<T, order> in{};
for (size_t i = 0; i < order; i++) {
for (size_t j = 0; j < order; j++) {
in[i][j] = m[i][j];
}
}
for (size_t i = 0; i < order; i++) {
for (size_t j = 0; j < order; j++) {
T factor = ((i + j) % 2 == 1) ? T(-1) : T(1);
out[i][j] = Determinant<T, order - 1>::determinant(
Matrix<T, order>::submatrix(in, i, j)) * factor;
}
}
return out;
}
template<typename MATRIX>
constexpr MATRIX MATH_PURE fastCofactor2(const MATRIX& m) {
typedef typename MATRIX::value_type T;
// Assuming the input matrix is:
// | a b |
// | c d |
//
// The cofactor are
// | d -c |
// | -b a |
//
// Importantly, our matrices are column-major!
MATRIX cof{};
const T a = m[0][0];
const T c = m[0][1];
const T b = m[1][0];
const T d = m[1][1];
cof[0][0] = d;
cof[0][1] = -b;
cof[1][0] = -c;
cof[1][1] = a;
return cof;
}
template<typename MATRIX>
constexpr MATRIX MATH_PURE fastCofactor3(const MATRIX& m) {
typedef typename MATRIX::value_type T;
// Assuming the input matrix is:
// | a b c |
// | d e f |
// | g h i |
//
// The cofactor are
// | A B C |
// | D E F |
// | G H I |
// Where:
// A = (ei - fh), B = (fg - di), C = (dh - eg)
// D = (ch - bi), E = (ai - cg), F = (bg - ah)
// G = (bf - ce), H = (cd - af), I = (ae - bd)
// Importantly, our matrices are column-major!
MATRIX cof{};
const T a = m[0][0];
const T b = m[1][0];
const T c = m[2][0];
const T d = m[0][1];
const T e = m[1][1];
const T f = m[2][1];
const T g = m[0][2];
const T h = m[1][2];
const T i = m[2][2];
cof[0][0] = e * i - f * h; // A
cof[0][1] = c * h - b * i; // D
cof[0][2] = b * f - c * e; // G
cof[1][0] = f * g - d * i; // B
cof[1][1] = a * i - c * g; // E
cof[1][2] = c * d - a * f; // H
cof[2][0] = d * h - e * g; // C
cof[2][1] = b * g - a * h; // F
cof[2][2] = a * e - b * d; // I
return cof;
}
/**
* Cofactor function which switches on the matrix size.
*/
template<typename MATRIX,
typename = std::enable_if_t<MATRIX::NUM_ROWS == MATRIX::NUM_COLS, int>>
inline constexpr MATRIX MATH_PURE cof(const MATRIX& matrix) {
return (MATRIX::NUM_ROWS == 2) ? fastCofactor2<MATRIX>(matrix) :
((MATRIX::NUM_ROWS == 3) ? fastCofactor3<MATRIX>(matrix) :
cofactor<MATRIX>(matrix));
}
/**
* Determinant of a matrix
*/
template<typename MATRIX,
typename = std::enable_if_t<MATRIX::NUM_ROWS == MATRIX::NUM_COLS, int>>
inline constexpr typename MATRIX::value_type MATH_PURE det(const MATRIX& matrix) {
typedef typename MATRIX::value_type T;
constexpr unsigned int N = MATRIX::NUM_ROWS;
Matrix<T, N> in{};
for (size_t i = 0; i < N; i++) {
for (size_t j = 0; j < N; j++) {
in[i][j] = matrix[i][j];
}
}
return Determinant<typename MATRIX::value_type, MATRIX::NUM_COLS>::determinant(in);
}
/**
* Inversion function which switches on the matrix size.
* @warning This function assumes the matrix is invertible. The result is
* undefined if it is not. It is the responsibility of the caller to
* make sure the matrix is not singular.
*/
template<typename MATRIX,
typename = std::enable_if_t<MATRIX::NUM_ROWS == MATRIX::NUM_COLS, int>>
inline constexpr MATRIX MATH_PURE inverse(const MATRIX& matrix) {
return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
gaussJordanInverse<MATRIX>(matrix));
}
template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B,
typename = std::enable_if_t<
MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS &&
MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS &&
MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS, int>>
constexpr MATRIX_R MATH_PURE multiply(MATRIX_A lhs, MATRIX_B rhs) {
// pre-requisite:
// lhs : D columns, R rows
// rhs : C columns, D rows
// res : C columns, R rows
MATRIX_R res{};
for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
res[col] = lhs * rhs[col];
}
return res;
}
template<typename MATRIX,
typename = std::enable_if_t<MATRIX::NUM_ROWS == MATRIX::NUM_COLS, int>>
inline constexpr MATRIX MATH_PURE transpose(MATRIX m) {
// for now we only handle square matrix transpose
MATRIX result{};
for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
result[col][row] = m[row][col];
}
}
return result;
}
template<typename MATRIX,
typename = std::enable_if_t<MATRIX::NUM_ROWS == MATRIX::NUM_COLS, int>>
inline constexpr typename MATRIX::value_type MATH_PURE trace(MATRIX m) {
typename MATRIX::value_type result{};
for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
result += m[col][col];
}
return result;
}
template<typename MATRIX,
typename = std::enable_if_t<MATRIX::NUM_ROWS == MATRIX::NUM_COLS, int>>
inline constexpr typename MATRIX::col_type MATH_PURE diag(MATRIX m) {
typename MATRIX::col_type result{};
for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
result[col] = m[col][col];
}
return result;
}
//------------------------------------------------------------------------------
// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
template<typename MATRIX>
TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
typedef typename MATRIX::value_type T;
TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
// Compute the trace to see if it is positive or not.
const T trace = mat[0][0] + mat[1][1] + mat[2][2];
// check the sign of the trace
if (MATH_LIKELY(trace > 0)) {
// trace is positive
T s = std::sqrt(trace + 1);
quat.w = T(0.5) * s;
s = T(0.5) / s;
quat.x = (mat[1][2] - mat[2][1]) * s;
quat.y = (mat[2][0] - mat[0][2]) * s;
quat.z = (mat[0][1] - mat[1][0]) * s;
} else {
// trace is negative
// Find the index of the greatest diagonal
size_t i = 0;
if (mat[1][1] > mat[0][0]) { i = 1; }
if (mat[2][2] > mat[i][i]) { i = 2; }
// Get the next indices: (n+1)%3
static constexpr size_t next_ijk[3] = { 1, 2, 0 };
size_t j = next_ijk[i];
size_t k = next_ijk[j];
T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
quat[i] = T(0.5) * s;
if (s != 0) {
s = T(0.5) / s;
}
quat.w = (mat[j][k] - mat[k][j]) * s;
quat[j] = (mat[i][j] + mat[j][i]) * s;
quat[k] = (mat[i][k] + mat[k][i]) * s;
}
return quat;
}
} // namespace matrix
// -------------------------------------------------------------------------------------
/*
* TMatProductOperators implements basic arithmetic and basic compound assignments
* operators on a vector of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename> class BASE, typename T,
template<typename> class VEC>
class TMatProductOperators {
public:
// matrix *= matrix
template<typename U>
constexpr BASE<T>& operator*=(const BASE<U>& rhs) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
lhs = matrix::multiply<BASE<T>>(lhs, rhs);
return lhs;
}
// matrix *= scalar
template<typename U, typename = enable_if_arithmetic_t<U>>
constexpr BASE<T>& operator*=(U v) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
lhs[col] *= v;
}
return lhs;
}
// matrix /= scalar
template<typename U, typename = enable_if_arithmetic_t<U>>
constexpr BASE<T>& operator/=(U v) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
lhs[col] /= v;
}
return lhs;
}
private:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
// matrix * matrix
template<typename U>
friend inline constexpr BASE<arithmetic_result_t<T, U>> MATH_PURE
operator*(BASE<T> lhs, BASE<U> rhs) {
return matrix::multiply<BASE<arithmetic_result_t<T, U>>>(lhs, rhs);
}
// matrix * vector
template<typename U>
friend inline constexpr typename BASE<arithmetic_result_t<T, U>>::col_type MATH_PURE
operator*(const BASE<T>& lhs, const VEC<U>& rhs) {
typename BASE<arithmetic_result_t<T, U>>::col_type result{};
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
result += lhs[col] * rhs[col];
}
return result;
}
// row-vector * matrix
template<typename U>
friend inline constexpr typename BASE<arithmetic_result_t<T, U>>::row_type MATH_PURE
operator*(const VEC<U>& lhs, const BASE<T>& rhs) {
typename BASE<arithmetic_result_t<T, U>>::row_type result{};
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
result[col] = dot(lhs, rhs[col]);
}
return result;
}
// matrix * scalar
template<typename U, typename = enable_if_arithmetic_t <U>>
friend inline constexpr BASE<arithmetic_result_t < T, U>> MATH_PURE
operator*(const BASE<T>& lhs, U rhs) {
BASE<arithmetic_result_t<T, U>> result{};
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
result[col] = lhs[col] * rhs;
}
return result;
}
// scalar * matrix
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr BASE<arithmetic_result_t<T, U>> MATH_PURE
operator*(U rhs, const BASE<T>& lhs) {
return lhs * rhs;
}
// matrix / scalar
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr BASE<arithmetic_result_t<T, U>> MATH_PURE
operator/(const BASE<T>& lhs, U rhs) {
BASE<arithmetic_result_t<T, U>> result{};
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
result[col] = lhs[col] / rhs;
}
return result;
}
};
/*
* TMatSquareFunctions implements functions on a matrix of type BASE<T>.
*
* BASE only needs to implement:
* - operator[]
* - col_type
* - row_type
* - COL_SIZE
* - ROW_SIZE
*
* By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename U> class BASE, typename T>
class TMatSquareFunctions {
private:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
friend inline constexpr BASE<T> MATH_PURE inverse(const BASE<T>& matrix) {
return matrix::inverse(matrix);
}
friend inline constexpr BASE<T> MATH_PURE cof(const BASE<T>& matrix) {
return matrix::cof(matrix);
}
friend inline constexpr BASE<T> MATH_PURE transpose(BASE<T> m) {
return matrix::transpose(m);
}
friend inline constexpr T MATH_PURE trace(BASE<T> m) {
return matrix::trace(m);
}
friend inline constexpr T MATH_PURE det(const BASE<T>& m) {
return matrix::det(m);
}
// unclear why we have to use 'auto' here. 'typename BASE<T>::col_type' produces
// error: no type named 'col_type' in 'filament::math::details::TMat44<float>'
friend inline constexpr auto MATH_PURE diag(const BASE<T>& m) {
return matrix::diag(m);
}
};
template<template<typename U> class BASE, typename T>
class TMatHelpers {
public:
constexpr inline size_t getColumnSize() const { return BASE<T>::COL_SIZE; }
constexpr inline size_t getRowSize() const { return BASE<T>::ROW_SIZE; }
constexpr inline size_t getColumnCount() const { return BASE<T>::NUM_COLS; }
constexpr inline size_t getRowCount() const { return BASE<T>::NUM_ROWS; }
constexpr inline size_t size() const { return BASE<T>::ROW_SIZE; } // for TVec*<>
// array access
constexpr T const* asArray() const {
return &static_cast<BASE<T> const &>(*this)[0][0];
}
// element access
inline constexpr T const& operator()(size_t row, size_t col) const {
return static_cast<BASE<T> const &>(*this)[col][row];
}
inline T& operator()(size_t row, size_t col) {
return static_cast<BASE<T>&>(*this)[col][row];
}
private:
constexpr friend inline BASE<T> MATH_PURE abs(BASE<T> m) {
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
m[col] = abs(m[col]);
}
return m;
}
};
// functions for 3x3 and 4x4 matrices
template<template<typename U> class BASE, typename T>
class TMatTransform {
public:
inline constexpr TMatTransform() {
static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
}
template<typename A, typename VEC, typename = enable_if_arithmetic_t<A>>
static BASE<T> rotation(A radian, VEC about) {
BASE<T> r;
T c = std::cos(radian);
T s = std::sin(radian);
if (about[0] == 1 && about[1] == 0 && about[2] == 0) {
r[1][1] = c; r[2][2] = c;
r[1][2] = s; r[2][1] = -s;
} else if (about[0] == 0 && about[1] == 1 && about[2] == 0) {
r[0][0] = c; r[2][2] = c;
r[2][0] = s; r[0][2] = -s;
} else if (about[0] == 0 && about[1] == 0 && about[2] == 1) {
r[0][0] = c; r[1][1] = c;
r[0][1] = s; r[1][0] = -s;
} else {
VEC nabout = normalize(about);
typename VEC::value_type x = nabout[0];
typename VEC::value_type y = nabout[1];
typename VEC::value_type z = nabout[2];
T nc = 1 - c;
T xy = x * y;
T yz = y * z;
T zx = z * x;
T xs = x * s;
T ys = y * s;
T zs = z * s;
r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys;
r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs;
r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c;
// Clamp results to -1, 1.
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
}
}
}
return r;
}
/**
* Create a matrix from euler angles using YPR around YXZ respectively
* @param yaw about Y axis
* @param pitch about X axis
* @param roll about Z axis
*/
template<typename Y, typename P, typename R, typename = enable_if_arithmetic_t<Y, P, R>>
static BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
return eulerZYX(roll, pitch, yaw);
}
/**
* Create a matrix from euler angles using YPR around ZYX respectively
* @param roll about X axis
* @param pitch about Y axis
* @param yaw about Z axis
*
* The euler angles are applied in ZYX order. i.e: a vector is first rotated
* about X (roll) then Y (pitch) and then Z (yaw).
*/
template<typename Y, typename P, typename R, typename = enable_if_arithmetic_t<Y, P, R>>
static BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
BASE<T> r;
T cy = std::cos(yaw);
T sy = std::sin(yaw);
T cp = std::cos(pitch);
T sp = std::sin(pitch);
T cr = std::cos(roll);
T sr = std::sin(roll);
T cc = cr * cy;
T cs = cr * sy;
T sc = sr * cy;
T ss = sr * sy;
r[0][0] = cp * cy;
r[0][1] = cp * sy;
r[0][2] = -sp;
r[1][0] = sp * sc - cs;
r[1][1] = sp * ss + cc;
r[1][2] = cp * sr;
r[2][0] = sp * cc + ss;
r[2][1] = sp * cs - sc;
r[2][2] = cp * cr;
// Clamp results to -1, 1.
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
}
}
return r;
}
TQuaternion <T> toQuaternion() const {
return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
}
};
// -------------------------------------------------------------------------------------
} // namespace details
} // namespace math
} // namespace filament
#endif // MATH_TMATHELPERS_H_

View File

@@ -0,0 +1,292 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_TQUATHELPERS_H_
#define MATH_TQUATHELPERS_H_
#include <math.h>
#include <stdint.h>
#include <sys/types.h>
#include <math/compiler.h>
#include <math/scalar.h>
#include <math/vec3.h>
namespace filament {
namespace math {
namespace details {
// -------------------------------------------------------------------------------------
/*
* No user serviceable parts here.
*
* Don't use this file directly, instead include math/quat.h
*/
/*
* TQuatProductOperators implements basic arithmetic and basic compound assignment
* operators on a quaternion of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename T> class QUATERNION, typename T>
class TQuatProductOperators {
public:
/* compound assignment from a another quaternion of the same size but different
* element type.
*/
template<typename OTHER>
constexpr QUATERNION<T>& operator*=(const QUATERNION<OTHER>& r) {
QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this);
q = q * r;
return q;
}
/* compound assignment products by a scalar
*/
constexpr QUATERNION<T>& operator*=(T v) {
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
lhs[i] *= v;
}
return lhs;
}
constexpr QUATERNION<T>& operator/=(T v) {
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
lhs[i] /= v;
}
return lhs;
}
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
/* The operators below handle operation between quaternion of the same size
* but of a different element type.
*/
template<typename RT>
friend inline
constexpr QUATERNION<T> MATH_PURE operator*(const QUATERNION<T>& q, const QUATERNION<RT>& r) {
// could be written as:
// return QUATERNION<T>(
// q.w*r.w - dot(q.xyz, r.xyz),
// q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz));
return QUATERNION<T>(
q.w * r.w - q.x * r.x - q.y * r.y - q.z * r.z,
q.w * r.x + q.x * r.w + q.y * r.z - q.z * r.y,
q.w * r.y - q.x * r.z + q.y * r.w + q.z * r.x,
q.w * r.z + q.x * r.y - q.y * r.x + q.z * r.w);
}
template<typename RT>
friend inline
constexpr TVec3<T> MATH_PURE operator*(const QUATERNION<T>& q, const TVec3<RT>& v) {
// note: if q is known to be a unit quaternion, then this simplifies to:
// TVec3<T> t = 2 * cross(q.xyz, v)
// return v + (q.w * t) + cross(q.xyz, t)
return imaginary(q * QUATERNION<T>(v, 0) * inverse(q));
}
/* For quaternions, we use explicit "by a scalar" products because it's much faster
* than going (implicitly) through the quaternion multiplication.
* For reference: we could use the code below instead, but it would be a lot slower.
* friend inline
* constexpr BASE<T> MATH_PURE operator *(const BASE<T>& q, const BASE<T>& r) {
* return BASE<T>(
* q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
* q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
* q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
* q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
*
*/
friend inline
constexpr QUATERNION<T> MATH_PURE operator*(QUATERNION<T> q, T scalar) {
// don't pass q by reference because we need a copy anyways
return q *= scalar;
}
friend inline
constexpr QUATERNION<T> MATH_PURE operator*(T scalar, QUATERNION<T> q) {
// don't pass q by reference because we need a copy anyways
return q *= scalar;
}
friend inline
constexpr QUATERNION<T> MATH_PURE operator/(QUATERNION<T> q, T scalar) {
// don't pass q by reference because we need a copy anyways
return q /= scalar;
}
};
/*
* TQuatFunctions implements functions on a quaternion of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename T> class QUATERNION, typename T>
class TQuatFunctions {
public:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename RT>
friend inline
constexpr T MATH_PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) {
return p.x * q.x +
p.y * q.y +
p.z * q.z +
p.w * q.w;
}
friend inline
T MATH_PURE norm(const QUATERNION<T>& q) {
return std::sqrt(dot(q, q));
}
friend inline
T MATH_PURE length(const QUATERNION<T>& q) {
return norm(q);
}
friend inline
constexpr T MATH_PURE length2(const QUATERNION<T>& q) {
return dot(q, q);
}
friend inline
QUATERNION<T> MATH_PURE normalize(const QUATERNION<T>& q) {
return length(q) ? q / length(q) : QUATERNION<T>(static_cast<T>(1));
}
friend inline
constexpr QUATERNION<T> MATH_PURE conj(const QUATERNION<T>& q) {
return QUATERNION<T>(q.w, -q.x, -q.y, -q.z);
}
friend inline
constexpr QUATERNION<T> MATH_PURE inverse(const QUATERNION<T>& q) {
return conj(q) * (1 / dot(q, q));
}
friend inline
constexpr T MATH_PURE real(const QUATERNION<T>& q) {
return q.w;
}
friend inline
constexpr TVec3<T> MATH_PURE imaginary(const QUATERNION<T>& q) {
return q.xyz;
}
friend inline
constexpr QUATERNION<T> MATH_PURE unreal(const QUATERNION<T>& q) {
return QUATERNION<T>(q.xyz, 0);
}
friend inline
constexpr QUATERNION<T> MATH_PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) {
return unreal(p * q);
}
friend inline
QUATERNION<T> MATH_PURE exp(const QUATERNION<T>& q) {
const T nq(norm(q.xyz));
return std::exp(q.w) * QUATERNION<T>((sin(nq) / nq) * q.xyz, cos(nq));
}
friend inline
QUATERNION<T> MATH_PURE log(const QUATERNION<T>& q) {
const T nq(norm(q));
return QUATERNION<T>((std::acos(q.w / nq) / norm(q.xyz)) * q.xyz, std::log(nq));
}
friend inline
QUATERNION<T> MATH_PURE pow(const QUATERNION<T>& q, T a) {
// could also be computed as: exp(a*log(q));
const T nq(norm(q));
const T theta(a * std::acos(q.w / nq));
return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta));
}
friend inline
QUATERNION<T> MATH_PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
// could also be computed as: pow(q * inverse(p), t) * p;
const T d = dot(p, q);
const T absd = std::abs(d);
static constexpr T value_eps = T(10) * std::numeric_limits<T>::epsilon();
// Prevent blowing up when slerping between two quaternions that are very near each other.
if ((T(1) - absd) < value_eps) {
return normalize(lerp(d < 0 ? -p : p, q, t));
}
const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q||
const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1)));
const T a0 = a * (1 - t);
const T a1 = a * t;
const T sina = sin(a);
if (sina < value_eps) {
return normalize(lerp(p, q, t));
}
const T isina = 1 / sina;
const T s0 = std::sin(a0) * isina;
const T s1 = std::sin(a1) * isina;
// ensure we're taking the "short" side
return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q);
}
friend inline
constexpr QUATERNION<T> MATH_PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
return ((1 - t) * p) + (t * q);
}
friend inline
constexpr QUATERNION<T> MATH_PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
return normalize(lerp(p, q, t));
}
friend inline
constexpr QUATERNION<T> MATH_PURE positive(const QUATERNION<T>& q) {
return q.w < 0 ? -q : q;
}
};
// -------------------------------------------------------------------------------------
} // namespace details
} // namespace math
} // namespace filament
#endif // MATH_TQUATHELPERS_H_

View File

@@ -0,0 +1,625 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_TVECHELPERS_H_
#define MATH_TVECHELPERS_H_
#include <math/compiler.h>
#include <cmath> // for std:: namespace
#include <math.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
namespace details {
// -------------------------------------------------------------------------------------
template<typename U>
inline constexpr U min(U a, U b) noexcept {
return a < b ? a : b;
}
template<typename U>
inline constexpr U max(U a, U b) noexcept {
return a > b ? a : b;
}
template<typename T, typename U>
struct arithmetic_result {
using type = decltype(std::declval<T>() + std::declval<U>());
};
template<typename T, typename U>
using arithmetic_result_t = typename arithmetic_result<T, U>::type;
template<typename A, typename B = int, typename C = int, typename D = int>
using enable_if_arithmetic_t = std::enable_if_t<
is_arithmetic<A>::value &&
is_arithmetic<B>::value &&
is_arithmetic<C>::value &&
is_arithmetic<D>::value>;
/*
* No user serviceable parts here.
*
* Don't use this file directly, instead include math/vec{2|3|4}.h
*/
/*
* TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
* operators on a vector of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename T> class VECTOR, typename T>
class TVecAddOperators {
public:
/* compound assignment from a another vector of the same size but different
* element type.
*/
template<typename U>
constexpr VECTOR<T>& operator+=(const VECTOR<U>& v) {
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
for (size_t i = 0; i < lhs.size(); i++) {
lhs[i] += v[i];
}
return lhs;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
constexpr VECTOR<T>& operator+=(U v) {
return operator+=(VECTOR<U>(v));
}
template<typename U>
constexpr VECTOR<T>& operator-=(const VECTOR<U>& v) {
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
for (size_t i = 0; i < lhs.size(); i++) {
lhs[i] -= v[i];
}
return lhs;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
constexpr VECTOR<T>& operator-=(U v) {
return operator-=(VECTOR<U>(v));
}
private:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res += rv;
return res;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, U rv) {
return lv + VECTOR<U>(rv);
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(U lv, const VECTOR<T>& rv) {
return VECTOR<U>(lv) + rv;
}
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res -= rv;
return res;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, U rv) {
return lv - VECTOR<U>(rv);
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(U lv, const VECTOR<T>& rv) {
return VECTOR<U>(lv) - rv;
}
};
template<template<typename T> class VECTOR, typename T>
class TVecProductOperators {
public:
/* compound assignment from a another vector of the same size but different
* element type.
*/
template<typename U>
constexpr VECTOR<T>& operator*=(const VECTOR<U>& v) {
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
for (size_t i = 0; i < lhs.size(); i++) {
lhs[i] *= v[i];
}
return lhs;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
constexpr VECTOR<T>& operator*=(U v) {
return operator*=(VECTOR<U>(v));
}
template<typename U>
constexpr VECTOR<T>& operator/=(const VECTOR<U>& v) {
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
for (size_t i = 0; i < lhs.size(); i++) {
lhs[i] /= v[i];
}
return lhs;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
constexpr VECTOR<T>& operator/=(U v) {
return operator/=(VECTOR<U>(v));
}
private:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res *= rv;
return res;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, U rv) {
return lv * VECTOR<U>(rv);
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(U lv, const VECTOR<T>& rv) {
return VECTOR<U>(lv) * rv;
}
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res /= rv;
return res;
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, U rv) {
return lv / VECTOR<U>(rv);
}
template<typename U, typename = enable_if_arithmetic_t<U>>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(U lv, const VECTOR<T>& rv) {
return VECTOR<U>(lv) / rv;
}
};
/*
* TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
* get all the functionality here.
*
* These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
*/
template<template<typename T> class VECTOR, typename T>
class TVecUnaryOperators {
public:
constexpr VECTOR<T> operator-() const {
VECTOR<T> r{};
VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
for (size_t i = 0; i < r.size(); i++) {
r[i] = -rv[i];
}
return r;
}
};
/*
* TVecComparisonOperators implements relational/comparison operators
* on a vector of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename T> class VECTOR, typename T>
class TVecComparisonOperators {
private:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename U>
friend inline constexpr
bool MATH_PURE operator==(const VECTOR<T>& lv, const VECTOR<U>& rv) {
// w/ inlining we end-up with many branches that will pollute the BPU cache
MATH_NOUNROLL
for (size_t i = 0; i < lv.size(); i++) {
if (lv[i] != rv[i]) {
return false;
}
}
return true;
}
template<typename U>
friend inline constexpr
bool MATH_PURE operator!=(const VECTOR<T>& lv, const VECTOR<U>& rv) {
return !operator==(lv, rv);
}
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE equal(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] == rv[i];
}
return r;
}
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE notEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] != rv[i];
}
return r;
}
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE lessThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] < rv[i];
}
return r;
}
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] <= rv[i];
}
return r;
}
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE greaterThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r;
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] > rv[i];
}
return r;
}
template<typename U>
friend inline
VECTOR<bool> MATH_PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] >= rv[i];
}
return r;
}
};
/*
* TVecFunctions implements functions on a vector of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename T> class VECTOR, typename T>
class TVecFunctions {
private:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename U>
friend constexpr inline
arithmetic_result_t<T, U> MATH_PURE dot(const VECTOR<T>& lv, const VECTOR<U>& rv) {
arithmetic_result_t<T, U> r{};
for (size_t i = 0; i < lv.size(); i++) {
r += lv[i] * rv[i];
}
return r;
}
friend inline T MATH_PURE norm(const VECTOR<T>& lv) {
return std::sqrt(dot(lv, lv));
}
friend inline T MATH_PURE length(const VECTOR<T>& lv) {
return norm(lv);
}
friend inline constexpr T MATH_PURE norm2(const VECTOR<T>& lv) {
return dot(lv, lv);
}
friend inline constexpr T MATH_PURE length2(const VECTOR<T>& lv) {
return norm2(lv);
}
template<typename U>
friend inline constexpr
arithmetic_result_t<T, U> MATH_PURE distance(const VECTOR<T>& lv, const VECTOR<U>& rv) {
return length(rv - lv);
}
template<typename U>
friend inline constexpr
arithmetic_result_t<T, U> MATH_PURE distance2(const VECTOR<T>& lv, const VECTOR<U>& rv) {
return length2(rv - lv);
}
friend inline VECTOR<T> MATH_PURE normalize(const VECTOR<T>& lv) {
return lv * (T(1) / length(lv));
}
friend inline VECTOR<T> MATH_PURE rcp(VECTOR<T> v) {
return T(1) / v;
}
friend inline constexpr VECTOR<T> MATH_PURE abs(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = v[i] < 0 ? -v[i] : v[i];
}
return v;
}
friend inline VECTOR<T> MATH_PURE floor(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::floor(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE ceil(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::ceil(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE round(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::round(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE inversesqrt(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = T(1) / std::sqrt(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE sqrt(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::sqrt(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE cbrt(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::cbrt(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE exp(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::exp(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, T p) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::pow(v[i], p);
}
return v;
}
friend inline VECTOR<T> MATH_PURE pow(T v, VECTOR<T> p) {
for (size_t i = 0; i < p.size(); i++) {
p[i] = std::pow(v, p[i]);
}
return p;
}
friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, VECTOR<T> p) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::pow(v[i], p[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE log(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::log(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE log10(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::log10(v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE log2(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::log2(v[i]);
}
return v;
}
friend inline constexpr VECTOR<T> MATH_PURE saturate(const VECTOR<T>& lv) {
return clamp(lv, T(0), T(1));
}
friend inline constexpr VECTOR<T> MATH_PURE clamp(VECTOR<T> v, T min, T max) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = details::min(max, details::max(min, v[i]));
}
return v;
}
friend inline constexpr VECTOR<T> MATH_PURE clamp(VECTOR<T> v, VECTOR<T> min, VECTOR<T> max) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = details::min(max[i], details::max(min[i], v[i]));
}
return v;
}
friend inline constexpr VECTOR<T> MATH_PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv,
VECTOR<T> a) {
for (size_t i = 0; i < lv.size(); i++) {
a[i] += (lv[i] * rv[i]);
}
return a;
}
friend inline constexpr VECTOR<T> MATH_PURE min(const VECTOR<T>& u, VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = details::min(u[i], v[i]);
}
return v;
}
friend inline constexpr VECTOR<T> MATH_PURE max(const VECTOR<T>& u, VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = details::max(u[i], v[i]);
}
return v;
}
friend inline constexpr T MATH_PURE max(const VECTOR<T>& v) {
T r(v[0]);
for (size_t i = 1; i < v.size(); i++) {
r = max(r, v[i]);
}
return r;
}
friend inline constexpr T MATH_PURE min(const VECTOR<T>& v) {
T r(v[0]);
for (size_t i = 1; i < v.size(); i++) {
r = min(r, v[i]);
}
return r;
}
friend inline constexpr VECTOR<T> MATH_PURE mix(const VECTOR<T>& u, VECTOR<T> v, T a) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = u[i] * (T(1) - a) + v[i] * a;
}
return v;
}
friend inline constexpr VECTOR<T> MATH_PURE smoothstep(T edge0, T edge1, VECTOR<T> v) {
VECTOR<T> t = saturate((v - edge0) / (edge1 - edge0));
return t * t * (T(3) - T(2) * t);
}
friend inline constexpr VECTOR<T> MATH_PURE step(T edge, VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = v[i] < edge ? T(0) : T(1);
}
return v;
}
friend inline constexpr VECTOR<T> MATH_PURE step(VECTOR<T> edge, VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = v[i] < edge[i] ? T(0) : T(1);
}
return v;
}
friend inline constexpr bool MATH_PURE any(const VECTOR<T>& v) {
for (size_t i = 0; i < v.size(); i++) {
if (v[i] != T(0)) return true;
}
return false;
}
friend inline constexpr bool MATH_PURE all(const VECTOR<T>& v) {
bool result = true;
for (size_t i = 0; i < v.size(); i++) {
result &= (v[i] != T(0));
}
return result;
}
};
// -------------------------------------------------------------------------------------
} // namespace details
} // namespace math
} // namespace filament
#endif // MATH_TVECHELPERS_H_

126
ios/include/math/compiler.h Normal file
View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <type_traits>
#if defined (WIN32)
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#ifdef far
#undef far
#endif
#ifdef near
#undef near
#endif
#endif
// compatibility with non-clang compilers...
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_builtin(__builtin_expect)
# ifdef __cplusplus
# define MATH_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
# define MATH_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
# else
# define MATH_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
# define MATH_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
# endif
#else
# define MATH_LIKELY( exp ) (exp)
# define MATH_UNLIKELY( exp ) (exp)
#endif
#if __has_attribute(unused)
# define MATH_UNUSED __attribute__((unused))
#else
# define MATH_UNUSED
#endif
#if __has_attribute(pure)
# define MATH_PURE __attribute__((pure))
#else
# define MATH_PURE
#endif
#ifdef _MSC_VER
# define MATH_EMPTY_BASES __declspec(empty_bases)
// MSVC does not support loop unrolling hints
# define MATH_NOUNROLL
// Sadly, MSVC does not support __builtin_constant_p
# ifndef MAKE_CONSTEXPR
# define MAKE_CONSTEXPR(e) (e)
# endif
// About value initialization, the C++ standard says:
// if T is a class type with a default constructor that is neither user-provided nor deleted
// (that is, it may be a class with an implicitly-defined or defaulted default constructor),
// the object is zero-initialized and then it is default-initialized
// if it has a non-trivial default constructor;
// Unfortunately, MSVC always calls the default constructor, even if it is trivial, which
// breaks constexpr-ness. To workaround this, we're always zero-initializing TVecN<>
# define MATH_CONSTEXPR_INIT {}
# define MATH_DEFAULT_CTOR {}
# define MATH_DEFAULT_CTOR_CONSTEXPR constexpr
# define CONSTEXPR_IF_NOT_MSVC // when declared constexpr, msvc fails with "failure was caused by cast of object of dynamic type"
#else // _MSC_VER
# define MATH_EMPTY_BASES
// C++11 allows pragmas to be specified as part of defines using the _Pragma syntax.
# define MATH_NOUNROLL _Pragma("nounroll")
# ifndef MAKE_CONSTEXPR
# define MAKE_CONSTEXPR(e) __builtin_constant_p(e) ? (e) : (e)
# endif
# define MATH_CONSTEXPR_INIT
# define MATH_DEFAULT_CTOR = default;
# define MATH_DEFAULT_CTOR_CONSTEXPR
# define CONSTEXPR_IF_NOT_MSVC constexpr
#endif // _MSC_VER
namespace filament {
namespace math {
// MSVC 2019 16.4 doesn't seem to like it when we specialize std::is_arithmetic for
// filament::math::half, so we're forced to create our own is_arithmetic here and specialize it
// inside of half.h.
template<typename T>
struct is_arithmetic : std::integral_constant<bool,
std::is_integral<T>::value || std::is_floating_point<T>::value> {
};
}
}

174
ios/include/math/fast.h Normal file
View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MATH_FAST_H
#define TNT_MATH_FAST_H
#include <cmath>
#include <cstdint>
#include <type_traits>
#include <math/compiler.h>
#include <math/scalar.h>
#ifdef __ARM_NEON
#include <arm_neon.h>
#endif
namespace filament {
namespace math {
namespace fast {
// fast cos(x), ~8 cycles (vs. 66 cycles on ARM)
// can be vectorized
// x between -pi and pi
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
constexpr T MATH_PURE cos(T x) noexcept {
x *= T(F_1_PI / 2);
x -= T(0.25) + std::floor(x + T(0.25));
x *= T(16.0) * std::abs(x) - T(8.0);
x += T(0.225) * x * (std::abs(x) - T(1.0));
return x;
}
// fast sin(x), ~8 cycles (vs. 66 cycles on ARM)
// can be vectorized
// x between -pi and pi
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
constexpr T MATH_PURE sin(T x) noexcept {
return filament::math::fast::cos<T>(x - T(F_PI_2));
}
constexpr inline float MATH_PURE ilog2(float x) noexcept {
union {
float val;
int32_t x;
} u = { x };
return float(((u.x >> 23) & 0xff) - 127);
}
constexpr inline float MATH_PURE log2(float x) noexcept {
union {
float val;
int32_t x;
} u = { x };
float ilog2 = float(((u.x >> 23) & 0xff) - 128);
u.x = (u.x & 0x007fffff) | 0x3f800000;
return ilog2 + (-0.34484843f * u.val + 2.02466578f) * u.val - 0.67487759f;
}
// fast 1/sqrt(), on ARMv8 this is 5 cycles vs. 7 cycles, so maybe not worth it.
// we keep this mostly for reference and benchmarking.
inline float MATH_PURE isqrt(float x) noexcept {
#if defined(__ARM_NEON) && defined(__aarch64__)
float y = vrsqrtes_f32(x);
return y * vrsqrtss_f32(x, y * y);
#else
return 1 / std::sqrt(x);
#endif
}
inline double MATH_PURE isqrt(double x) noexcept {
#if defined(__ARM_NEON) && defined(__aarch64__)
double y = vrsqrted_f64(x);
return y * vrsqrtsd_f64(x, y * y);
#else
return 1 / std::sqrt(x);
#endif
}
inline int signbit(float x) noexcept {
#if __has_builtin(__builtin_signbitf)
// Note: on Android NDK, signbit() is a function call -- not what we want.
return __builtin_signbitf(x);
#else
return std::signbit(x);
#endif
}
/*
* constexpr exp(), pow(), factorial()
*/
constexpr double pow(double x, unsigned int y) noexcept {
return y == 0 ? 1.0 : x * pow(x, y - 1);
}
constexpr unsigned int factorial(unsigned int x) noexcept {
return x == 0 ? 1 : x * factorial(x - 1);
}
constexpr double exp(double x) noexcept {
return 1.0 + x + pow(x, 2) / factorial(2) + pow(x, 3) / factorial(3)
+ pow(x, 4) / factorial(4) + pow(x, 5) / factorial(5)
+ pow(x, 6) / factorial(6) + pow(x, 7) / factorial(7)
+ pow(x, 8) / factorial(8) + pow(x, 9) / factorial(9);
}
constexpr float exp(float x) noexcept {
return float(exp(double(x)));
}
/*
* unsigned saturated arithmetic
*/
#if defined(__ARM_NEON) && defined(__aarch64__)
inline uint8_t MATH_PURE qadd(uint8_t a, uint8_t b) noexcept { return vuqaddb_s8(a, b); }
inline uint16_t MATH_PURE qadd(uint16_t a, uint16_t b) noexcept { return vuqaddh_s16(a, b); }
inline uint32_t MATH_PURE qadd(uint32_t a, uint32_t b) noexcept { return vuqadds_s32(a, b); }
inline uint8_t MATH_PURE qsub(uint8_t a, uint8_t b) noexcept { return vqsubb_s8(a, b); }
inline uint16_t MATH_PURE qsub(uint16_t a, uint16_t b) noexcept { return vqsubh_s16(a, b); }
inline uint32_t MATH_PURE qsub(uint32_t a, uint32_t b) noexcept { return vqsubs_s32(a, b); }
#else
template<typename T, typename = std::enable_if_t<
std::is_same<uint8_t, T>::value ||
std::is_same<uint16_t, T>::value ||
std::is_same<uint32_t, T>::value>>
inline T MATH_PURE qadd(T a, T b) noexcept {
T r = a + b;
return r | -T(r < a);
}
template<typename T, typename = std::enable_if_t<
std::is_same<uint8_t, T>::value ||
std::is_same<uint16_t, T>::value ||
std::is_same<uint32_t, T>::value>>
inline T MATH_PURE qsub(T a, T b) noexcept {
T r = a - b;
return r & -T(r <= a);
}
#endif
template<typename T>
inline T MATH_PURE qinc(T a) noexcept {
return qadd(a, T(1));
}
template<typename T>
inline T MATH_PURE qdec(T a) noexcept {
return qsub(a, T(1));
}
} // namespace fast
} // namespace math
} // namespace filament
#endif // TNT_MATH_FAST_H

224
ios/include/math/half.h Normal file
View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MATH_HALF_H
#define TNT_MATH_HALF_H
#include <limits>
#include <type_traits>
#include <stddef.h>
#include <stdint.h>
#include <math/compiler.h>
namespace filament {
namespace math {
template<unsigned S, unsigned E, unsigned M>
class fp {
static_assert(S + E + M <= 16, "we only support 16-bits max custom floats");
using TYPE = uint16_t; // this should be dynamic
static constexpr unsigned S_SHIFT = E + M;
static constexpr unsigned E_SHIFT = M;
static constexpr unsigned M_SHIFT = 0;
static constexpr unsigned S_MASK = ((1u << S) - 1u) << S_SHIFT;
static constexpr unsigned E_MASK = ((1u << E) - 1u) << E_SHIFT;
static constexpr unsigned M_MASK = ((1u << M) - 1u) << M_SHIFT;
struct fp32 {
explicit constexpr fp32(float f) noexcept : fp(f) { } // NOLINT
explicit constexpr fp32(uint32_t b) noexcept : bits(b) { } // NOLINT
constexpr void setS(unsigned int s) noexcept {
bits = uint32_t((bits & 0x7FFFFFFFu) | (s << 31u));
}
constexpr unsigned int getS() const noexcept { return bits >> 31u; }
constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; }
union {
uint32_t bits;
float fp;
};
};
public:
static constexpr fp fromf(float f) noexcept {
fp out;
if (S == 0 && f < 0.0f) {
return out;
}
fp32 in(f);
unsigned int sign = in.getS();
in.setS(0);
if (MATH_UNLIKELY(in.getE() == 0xFF)) { // inf or nan
out.setE((1u << E) - 1u);
out.setM(in.getM() ? (1u << (M - 1u)) : 0);
} else {
constexpr fp32 infinity(((1u << E) - 1u) << 23u); // fp infinity in fp32 position
constexpr fp32 magic(((1u << (E - 1u)) - 1u) << 23u); // exponent offset
in.bits &= ~((1u << (22 - M)) - 1u); // erase extra mantissa bits
in.bits += 1u << (22 - M); // rounding
in.fp *= magic.fp; // add exponent offset
in.bits = in.bits < infinity.bits ? in.bits : infinity.bits;
out.bits = uint16_t(in.bits >> (23 - M));
}
out.setS(sign);
return out;
}
static constexpr float tof(fp in) noexcept {
constexpr fp32 magic ((0xFE - ((1u << (E - 1u)) - 1u)) << 23u);
constexpr fp32 infnan((0x80 + ((1u << (E - 1u)) - 1u)) << 23u);
fp32 out((in.bits & ((1u << (E + M)) - 1u)) << (23u - M));
out.fp *= magic.fp;
if (out.fp >= infnan.fp) {
out.bits |= 0xFFu << 23u;
}
out.bits |= (in.bits & S_MASK) << (31u - S_SHIFT);
return out.fp;
}
TYPE bits{};
static constexpr size_t getBitCount() noexcept { return S + E + M; }
constexpr fp() noexcept = default;
explicit constexpr fp(TYPE bits) noexcept : bits(bits) { }
constexpr void setS(unsigned int s) noexcept { bits = TYPE((bits & ~S_MASK) | (s << S_SHIFT)); }
constexpr void setE(unsigned int s) noexcept { bits = TYPE((bits & ~E_MASK) | (s << E_SHIFT)); }
constexpr void setM(unsigned int s) noexcept { bits = TYPE((bits & ~M_MASK) | (s << M_SHIFT)); }
constexpr unsigned int getS() const noexcept { return (bits & S_MASK) >> S_SHIFT; }
constexpr unsigned int getE() const noexcept { return (bits & E_MASK) >> E_SHIFT; }
constexpr unsigned int getM() const noexcept { return (bits & M_MASK) >> M_SHIFT; }
};
/*
* half-float
*
* 1 5 10
* +-+------+------------+
* |s|eee.ee|mm.mmmm.mmmm|
* +-+------+------------+
*
* minimum (denormal) value: 2^-24 = 5.96e-8
* minimum (normal) value: 2^-14 = 6.10e-5
* maximum value: (2 - 2^-10) * 2^15 = 65504
*
* Integers between 0 and 2048 can be represented exactly
*/
#ifdef __ARM_NEON
using half = __fp16;
inline constexpr uint16_t getBits(half const& h) noexcept {
return MAKE_CONSTEXPR(reinterpret_cast<uint16_t const&>(h));
}
inline constexpr half makeHalf(uint16_t bits) noexcept {
return MAKE_CONSTEXPR(reinterpret_cast<half const&>(bits));
}
#else
class half {
using fp16 = fp<1, 5, 10>;
public:
half() = default;
constexpr half(float v) noexcept : mBits(fp16::fromf(v)) { } // NOLINT
constexpr operator float() const noexcept { return fp16::tof(mBits); } // NOLINT
private:
// these are friends, not members (and they're not "private")
friend constexpr uint16_t getBits(half const& h) noexcept { return h.mBits.bits; }
friend constexpr inline half makeHalf(uint16_t bits) noexcept;
enum Binary { binary };
explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { }
fp16 mBits;
};
constexpr inline half makeHalf(uint16_t bits) noexcept {
return half(half::binary, bits);
}
#endif // __ARM_NEON
inline constexpr half operator "" _h(long double v) {
return half( static_cast<float>(v) );
}
template<> struct is_arithmetic<filament::math::half> : public std::true_type {};
} // namespace math
} // namespace filament
namespace std {
template<> struct is_floating_point<filament::math::half> : public std::true_type {};
// note: this shouldn't be needed (is_floating_point<> is enough) but some version of msvc need it
// This stopped working with MSVC 2019 16.4, so we specialize our own version of is_arithmetic in
// the math::filament namespace (see above).
template<> struct is_arithmetic<filament::math::half> : public std::true_type {};
template<>
class numeric_limits<filament::math::half> {
public:
typedef filament::math::half type;
static constexpr const bool is_specialized = true;
static constexpr const bool is_signed = true;
static constexpr const bool is_integer = false;
static constexpr const bool is_exact = false;
static constexpr const bool has_infinity = true;
static constexpr const bool has_quiet_NaN = true;
static constexpr const bool has_signaling_NaN = false;
static constexpr const float_denorm_style has_denorm = denorm_absent;
static constexpr const bool has_denorm_loss = true;
static constexpr const bool is_iec559 = false;
static constexpr const bool is_bounded = true;
static constexpr const bool is_modulo = false;
static constexpr const bool traps = false;
static constexpr const bool tinyness_before = false;
static constexpr const float_round_style round_style = round_indeterminate;
static constexpr const int digits = 11;
static constexpr const int digits10 = 3;
static constexpr const int max_digits10 = 5;
static constexpr const int radix = 2;
static constexpr const int min_exponent = -13;
static constexpr const int min_exponent10 = -4;
static constexpr const int max_exponent = 16;
static constexpr const int max_exponent10 = 4;
inline static constexpr type round_error() noexcept { return filament::math::makeHalf(0x3800); }
inline static constexpr type min() noexcept { return filament::math::makeHalf(0x0400); }
inline static constexpr type max() noexcept { return filament::math::makeHalf(0x7bff); }
inline static constexpr type lowest() noexcept { return filament::math::makeHalf(0xfbff); }
inline static constexpr type epsilon() noexcept { return filament::math::makeHalf(0x1400); }
inline static constexpr type infinity() noexcept { return filament::math::makeHalf(0x7c00); }
inline static constexpr type quiet_NaN() noexcept { return filament::math::makeHalf(0x7fff); }
inline static constexpr type denorm_min() noexcept { return filament::math::makeHalf(0x0001); }
inline static constexpr type signaling_NaN() noexcept { return filament::math::makeHalf(0x7dff); }
};
} // namespace std
#endif // TNT_MATH_HALF_H

361
ios/include/math/mat2.h Normal file
View File

@@ -0,0 +1,361 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_MAT2_H_
#define MATH_MAT2_H_
#include <math/TMatHelpers.h>
#include <math/vec2.h>
#include <math/compiler.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
/**
* A 2x2 column-major matrix class.
*
* Conceptually a 2x2 matrix is a an array of 2 column vec2:
*
* mat2 m =
* \f$
* \left(
* \begin{array}{cc}
* m[0] & m[1] \\
* \end{array}
* \right)
* \f$
* =
* \f$
* \left(
* \begin{array}{cc}
* m[0][0] & m[1][0] \\
* m[0][1] & m[1][1] \\
* \end{array}
* \right)
* \f$
* =
* \f$
* \left(
* \begin{array}{cc}
* m(0,0) & m(0,1) \\
* m(1,0) & m(1,1) \\
* \end{array}
* \right)
* \f$
*
* m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2.
*
*/
template<typename T>
class MATH_EMPTY_BASES TMat22 :
public TVecUnaryOperators<TMat22, T>,
public TVecComparisonOperators<TMat22, T>,
public TVecAddOperators<TMat22, T>,
public TMatProductOperators<TMat22, T, TVec2>,
public TMatSquareFunctions<TMat22, T>,
public TMatHelpers<TMat22, T> {
public:
enum no_init {
NO_INIT
};
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
typedef TVec2<T> col_type;
typedef TVec2<T> row_type;
static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
static constexpr size_t NUM_ROWS = COL_SIZE;
static constexpr size_t NUM_COLS = ROW_SIZE;
private:
/*
* <-- N columns -->
*
* a[0][0] a[1][0] a[2][0] ... a[N][0] ^
* a[0][1] a[1][1] a[2][1] ... a[N][1] |
* a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
* ... |
* a[0][M] a[1][M] a[2][M] ... a[N][M] v
*
* COL_SIZE = M
* ROW_SIZE = N
* m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
*/
col_type m_value[NUM_COLS];
public:
// array access
inline constexpr col_type const& operator[](size_t column) const noexcept {
assert(column < NUM_COLS);
return m_value[column];
}
inline constexpr col_type& operator[](size_t column) noexcept {
assert(column < NUM_COLS);
return m_value[column];
}
/**
* constructors
*/
/**
* leaves object uninitialized. use with caution.
*/
constexpr explicit TMat22(no_init) noexcept {}
/**
* initialize to identity.
*
* \f$
* \left(
* \begin{array}{cc}
* 1 & 0 \\
* 0 & 1 \\
* \end{array}
* \right)
* \f$
*/
constexpr TMat22() noexcept ;
/**
* initialize to Identity*scalar.
*
* \f$
* \left(
* \begin{array}{cc}
* v & 0 \\
* 0 & v \\
* \end{array}
* \right)
* \f$
*/
template<typename U>
constexpr explicit TMat22(U v) noexcept;
/**
* sets the diagonal to a vector.
*
* \f$
* \left(
* \begin{array}{cc}
* v[0] & 0 \\
* 0 & v[1] \\
* \end{array}
* \right)
* \f$
*/
template<typename U>
constexpr explicit TMat22(const TVec2<U>& v) noexcept;
/**
* construct from another matrix of the same size
*/
template<typename U>
constexpr explicit TMat22(const TMat22<U>& rhs) noexcept;
/**
* construct from 2 column vectors.
*
* \f$
* \left(
* \begin{array}{cc}
* v0 & v1 \\
* \end{array}
* \right)
* \f$
*/
template<typename A, typename B>
constexpr TMat22(const TVec2<A>& v0, const TVec2<B>& v1) noexcept;
/** construct from 4 elements in column-major form.
*
* \f$
* \left(
* \begin{array}{cc}
* m[0][0] & m[1][0] \\
* m[0][1] & m[1][1] \\
* \end{array}
* \right)
* \f$
*/
template<
typename A, typename B,
typename C, typename D>
constexpr explicit TMat22(A m00, B m01, C m10, D m11) noexcept ;
struct row_major_init {
template<typename A, typename B,
typename C, typename D>
constexpr explicit row_major_init(A m00, B m01, C m10, D m11) noexcept
: m(m00, m10, m01, m11) {}
private:
friend TMat22;
TMat22 m;
};
constexpr explicit TMat22(row_major_init c) noexcept : TMat22(std::move(c.m)) {}
/**
* Rotate by radians in the 2D plane
*/
static TMat22<T> rotate(T radian) noexcept {
TMat22<T> r(TMat22<T>::NO_INIT);
T c = std::cos(radian);
T s = std::sin(radian);
r[0][0] = c;
r[1][1] = c;
r[0][1] = s;
r[1][0] = -s;
return r;
}
// returns false if the two matrices are different. May return false if they're the
// same, with some elements only differing by +0 or -0. Behaviour is undefined with NaNs.
static constexpr bool fuzzyEqual(TMat22 l, TMat22 r) noexcept {
uint64_t const* const li = reinterpret_cast<uint64_t const*>(&l);
uint64_t const* const ri = reinterpret_cast<uint64_t const*>(&r);
uint64_t result = 0;
// For some reason clang is not able to vectoize this loop when the number of iteration
// is known and constant (!?!?!). Still this is better than operator==.
#pragma clang loop vectorize_width(2)
for (size_t i = 0; i < sizeof(TMat22) / sizeof(uint64_t); i++) {
result |= li[i] ^ ri[i];
}
return result != 0;
}
template<typename A>
static constexpr TMat22 translation(const TVec2<A>& t) noexcept {
TMat22 r;
r[2] = t;
return r;
}
template<typename A>
static constexpr TMat22 scaling(const TVec2<A>& s) noexcept {
return TMat22{ s };
}
template<typename A>
static constexpr TMat22 scaling(A s) noexcept {
return TMat22{ TVec2<T>{ s, s }};
}
};
// ----------------------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------------------
// Since the matrix code could become pretty big quickly, we don't inline most
// operations.
template<typename T>
constexpr TMat22<T>::TMat22() noexcept
: m_value{ col_type(1, 0), col_type(0, 1) } {
}
template<typename T>
template<typename U>
constexpr TMat22<T>::TMat22(U v) noexcept
: m_value{ col_type(v, 0), col_type(0, v) } {
}
template<typename T>
template<typename U>
constexpr TMat22<T>::TMat22(const TVec2<U>& v) noexcept
: m_value{ col_type(v[0], 0), col_type(0, v[1]) } {
}
// construct from 4 scalars. Note that the arrangement
// of values in the constructor is the transpose of the matrix
// notation.
template<typename T>
template<typename A, typename B,
typename C, typename D>
constexpr TMat22<T>::TMat22(A m00, B m01, C m10, D m11) noexcept
: m_value{ col_type(m00, m01), col_type(m10, m11) } {
}
template<typename T>
template<typename U>
constexpr TMat22<T>::TMat22(const TMat22<U>& rhs) noexcept {
for (size_t col = 0; col < NUM_COLS; ++col) {
m_value[col] = col_type(rhs[col]);
}
}
// Construct from 2 column vectors.
template<typename T>
template<typename A, typename B>
constexpr TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) noexcept
: m_value{ v0, v1 } {
}
} // namespace details
// ----------------------------------------------------------------------------------------
typedef details::TMat22<double> mat2;
typedef details::TMat22<float> mat2f;
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
namespace std {
template<typename T>
constexpr void swap(filament::math::details::TMat22<T>& lhs,
filament::math::details::TMat22<T>& rhs) noexcept {
// This generates much better code than the default implementation
// It's unclear why, I believe this is due to an optimization bug in the clang.
//
// filament::math::details::TMat22<T> t(lhs);
// lhs = rhs;
// rhs = t;
//
// clang always copy lhs on the stack, even if it's never using it (it's using the
// copy it has in registers).
const T t00 = lhs[0][0];
const T t01 = lhs[0][1];
const T t10 = lhs[1][0];
const T t11 = lhs[1][1];
lhs[0][0] = rhs[0][0];
lhs[0][1] = rhs[0][1];
lhs[1][0] = rhs[1][0];
lhs[1][1] = rhs[1][1];
rhs[0][0] = t00;
rhs[0][1] = t01;
rhs[1][0] = t10;
rhs[1][1] = t11;
}
}
#endif // MATH_MAT2_H_

490
ios/include/math/mat3.h Normal file
View File

@@ -0,0 +1,490 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_MAT3_H_
#define MATH_MAT3_H_
#include <math/quat.h>
#include <math/TMatHelpers.h>
#include <math/vec3.h>
#include <math/compiler.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
/**
* A 3x3 column-major matrix class.
*
* Conceptually a 3x3 matrix is a an array of 3 column vec3:
*
* mat3 m =
* \f$
* \left(
* \begin{array}{ccc}
* m[0] & m[1] & m[2] \\
* \end{array}
* \right)
* \f$
* =
* \f$
* \left(
* \begin{array}{ccc}
* m[0][0] & m[1][0] & m[2][0] \\
* m[0][1] & m[1][1] & m[2][1] \\
* m[0][2] & m[1][2] & m[2][2] \\
* \end{array}
* \right)
* \f$
* =
* \f$
* \left(
* \begin{array}{ccc}
* m(0,0) & m(0,1) & m(0,2) \\
* m(1,0) & m(1,1) & m(1,2) \\
* m(2,0) & m(2,1) & m(2,2) \\
* \end{array}
* \right)
* \f$
*
* m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3.
*
*/
template<typename T>
class MATH_EMPTY_BASES TMat33 :
public TVecUnaryOperators<TMat33, T>,
public TVecComparisonOperators<TMat33, T>,
public TVecAddOperators<TMat33, T>,
public TMatProductOperators<TMat33, T, TVec3>,
public TMatSquareFunctions<TMat33, T>,
public TMatTransform<TMat33, T>,
public TMatHelpers<TMat33, T> {
public:
enum no_init {
NO_INIT
};
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
typedef TVec3<T> col_type;
typedef TVec3<T> row_type;
static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
static constexpr size_t NUM_ROWS = COL_SIZE;
static constexpr size_t NUM_COLS = ROW_SIZE;
private:
/*
* <-- N columns -->
*
* a[0][0] a[1][0] a[2][0] ... a[N][0] ^
* a[0][1] a[1][1] a[2][1] ... a[N][1] |
* a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
* ... |
* a[0][M] a[1][M] a[2][M] ... a[N][M] v
*
* COL_SIZE = M
* ROW_SIZE = N
* m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
*/
col_type m_value[NUM_COLS];
public:
// array access
inline constexpr col_type const& operator[](size_t column) const noexcept {
assert(column < NUM_COLS);
return m_value[column];
}
inline constexpr col_type& operator[](size_t column) noexcept {
assert(column < NUM_COLS);
return m_value[column];
}
/**
* constructors
*/
/**
* leaves object uninitialized. use with caution.
*/
constexpr explicit TMat33(no_init) noexcept {}
/**
* initialize to identity.
*
* \f$
* \left(
* \begin{array}{ccc}
* 1 & 0 & 0 \\
* 0 & 1 & 0 \\
* 0 & 0 & 1 \\
* \end{array}
* \right)
* \f$
*/
constexpr TMat33() noexcept;
/**
* initialize to Identity*scalar.
*
* \f$
* \left(
* \begin{array}{ccc}
* v & 0 & 0 \\
* 0 & v & 0 \\
* 0 & 0 & v \\
* \end{array}
* \right)
* \f$
*/
template<typename U>
constexpr explicit TMat33(U v) noexcept;
/**
* sets the diagonal to a vector.
*
* \f$
* \left(
* \begin{array}{ccc}
* v[0] & 0 & 0 \\
* 0 & v[1] & 0 \\
* 0 & 0 & v[2] \\
* \end{array}
* \right)
* \f$
*/
template<typename U>
constexpr explicit TMat33(const TVec3<U>& v) noexcept;
/**
* construct from another matrix of the same size
*/
template<typename U>
constexpr explicit TMat33(const TMat33<U>& rhs) noexcept;
/**
* construct from 3 column vectors.
*
* \f$
* \left(
* \begin{array}{ccc}
* v0 & v1 & v2 \\
* \end{array}
* \right)
* \f$
*/
template<typename A, typename B, typename C>
constexpr TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) noexcept;
/** construct from 9 elements in column-major form.
*
* \f$
* \left(
* \begin{array}{ccc}
* m[0][0] & m[1][0] & m[2][0] \\
* m[0][1] & m[1][1] & m[2][1] \\
* m[0][2] & m[1][2] & m[2][2] \\
* \end{array}
* \right)
* \f$
*/
template<
typename A, typename B, typename C,
typename D, typename E, typename F,
typename G, typename H, typename I>
constexpr explicit TMat33(A m00, B m01, C m02,
D m10, E m11, F m12,
G m20, H m21, I m22) noexcept;
struct row_major_init {
template<
typename A, typename B, typename C,
typename D, typename E, typename F,
typename G, typename H, typename I>
constexpr explicit row_major_init(A m00, B m01, C m02,
D m10, E m11, F m12,
G m20, H m21, I m22) noexcept
: m(m00, m10, m20,
m01, m11, m21,
m02, m12, m22) {}
private:
friend TMat33;
TMat33 m;
};
constexpr explicit TMat33(row_major_init c) noexcept : TMat33(std::move(c.m)) {}
/**
* construct from a quaternion
*/
template<typename U>
constexpr explicit TMat33(const TQuaternion<U>& q) noexcept;
/**
* orthogonalize only works on matrices of size 3x3
*/
friend inline
constexpr TMat33 orthogonalize(const TMat33& m) noexcept {
TMat33 ret(TMat33::NO_INIT);
ret[0] = normalize(m[0]);
ret[2] = normalize(cross(ret[0], m[1]));
ret[1] = normalize(cross(ret[2], ret[0]));
return ret;
}
/**
* Returns a matrix suitable for transforming normals
*
* Note that the inverse-transpose of a matrix is equal to its cofactor matrix divided by its
* determinant:
*
* transpose(inverse(M)) = cof(M) / det(M)
*
* The cofactor matrix is faster to compute than the inverse-transpose, and it can be argued
* that it is a more correct way of transforming normals anyway. Some references from Dale
* Weiler, Nathan Reed, Inigo Quilez, and Eric Lengyel:
*
* - https://github.com/graphitemaster/normals_revisited
* - http://www.reedbeta.com/blog/normals-inverse-transpose-part-1/
* - https://www.shadertoy.com/view/3s33zj
* - FGED Volume 1, section 1.7.5 "Inverses of Small Matrices"
* - FGED Volume 1, section 3.2.2 "Transforming Normal Vectors"
*
* In "Transforming Normal Vectors", Lengyel notes that there are two types of transformed
* normals: one that uses the transposed adjugate (aka cofactor matrix) and one that uses the
* transposed inverse. He goes on to say that this difference is inconsequential, except when
* mirroring is involved.
*
* @param m the transform applied to vertices
* @return a matrix to apply to normals
*
* @warning normals transformed by this matrix must be normalized
*/
static constexpr TMat33 getTransformForNormals(const TMat33& m) noexcept {
return matrix::cof(m);
}
/**
* Packs the tangent frame represented by the specified matrix into a quaternion.
* Reflection is preserved by encoding it as the sign of the w component in the
* resulting quaternion. Since -0 cannot always be represented on the GPU, this
* function computes a bias to ensure values are always either positive or negative,
* never 0. The bias is computed based on the specified storageSize, which defaults
* to 2 bytes, making the resulting quaternion suitable for storage into an SNORM16
* vector.
*/
static constexpr TQuaternion<T> packTangentFrame(
const TMat33& m, size_t storageSize = sizeof(int16_t)) noexcept;
template<typename A>
static constexpr TMat33 translation(const TVec3<A>& t) noexcept {
TMat33 r;
r[2] = t;
return r;
}
template<typename A>
static constexpr TMat33 scaling(const TVec3<A>& s) noexcept {
return TMat33{ s };
}
template<typename A>
static constexpr TMat33 scaling(A s) noexcept {
return TMat33{ TVec3<T>{ s }};
}
};
// ----------------------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------------------
// Since the matrix code could become pretty big quickly, we don't inline most
// operations.
template<typename T>
constexpr TMat33<T>::TMat33() noexcept
: m_value{
col_type(1, 0, 0),
col_type(0, 1, 0),
col_type(0, 0, 1) } {
}
template<typename T>
template<typename U>
constexpr TMat33<T>::TMat33(U v) noexcept
: m_value{
col_type(v, 0, 0),
col_type(0, v, 0),
col_type(0, 0, v) } {
}
template<typename T>
template<typename U>
constexpr TMat33<T>::TMat33(const TVec3<U>& v) noexcept
: m_value{
col_type(v[0], 0, 0),
col_type(0, v[1], 0),
col_type(0, 0, v[2]) } {
}
// construct from 16 scalars. Note that the arrangement
// of values in the constructor is the transpose of the matrix
// notation.
template<typename T>
template<
typename A, typename B, typename C,
typename D, typename E, typename F,
typename G, typename H, typename I>
constexpr TMat33<T>::TMat33(A m00, B m01, C m02,
D m10, E m11, F m12,
G m20, H m21, I m22) noexcept
: m_value{
col_type(m00, m01, m02),
col_type(m10, m11, m12),
col_type(m20, m21, m22) } {
}
template<typename T>
template<typename U>
constexpr TMat33<T>::TMat33(const TMat33<U>& rhs) noexcept {
for (size_t col = 0; col < NUM_COLS; ++col) {
m_value[col] = col_type(rhs[col]);
}
}
// Construct from 3 column vectors.
template<typename T>
template<typename A, typename B, typename C>
constexpr TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) noexcept
: m_value{ v0, v1, v2 } {
}
template<typename T>
template<typename U>
constexpr TMat33<T>::TMat33(const TQuaternion<U>& q) noexcept : m_value{} {
const U n = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
const U s = n > 0 ? 2 / n : 0;
const U x = s * q.x;
const U y = s * q.y;
const U z = s * q.z;
const U xx = x * q.x;
const U xy = x * q.y;
const U xz = x * q.z;
const U xw = x * q.w;
const U yy = y * q.y;
const U yz = y * q.z;
const U yw = y * q.w;
const U zz = z * q.z;
const U zw = z * q.w;
m_value[0] = col_type(1 - yy - zz, xy + zw, xz - yw); // NOLINT
m_value[1] = col_type(xy - zw, 1 - xx - zz, yz + xw); // NOLINT
m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy); // NOLINT
}
//------------------------------------------------------------------------------
template<typename T>
constexpr TQuaternion<T> TMat33<T>::packTangentFrame(const TMat33<T>& m, size_t storageSize) noexcept {
TQuaternion<T> q = TMat33<T>{ m[0], cross(m[2], m[0]), m[2] }.toQuaternion();
q = positive(normalize(q));
// Ensure w is never 0.0
// Bias is 2^(nb_bits - 1) - 1
const T bias = T(1.0) / T((1 << (storageSize * CHAR_BIT - 1)) - 1);
if (q.w < bias) {
q.w = bias;
const T factor = (T)(std::sqrt(1.0 - (double)bias * (double)bias));
q.xyz *= factor;
}
// If there's a reflection ((n x t) . b <= 0), make sure w is negative
if (dot(cross(m[0], m[2]), m[1]) < T(0)) {
q = -q;
}
return q;
}
} // namespace details
// ----------------------------------------------------------------------------------------
typedef details::TMat33<double> mat3;
typedef details::TMat33<float> mat3f;
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
namespace std {
template<typename T>
constexpr void swap(filament::math::details::TMat33<T>& lhs,
filament::math::details::TMat33<T>& rhs) noexcept {
// This generates much better code than the default implementation
// It's unclear why, I believe this is due to an optimization bug in the clang.
//
// filament::math::details::TMat33<T> t(lhs);
// lhs = rhs;
// rhs = t;
//
// clang always copy lhs on the stack, even if it's never using it (it's using the
// copy it has in registers).
const T t00 = lhs[0][0];
const T t01 = lhs[0][1];
const T t02 = lhs[0][2];
const T t10 = lhs[1][0];
const T t11 = lhs[1][1];
const T t12 = lhs[1][2];
const T t20 = lhs[2][0];
const T t21 = lhs[2][1];
const T t22 = lhs[2][2];
lhs[0][0] = rhs[0][0];
lhs[0][1] = rhs[0][1];
lhs[0][2] = rhs[0][2];
lhs[1][0] = rhs[1][0];
lhs[1][1] = rhs[1][1];
lhs[1][2] = rhs[1][2];
lhs[2][0] = rhs[2][0];
lhs[2][1] = rhs[2][1];
lhs[2][2] = rhs[2][2];
rhs[0][0] = t00;
rhs[0][1] = t01;
rhs[0][2] = t02;
rhs[1][0] = t10;
rhs[1][1] = t11;
rhs[1][2] = t12;
rhs[2][0] = t20;
rhs[2][1] = t21;
rhs[2][2] = t22;
}
}
#endif // MATH_MAT3_H_

631
ios/include/math/mat4.h Normal file
View File

@@ -0,0 +1,631 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_MAT4_H_
#define MATH_MAT4_H_
#include <math/compiler.h>
#include <math/mat3.h>
#include <math/quat.h>
#include <math/scalar.h>
#include <math/TMatHelpers.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <stdint.h>
#include <sys/types.h>
#include <limits>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
template<typename T>
class TQuaternion;
/**
* A 4x4 column-major matrix class.
*
* Conceptually a 4x4 matrix is a an array of 4 column double4:
*
* mat4 m =
* \f$
* \left(
* \begin{array}{cccc}
* m[0] & m[1] & m[2] & m[3] \\
* \end{array}
* \right)
* \f$
* =
* \f$
* \left(
* \begin{array}{cccc}
* m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
* m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
* m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
* m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
* \end{array}
* \right)
* \f$
* =
* \f$
* \left(
* \begin{array}{cccc}
* m(0,0) & m(0,1) & m(0,2) & m(0,3) \\
* m(1,0) & m(1,1) & m(1,2) & m(1,3) \\
* m(2,0) & m(2,1) & m(2,2) & m(2,3) \\
* m(3,0) & m(3,1) & m(3,2) & m(3,3) \\
* \end{array}
* \right)
* \f$
*
* m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4.
*
*/
template<typename T>
class MATH_EMPTY_BASES TMat44 :
public TVecUnaryOperators<TMat44, T>,
public TVecComparisonOperators<TMat44, T>,
public TVecAddOperators<TMat44, T>,
public TMatProductOperators<TMat44, T, TVec4>,
public TMatSquareFunctions<TMat44, T>,
public TMatTransform<TMat44, T>,
public TMatHelpers<TMat44, T> {
public:
enum no_init {
NO_INIT
};
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
typedef TVec4<T> col_type;
typedef TVec4<T> row_type;
static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
static constexpr size_t NUM_ROWS = COL_SIZE;
static constexpr size_t NUM_COLS = ROW_SIZE;
private:
/*
* <-- N columns -->
*
* a[0][0] a[1][0] a[2][0] ... a[N][0] ^
* a[0][1] a[1][1] a[2][1] ... a[N][1] |
* a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
* ... |
* a[0][M] a[1][M] a[2][M] ... a[N][M] v
*
* COL_SIZE = M
* ROW_SIZE = N
* m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
*/
col_type m_value[NUM_COLS];
public:
// array access
inline constexpr col_type const& operator[](size_t column) const noexcept {
assert(column < NUM_COLS);
return m_value[column];
}
inline constexpr col_type& operator[](size_t column) noexcept {
assert(column < NUM_COLS);
return m_value[column];
}
/*
* constructors
*/
// leaves object uninitialized. use with caution.
constexpr explicit TMat44(no_init) noexcept {}
/** initialize to identity.
*
* \f$
* \left(
* \begin{array}{cccc}
* 1 & 0 & 0 & 0 \\
* 0 & 1 & 0 & 0 \\
* 0 & 0 & 1 & 0 \\
* 0 & 0 & 0 & 1 \\
* \end{array}
* \right)
* \f$
*/
constexpr TMat44() noexcept;
/** initialize to Identity*scalar.
*
* \f$
* \left(
* \begin{array}{cccc}
* v & 0 & 0 & 0 \\
* 0 & v & 0 & 0 \\
* 0 & 0 & v & 0 \\
* 0 & 0 & 0 & v \\
* \end{array}
* \right)
* \f$
*/
template<typename U>
constexpr explicit TMat44(U v) noexcept;
/** sets the diagonal to a vector.
*
* \f$
* \left(
* \begin{array}{cccc}
* v[0] & 0 & 0 & 0 \\
* 0 & v[1] & 0 & 0 \\
* 0 & 0 & v[2] & 0 \\
* 0 & 0 & 0 & v[3] \\
* \end{array}
* \right)
* \f$
*/
template<typename U>
constexpr explicit TMat44(const TVec4<U>& v) noexcept;
// construct from another matrix of the same size
template<typename U>
constexpr explicit TMat44(const TMat44<U>& rhs) noexcept;
/** construct from 4 column vectors.
*
* \f$
* \left(
* \begin{array}{cccc}
* v0 & v1 & v2 & v3 \\
* \end{array}
* \right)
* \f$
*/
template<typename A, typename B, typename C, typename D>
constexpr TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2,
const TVec4<D>& v3) noexcept;
/** construct from 16 elements in column-major form.
*
* \f$
* \left(
* \begin{array}{cccc}
* m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
* m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
* m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
* m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
* \end{array}
* \right)
* \f$
*/
template<
typename A, typename B, typename C, typename D,
typename E, typename F, typename G, typename H,
typename I, typename J, typename K, typename L,
typename M, typename N, typename O, typename P>
constexpr explicit TMat44(A m00, B m01, C m02, D m03,
E m10, F m11, G m12, H m13,
I m20, J m21, K m22, L m23,
M m30, N m31, O m32, P m33) noexcept;
struct row_major_init {
template<
typename A, typename B, typename C, typename D,
typename E, typename F, typename G, typename H,
typename I, typename J, typename K, typename L,
typename M, typename N, typename O, typename P>
constexpr explicit row_major_init(A m00, B m01, C m02, D m03,
E m10, F m11, G m12, H m13,
I m20, J m21, K m22, L m23,
M m30, N m31, O m32, P m33) noexcept
: m(m00, m10, m20, m30,
m01, m11, m21, m31,
m02, m12, m22, m32,
m03, m13, m23, m33) {}
private:
friend TMat44;
TMat44 m;
};
constexpr explicit TMat44(row_major_init c) noexcept : TMat44(std::move(c.m)) {}
/**
* construct from a quaternion
*/
template<typename U>
constexpr explicit TMat44(const TQuaternion<U>& q) noexcept;
/**
* construct from a 3x3 matrix
*/
template<typename U>
constexpr explicit TMat44(const TMat33<U>& matrix) noexcept;
/**
* construct from a 3x3 matrix and 3d translation
*/
template<typename U, typename V>
constexpr TMat44(const TMat33<U>& matrix, const TVec3<V>& translation) noexcept;
/**
* construct from a 3x3 matrix and 4d last column.
*/
template<typename U, typename V>
constexpr TMat44(const TMat33<U>& matrix, const TVec4<V>& column3) noexcept;
/*
* helpers
*/
// returns false if the two matrices are different. May return false if they're the
// same, with some elements only differing by +0 or -0. Behaviour is undefined with NaNs.
static constexpr bool fuzzyEqual(TMat44 const& l, TMat44 const& r) noexcept {
uint64_t const* const li = reinterpret_cast<uint64_t const*>(&l);
uint64_t const* const ri = reinterpret_cast<uint64_t const*>(&r);
uint64_t result = 0;
// For some reason clang is not able to vectorize this loop when the number of iteration
// is known and constant (!?!?!). Still this is better than operator==.
for (size_t i = 0; i < sizeof(TMat44) / sizeof(uint64_t); i++) {
result |= li[i] ^ ri[i];
}
return result != 0;
}
static constexpr TMat44 ortho(T left, T right, T bottom, T top, T near, T far) noexcept;
static constexpr TMat44 frustum(T left, T right, T bottom, T top, T near, T far) noexcept;
enum class Fov {
HORIZONTAL,
VERTICAL
};
static TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL) noexcept;
template<typename A, typename B, typename C>
static TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) noexcept;
template<typename A>
static constexpr TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) noexcept{
TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
return TVec3<A>{ r[0], r[1], r[2] } * (1 / r[3]);
}
template<typename A>
static constexpr TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) noexcept{
vertice = projectionMatrix * vertice;
return { TVec3<A>{ vertice[0], vertice[1], vertice[2] } * (1 / vertice[3]), 1 };
}
/**
* Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix
*/
inline constexpr TMat33<T> upperLeft() const noexcept {
const TVec3<T> v0 = { m_value[0][0], m_value[0][1], m_value[0][2] };
const TVec3<T> v1 = { m_value[1][0], m_value[1][1], m_value[1][2] };
const TVec3<T> v2 = { m_value[2][0], m_value[2][1], m_value[2][2] };
return TMat33<T>(v0, v1, v2);
}
template<typename A>
static constexpr TMat44 translation(const TVec3<A>& t) noexcept {
TMat44 r;
r[3] = TVec4<T>{ t, 1 };
return r;
}
template<typename A>
static constexpr TMat44 scaling(const TVec3<A>& s) noexcept {
return TMat44{ TVec4<T>{ s, 1 }};
}
template<typename A>
static constexpr TMat44 scaling(A s) noexcept {
return TMat44{ TVec4<T>{ s, s, s, 1 }};
}
};
// ----------------------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------------------
// Since the matrix code could become pretty big quickly, we don't inline most
// operations.
template<typename T>
constexpr TMat44<T>::TMat44() noexcept
: m_value{
col_type(1, 0, 0, 0),
col_type(0, 1, 0, 0),
col_type(0, 0, 1, 0),
col_type(0, 0, 0, 1) } {
}
template<typename T>
template<typename U>
constexpr TMat44<T>::TMat44(U v) noexcept
: m_value{
col_type(v, 0, 0, 0),
col_type(0, v, 0, 0),
col_type(0, 0, v, 0),
col_type(0, 0, 0, v) } {
}
template<typename T>
template<typename U>
constexpr TMat44<T>::TMat44(const TVec4<U>& v) noexcept
: m_value{
col_type(v[0], 0, 0, 0),
col_type(0, v[1], 0, 0),
col_type(0, 0, v[2], 0),
col_type(0, 0, 0, v[3]) } {
}
// construct from 16 scalars
template<typename T>
template<
typename A, typename B, typename C, typename D,
typename E, typename F, typename G, typename H,
typename I, typename J, typename K, typename L,
typename M, typename N, typename O, typename P>
constexpr TMat44<T>::TMat44(A m00, B m01, C m02, D m03,
E m10, F m11, G m12, H m13,
I m20, J m21, K m22, L m23,
M m30, N m31, O m32, P m33) noexcept
: m_value{
col_type(m00, m01, m02, m03),
col_type(m10, m11, m12, m13),
col_type(m20, m21, m22, m23),
col_type(m30, m31, m32, m33) } {
}
template<typename T>
template<typename U>
constexpr TMat44<T>::TMat44(const TMat44<U>& rhs) noexcept {
for (size_t col = 0; col < NUM_COLS; ++col) {
m_value[col] = col_type(rhs[col]);
}
}
// Construct from 4 column vectors.
template<typename T>
template<typename A, typename B, typename C, typename D>
constexpr TMat44<T>::TMat44(const TVec4<A>& v0, const TVec4<B>& v1,
const TVec4<C>& v2, const TVec4<D>& v3) noexcept
: m_value{ v0, v1, v2, v3 } {
}
template<typename T>
template<typename U>
constexpr TMat44<T>::TMat44(const TQuaternion<U>& q) noexcept : m_value{} {
const U n = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
const U s = n > 0 ? 2 / n : 0;
const U x = s * q.x;
const U y = s * q.y;
const U z = s * q.z;
const U xx = x * q.x;
const U xy = x * q.y;
const U xz = x * q.z;
const U xw = x * q.w;
const U yy = y * q.y;
const U yz = y * q.z;
const U yw = y * q.w;
const U zz = z * q.z;
const U zw = z * q.w;
m_value[0] = col_type(1 - yy - zz, xy + zw, xz - yw, 0);
m_value[1] = col_type(xy - zw, 1 - xx - zz, yz + xw, 0); // NOLINT
m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy, 0); // NOLINT
m_value[3] = col_type(0, 0, 0, 1); // NOLINT
}
template<typename T>
template<typename U>
constexpr TMat44<T>::TMat44(const TMat33<U>& m) noexcept
: m_value{
col_type(m[0][0], m[0][1], m[0][2], 0),
col_type(m[1][0], m[1][1], m[1][2], 0),
col_type(m[2][0], m[2][1], m[2][2], 0),
col_type(0, 0, 0, 1) } // NOLINT
{
}
template<typename T>
template<typename U, typename V>
constexpr TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) noexcept
: m_value{
col_type(m[0][0], m[0][1], m[0][2], 0),
col_type(m[1][0], m[1][1], m[1][2], 0),
col_type(m[2][0], m[2][1], m[2][2], 0),
col_type(v[0], v[1], v[2], 1) } // NOLINT
{
}
template<typename T>
template<typename U, typename V>
constexpr TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) noexcept
: m_value{
col_type(m[0][0], m[0][1], m[0][2], 0),
col_type(m[1][0], m[1][1], m[1][2], 0),
col_type(m[2][0], m[2][1], m[2][2], 0),
col_type(v[0], v[1], v[2], v[3]) } // NOLINT
{
}
// ----------------------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------------------
template<typename T>
constexpr TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) noexcept {
TMat44<T> m;
m[0][0] = 2 / (right - left);
m[1][1] = 2 / (top - bottom);
m[2][2] = -2 / (far - near);
m[3][0] = -(right + left) / (right - left);
m[3][1] = -(top + bottom) / (top - bottom);
m[3][2] = -(far + near) / (far - near);
return m;
}
template<typename T>
constexpr TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) noexcept {
TMat44<T> m;
m[0][0] = (2 * near) / (right - left);
m[1][1] = (2 * near) / (top - bottom);
m[2][0] = (right + left) / (right - left);
m[2][1] = (top + bottom) / (top - bottom);
m[2][2] = -(far + near) / (far - near);
m[2][3] = -1;
m[3][2] = -(2 * far * near) / (far - near);
m[3][3] = 0;
return m;
}
template<typename T>
TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) noexcept {
T h, w;
if (direction == TMat44::Fov::VERTICAL) {
h = std::tan(fov * F_PI / 360.0f) * near;
w = h * aspect;
} else {
w = std::tan(fov * F_PI / 360.0f) * near;
h = w / aspect;
}
return frustum(-w, w, -h, h, near, far);
}
/*
* Returns a matrix representing the pose of a virtual camera looking towards -Z in its
* local Y-up coordinate system. "eye" is where the camera is located, "center" is the point it's
* looking at and "up" defines where the Y axis of the camera's local coordinate system is.
*/
template<typename T>
template<typename A, typename B, typename C>
TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center,
const TVec3<C>& up) noexcept {
TVec3<T> z_axis(normalize(center - eye));
TVec3<T> norm_up(normalize(up));
if (std::abs(dot(z_axis, norm_up)) > T(0.999)) {
// Fix up vector if we're degenerate (looking straight up, basically)
norm_up = { norm_up.z, norm_up.x, norm_up.y };
}
TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
TVec3<T> y_axis(cross(x_axis, z_axis));
return TMat44<T>(
TVec4<T>(x_axis, 0),
TVec4<T>(y_axis, 0),
TVec4<T>(-z_axis, 0),
TVec4<T>(eye, 1));
}
// ----------------------------------------------------------------------------------------
// Arithmetic operators outside of class
// ----------------------------------------------------------------------------------------
// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
template<typename T, typename U>
constexpr typename TMat44<T>::col_type MATH_PURE operator*(const TMat44<T>& lhs,
const TVec3<U>& rhs) noexcept {
return lhs * TVec4<U>{ rhs, 1 };
}
} // namespace details
// ----------------------------------------------------------------------------------------
typedef details::TMat44<double> mat4;
typedef details::TMat44<float> mat4f;
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
namespace std {
template<typename T>
constexpr void swap(filament::math::details::TMat44<T>& lhs,
filament::math::details::TMat44<T>& rhs) noexcept {
// This generates much better code than the default implementation
// It's unclear why, I believe this is due to an optimization bug in the clang.
//
// filament::math::details::TMat44<T> t(lhs);
// lhs = rhs;
// rhs = t;
//
// clang always copy lhs on the stack, even if it's never using it (it's using the
// copy it has in registers).
const T t00 = lhs[0][0];
const T t01 = lhs[0][1];
const T t02 = lhs[0][2];
const T t03 = lhs[0][3];
const T t10 = lhs[1][0];
const T t11 = lhs[1][1];
const T t12 = lhs[1][2];
const T t13 = lhs[1][3];
const T t20 = lhs[2][0];
const T t21 = lhs[2][1];
const T t22 = lhs[2][2];
const T t23 = lhs[2][3];
const T t30 = lhs[3][0];
const T t31 = lhs[3][1];
const T t32 = lhs[3][2];
const T t33 = lhs[3][3];
lhs[0][0] = rhs[0][0];
lhs[0][1] = rhs[0][1];
lhs[0][2] = rhs[0][2];
lhs[0][3] = rhs[0][3];
lhs[1][0] = rhs[1][0];
lhs[1][1] = rhs[1][1];
lhs[1][2] = rhs[1][2];
lhs[1][3] = rhs[1][3];
lhs[2][0] = rhs[2][0];
lhs[2][1] = rhs[2][1];
lhs[2][2] = rhs[2][2];
lhs[2][3] = rhs[2][3];
lhs[3][0] = rhs[3][0];
lhs[3][1] = rhs[3][1];
lhs[3][2] = rhs[3][2];
lhs[3][3] = rhs[3][3];
rhs[0][0] = t00;
rhs[0][1] = t01;
rhs[0][2] = t02;
rhs[0][3] = t03;
rhs[1][0] = t10;
rhs[1][1] = t11;
rhs[1][2] = t12;
rhs[1][3] = t13;
rhs[2][0] = t20;
rhs[2][1] = t21;
rhs[2][2] = t22;
rhs[2][3] = t23;
rhs[3][0] = t30;
rhs[3][1] = t31;
rhs[3][2] = t32;
rhs[3][3] = t33;
}
}
#endif // MATH_MAT4_H_

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_MATHFWD_H_
#define MATH_MATHFWD_H_
#ifdef _MSC_VER
// MSVC cannot compute the size of math types correctly when this file is included before the
// actual implementations.
// See github.com/google/filament/issues/2190.
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat2.h>
#include <math/mat3.h>
#include <math/mat4.h>
#else
#include <stdint.h>
namespace filament {
namespace math {
namespace details {
template<typename T> class TVec2;
template<typename T> class TVec3;
template<typename T> class TVec4;
template<typename T> class TMat22;
template<typename T> class TMat33;
template<typename T> class TMat44;
} // namespace details
using double2 = details::TVec2<double>;
using float2 = details::TVec2<float>;
using int2 = details::TVec2<int32_t>;
using uint2 = details::TVec2<uint32_t>;
using short2 = details::TVec2<int16_t>;
using ushort2 = details::TVec2<uint16_t>;
using byte2 = details::TVec2<int8_t>;
using ubyte2 = details::TVec2<uint8_t>;
using bool2 = details::TVec2<bool>;
using double3 = details::TVec3<double>;
using float3 = details::TVec3<float>;
using int3 = details::TVec3<int32_t>;
using uint3 = details::TVec3<uint32_t>;
using short3 = details::TVec3<int16_t>;
using ushort3 = details::TVec3<uint16_t>;
using byte3 = details::TVec3<int8_t>;
using ubyte3 = details::TVec3<uint8_t>;
using bool3 = details::TVec3<bool>;
using double4 = details::TVec4<double>;
using float4 = details::TVec4<float>;
using int4 = details::TVec4<int32_t>;
using uint4 = details::TVec4<uint32_t>;
using short4 = details::TVec4<int16_t>;
using ushort4 = details::TVec4<uint16_t>;
using byte4 = details::TVec4<int8_t>;
using ubyte4 = details::TVec4<uint8_t>;
using bool4 = details::TVec4<bool>;
using mat2 = details::TMat22<double>;
using mat2f = details::TMat22<float>;
using mat3 = details::TMat33<double>;
using mat3f = details::TMat33<float>;
using mat4 = details::TMat44<double>;
using mat4f = details::TMat44<float>;
} // namespace math
} // namespace filament
#endif // _MSC_VER
#endif // MATH_MATHFWD_H_

100
ios/include/math/norm.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MATH_NORM_H
#define TNT_MATH_NORM_H
#include <math/scalar.h>
#include <math/vec4.h>
#include <cmath>
#include <stdint.h>
namespace filament {
namespace math {
inline uint16_t packUnorm16(float v) noexcept {
return static_cast<uint16_t>(std::round(clamp(v, 0.0f, 1.0f) * 65535.0f));
}
inline ushort4 packUnorm16(float4 v) noexcept {
return ushort4{ packUnorm16(v.x), packUnorm16(v.y), packUnorm16(v.z), packUnorm16(v.w) };
}
inline int16_t packSnorm16(float v) noexcept {
return static_cast<int16_t>(std::round(clamp(v, -1.0f, 1.0f) * 32767.0f));
}
inline short2 packSnorm16(float2 v) noexcept {
return short2{ packSnorm16(v.x), packSnorm16(v.y) };
}
inline short4 packSnorm16(float4 v) noexcept {
return short4{ packSnorm16(v.x), packSnorm16(v.y), packSnorm16(v.z), packSnorm16(v.w) };
}
inline float unpackUnorm16(uint16_t v) noexcept {
return v / 65535.0f;
}
inline float4 unpackUnorm16(ushort4 v) noexcept {
return float4{ unpackUnorm16(v.x), unpackUnorm16(v.y), unpackUnorm16(v.z), unpackUnorm16(v.w) };
}
inline float unpackSnorm16(int16_t v) noexcept {
return clamp(v / 32767.0f, -1.0f, 1.0f);
}
inline float4 unpackSnorm16(short4 v) noexcept {
return float4{ unpackSnorm16(v.x), unpackSnorm16(v.y), unpackSnorm16(v.z), unpackSnorm16(v.w) };
}
inline uint8_t packUnorm8(float v) noexcept {
return static_cast<uint8_t>(std::round(clamp(v, 0.0f, 1.0f) * 255.0));
}
inline ubyte4 packUnorm8(float4 v) noexcept {
return ubyte4{ packUnorm8(v.x), packUnorm8(v.y), packUnorm8(v.z), packUnorm8(v.w) };
}
inline int8_t packSnorm8(float v) noexcept {
return static_cast<int8_t>(std::round(clamp(v, -1.0f, 1.0f) * 127.0));
}
inline byte4 packSnorm8(float4 v) noexcept {
return byte4{ packSnorm8(v.x), packSnorm8(v.y), packSnorm8(v.z), packSnorm8(v.w) };
}
inline float unpackUnorm8(uint8_t v) noexcept {
return v / 255.0f;
}
inline float4 unpackUnorm8(ubyte4 v) noexcept {
return float4{ unpackUnorm8(v.x), unpackUnorm8(v.y), unpackUnorm8(v.z), unpackUnorm8(v.w) };
}
inline float unpackSnorm8(int8_t v) noexcept {
return clamp(v / 127.0f, -1.0f, 1.0f);
}
inline float4 unpackSnorm8(byte4 v) noexcept {
return float4{ unpackSnorm8(v.x), unpackSnorm8(v.y), unpackSnorm8(v.z), unpackSnorm8(v.w) };
}
} // namespace math
} // namespace filament
#endif // TNT_MATH_NORM_H

167
ios/include/math/quat.h Normal file
View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_QUAT_H_
#define MATH_QUAT_H_
#include <math/half.h>
#include <math/TQuatHelpers.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/compiler.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
template<typename T>
class MATH_EMPTY_BASES TQuaternion :
public TVecAddOperators<TQuaternion, T>,
public TVecUnaryOperators<TQuaternion, T>,
public TVecComparisonOperators<TQuaternion, T>,
public TQuatProductOperators<TQuaternion, T>,
public TQuatFunctions<TQuaternion, T> {
public:
enum no_init {
NO_INIT
};
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
/*
* quaternion internals stored as:
*
* q = w + xi + yj + zk
*
* q[0] = x;
* q[1] = y;
* q[2] = z;
* q[3] = w;
*
*/
union {
struct { T x, y, z, w; };
TVec4<T> xyzw;
TVec3<T> xyz;
TVec2<T> xy;
};
enum { SIZE = 4 };
inline constexpr static size_type size() { return SIZE; }
// array access
inline constexpr T const& operator[](size_t i) const {
// only possible in C++0x14 with constexpr
assert(i < SIZE);
return (&x)[i];
}
inline constexpr T& operator[](size_t i) {
assert(i < SIZE);
return (&x)[i];
}
// -----------------------------------------------------------------------
// we want the compiler generated versions for these...
TQuaternion(const TQuaternion&) = default;
~TQuaternion() = default;
TQuaternion& operator=(const TQuaternion&) = default;
// constructors
// leaves object uninitialized. use with caution.
explicit constexpr TQuaternion(no_init) {}
// default constructor. sets all values to zero.
constexpr TQuaternion() : x(0), y(0), z(0), w(0) {}
// handles implicit conversion to a quat. must not be explicit.
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {}
// initialize from 4 values to w + xi + yj + zk
template<typename A, typename B, typename C, typename D,
typename = enable_if_arithmetic_t<A, B, C, D>>
constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) {}
// initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w
template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) {}
// initialize from a vec4
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) {}
// initialize from a quaternion of a different type
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) {}
// conjugate operator
constexpr TQuaternion operator~() const {
return conj(*this);
}
// constructs a quaternion from an axis and angle
template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
constexpr static TQuaternion MATH_PURE fromAxisAngle(const TVec3<A>& axis, B angle) {
return TQuaternion(std::sin(angle * 0.5) * normalize(axis), std::cos(angle * 0.5));
}
};
} // namespace details
// ----------------------------------------------------------------------------------------
typedef details::TQuaternion<double> quat;
typedef details::TQuaternion<float> quatf;
typedef details::TQuaternion<half> quath;
constexpr inline quat operator "" _i(long double v) {
return quat(0.0, double(v), 0.0, 0.0);
}
constexpr inline quat operator "" _j(long double v) {
return quat(0.0, 0.0, double(v), 0.0);
}
constexpr inline quat operator "" _k(long double v) {
return quat(0.0, 0.0, 0.0, double(v));
}
constexpr inline quat operator "" _i(unsigned long long v) {
return quat(0.0, double(v), 0.0, 0.0);
}
constexpr inline quat operator "" _j(unsigned long long v) {
return quat(0.0, 0.0, double(v), 0.0);
}
constexpr inline quat operator "" _k(unsigned long long v) {
return quat(0.0, 0.0, 0.0, double(v));
}
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
#endif // MATH_QUAT_H_

124
ios/include/math/scalar.h Normal file
View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MATH_SCALAR_H
#define TNT_MATH_SCALAR_H
#include <math/compiler.h>
#include <assert.h>
namespace filament {
namespace math {
constexpr const double F_E = 2.71828182845904523536028747135266250;
constexpr const double F_LOG2E = 1.44269504088896340735992468100189214;
constexpr const double F_LOG10E = 0.434294481903251827651128918916605082;
constexpr const double F_LN2 = 0.693147180559945309417232121458176568;
constexpr const double F_LN10 = 2.30258509299404568401799145468436421;
constexpr const double F_PI = 3.14159265358979323846264338327950288;
constexpr const double F_PI_2 = 1.57079632679489661923132169163975144;
constexpr const double F_PI_4 = 0.785398163397448309615660845819875721;
constexpr const double F_1_PI = 0.318309886183790671537767526745028724;
constexpr const double F_2_PI = 0.636619772367581343075535053490057448;
constexpr const double F_2_SQRTPI = 1.12837916709551257389615890312154517;
constexpr const double F_SQRT2 = 1.41421356237309504880168872420969808;
constexpr const double F_SQRT1_2 = 0.707106781186547524400844362104849039;
constexpr const double F_TAU = 2.0 * F_PI;
namespace d {
constexpr const double E = F_E;
constexpr const double LOG2E = F_LOG2E;
constexpr const double LOG10E = F_LOG10E;
constexpr const double LN2 = F_LN2;
constexpr const double LN10 = F_LN10;
constexpr const double PI = F_PI;
constexpr const double PI_2 = F_PI_2;
constexpr const double PI_4 = F_PI_4;
constexpr const double ONE_OVER_PI = F_1_PI;
constexpr const double TWO_OVER_PI = F_2_PI;
constexpr const double TWO_OVER_SQRTPI = F_2_SQRTPI;
constexpr const double SQRT2 = F_SQRT2;
constexpr const double SQRT1_2 = F_SQRT1_2;
constexpr const double TAU = F_TAU;
constexpr const double DEG_TO_RAD = F_PI / 180.0;
constexpr const double RAD_TO_DEG = 180.0 / F_PI;
} // namespace d
namespace f {
constexpr const float E = (float)d::E;
constexpr const float LOG2E = (float)d::LOG2E;
constexpr const float LOG10E = (float)d::LOG10E;
constexpr const float LN2 = (float)d::LN2;
constexpr const float LN10 = (float)d::LN10;
constexpr const float PI = (float)d::PI;
constexpr const float PI_2 = (float)d::PI_2;
constexpr const float PI_4 = (float)d::PI_4;
constexpr const float ONE_OVER_PI = (float)d::ONE_OVER_PI;
constexpr const float TWO_OVER_PI = (float)d::TWO_OVER_PI;
constexpr const float TWO_OVER_SQRTPI = (float)d::TWO_OVER_SQRTPI;
constexpr const float SQRT2 = (float)d::SQRT2;
constexpr const float SQRT1_2 = (float)d::SQRT1_2;
constexpr const float TAU = (float)d::TAU;
constexpr const float DEG_TO_RAD = (float)d::DEG_TO_RAD;
constexpr const float RAD_TO_DEG = (float)d::RAD_TO_DEG;
} // namespace f
template<typename T>
inline constexpr T MATH_PURE min(T a, T b) noexcept {
return a < b ? a : b;
}
template<typename T>
inline constexpr T MATH_PURE max(T a, T b) noexcept {
return a > b ? a : b;
}
template<typename T>
inline constexpr T MATH_PURE clamp(T v, T min, T max) noexcept {
assert(min <= max);
return T(math::min(max, math::max(min, v)));
}
template<typename T>
inline constexpr T MATH_PURE saturate(T v) noexcept {
return clamp(v, T(0), T(1));
}
template<typename T>
inline constexpr T MATH_PURE mix(T x, T y, T a) noexcept {
return x * (T(1) - a) + y * a;
}
template<typename T>
inline constexpr T MATH_PURE lerp(T x, T y, T a) noexcept {
return mix(x, y, a);
}
template<typename T>
inline constexpr T MATH_PURE smoothstep(T e0, T e1, T x) noexcept {
T t = clamp((x - e0) / (e1 - e0), T(0), T(1));
return t * t * (T(3) - T(2) * t);
}
template<typename T>
inline constexpr T sign(T x) noexcept {
return x < T(0) ? T(-1) : T(1);
}
} // namespace math
} // namespace filament
#endif // TNT_MATH_SCALAR_H

113
ios/include/math/vec2.h Normal file
View File

@@ -0,0 +1,113 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_VEC2_H_
#define MATH_VEC2_H_
#include <math/TVecHelpers.h>
#include <math/half.h>
#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <type_traits>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
template<typename T>
class MATH_EMPTY_BASES TVec2 :
public TVecProductOperators<TVec2, T>,
public TVecAddOperators<TVec2, T>,
public TVecUnaryOperators<TVec2, T>,
public TVecComparisonOperators<TVec2, T>,
public TVecFunctions<TVec2, T> {
public:
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
static constexpr size_t SIZE = 2;
union {
T v[SIZE] MATH_CONSTEXPR_INIT;
struct { T x, y; };
struct { T s, t; };
struct { T r, g; };
};
inline constexpr size_type size() const { return SIZE; }
// array access
inline constexpr T const& operator[](size_t i) const noexcept {
assert(i < SIZE);
return v[i];
}
inline constexpr T& operator[](size_t i) noexcept {
assert(i < SIZE);
return v[i];
}
// constructors
// default constructor
MATH_DEFAULT_CTOR_CONSTEXPR TVec2() MATH_DEFAULT_CTOR
// handles implicit conversion to a tvec4. must not be explicit.
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TVec2(A v) noexcept : v{ T(v), T(v) } {}
template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
constexpr TVec2(A x, B y) noexcept : v{ T(x), T(y) } {}
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TVec2(const TVec2<A>& v) noexcept : v{ T(v[0]), T(v[1]) } {}
// cross product works only on vectors of size 2 or 3
template<typename U>
friend inline constexpr
arithmetic_result_t<T, U> cross(const TVec2& u, const TVec2<U>& v) noexcept {
return u[0] * v[1] - u[1] * v[0];
}
};
} // namespace details
// ----------------------------------------------------------------------------------------
template<typename T, typename = details::enable_if_arithmetic_t<T>>
using vec2 = details::TVec2<T>;
using double2 = vec2<double>;
using float2 = vec2<float>;
using half2 = vec2<half>;
using int2 = vec2<int32_t>;
using uint2 = vec2<uint32_t>;
using short2 = vec2<int16_t>;
using ushort2 = vec2<uint16_t>;
using byte2 = vec2<int8_t>;
using ubyte2 = vec2<uint8_t>;
using bool2 = vec2<bool>;
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
#endif // MATH_VEC2_H_

133
ios/include/math/vec3.h Normal file
View File

@@ -0,0 +1,133 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_VEC3_H_
#define MATH_VEC3_H_
#include <math/vec2.h>
#include <math/half.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
template<typename T>
class MATH_EMPTY_BASES TVec3 :
public TVecProductOperators<TVec3, T>,
public TVecAddOperators<TVec3, T>,
public TVecUnaryOperators<TVec3, T>,
public TVecComparisonOperators<TVec3, T>,
public TVecFunctions<TVec3, T> {
public:
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
static constexpr size_t SIZE = 3;
union {
T v[SIZE] MATH_CONSTEXPR_INIT;
TVec2<T> xy;
TVec2<T> st;
TVec2<T> rg;
struct {
union {
T x;
T s;
T r;
};
union {
struct { T y, z; };
struct { T t, p; };
struct { T g, b; };
TVec2<T> yz;
TVec2<T> tp;
TVec2<T> gb;
};
};
};
inline constexpr size_type size() const { return SIZE; }
// array access
inline constexpr T const& operator[](size_t i) const noexcept {
assert(i < SIZE);
return v[i];
}
inline constexpr T& operator[](size_t i) noexcept {
assert(i < SIZE);
return v[i];
}
// constructors
// default constructor
MATH_DEFAULT_CTOR_CONSTEXPR TVec3() noexcept MATH_DEFAULT_CTOR
// handles implicit conversion to a tvec3. must not be explicit.
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TVec3(A v) noexcept : v{ T(v), T(v), T(v) } {}
template<typename A, typename B, typename C,
typename = enable_if_arithmetic_t<A, B, C>>
constexpr TVec3(A x, B y, C z) noexcept : v{ T(x), T(y), T(z) } {}
template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
constexpr TVec3(const TVec2<A>& v, B z) noexcept : v{ T(v[0]), T(v[1]), T(z) } {}
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TVec3(const TVec3<A>& v) noexcept : v{ T(v[0]), T(v[1]), T(v[2]) } {}
// cross product works only on vectors of size 3
template<typename U>
friend inline constexpr
TVec3<arithmetic_result_t<T, U>> cross(const TVec3& u, const TVec3<U>& v) noexcept {
return {
u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2],
u[0] * v[1] - u[1] * v[0] };
}
};
} // namespace details
// ----------------------------------------------------------------------------------------
template<typename T, typename = details::enable_if_arithmetic_t<T>>
using vec3 = details::TVec3<T>;
using double3 = vec3<double>;
using float3 = vec3<float>;
using half3 = vec3<half>;
using int3 = vec3<int32_t>;
using uint3 = vec3<uint32_t>;
using short3 = vec3<int16_t>;
using ushort3 = vec3<uint16_t>;
using byte3 = vec3<int8_t>;
using ubyte3 = vec3<uint8_t>;
using bool3 = vec3<bool>;
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
#endif // MATH_VEC3_H_

132
ios/include/math/vec4.h Normal file
View File

@@ -0,0 +1,132 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MATH_VEC4_H_
#define MATH_VEC4_H_
#include <math/vec3.h>
#include <math/half.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details {
template<typename T>
class MATH_EMPTY_BASES TVec4 :
public TVecProductOperators<TVec4, T>,
public TVecAddOperators<TVec4, T>,
public TVecUnaryOperators<TVec4, T>,
public TVecComparisonOperators<TVec4, T>,
public TVecFunctions<TVec4, T> {
public:
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
static constexpr size_t SIZE = 4;
union {
T v[SIZE] MATH_CONSTEXPR_INIT;
TVec2<T> xy, st, rg;
TVec3<T> xyz, stp, rgb;
struct {
union { T x, s, r; };
union {
TVec2<T> yz, tp, gb;
TVec3<T> yzw, tpq, gba;
struct {
union { T y, t, g; };
union {
TVec2<T> zw, pq, ba;
struct { T z, w; };
struct { T p, q; };
struct { T b, a; };
};
};
};
};
};
inline constexpr size_type size() const { return SIZE; }
// array access
inline constexpr T const& operator[](size_t i) const noexcept {
assert(i < SIZE);
return v[i];
}
inline constexpr T& operator[](size_t i) noexcept {
assert(i < SIZE);
return v[i];
}
// constructors
// default constructor
MATH_DEFAULT_CTOR_CONSTEXPR TVec4() noexcept MATH_DEFAULT_CTOR
// handles implicit conversion to a tvec4. must not be explicit.
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TVec4(A v) noexcept : v{ T(v), T(v), T(v), T(v) } {}
template<typename A, typename B, typename C, typename D,
typename = enable_if_arithmetic_t<A, B, C, D>>
constexpr TVec4(A x, B y, C z, D w) noexcept : v{ T(x), T(y), T(z), T(w) } {}
template<typename A, typename B, typename C,
typename = enable_if_arithmetic_t<A, B, C>>
constexpr TVec4(const TVec2<A>& v, B z, C w) noexcept : v{ T(v[0]), T(v[1]), T(z), T(w) } {}
template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
constexpr TVec4(const TVec2<A>& v, const TVec2<B>& w) noexcept : v{
T(v[0]), T(v[1]), T(w[0]), T(w[1]) } {}
template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
constexpr TVec4(const TVec3<A>& v, B w) noexcept : v{ T(v[0]), T(v[1]), T(v[2]), T(w) } {}
template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TVec4(const TVec4<A>& v) noexcept : v{ T(v[0]), T(v[1]), T(v[2]), T(v[3]) } {}
};
} // namespace details
// ----------------------------------------------------------------------------------------
template<typename T, typename = details::enable_if_arithmetic_t<T>>
using vec4 = details::TVec4<T>;
using double4 = vec4<double>;
using float4 = vec4<float>;
using half4 = vec4<half>;
using int4 = vec4<int32_t>;
using uint4 = vec4<uint32_t>;
using short4 = vec4<int16_t>;
using ushort4 = vec4<uint16_t>;
using byte4 = vec4<int8_t>;
using ubyte4 = vec4<uint8_t>;
using bool4 = vec4<bool>;
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
#endif // MATH_VEC4_H_

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iosfwd>
#include <math/mathfwd.h>
#if __has_attribute(visibility)
# define MATHIO_PUBLIC __attribute__((visibility("default")))
#else
# define MATHIO_PUBLIC
#endif
namespace filament {
namespace math {
namespace details { template<typename T> class TQuaternion; }
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TVec2<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TVec3<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TVec4<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TMat22<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TMat33<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TMat44<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TQuaternion<T>& v) noexcept;
} // namespace math
} // namespace filament

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,647 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* 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 TSL_HTRIE_MAP_H
#define TSL_HTRIE_MAP_H
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <string>
#include <utility>
#include "htrie_hash.h"
namespace tsl {
/**
* Implementation of a hat-trie map.
*
* The value T must be either nothrow move-constructible/assignable, copy-constructible or both.
*
* The size of a key string is limited to std::numeric_limits<KeySizeT>::max() - 1.
* That is 65 535 characters by default, but can be raised with the KeySizeT template parameter.
* See max_key_size() for an easy access to this limit.
*
* Iterators invalidation:
* - clear, operator=: always invalidate the iterators.
* - insert, emplace, operator[]: always invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class CharT,
class T,
class Hash = tsl::ah::str_hash<CharT>,
class KeySizeT = std::uint16_t>
class htrie_map {
private:
template<typename U>
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, T, Hash, KeySizeT>;
public:
using char_type = typename ht::char_type;
using mapped_type = T;
using key_size_type = typename ht::key_size_type;
using size_type = typename ht::size_type;
using hasher = typename ht::hasher;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
using prefix_iterator = typename ht::prefix_iterator;
using const_prefix_iterator = typename ht::const_prefix_iterator;
public:
explicit htrie_map(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
ht::DEFAULT_BURST_THRESHOLD)
{
}
explicit htrie_map(size_type burst_threshold,
const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
burst_threshold)
{
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
htrie_map(InputIt first, InputIt last,
const Hash& hash = Hash()): htrie_map(hash)
{
insert(first, last);
}
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_map(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> init,
const Hash& hash = Hash()): htrie_map(hash)
{
insert(init);
}
#else
htrie_map(std::initializer_list<std::pair<const CharT*, T>> init,
const Hash& hash = Hash()): htrie_map(hash)
{
insert(init);
}
#endif
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
clear();
insert(ilist);
return *this;
}
#else
htrie_map& operator=(std::initializer_list<std::pair<const CharT*, T>> ilist) {
clear();
insert(ilist);
return *this;
}
#endif
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
/**
* Call shrink_to_fit() on each hash node of the hat-trie to reduce its size.
*/
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, const T& value) {
return m_ht.insert(key, key_size, value);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
return m_ht.insert(key.data(), key.size(), value);
}
#else
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
return m_ht.insert(key, std::strlen(key), value);
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
return m_ht.insert(key.data(), key.size(), value);
}
#endif
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
return m_ht.insert(key, key_size, std::move(value));
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
return m_ht.insert(key.data(), key.size(), std::move(value));
}
#else
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
return m_ht.insert(key, std::strlen(key), std::move(value));
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
return m_ht.insert(key.data(), key.size(), std::move(value));
}
#endif
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
void insert(InputIt first, InputIt last) {
for(auto it = first; it != last; ++it) {
insert_pair(*it);
}
}
#ifdef TSL_HT_HAS_STRING_VIEW
void insert(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
insert(ilist.begin(), ilist.end());
}
#else
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
insert(ilist.begin(), ilist.end());
}
#endif
template<class... Args>
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
return m_ht.insert(key, key_size, std::forward<Args>(args)...);
}
#ifdef TSL_HT_HAS_STRING_VIEW
template<class... Args>
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
return m_ht.insert(key.data(), key.size(), std::forward<Args>(args)...);
}
#else
template<class... Args>
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
return m_ht.insert(key, std::strlen(key), std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
return m_ht.insert(key.data(), key.size(), std::forward<Args>(args)...);
}
#endif
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase_ks(const CharT* key, size_type key_size) {
return m_ht.erase(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
size_type erase(const std::basic_string_view<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#else
size_type erase(const CharT* key) {
return m_ht.erase(key, std::strlen(key));
}
size_type erase(const std::basic_string<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#endif
/**
* Erase all the elements which have 'prefix' as prefix. Return the number of erase elements.
*/
size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.erase_prefix(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string_view<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#else
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const CharT* prefix) {
return m_ht.erase_prefix(prefix, std::strlen(prefix));
}
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#endif
void swap(htrie_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
T& at_ks(const CharT* key, size_type key_size) { return m_ht.at(key, key_size); }
const T& at_ks(const CharT* key, size_type key_size) const { return m_ht.at(key, key_size); }
#ifdef TSL_HT_HAS_STRING_VIEW
T& at(const std::basic_string_view<CharT>& key) { return m_ht.at(key.data(), key.size()); }
const T& at(const std::basic_string_view<CharT>& key) const { return m_ht.at(key.data(), key.size()); }
#else
T& at(const CharT* key) { return m_ht.at(key, std::strlen(key)); }
const T& at(const CharT* key) const { return m_ht.at(key, std::strlen(key)); }
T& at(const std::basic_string<CharT>& key) { return m_ht.at(key.data(), key.size()); }
const T& at(const std::basic_string<CharT>& key) const { return m_ht.at(key.data(), key.size()); }
#endif
#ifdef TSL_HT_HAS_STRING_VIEW
T& operator[](const std::basic_string_view<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
#else
T& operator[](const CharT* key) { return m_ht.access_operator(key, std::strlen(key)); }
T& operator[](const std::basic_string<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
#endif
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
#ifdef TSL_HT_HAS_STRING_VIEW
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#else
size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); }
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#endif
iterator find_ks(const CharT* key, size_type key_size) {
return m_ht.find(key, key_size);
}
const_iterator find_ks(const CharT* key, size_type key_size) const {
return m_ht.find(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
iterator find(const std::basic_string_view<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string_view<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#else
iterator find(const CharT* key) {
return m_ht.find(key, std::strlen(key));
}
const_iterator find(const CharT* key) const {
return m_ht.find(key, std::strlen(key));
}
iterator find(const std::basic_string<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#endif
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
return m_ht.equal_range(key, key_size);
}
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
return m_ht.equal_range(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#else
std::pair<iterator, iterator> equal_range(const CharT* key) {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#endif
/**
* Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair
* of iterator, the first being the begin iterator and the second being the end iterator.
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#else
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const CharT* prefix) {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const CharT* prefix) const {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#endif
/**
* Return the element in the trie which is the longest prefix of `key`. If no
* element in the trie is a prefix of `key`, the end iterator is returned.
*
* Example:
*
* tsl::htrie_map<char, int> map = {{"/foo", 1}, {"/foo/bar", 1}};
*
* map.longest_prefix("/foo"); // returns {"/foo", 1}
* map.longest_prefix("/foo/baz"); // returns {"/foo", 1}
* map.longest_prefix("/foo/bar/baz"); // returns {"/foo/bar", 1}
* map.longest_prefix("/foo/bar/"); // returns {"/foo/bar", 1}
* map.longest_prefix("/bar"); // returns end()
* map.longest_prefix(""); // returns end()
*/
iterator longest_prefix_ks(const CharT* key, size_type key_size) {
return m_ht.longest_prefix(key, key_size);
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const {
return m_ht.longest_prefix(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string_view<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string_view<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#else
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const CharT* key) {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const CharT* key) const {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#endif
/*
* Hash policy
*/
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
/*
* Burst policy
*/
size_type burst_threshold() const { return m_ht.burst_threshold(); }
void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
/*
* Other
*/
/**
* Serialize the map through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the following calls:
* - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U.
* - `void operator()(const CharT* value, std::size_t value_size);`
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
* in the hands of the `Serializer` function object if compatibility is required.
*/
template<class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized map through the `deserializer` parameter.
*
* The `deserializer` parameter must be a function object that supports the following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U.
* - `void operator()(CharT* value_out, std::size_t value_size);`
*
* If the deserialized hash map part of the hat-trie is hash compatible with the serialized map, the deserialization process
* can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
* and KeySizeT must behave the same than the ones used in the serialized map. Otherwise the behaviour is undefined
* with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `CharT` and `T` of the `htrie_map` are not the same as the
* types used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
*/
template<class Deserializer>
static htrie_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
htrie_map map;
map.m_ht.deserialize(deserializer, hash_compatible);
return map;
}
friend bool operator==(const htrie_map& lhs, const htrie_map& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
std::string key_buffer;
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
it.key(key_buffer);
const auto it_element_rhs = rhs.find(key_buffer);
if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) {
return false;
}
}
return true;
}
friend bool operator!=(const htrie_map& lhs, const htrie_map& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(htrie_map& lhs, htrie_map& rhs) {
lhs.swap(rhs);
}
private:
template<class U, class V>
void insert_pair(const std::pair<U, V>& value) {
insert(value.first, value.second);
}
template<class U, class V>
void insert_pair(std::pair<U, V>&& value) {
insert(value.first, std::move(value.second));
}
private:
ht m_ht;
};
} // end namespace tsl
#endif

View File

@@ -0,0 +1,586 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* 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 TSL_HTRIE_SET_H
#define TSL_HTRIE_SET_H
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <string>
#include <utility>
#include "htrie_hash.h"
namespace tsl {
/**
* Implementation of a hat-trie set.
*
* The size of a key string is limited to std::numeric_limits<KeySizeT>::max() - 1.
* That is 65 535 characters by default, but can be raised with the KeySizeT template parameter.
* See max_key_size() for an easy access to this limit.
*
* Iterators invalidation:
* - clear, operator=: always invalidate the iterators.
* - insert: always invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class CharT,
class Hash = tsl::ah::str_hash<CharT>,
class KeySizeT = std::uint16_t>
class htrie_set {
private:
template<typename U>
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, void, Hash, KeySizeT>;
public:
using char_type = typename ht::char_type;
using key_size_type = typename ht::key_size_type;
using size_type = typename ht::size_type;
using hasher = typename ht::hasher;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
using prefix_iterator = typename ht::prefix_iterator;
using const_prefix_iterator = typename ht::const_prefix_iterator;
public:
explicit htrie_set(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
ht::DEFAULT_BURST_THRESHOLD)
{
}
explicit htrie_set(size_type burst_threshold,
const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
burst_threshold)
{
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
htrie_set(InputIt first, InputIt last,
const Hash& hash = Hash()): htrie_set(hash)
{
insert(first, last);
}
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_set(std::initializer_list<std::basic_string_view<CharT>> init,
const Hash& hash = Hash()): htrie_set(hash)
{
insert(init);
}
#else
htrie_set(std::initializer_list<const CharT*> init,
const Hash& hash = Hash()): htrie_set(hash)
{
insert(init);
}
#endif
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
clear();
insert(ilist);
return *this;
}
#else
htrie_set& operator=(std::initializer_list<const CharT*> ilist) {
clear();
insert(ilist);
return *this;
}
#endif
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
/**
* Call shrink_to_fit() on each hash node of the hat-trie to reduce its size.
*/
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
return m_ht.insert(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#else
std::pair<iterator, bool> insert(const CharT* key) {
return m_ht.insert(key, std::strlen(key));
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#endif
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
void insert(InputIt first, InputIt last) {
for(auto it = first; it != last; ++it) {
insert(*it);
}
}
#ifdef TSL_HT_HAS_STRING_VIEW
void insert(std::initializer_list<std::basic_string_view<CharT>> ilist) {
insert(ilist.begin(), ilist.end());
}
#else
void insert(std::initializer_list<const CharT*> ilist) {
insert(ilist.begin(), ilist.end());
}
#endif
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
return m_ht.insert(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#else
std::pair<iterator, bool> emplace(const CharT* key) {
return m_ht.insert(key, std::strlen(key));
}
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#endif
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase_ks(const CharT* key, size_type key_size) {
return m_ht.erase(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
size_type erase(const std::basic_string_view<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#else
size_type erase(const CharT* key) {
return m_ht.erase(key, std::strlen(key));
}
size_type erase(const std::basic_string<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#endif
/**
* Erase all the elements which have 'prefix' as prefix. Return the number of erase elements.
*/
size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.erase_prefix(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string_view<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#else
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const CharT* prefix) {
return m_ht.erase_prefix(prefix, std::strlen(prefix));
}
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#endif
void swap(htrie_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
#ifdef TSL_HT_HAS_STRING_VIEW
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#else
size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); }
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#endif
iterator find_ks(const CharT* key, size_type key_size) {
return m_ht.find(key, key_size);
}
const_iterator find_ks(const CharT* key, size_type key_size) const {
return m_ht.find(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
iterator find(const std::basic_string_view<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string_view<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#else
iterator find(const CharT* key) {
return m_ht.find(key, std::strlen(key));
}
const_iterator find(const CharT* key) const {
return m_ht.find(key, std::strlen(key));
}
iterator find(const std::basic_string<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#endif
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
return m_ht.equal_range(key, key_size);
}
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
return m_ht.equal_range(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#else
std::pair<iterator, iterator> equal_range(const CharT* key) {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#endif
/**
* Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair
* of iterator, the first being the begin iterator and the second being the end iterator.
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#else
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const CharT* prefix) {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const CharT* prefix) const {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#endif
/**
* Return the element in the trie which is the longest prefix of `key`. If no
* element in the trie is a prefix of `key`, the end iterator is returned.
*
* Example:
*
* tsl::htrie_set<char> set = {"/foo", "/foo/bar"};
*
* set.longest_prefix("/foo"); // returns "/foo"
* set.longest_prefix("/foo/baz"); // returns "/foo"
* set.longest_prefix("/foo/bar/baz"); // returns "/foo/bar"
* set.longest_prefix("/foo/bar/"); // returns "/foo/bar"
* set.longest_prefix("/bar"); // returns end()
* set.longest_prefix(""); // returns end()
*/
iterator longest_prefix_ks(const CharT* key, size_type key_size) {
return m_ht.longest_prefix(key, key_size);
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const {
return m_ht.longest_prefix(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string_view<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string_view<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#else
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const CharT* key) {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const CharT* key) const {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#endif
/*
* Hash policy
*/
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
/*
* Burst policy
*/
size_type burst_threshold() const { return m_ht.burst_threshold(); }
void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
/*
* Other
*/
/**
* Serialize the set through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the following calls:
* - `void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U.
* - `void operator()(const CharT* value, std::size_t value_size);`
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
* in the hands of the `Serializer` function object if compatibility is required.
*/
template<class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized set through the `deserializer` parameter.
*
* The `deserializer` parameter must be a function object that supports the following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t` and `float` must be supported for U.
* - `void operator()(CharT* value_out, std::size_t value_size);`
*
* If the deserialized hash set part of the hat-trie is hash compatible with the serialized set, the deserialization process
* can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
* and KeySizeT must behave the same than the ones used in the serialized set. Otherwise the behaviour is undefined
* with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `CharT` of the `htrie_set` is not the same as the
* type used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
*/
template<class Deserializer>
static htrie_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
htrie_set set;
set.m_ht.deserialize(deserializer, hash_compatible);
return set;
}
friend bool operator==(const htrie_set& lhs, const htrie_set& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
std::string key_buffer;
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
it.key(key_buffer);
const auto it_element_rhs = rhs.find(key_buffer);
if(it_element_rhs == rhs.cend()) {
return false;
}
}
return true;
}
friend bool operator!=(const htrie_set& lhs, const htrie_set& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(htrie_set& lhs, htrie_set& rhs) {
lhs.swap(rhs);
}
private:
ht m_ht;
};
} // end namespace tsl
#endif

View File

@@ -0,0 +1,290 @@
/**
* MIT License
*
* Copyright (c) 2017 Tessil
*
* 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 TSL_ROBIN_GROWTH_POLICY_H
#define TSL_ROBIN_GROWTH_POLICY_H
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstddef>
#include <iterator>
#include <limits>
#include <ratio>
#include <stdexcept>
#ifdef __EXCEPTIONS
# define THROW(_e, _m) throw _e(_m)
#else
# include <stdio.h>
# ifndef NDEBUG
# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0)
# else
# define THROW(_e, _m) std::terminate()
# endif
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_builtin(__builtin_expect)
# define TSL_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
#else
# define TSL_LIKELY( exp ) (exp)
#endif
namespace tsl {
namespace rh {
/**
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
*
* GrowthFactor must be a power of two >= 2.
*/
template<std::size_t GrowthFactor>
class power_of_two_growth_policy {
public:
/**
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
*/
power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0.");
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
m_mask = min_bucket_count_in_out - 1;
}
/**
* Return the bucket [0, bucket_count()) to which the hash belongs.
*/
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash & m_mask;
}
/**
* Return the bucket count to use when the bucket array grows on rehash.
*/
std::size_t next_bucket_count() const {
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
return (m_mask + 1) * GrowthFactor;
}
/**
* Return the maximum number of buckets supported by the policy.
*/
std::size_t max_bucket_count() const {
// Largest power of two.
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
}
private:
static std::size_t round_up_to_power_of_two(std::size_t value) {
if(is_power_of_two(value)) {
return value;
}
if(value == 0) {
return 1;
}
--value;
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
value |= value >> i;
}
return value + 1;
}
static constexpr bool is_power_of_two(std::size_t value) {
return value != 0 && (value & (value - 1)) == 0;
}
protected:
static const std::size_t MIN_BUCKETS_SIZE = 2;
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
std::size_t m_mask;
};
/**
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
* to a bucket. Slower but it can be usefull if you want a slower growth.
*/
template<class GrowthFactor = std::ratio<3, 2>>
class mod_growth_policy {
public:
mod_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0.");
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
m_bucket_count = min_bucket_count_in_out;
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash % m_bucket_count;
}
std::size_t next_bucket_count() const {
if(m_bucket_count == max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
const double next_bucket_count = std::ceil(double(m_bucket_count) * REHASH_SIZE_MULTIPLICATION_FACTOR);
if(!std::isnormal(next_bucket_count)) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
if(next_bucket_count > double(max_bucket_count())) {
return max_bucket_count();
}
else {
return std::size_t(next_bucket_count);
}
}
std::size_t max_bucket_count() const {
return MAX_BUCKET_COUNT;
}
private:
static const std::size_t MIN_BUCKETS_SIZE = 2;
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
static const std::size_t MAX_BUCKET_COUNT =
std::size_t(double(
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
));
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
std::size_t m_bucket_count;
};
namespace detail {
static constexpr const std::array<std::size_t, 39> PRIMES = {{
5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, 1543ul, 2053ul,
3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
}};
template<unsigned int IPrime>
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
// compiler can optimize the modulo code better with a constant known at the compilation.
static constexpr const std::array<std::size_t(*)(std::size_t), 39> MOD_PRIME = {{
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>
}};
}
/**
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in
* general but will probably distribute the values around better in the buckets with a poor hash function.
*
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
*
* With a switch the code would look like:
* \code
* switch(iprime) { // iprime is the current prime of the hash table
* case 0: hash % 5ul;
* break;
* case 1: hash % 17ul;
* break;
* case 2: hash % 29ul;
* break;
* ...
* }
* \endcode
*
* Due to the constant variable in the modulo the compiler is able to optimize the operation
* by a series of multiplications, substractions and shifts.
*
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
*/
class prime_growth_policy {
public:
prime_growth_policy(std::size_t& min_bucket_count_in_out) {
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
detail::PRIMES.end(), min_bucket_count_in_out);
if(it_prime == detail::PRIMES.end()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
min_bucket_count_in_out = *it_prime;
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return detail::MOD_PRIME[m_iprime](hash);
}
std::size_t next_bucket_count() const {
if(m_iprime + 1 >= detail::PRIMES.size()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
return detail::PRIMES[m_iprime + 1];
}
std::size_t max_bucket_count() const {
return detail::PRIMES.back();
}
private:
unsigned int m_iprime;
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
"The type of m_iprime is not big enough.");
};
}
}
#endif

1252
ios/include/tsl/robin_hash.h Normal file

File diff suppressed because it is too large Load Diff

668
ios/include/tsl/robin_map.h Normal file
View File

@@ -0,0 +1,668 @@
/**
* MIT License
*
* Copyright (c) 2017 Tessil
*
* 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 TSL_ROBIN_MAP_H
#define TSL_ROBIN_MAP_H
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "robin_hash.h"
namespace tsl {
/**
* Implementation of a hash map using open-adressing and the robin hood hashing algorithm with backward shift deletion.
*
* For operations modifying the hash map (insert, erase, rehash, ...), the strong exception guarantee
* is only guaranteed when the expression `std::is_nothrow_swappable<std::pair<Key, T>>::value &&
* std::is_nothrow_move_constructible<std::pair<Key, T>>::value` is true, otherwise if an exception
* is thrown during the swap or the move, the hash map may end up in a undefined state. Per the standard
* a `Key` or `T` with a noexcept copy constructor and no move constructor also satisfies the
* `std::is_nothrow_move_constructible<std::pair<Key, T>>::value` criterion (and will thus guarantee the
* strong exception for the map).
*
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
* the performance during lookups if the `KeyEqual` function takes time (if it engenders a cache-miss for example)
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
* When it is detected that storing the hash will not incur any memory penality due to alignement (i.e.
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
* not be used on lookups unless `StoreHash` is true).
*
* `GrowthPolicy` defines how the map grows and consequently how a hash value is mapped to a bucket.
* By default the map uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
* Other growth policies are available and you may define your own growth policy,
* check `tsl::rh::power_of_two_growth_policy` for the interface.
*
* If the destructor of `Key` or `T` throws an exception, the behaviour of the class is undefined.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators.
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
bool StoreHash = false,
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
class robin_map {
private:
template<typename U>
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
return key_value.first;
}
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.first;
}
};
class ValueSelect {
public:
using value_type = T;
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
return key_value.second;
}
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.second;
}
};
using ht = detail_robin_hash::robin_hash<std::pair<Key, T>, KeySelect, ValueSelect,
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
public:
using key_type = typename ht::key_type;
using mapped_type = T;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
public:
/*
* Constructors
*/
robin_map(): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
}
explicit robin_map(size_type bucket_count,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
{
}
robin_map(size_type bucket_count,
const Allocator& alloc): robin_map(bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_map(size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_map(bucket_count, hash, KeyEqual(), alloc)
{
}
explicit robin_map(const Allocator& alloc): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
}
template<class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()): robin_map(bucket_count, hash, equal, alloc)
{
insert(first, last);
}
template<class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count,
const Allocator& alloc): robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
{
}
template<class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_map(first, last, bucket_count, hash, KeyEqual(), alloc)
{
}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
{
}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count,
const Allocator& alloc):
robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc):
robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
{
}
robin_map& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
std::pair<iterator, bool> insert(P&& value) {
return m_ht.emplace(std::forward<P>(value));
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert(hint, value);
}
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
iterator insert(const_iterator hint, P&& value) {
return m_ht.emplace_hint(hint, std::forward<P>(value));
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert(hint, std::move(value));
}
template<class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
template<class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
return m_ht.insert_or_assign(k, std::forward<M>(obj));
}
template<class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
}
template<class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
}
template<class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
}
/**
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
return m_ht.try_emplace(k, std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template<class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
return m_ht.try_emplace(hint, k, std::forward<Args>(args)...);
}
template<class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
return m_ht.try_emplace(hint, std::move(k), std::forward<Args>(args)...);
}
iterator erase(iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) { return m_ht.erase(key); }
/**
* @copydoc erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(robin_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
T& at(const Key& key) { return m_ht.at(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
const T& at(const Key& key) const { return m_ht.at(key); }
/**
* @copydoc at(const Key& key, std::size_t precalculated_hash)
*/
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key) { return m_ht.at(key); }
/**
* @copydoc at(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
/**
* @copydoc at(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key) const { return m_ht.at(key); }
/**
* @copydoc at(const K& key, std::size_t precalculated_hash)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
T& operator[](const Key& key) { return m_ht[key]; }
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const { return m_ht.count(key); }
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
/**
* @copydoc find(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
friend bool operator==(const robin_map& lhs, const robin_map& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
for(const auto& element_lhs: lhs) {
const auto it_element_rhs = rhs.find(element_lhs.first);
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
return false;
}
}
return true;
}
friend bool operator!=(const robin_map& lhs, const robin_map& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(robin_map& lhs, robin_map& rhs) {
lhs.swap(rhs);
}
private:
ht m_ht;
};
/**
* Same as `tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
*/
template<class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
bool StoreHash = false>
using robin_pg_map = robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
} // end namespace tsl
#endif

535
ios/include/tsl/robin_set.h Normal file
View File

@@ -0,0 +1,535 @@
/**
* MIT License
*
* Copyright (c) 2017 Tessil
*
* 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 TSL_ROBIN_SET_H
#define TSL_ROBIN_SET_H
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "robin_hash.h"
namespace tsl {
/**
* Implementation of a hash set using open-adressing and the robin hood hashing algorithm with backward shift deletion.
*
* For operations modifying the hash set (insert, erase, rehash, ...), the strong exception guarantee
* is only guaranteed when the expression `std::is_nothrow_swappable<Key>::value &&
* std::is_nothrow_move_constructible<Key>::value` is true, otherwise if an exception
* is thrown during the swap or the move, the hash set may end up in a undefined state. Per the standard
* a `Key` with a noexcept copy constructor and no move constructor also satisfies the
* `std::is_nothrow_move_constructible<Key>::value` criterion (and will thus guarantee the
* strong exception for the set).
*
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
* the performance during lookups if the `KeyEqual` function takes time (or engenders a cache-miss for example)
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
* When it is detected that storing the hash will not incur any memory penality due to alignement (i.e.
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
* not be used on lookups unless `StoreHash` is true).
*
* `GrowthPolicy` defines how the set grows and consequently how a hash value is mapped to a bucket.
* By default the set uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
* to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo.
* Other growth policies are available and you may define your own growth policy,
* check `tsl::rh::power_of_two_growth_policy` for the interface.
*
* If the destructor of `Key` throws an exception, the behaviour of the class is undefined.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators.
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class Key,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>,
bool StoreHash = false,
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
class robin_set {
private:
template<typename U>
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(const Key& key) const noexcept {
return key;
}
key_type& operator()(Key& key) noexcept {
return key;
}
};
using ht = detail_robin_hash::robin_hash<Key, KeySelect, void,
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
public:
using key_type = typename ht::key_type;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
/*
* Constructors
*/
robin_set(): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
}
explicit robin_set(size_type bucket_count,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
{
}
robin_set(size_type bucket_count,
const Allocator& alloc): robin_set(bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_set(size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_set(bucket_count, hash, KeyEqual(), alloc)
{
}
explicit robin_set(const Allocator& alloc): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
}
template<class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()): robin_set(bucket_count, hash, equal, alloc)
{
insert(first, last);
}
template<class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count,
const Allocator& alloc): robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
{
}
template<class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_set(first, last, bucket_count, hash, KeyEqual(), alloc)
{
}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
{
}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count,
const Allocator& alloc):
robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc):
robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
{
}
robin_set& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert(hint, value);
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert(hint, std::move(value));
}
template<class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
/**
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
iterator erase(iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) { return m_ht.erase(key); }
/**
* @copydoc erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(robin_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const { return m_ht.count(key); }
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
/**
* @copydoc find(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
friend bool operator==(const robin_set& lhs, const robin_set& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
for(const auto& element_lhs: lhs) {
const auto it_element_rhs = rhs.find(element_lhs);
if(it_element_rhs == rhs.cend()) {
return false;
}
}
return true;
}
friend bool operator!=(const robin_set& lhs, const robin_set& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(robin_set& lhs, robin_set& rhs) {
lhs.swap(rhs);
}
private:
ht m_ht;
};
/**
* Same as `tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
*/
template<class Key,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>,
bool StoreHash = false>
using robin_pg_set = robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
} // end namespace tsl
#endif

View File

@@ -0,0 +1,824 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_ALLOCATOR_H
#define TNT_UTILS_ALLOCATOR_H
#include <utils/compiler.h>
#include <utils/memalign.h>
#include <utils/Mutex.h>
#include <utils/SpinLock.h>
#include <atomic>
#include <cstddef>
#include <mutex>
#include <type_traits>
#include <assert.h>
#include <stdlib.h>
namespace utils {
namespace pointermath {
template <typename P, typename T>
static inline P* add(P* a, T b) noexcept {
return (P*)(uintptr_t(a) + uintptr_t(b));
}
template <typename P>
static inline P* align(P* p, size_t alignment) noexcept {
// alignment must be a power-of-two
assert(alignment && !(alignment & alignment-1));
return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1));
}
template <typename P>
static inline P* align(P* p, size_t alignment, size_t offset) noexcept {
P* const r = align(add(p, offset), alignment);
assert(pointermath::add(r, -offset) >= p);
return r;
}
}
/* ------------------------------------------------------------------------------------------------
* LinearAllocator
*
* + Allocates blocks linearly
* + Cannot free individual blocks
* + Can free top of memory back up to a specified point
* + Doesn't call destructors
* ------------------------------------------------------------------------------------------------
*/
class LinearAllocator {
public:
// use memory area provided
LinearAllocator(void* begin, void* end) noexcept;
template <typename AREA>
explicit LinearAllocator(const AREA& area) : LinearAllocator(area.begin(), area.end()) { }
// Allocators can't be copied
LinearAllocator(const LinearAllocator& rhs) = delete;
LinearAllocator& operator=(const LinearAllocator& rhs) = delete;
// Allocators can be moved
LinearAllocator(LinearAllocator&& rhs) noexcept;
LinearAllocator& operator=(LinearAllocator&& rhs) noexcept;
~LinearAllocator() noexcept = default;
// our allocator concept
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) UTILS_RESTRICT {
// branch-less allocation
void* const p = pointermath::align(current(), alignment, extra);
void* const c = pointermath::add(p, size);
bool success = c <= end();
set_current(success ? c : current());
return success ? p : nullptr;
}
// API specific to this allocator
void *getCurrent() UTILS_RESTRICT noexcept {
return current();
}
// free memory back to the specified point
void rewind(void* p) UTILS_RESTRICT noexcept {
assert(p>=mBegin && p<end());
set_current(p);
}
// frees all allocated blocks
void reset() UTILS_RESTRICT noexcept {
rewind(mBegin);
}
size_t allocated() const UTILS_RESTRICT noexcept {
return mSize;
}
size_t available() const UTILS_RESTRICT noexcept {
return mSize - mCur;
}
void swap(LinearAllocator& rhs) noexcept;
void *base() noexcept { return mBegin; }
void free(void*, size_t) UTILS_RESTRICT noexcept { }
private:
void* end() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mSize); }
void* current() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mCur); }
void set_current(void* p) UTILS_RESTRICT noexcept { mCur = uintptr_t(p) - uintptr_t(mBegin); }
void* mBegin = nullptr;
uint32_t mSize = 0;
uint32_t mCur = 0;
};
/* ------------------------------------------------------------------------------------------------
* HeapAllocator
*
* + uses malloc() for all allocations
* + frees blocks with free()
* ------------------------------------------------------------------------------------------------
*/
class HeapAllocator {
public:
HeapAllocator() noexcept = default;
template <typename AREA>
explicit HeapAllocator(const AREA&) { }
// our allocator concept
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) {
// this allocator doesn't support 'extra'
assert(extra == 0);
return aligned_alloc(size, alignment);
}
void free(void* p) noexcept {
aligned_free(p);
}
void free(void* p, size_t) noexcept {
free(p);
}
~HeapAllocator() noexcept = default;
void swap(HeapAllocator& rhs) noexcept { }
};
// ------------------------------------------------------------------------------------------------
class FreeList {
public:
FreeList() noexcept = default;
FreeList(void* begin, void* end, size_t elementSize, size_t alignment, size_t extra) noexcept;
FreeList(const FreeList& rhs) = delete;
FreeList& operator=(const FreeList& rhs) = delete;
FreeList(FreeList&& rhs) noexcept = default;
FreeList& operator=(FreeList&& rhs) noexcept = default;
void* pop() noexcept {
Node* const head = mHead;
mHead = head ? head->next : nullptr;
// this could indicate a use after free
assert(!mHead || mHead >= mBegin && mHead < mEnd);
return head;
}
void push(void* p) noexcept {
assert(p);
assert(p >= mBegin && p < mEnd);
// TODO: assert this is one of our pointer (i.e.: it's address match one of ours)
Node* const head = static_cast<Node*>(p);
head->next = mHead;
mHead = head;
}
void *getFirst() noexcept {
return mHead;
}
private:
struct Node {
Node* next;
};
static Node* init(void* begin, void* end,
size_t elementSize, size_t alignment, size_t extra) noexcept;
Node* mHead = nullptr;
#ifndef NDEBUG
// These are needed only for debugging...
void* mBegin = nullptr;
void* mEnd = nullptr;
#endif
};
class AtomicFreeList {
public:
AtomicFreeList() noexcept = default;
AtomicFreeList(void* begin, void* end,
size_t elementSize, size_t alignment, size_t extra) noexcept;
AtomicFreeList(const FreeList& rhs) = delete;
AtomicFreeList& operator=(const FreeList& rhs) = delete;
void* pop() noexcept {
Node* const storage = mStorage;
HeadPtr currentHead = mHead.load();
while (currentHead.offset >= 0) {
// The value of "next" we load here might already contain application data if another
// thread raced ahead of us. But in that case, the computed "newHead" will be discarded
// since compare_exchange_weak fails. Then this thread will loop with the updated
// value of currentHead, and try again.
Node* const next = storage[currentHead.offset].next.load(std::memory_order_relaxed);
const HeadPtr newHead{ next ? int32_t(next - storage) : -1, currentHead.tag + 1 };
// In the rare case that the other thread that raced ahead of us already returned the
// same mHead we just loaded, but it now has a different "next" value, the tag field will not
// match, and compare_exchange_weak will fail and prevent that particular race condition.
if (mHead.compare_exchange_weak(currentHead, newHead)) {
// This assert needs to occur after we have validated that there was no race condition
// Otherwise, next might already contain application data, if another thread
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
assert(!next || next >= storage);
break;
}
}
void* p = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
assert(!p || p >= storage);
return p;
}
void push(void* p) noexcept {
Node* const storage = mStorage;
assert(p && p >= storage);
Node* const node = static_cast<Node*>(p);
HeadPtr currentHead = mHead.load();
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
do {
newHead.tag = currentHead.tag + 1;
Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
node->next.store(n, std::memory_order_relaxed);
} while(!mHead.compare_exchange_weak(currentHead, newHead));
}
void* getFirst() noexcept {
return mStorage + mHead.load(std::memory_order_relaxed).offset;
}
private:
struct Node {
// This should be a regular (non-atomic) pointer, but this causes TSAN to complain
// about a data-race that exists but is benin. We always use this atomic<> in
// relaxed mode.
// The data race TSAN complains about is when a pop() is interrupted by a
// pop() + push() just after mHead->next is read -- it appears as though it is written
// without synchronization (by the push), however in that case, the pop's CAS will fail
// and things will auto-correct.
//
// Pop() |
// | |
// read head->next |
// | pop()
// | |
// | read head->next
// | CAS, tag++
// | |
// | push()
// | |
// [TSAN: data-race here] write head->next
// | CAS, tag++
// CAS fails
// |
// read head->next
// |
// CAS, tag++
//
std::atomic<Node*> next;
};
// This struct is using a 32-bit offset into the arena rather than
// a direct pointer, because together with the 32-bit tag, it needs to
// fit into 8 bytes. If it was any larger, it would not be possible to
// access it atomically.
struct alignas(8) HeadPtr {
int32_t offset;
uint32_t tag;
};
std::atomic<HeadPtr> mHead{};
Node* mStorage = nullptr;
};
// ------------------------------------------------------------------------------------------------
template <
size_t ELEMENT_SIZE,
size_t ALIGNMENT = alignof(std::max_align_t),
size_t OFFSET = 0,
typename FREELIST = FreeList>
class PoolAllocator {
static_assert(ELEMENT_SIZE >= sizeof(void*), "ELEMENT_SIZE must accommodate at least a pointer");
public:
// our allocator concept
void* alloc(size_t size = ELEMENT_SIZE,
size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept {
assert(size <= ELEMENT_SIZE);
assert(alignment <= ALIGNMENT);
assert(offset == OFFSET);
return mFreeList.pop();
}
void free(void* p, size_t = ELEMENT_SIZE) noexcept {
mFreeList.push(p);
}
constexpr size_t getSize() const noexcept { return ELEMENT_SIZE; }
PoolAllocator(void* begin, void* end) noexcept
: mFreeList(begin, end, ELEMENT_SIZE, ALIGNMENT, OFFSET) {
}
template <typename AREA>
explicit PoolAllocator(const AREA& area) noexcept
: PoolAllocator(area.begin(), area.end()) {
}
// Allocators can't be copied
PoolAllocator(const PoolAllocator& rhs) = delete;
PoolAllocator& operator=(const PoolAllocator& rhs) = delete;
// Allocators can be moved
PoolAllocator(PoolAllocator&& rhs) = default;
PoolAllocator& operator=(PoolAllocator&& rhs) = default;
PoolAllocator() noexcept = default;
~PoolAllocator() noexcept = default;
// API specific to this allocator
void *getCurrent() noexcept {
return mFreeList.getFirst();
}
private:
FREELIST mFreeList;
};
#define UTILS_MAX(a,b) ((a) > (b) ? (a) : (b))
template <typename T, size_t OFFSET = 0>
using ObjectPoolAllocator = PoolAllocator<sizeof(T),
UTILS_MAX(alignof(FreeList), alignof(T)), OFFSET>;
template <typename T, size_t OFFSET = 0>
using ThreadSafeObjectPoolAllocator = PoolAllocator<sizeof(T),
UTILS_MAX(alignof(FreeList), alignof(T)), OFFSET, AtomicFreeList>;
// ------------------------------------------------------------------------------------------------
// Areas
// ------------------------------------------------------------------------------------------------
namespace AreaPolicy {
class StaticArea {
public:
StaticArea() noexcept = default;
StaticArea(void* b, void* e) noexcept
: mBegin(b), mEnd(e) {
}
~StaticArea() noexcept = default;
StaticArea(const StaticArea& rhs) = default;
StaticArea& operator=(const StaticArea& rhs) = default;
StaticArea(StaticArea&& rhs) noexcept = default;
StaticArea& operator=(StaticArea&& rhs) noexcept = default;
void* data() const noexcept { return mBegin; }
void* begin() const noexcept { return mBegin; }
void* end() const noexcept { return mEnd; }
size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
friend void swap(StaticArea& lhs, StaticArea& rhs) noexcept {
using std::swap;
swap(lhs.mBegin, rhs.mBegin);
swap(lhs.mEnd, rhs.mEnd);
}
private:
void* mBegin = nullptr;
void* mEnd = nullptr;
};
class HeapArea {
public:
HeapArea() noexcept = default;
explicit HeapArea(size_t size) {
if (size) {
// TODO: policy committing memory
mBegin = malloc(size);
mEnd = pointermath::add(mBegin, size);
}
}
~HeapArea() noexcept {
// TODO: policy for returning memory to system
free(mBegin);
}
HeapArea(const HeapArea& rhs) = delete;
HeapArea& operator=(const HeapArea& rhs) = delete;
HeapArea(HeapArea&& rhs) noexcept = delete;
HeapArea& operator=(HeapArea&& rhs) noexcept = delete;
void* data() const noexcept { return mBegin; }
void* begin() const noexcept { return mBegin; }
void* end() const noexcept { return mEnd; }
size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
friend void swap(HeapArea& lhs, HeapArea& rhs) noexcept {
using std::swap;
swap(lhs.mBegin, rhs.mBegin);
swap(lhs.mEnd, rhs.mEnd);
}
private:
void* mBegin = nullptr;
void* mEnd = nullptr;
};
} // namespace AreaPolicy
// ------------------------------------------------------------------------------------------------
// Policies
// ------------------------------------------------------------------------------------------------
namespace LockingPolicy {
struct NoLock {
void lock() noexcept { }
void unlock() noexcept { }
};
using SpinLock = utils::SpinLock;
using Mutex = utils::Mutex;
} // namespace LockingPolicy
namespace TrackingPolicy {
// default no-op tracker
struct Untracked {
Untracked() noexcept = default;
Untracked(const char* name, void* base, size_t size) noexcept { }
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept { }
void onFree(void* p, size_t = 0) noexcept { }
void onReset() noexcept { }
void onRewind(void* addr) noexcept { }
};
// This just track the max memory usage and logs it in the destructor
struct HighWatermark {
HighWatermark() noexcept = default;
HighWatermark(const char* name, void* base, size_t size) noexcept
: mName(name), mBase(base), mSize(uint32_t(size)) { }
~HighWatermark() noexcept;
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept;
void onFree(void* p, size_t size) noexcept;
void onReset() noexcept;
void onRewind(void const* addr) noexcept;
protected:
const char* mName = nullptr;
void* mBase = nullptr;
uint32_t mSize = 0;
uint32_t mCurrent = 0;
uint32_t mHighWaterMark = 0;
};
// This just fills buffers with known values to help catch uninitialized access and use after free.
struct Debug {
Debug() noexcept = default;
Debug(const char* name, void* base, size_t size) noexcept
: mName(name), mBase(base), mSize(uint32_t(size)) { }
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept;
void onFree(void* p, size_t size) noexcept;
void onReset() noexcept;
void onRewind(void* addr) noexcept;
protected:
const char* mName = nullptr;
void* mBase = nullptr;
uint32_t mSize = 0;
};
struct DebugAndHighWatermark : protected HighWatermark, protected Debug {
DebugAndHighWatermark() noexcept = default;
DebugAndHighWatermark(const char* name, void* base, size_t size) noexcept
: HighWatermark(name, base, size), Debug(name, base, size) { }
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept {
HighWatermark::onAlloc(p, size, alignment, extra);
Debug::onAlloc(p, size, alignment, extra);
}
void onFree(void* p, size_t size) noexcept {
HighWatermark::onFree(p, size);
Debug::onFree(p, size);
}
void onReset() noexcept {
HighWatermark::onReset();
Debug::onReset();
}
void onRewind(void* addr) noexcept {
HighWatermark::onRewind(addr);
Debug::onRewind(addr);
}
};
} // namespace TrackingPolicy
// ------------------------------------------------------------------------------------------------
// Arenas
// ------------------------------------------------------------------------------------------------
template<typename AllocatorPolicy, typename LockingPolicy,
typename TrackingPolicy = TrackingPolicy::Untracked,
typename AreaPolicy = AreaPolicy::HeapArea>
class Arena {
public:
Arena() = default;
// construct an arena with a name and forward argument to its allocator
template<typename ... ARGS>
Arena(const char* name, size_t size, ARGS&& ... args)
: mArenaName(name),
mArea(size),
mAllocator(mArea, std::forward<ARGS>(args) ... ),
mListener(name, mArea.data(), mArea.size()) {
}
template<typename ... ARGS>
Arena(const char* name, AreaPolicy&& area, ARGS&& ... args)
: mArenaName(name),
mArea(std::forward<AreaPolicy>(area)),
mAllocator(mArea, std::forward<ARGS>(args) ... ),
mListener(name, mArea.data(), mArea.size()) {
}
// allocate memory from arena with given size and alignment
// (acceptable size/alignment may depend on the allocator provided)
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
void* p = mAllocator.alloc(size, alignment, extra);
mListener.onAlloc(p, size, alignment, extra);
return p;
}
// Allocate an array of trivially destructible objects
// for safety, we disable the object-based alloc method if the object type is not
// trivially destructible, since free() won't call the destructor and this is allocating
// an array.
template <typename T,
typename = typename std::enable_if<std::is_trivially_destructible<T>::value>::type>
T* alloc(size_t count, size_t alignment = alignof(T), size_t extra = 0) noexcept {
return (T*)alloc(count * sizeof(T), alignment, extra);
}
// return memory pointed by p to the arena
// (actual behaviour may depend on allocator provided)
void free(void* p) noexcept {
if (p) {
std::lock_guard<LockingPolicy> guard(mLock);
mListener.onFree(p);
mAllocator.free(p);
}
}
// some allocators require the size of the allocation for free
void free(void* p, size_t size) noexcept {
if (p) {
std::lock_guard<LockingPolicy> guard(mLock);
mListener.onFree(p, size);
mAllocator.free(p, size);
}
}
// some allocators don't have a free() call, but a single reset() or rewind() instead
void reset() noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
mListener.onReset();
mAllocator.reset();
}
void* getCurrent() noexcept { return mAllocator.getCurrent(); }
void rewind(void *addr) noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
mListener.onRewind(addr);
mAllocator.rewind(addr);
}
// Allocate and construct an object
template<typename T, size_t ALIGN = alignof(T), typename... ARGS>
T* make(ARGS&& ... args) noexcept {
void* const p = this->alloc(sizeof(T), ALIGN);
return p ? new(p) T(std::forward<ARGS>(args)...) : nullptr;
}
// destroys an object created with make<T>() above, and frees associated memory
template<typename T>
void destroy(T* p) noexcept {
if (p) {
p->~T();
this->free((void*)p, sizeof(T));
}
}
char const* getName() const noexcept { return mArenaName; }
AllocatorPolicy& getAllocator() noexcept { return mAllocator; }
AllocatorPolicy const& getAllocator() const noexcept { return mAllocator; }
TrackingPolicy& getListener() noexcept { return mListener; }
TrackingPolicy const& getListener() const noexcept { return mListener; }
AreaPolicy& getArea() noexcept { return mArea; }
AreaPolicy const& getArea() const noexcept { return mArea; }
void setListener(TrackingPolicy listener) noexcept {
std::swap(mListener, listener);
}
template <typename ... ARGS>
void emplaceListener(ARGS&& ... args) noexcept {
mListener.~TrackingPolicy();
new (&mListener) TrackingPolicy(std::forward<ARGS>(args)...);
}
// An arena can't be copied
Arena(Arena const& rhs) noexcept = delete;
Arena& operator=(Arena const& rhs) noexcept = delete;
friend void swap(Arena& lhs, Arena& rhs) noexcept {
using std::swap;
swap(lhs.mArea, rhs.mArea);
swap(lhs.mAllocator, rhs.mAllocator);
swap(lhs.mLock, rhs.mLock);
swap(lhs.mListener, rhs.mListener);
swap(lhs.mArenaName, rhs.mArenaName);
}
private:
char const* mArenaName = nullptr;
AreaPolicy mArea;
// note: we should use something like compressed_pair for the members below
AllocatorPolicy mAllocator;
LockingPolicy mLock;
TrackingPolicy mListener;
};
// ------------------------------------------------------------------------------------------------
template<typename TrackingPolicy = TrackingPolicy::Untracked>
using HeapArena = Arena<HeapAllocator, LockingPolicy::NoLock, TrackingPolicy>;
// ------------------------------------------------------------------------------------------------
// This doesn't implement our allocator concept, because it's too risky to use this as an allocator
// in particular, doing ArenaScope<ArenaScope>.
template<typename ARENA>
class ArenaScope {
struct Finalizer {
void (*finalizer)(void* p) = nullptr;
Finalizer* next = nullptr;
};
template <typename T>
static void destruct(void* p) noexcept {
static_cast<T*>(p)->~T();
}
public:
explicit ArenaScope(ARENA& allocator)
: mArena(allocator), mRewind(allocator.getCurrent()) {
}
ArenaScope& operator=(const ArenaScope& rhs) = delete;
ArenaScope(ArenaScope&& rhs) noexcept = delete;
ArenaScope& operator=(ArenaScope&& rhs) noexcept = delete;
~ArenaScope() {
// run the finalizer chain
Finalizer* head = mFinalizerHead;
while (head) {
void* p = pointermath::add(head, sizeof(Finalizer));
head->finalizer(p);
head = head->next;
}
// ArenaScope works only with Arena that implements rewind()
mArena.rewind(mRewind);
}
template<typename T, size_t ALIGN = alignof(T), typename... ARGS>
T* make(ARGS&& ... args) noexcept {
T* o = nullptr;
if (std::is_trivially_destructible<T>::value) {
o = mArena.template make<T, ALIGN>(std::forward<ARGS>(args)...);
} else {
void* const p = (Finalizer*)mArena.alloc(sizeof(T), ALIGN, sizeof(Finalizer));
if (p != nullptr) {
Finalizer* const f = static_cast<Finalizer*>(p) - 1;
// constructor must be called before adding the dtor to the list
// so that the ctor can allocate objects in a nested scope and have the
// finalizers called in reverse order.
o = new(p) T(std::forward<ARGS>(args)...);
f->finalizer = &destruct<T>;
f->next = mFinalizerHead;
mFinalizerHead = f;
}
}
return o;
}
void* allocate(size_t size, size_t alignment = 1) noexcept {
return mArena.template alloc<uint8_t>(size, alignment, 0);
}
template <typename T>
T* allocate(size_t size, size_t alignment = alignof(T), size_t extra = 0) noexcept {
return mArena.template alloc<T>(size, alignment, extra);
}
// use with caution
ARENA& getAllocator() noexcept { return mArena; }
private:
ARENA& mArena;
void* mRewind = nullptr;
Finalizer* mFinalizerHead = nullptr;
};
template <typename TYPE, typename ARENA>
class STLAllocator {
public:
using value_type = TYPE;
using pointer = TYPE*;
using const_pointer = const TYPE*;
using reference = TYPE&;
using const_reference = const TYPE&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assignment = std::true_type;
using is_always_equal = std::true_type;
template<typename OTHER>
struct rebind { using other = STLAllocator<OTHER, ARENA>; };
public:
// we don't make this explicit, so that we can initialize a vector using a STLAllocator
// from an Arena, avoiding to have to repeat the vector type.
STLAllocator(ARENA& arena) : mArena(arena) { } // NOLINT(google-explicit-constructor)
template<typename U>
explicit STLAllocator(STLAllocator<U, ARENA> const& rhs) : mArena(rhs.mArena) { }
TYPE* allocate(std::size_t n) {
return static_cast<TYPE *>(mArena.alloc(n * sizeof(TYPE), alignof(TYPE)));
}
void deallocate(TYPE* p, std::size_t n) {
mArena.free(p, n * sizeof(TYPE));
}
// these should be out-of-class friends, but this doesn't seem to work with some compilers
// which complain about multiple definition each time a STLAllocator<> is instantiated.
template <typename U, typename A>
bool operator==(const STLAllocator<U, A>& rhs) const noexcept {
return std::addressof(mArena) == std::addressof(rhs.mArena);
}
template <typename U, typename A>
bool operator!=(const STLAllocator<U, A>& rhs) const noexcept {
return !operator==(rhs);
}
private:
template<typename U, typename A>
friend class STLAllocator;
ARENA& mArena;
};
} // namespace utils
#endif // TNT_UTILS_ALLOCATOR_H

Some files were not shown because too many files have changed in this diff Show More