Compare commits

..

25 Commits

Author SHA1 Message Date
Benjamin Doherty
4b3cde8b39 Merge branch 'rc/1.23.0' into release 2022-06-01 14:55:20 -07:00
Benjamin Doherty
c23f905858 Update RELEASE_NOTES for 1.23.0 2022-06-01 14:53:23 -07:00
Benjamin Doherty
8c7be0a1d0 Revert "iOS: implement headleass swapchain (#5486)"
This reverts commit 0e5ba60cb6.

This causes issues in G3, still need to investigate the root cause.
2022-05-31 11:29:38 -07:00
Benjamin Doherty
878497b3d5 Bump version to 1.23.0 2022-05-24 15:19:51 -07:00
Benjamin Doherty
648314d730 Release Filament 1.22.2 2022-05-24 15:17:19 -07:00
Philip Rideout
fc3c3376b7 Morphing API docstring fixups.
I think the most important thing here is that we now let users know that
"offset" is not a byte count.  This also fixes some small typos, e.g.
"weights" was used in the docstring instead of "positions".

To avoid making API changes, this does not fix a few weird things I
noticed in the Java API. For example, there is a redundant "count"
argument in methods that take an array.  Also some methods do not
provide an "offset" argument, which is not consistent with C++.
2022-05-24 15:03:45 -07:00
Philip Rideout
fcbc6d43a1 Fix morphed normals.
Our `morphNormal` GLSL function treats each normal target as a
displacement from the base normal (similar to the glTF spec) but the
normal that is extracted from the tangent frame is actually an absolute
normal.

In other words, if b is the base normal, we were doing:

    b = b + w0 * n0 + w1 * n1 + ...

This is obviously wrong, since the base normal has an overpowering
influence.

The fixed math looks like this:

    b = b + w0 * (n0 - b) + w1 * (n1 - b) + ...

Fixes #5584.
2022-05-24 15:03:30 -07:00
Philip Rideout
a01fa21209 gltfio: introduce cross-fade animation API
Fixes #4754
2022-05-24 13:22:21 -07:00
Mathias Agopian
60a6d4a348 pack PerRenderableUib a bit more so it occupy 128 bytes
We pack PerRenderableUib so that we could (in the future) store two
renderable datum per slot (each slot is required to be 256 bytes).
2022-05-24 10:16:16 -07:00
Mathias Agopian
6c740f060e move skinning info into PrimitiveInfo
By design we shouldn't access the scene SOA when executing the 
high level commands.

This change increases the size of a command to 64 bytes and 
PrimitiveInfo to 48 bytes (both with some small padding left).
2022-05-24 10:16:16 -07:00
daemyung jang
5a8bf42ddb Delete redundant semicolons 2022-05-24 09:46:46 -07:00
daemyung jang
c8cc74e384 Fix not calculating the normal while morphing in the legacy path 2022-05-23 16:00:57 -07:00
Philip Rideout
c82ae6a3a7 beamsplitter: add recursive descent parser
This removes usage of regexps from the parsing phase and splits the
program into three sub-packages: parser, database, and emitters.

The parser now generates an AST according to the formal grammar
described in the README:

https://github.com/google/filament/blob/pr/beamsplitter_rewrite2/tools/beamsplitter/README.md#grammar

This allows for nice readable error messages. More importantly, it
permits the C++ syntax to be less restrictive and paves the way for
possible expansion of the tool beyond `Options.h`.

I looked at the C++ AST generated by clang but it is huge and unwieldy.
For our purposes this simplified AST is much easier to work with.

The new lexer is inspired by the following Rob Pike talk.
- https://www.youtube.com/watch?v=HxaD_trXwRE

Beamsplitter does not use the state machine described in the above
prezo, but it does use a Go channel for separating the parser from the
lexer. In our case, the lexer is actually a recursive descent parser
with simple lookahead functionality. This made it easy for the "real"
parser to create an ergonomic coarse-grained AST.

This is a big change but it is a no-op in terms of the generated code.
2022-05-23 09:36:29 -07:00
daemyung jang
fbc43d24fe Delete a redundant semicolon 2022-05-21 16:40:14 -07:00
Philip Rideout
c0f8f3bdfe beamsplitter update 2022-05-21 11:57:18 -07:00
Ben Doherty
3461ec863b gltfio: Clear texture caches before loading resources (#5580)
* gltfio: Clear texture caches before loading resources

* iOS gltf-viewer: add double-tap to reload model for debugging

* iOS samples: add instructions on ASan / UBSan debugging
2022-05-20 11:41:24 -07:00
Ben Doherty
035f162ebc iOS: small improvements to sample projects (#5579)
- Ensure C++17 is used to compile sample projects
- Upgrade to the latest Xcodegen and re-generate projects
- Add a bundle ID disambiguator so users don't need create unique bundle Ids when opening sample projects
- Add a simple generate-samples.sh script
2022-05-19 15:50:51 -07:00
Ben Doherty
6754885795 imageio: Remove unnecessary dependency (#5574) 2022-05-19 11:16:53 -07:00
Ben Doherty
42e4e5e3ae Only build basisu tool on host platforms (#5575) 2022-05-19 11:16:37 -07:00
Ben Doherty
71a0f46b73 Add ktxreader and viewer libs to CocoaPods (#5573) 2022-05-18 16:25:17 -07:00
Philip Rideout
5f52afdc08 View.java now uses generated code.
The API is the same but there are some minor differences, which include:

- Ordering of fields in AmbientOcclusionOptions
- More @NonNull annotations that we forgot
- Javadoc formatting is different
2022-05-18 13:40:44 -07:00
Philip Rideout
54706ad65e Reorder items in View.java to prep for beamsplitter. 2022-05-18 13:40:44 -07:00
Philip Rideout
a96d5932a8 beamsplitter: finalize codegen for View.java
Note that JNI c++ functions are not generated.  The setter / getter
methods are also manually maintained.
2022-05-18 13:40:44 -07:00
Mathias Agopian
41c5615201 fix trailing zero when setting shader sources
the shader source blob's size included the null terminating character,
which ended up being passed to the opengl/metal API.
2022-05-18 11:52:30 -07:00
Romain Guy
719427764c Fix image_viewer (#5571)
Many EXR files may contain negative values, we don't want to output
those to our color buffer.
2022-05-17 19:59:41 -07:00
93 changed files with 3704 additions and 1886 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.22.2'
implementation 'com.google.android.filament:filament-android:1.23.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.22.2'
pod 'Filament', '~> 1.23.0'
```
### Snapshots

View File

@@ -5,6 +5,14 @@ A new header is inserted each time a *tag* is created.
## main branch
## v1.23.0
- engine: Changed UBOs layout [⚠️ **Material breakage**].
- engine: Normals on morphed models have been fixed (core Filament change).
- Java: View has several minor changes due to generated code, such as field ordering.
- gltfio: Fix crash when reloading glTF assets.
- gltfio: introduce cross-fade animation API [**NEW API**].
## v1.22.2
- Java: Minor API change: rename `ssctStartTraceDistance` to `ssctShadowDistance`. [⚠️ **API Change**]

View File

@@ -104,12 +104,12 @@ public class MorphTargetBuffer {
}
/**
* Updates positions of morph target at the index.
* Updates float4 positions for the given morph target.
*
* @param engine {@link Engine} instance
* @param targetIndex The index of morph target to be updated
* @param positions Pointer to at least count positions
* @param count Number of position elements in positions
* @param positions An array with at least count*4 floats
* @param count Number of float4 vectors in positions to be consumed
*/
public void setPositionsAt(@NonNull Engine engine,
@IntRange(from = 0) int targetIndex,
@@ -122,12 +122,15 @@ public class MorphTargetBuffer {
}
/**
* Updates tangents of morph target at the index.
* Updates tangents for the given morph target.
*
* These quaternions must be represented as signed shorts, where real numbers in the [-1,+1]
* range multiplied by 32767.
*
* @param engine {@link Engine} instance
* @param targetIndex The index of morph target to be updated
* @param tangents Pointer to at least count tangents
* @param count Number of tangent elements in tangents
* @param tangents An array with at least "count*4" shorts
* @param count number of short4 quaternions in tangents
*/
public void setTangentsAt(@NonNull Engine engine,
@IntRange(from = 0) int targetIndex,
@@ -140,14 +143,14 @@ public class MorphTargetBuffer {
}
/**
* @return number of vertex count in this {@link MorphTargetBuffer}
* @return number of vertices in this {@link MorphTargetBuffer}
*/
public int getVertexCount() {
return nGetVertexCount(mNativeObject);
}
/**
* @return number of target count in this {@link MorphTargetBuffer}
* @return number of morph targets in this {@link MorphTargetBuffer}
*/
public int getCount() {
return nGetCount(mNativeObject);

View File

@@ -36,6 +36,13 @@ Java_com_google_android_filament_gltfio_Animator_nUpdateBoneMatrices(JNIEnv*, jc
animator->updateBoneMatrices();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_Animator_nApplyCrossFade(JNIEnv*, jclass, jlong nativeAnimator,
jint previousAnimIndex, jfloat previousAnimTime, jfloat alpha) {
Animator* animator = (Animator*) nativeAnimator;
animator->applyCrossFade(previousAnimIndex, previousAnimTime, alpha);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_Animator_nResetBoneMatrices(JNIEnv*, jclass, jlong nativeAnimator) {
Animator* animator = (Animator*) nativeAnimator;

View File

@@ -66,6 +66,27 @@ public class Animator {
nUpdateBoneMatrices(getNativeObject());
}
/**
* Applies a blended transform to the union of nodes affected by two animations.
* Used for cross-fading from a previous skinning-based animation or rigid body animation.
*
* First, this stashes the current transform hierarchy into a transient memory buffer.
*
* Next, this applies previousAnimIndex / previousAnimTime to the actual asset by internally
* calling applyAnimation().
*
* Finally, the stashed local transforms are lerped (via the scale / translation / rotation
* components) with their live counterparts, and the results are pushed to the asset.
*
* To achieve a cross fade effect with skinned models, clients will typically call animator
* methods in this order: (1) applyAnimation (2) applyCrossFade (3) updateBoneMatrices. The
* animation that clients pass to applyAnimation is the "current" animation corresponding to
* alpha=1, while the "previous" animation passed to applyCrossFade corresponds to alpha=0.
*/
public void applyCrossFade(int previousAnimIndex, float previousAnimTime, float alpha) {
nApplyCrossFade(getNativeObject(), previousAnimIndex, previousAnimTime, alpha);
}
/**
* Pass the identity matrix into all bone nodes, useful for returning to the T pose.
*
@@ -118,6 +139,7 @@ public class Animator {
private static native void nApplyAnimation(long nativeAnimator, int index, float time);
private static native void nUpdateBoneMatrices(long nativeAnimator);
private static native void nApplyCrossFade(long nativeAnimator, int animIndex, float animTime, float alpha);
private static native void nResetBoneMatrices(long nativeAnimator);
private static native int nGetAnimationCount(long nativeAnimator);
private static native float nGetAnimationDuration(long nativeAnimator, int index);

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.22.2
VERSION_NAME=1.23.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -67,6 +67,8 @@ public:
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)>&& logger);
// sets one of the program's shader (e.g. vertex, fragment)
// string-based shaders are null terminated, consequently the size parameter must include the
// null terminating character.
Program& shader(Shader shader, void const* data, size_t size) noexcept;
// sets the 'bindingPoint' uniform block's name for this program.
@@ -84,10 +86,14 @@ public:
Program& setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags,
Sampler const* samplers, size_t count) noexcept;
// string-based shaders are null terminated, consequently the size parameter must include the
// null terminating character.
Program& withVertexShader(void const* data, size_t size) {
return shader(Shader::VERTEX, data, size);
}
// string-based shaders are null terminated, consequently the size parameter must include the
// null terminating character.
Program& withFragmentShader(void const* data, size_t size) {
return shader(Shader::FRAGMENT, data, size);
}

View File

@@ -346,8 +346,11 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
continue;
}
assert_invariant( source[source.size() - 1] == '\0' );
// the shader string is null terminated and the length includes the null character
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
length:source.size()
length:source.size() - 1
encoding:NSUTF8StringEncoding];
NSError* error = nil;
// When options is nil, Metal uses the most recent language version available.

View File

@@ -186,7 +186,11 @@ highp uint packHalf2x16(vec2 v) {
GLuint shaderId = glCreateShader(glShaderType);
{ // scope for source/length (we don't want them to leak out)
const char* const source = shaderView.data();
const GLint length = (GLint)shaderView.length();
// the shader string is null terminated and the length includes the null character
const GLint length = (GLint)shaderView.length() - 1;
assert_invariant( source[length] == '\0' );
glShaderSource(shaderId, 1, &source, &length);
glCompileShader(shaderId);
#ifndef NDEBUG

View File

@@ -78,43 +78,49 @@ public:
};
/**
* Updates the position of morph target at the index.
* Updates positions for the given morph target.
*
* This is equivalent to the float4 method, but uses 1.0 for the 4th component.
*
* Both positions and tangents must be provided.
*
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param weights pointer to at least count positions
* @param count number of position elements in positions
* @param positions pointer to at least "count" positions
* @param count number of float3 vectors in positions
* @param offset offset into the target buffer, expressed as a number of float4 vectors
* @see setTangentsAt
*/
void setPositionsAt(Engine& engine, size_t targetIndex,
math::float3 const* positions, size_t count, size_t offset = 0);
/**
* Updates the position of morph target at the index.
* Updates positions for the given morph target.
*
* Both positions and tangents must be provided.
*
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param weights pointer to at least count positions
* @param count number of position elements in positions
* @see setPositionsAt
* @param positions pointer to at least "count" positions
* @param count number of float4 vectors in positions
* @param offset offset into the target buffer, expressed as a number of float4 vectors
* @see setTangentsAt
*/
void setPositionsAt(Engine& engine, size_t targetIndex,
math::float4 const* positions, size_t count, size_t offset = 0);
/**
* Updates the position of morph target at the index.
* Updates tangents for the given morph target.
*
* Both positions and tangents must be provided.
* These quaternions must be represented as signed shorts, where real numbers in the [-1,+1]
* range multiplied by 32767.
*
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param tangents pointer to at least count tangents
* @param count number of tangent elements in tangents
* @see setTangentsAt
* @param tangents pointer to at least "count" tangents
* @param count number of short4 quaternions in tangents
* @param offset offset into the target buffer, expressed as a number of short4 vectors
* @see setPositionsAt
*/
void setTangentsAt(Engine& engine, size_t targetIndex,
math::short4 const* tangents, size_t count, size_t offset = 0);

View File

@@ -275,17 +275,18 @@ struct AmbientOcclusionOptions {
* 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
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; // %codegen_skip_javascript% %codegen_java_flatten%
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 ssct; // %codegen_skip_javascript% %codegen_java_flatten%
};
/**

View File

@@ -51,6 +51,7 @@ RenderPass::RenderPass(RenderPass const& rhs) = default;
RenderPass::~RenderPass() noexcept = default;
RenderPass::Command* RenderPass::append(size_t count) noexcept {
// this is like a "in-place" realloc(). Works only with LinearAllocator.
Command* const curr = mCommandArena.alloc<Command>(count);
assert_invariant(mCommandBegin == nullptr || curr == mCommandEnd);
if (mCommandBegin == nullptr) {
@@ -301,9 +302,10 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
auto const* const UTILS_RESTRICT soaVisibility = soa.data<FScene::VISIBILITY_STATE>();
auto const* const UTILS_RESTRICT soaPrimitives = soa.data<FScene::PRIMITIVES>();
auto const* const UTILS_RESTRICT soaSkinning = soa.data<FScene::SKINNING_BUFFER>();
auto const* const UTILS_RESTRICT soaMorphing = soa.data<FScene::MORPHING_BUFFER>();
auto const* const UTILS_RESTRICT soaVisibilityMask = soa.data<FScene::VISIBLE_MASK>();
auto const* const UTILS_RESTRICT soaInstanceCount = soa.data<FScene::INSTANCE_COUNT>();
auto const* const UTILS_RESTRICT soaInstanceCount = soa.data<FScene::INSTANCE_COUNT>();
const bool hasShadowing = renderFlags & HAS_SHADOWING;
const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES;
@@ -396,6 +398,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
const bool writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster;
const Slice<FRenderPrimitive>& primitives = soaPrimitives[i];
const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i];
const FRenderableManager::MorphingBindingInfo& morphing = soaMorphing[i];
/*
@@ -412,6 +415,8 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
cmdColor.primitive.primitiveHandle = primitive.getHwHandle();
RenderPass::setupColorCommand(cmdColor, variant, mi, inverseFrontFaces);
cmdColor.primitive.skinningHandle = skinning.handle;
cmdColor.primitive.skinningOffset = skinning.offset;
cmdColor.primitive.morphWeightBuffer = morphing.handle;
cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
@@ -506,6 +511,8 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
cmdDepth.primitive.mi = mi;
cmdDepth.primitive.rasterState.culling = mi->getCullingMode();
cmdDepth.primitive.skinningHandle = skinning.handle;
cmdDepth.primitive.skinningOffset = skinning.offset;
cmdDepth.primitive.morphWeightBuffer = morphing.handle;
cmdDepth.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
@@ -552,22 +559,18 @@ void RenderPass::Executor::execute(const char* name,
engine.flush();
driver.beginRenderPass(renderTarget, params);
recordDriverCommands(engine, driver, mBegin, mEnd, mRenderableSoa, params.readOnlyDepthStencil);
recordDriverCommands(engine, driver, mBegin, mEnd, params.readOnlyDepthStencil);
driver.endRenderPass();
}
UTILS_NOINLINE // no need to be inlined
void RenderPass::Executor::recordDriverCommands(FEngine& engine,
backend::DriverApi& driver,
const Command* first, const Command* last,
FScene::RenderableSoa const& soa, uint16_t readOnlyDepthStencil) const noexcept {
void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::DriverApi& driver,
const Command* first, const Command* last, uint16_t readOnlyDepthStencil) const noexcept {
SYSTRACE_CALL();
if (first != last) {
SYSTRACE_VALUE32("commandCount", last - first);
auto const* const UTILS_RESTRICT soaSkinning = soa.data<FScene::SKINNING_BUFFER>();
PolygonOffset dummyPolyOffset;
PipelineState pipeline{ .polygonOffset = mPolygonOffset };
PolygonOffset* const pPipelinePolygonOffset =
@@ -614,14 +617,13 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine,
driver.bindUniformBufferRange(BindingPoints::PER_RENDERABLE,
uboHandle, offset, sizeof(PerRenderableUib));
auto skinning = soaSkinning[info.index];
if (UTILS_UNLIKELY(skinning.handle)) {
if (UTILS_UNLIKELY(info.skinningHandle)) {
// note: we can't bind less than CONFIG_MAX_BONE_COUNT due to glsl limitations
driver.bindUniformBufferRange(BindingPoints::PER_RENDERABLE_BONES,
skinning.handle,
skinning.offset * sizeof(PerRenderableUibBone),
info.skinningHandle,
info.skinningOffset * sizeof(PerRenderableUibBone),
CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone));
// note: even if skinning is only enabled, binding morphTargetBuffer is needed.
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
driver.bindSamplers(BindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
}
@@ -643,7 +645,7 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine,
// ------------------------------------------------------------------------------------------------
RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept
: mEngine(pass->mEngine), mBegin(b), mEnd(e), mRenderableSoa(*pass->mRenderableSoa),
: mEngine(pass->mEngine), mBegin(b), mEnd(e),
mCustomCommands(pass->mCustomCommands), mUboHandle(pass->mUboHandle),
mPolygonOffset(pass->mPolygonOffset),
mPolygonOffsetOverride(pass->mPolygonOffsetOverride) {

View File

@@ -213,25 +213,28 @@ public:
return boolish ? std::numeric_limits<uint64_t>::max() : uint64_t(0);
}
struct PrimitiveInfo { // 40 bytes
struct PrimitiveInfo { // 48 bytes
union {
FMaterialInstance const* mi;
uint64_t reserved0 = {}; // ensures mi is 8 bytes on all archs
uint64_t padding = {}; // ensures mi is 8 bytes on all archs
}; // 8 bytes
backend::RasterState rasterState; // 8 bytes
backend::Handle<backend::HwRenderPrimitive> primitiveHandle; // 4 bytes
backend::Handle<backend::HwBufferObject> skinningHandle; // 4 bytes
backend::Handle<backend::HwBufferObject> morphWeightBuffer; // 4 bytes
backend::Handle<backend::HwSamplerGroup> morphTargetBuffer; // 4 bytes
uint16_t index = 0; // 2 bytes
uint32_t index = 0; // 4 bytes
uint32_t skinningOffset = 0; // 4 bytes
uint16_t instanceCount; // 2 bytes
backend::RasterState rasterState; // 8 bytes
Variant materialVariant; // 1 byte
uint8_t reserved1[7] = {}; // 7 bytes
uint8_t reserved[5] = {}; // 5 bytes
};
static_assert(sizeof(PrimitiveInfo) == 40);
static_assert(sizeof(PrimitiveInfo) == 48);
struct alignas(8) Command { // 40 bytes
struct alignas(8) Command { // 64 bytes
CommandKey key = 0; // 8 bytes
PrimitiveInfo primitive; // 40 bytes
PrimitiveInfo primitive; // 48 bytes
uint64_t reserved = 0; // 8 bytes
bool operator < (Command const& rhs) const noexcept { return key < rhs.key; }
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new (std::size_t, void* ptr) {
@@ -239,7 +242,7 @@ public:
return ptr;
}
};
static_assert(sizeof(Command) == 48);
static_assert(sizeof(Command) == 64);
static_assert(std::is_trivially_destructible_v<Command>,
"Command isn't trivially destructible");
@@ -249,7 +252,7 @@ public:
// Arena used for commands
using Arena = utils::Arena<
utils::LinearAllocator,
utils::LinearAllocator, // note: can't change this allocator
utils::LockingPolicy::NoLock,
utils::TrackingPolicy::HighWatermark,
utils::AreaPolicy::StaticArea>;
@@ -319,7 +322,6 @@ public:
FEngine& mEngine;
Command const* mBegin;
Command const* mEnd;
FScene::RenderableSoa const& mRenderableSoa;
const CustomCommandVector mCustomCommands;
const backend::Handle<backend::HwBufferObject> mUboHandle;
const backend::PolygonOffset mPolygonOffset;
@@ -328,8 +330,7 @@ public:
Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept;
void recordDriverCommands(FEngine& engine, backend::DriverApi& driver,
const Command* first, const Command* last,
FScene::RenderableSoa const& soa, uint16_t readOnlyDepthStencil) const noexcept;
const Command* first, const Command* last, uint16_t readOnlyDepthStencil) const noexcept;
public:
Executor(Executor const& rhs);

View File

@@ -32,6 +32,7 @@
#include <algorithm>
using namespace filament::backend;
using namespace filament::math;
using namespace utils;
@@ -196,7 +197,9 @@ void FScene::prepare(const mat4& worldOriginTransform, bool shadowReceiversAreCa
}
}
void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Handle<backend::HwBufferObject> renderableUbh) noexcept {
void FScene::updateUBOs(
Range<uint32_t> visibleRenderables,
Handle<HwBufferObject> renderableUbh) noexcept {
FEngine::DriverApi& driver = mEngine.getDriverApi();
FRenderableManager& rcm = mEngine.getRenderableManager();
@@ -219,7 +222,7 @@ void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Hand
// Using mat3f::getTransformForNormals handles non-uniform scaling, but DOESN'T guarantee that
// the transformed normals will have unit-length, therefore they need to be normalized
// in the shader (that's already the case anyways, since normalization is needed after
// in the shader (that's already the case anyway, since normalization is needed after
// interpolation).
//
// We pre-scale normals by the inverse of the largest scale factor to avoid
@@ -247,20 +250,17 @@ void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Hand
hasContactShadows = hasContactShadows || visibility.screenSpaceContactShadows;
UniformBuffer::setUniform(buffer,
offset + offsetof(PerRenderableUib, flags),
PerRenderableUib::packFlags(
offset + offsetof(PerRenderableUib, flagsChannels),
PerRenderableUib::packFlagsChannels(
visibility.skinning,
visibility.morphing,
visibility.screenSpaceContactShadows));
visibility.screenSpaceContactShadows,
sceneData.elementAt<CHANNELS>(i)));
UniformBuffer::setUniform(buffer,
offset + offsetof(PerRenderableUib, morphTargetCount),
sceneData.elementAt<MORPHING_BUFFER>(i).count);
UniformBuffer::setUniform(buffer,
offset + offsetof(PerRenderableUib, channels),
(uint32_t)sceneData.elementAt<CHANNELS>(i));
UniformBuffer::setUniform(buffer,
offset + offsetof(PerRenderableUib, objectId),
rcm.getEntity(ri).getId()); // we could also store the entity in sceneData
@@ -287,7 +287,7 @@ void FScene::terminate(FEngine& engine) {
}
void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootArena,
backend::Handle<backend::HwBufferObject> lightUbh) noexcept {
Handle<HwBufferObject> lightUbh) noexcept {
FEngine::DriverApi& driver = mEngine.getDriverApi();
FLightManager& lcm = mEngine.getLightManager();
FScene::LightSoa& lightData = getLightData();
@@ -450,7 +450,7 @@ bool FScene::hasContactShadows() const noexcept {
}
UTILS_NOINLINE
void FScene::forEach(Invocable<void(utils::Entity)>&& functor) const noexcept {
void FScene::forEach(Invocable<void(Entity)>&& functor) const noexcept {
std::for_each(mEntities.begin(), mEntities.end(), std::move(functor));
}

View File

@@ -84,8 +84,8 @@ public:
enum {
RENDERABLE_INSTANCE, // 4 | instance of the Renderable component
WORLD_TRANSFORM, // 16 | instance of the Transform component
VISIBILITY_STATE, // 1 | visibility data of the component
SKINNING_BUFFER, // 8 | bones uniform buffer handle, count, offset
VISIBILITY_STATE, // 2 | visibility data of the component
SKINNING_BUFFER, // 8 | bones uniform buffer handle, offset
MORPHING_BUFFER, // 16 | weights uniform buffer handle, count, morph targets
WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable
VISIBLE_MASK, // 2 | each bit represents a visibility in a pass

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.22.2"
spec.version = "1.23.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.22.2/filament-v1.22.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.23.0/filament-v1.23.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -88,7 +88,7 @@ from scratch.
## XcodeGen
[XcodeGen](https://github.com/yonaskolb/XcodeGen) version 2.14.0 is used to generate the Xcode
[XcodeGen](https://github.com/yonaskolb/XcodeGen) version 2.29.0 is used to generate the Xcode
projects. While not required to run the samples, XcodeGen makes modifying them easier. Each sample
folder contains the `project.yml` file used for the sample, which includes a global
`app-template.yml` file. Simply run
@@ -99,3 +99,17 @@ $ xcodegen
within a sample folder to re-generate the Xcode project. You may need to close and re-open the
project in Xcode to see changes take effect.
## Building iOS Samples with ASan / UBSan
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by uncommenting the following line:
```
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=undefined -fsanitize=address")
```
2. In the Xcode project, navigate to Product -> Scheme -> Edit Scheme... Under the Diagnostics tab,
check the box next for Address Sanitizer and Undefined Behavior.
3. Build Filament and run the iOS sample as usual. Any errors will cause an exception to raise and
diagnostics to print in the console.

View File

@@ -22,6 +22,9 @@ targetTemplates:
OTHER_LDFLAGS: ["-lfilament", "-lbackend", "-lfilaflat", "-lktxreader",
"-lfilabridge", "-lutils", "-lsmol-v", "-lgeometry", "-libl"]
ENABLE_BITCODE: NO
CLANG_CXX_LANGUAGE_STANDARD: gnu++17
# This allows users to not have to specify a unique bundle ID when building the sample apps.
SAMPLE_CODE_DISAMBIGUATOR: ${DEVELOPMENT_TEAM}
configs:
debug:
HEADER_SEARCH_PATHS: ["../../../out/ios-debug/filament/include", "generated"]

View File

@@ -96,7 +96,7 @@
};
};
buildConfigurationList = E89E1EEA671E49DBAC9D5A9C /* Build configuration list for PBXProject "backend-test" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -165,7 +165,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -218,6 +218,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -242,6 +243,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -250,7 +252,8 @@
"-lfilamat",
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.backend-test";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -262,7 +265,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -322,6 +325,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -346,6 +350,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -354,7 +359,8 @@
"-lfilamat",
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.backend-test";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -366,7 +372,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -419,6 +425,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -443,6 +450,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -451,7 +459,8 @@
"-lfilamat",
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.backend-test";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -461,6 +470,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -485,6 +495,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -493,7 +504,8 @@
"-lfilamat",
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.backend-test";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -505,7 +517,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:backend-test.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"

View File

@@ -1,6 +1,6 @@
name: backend-test
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
backend-test:

11
ios/samples/generate-samples.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -ex
cd backend-test && xcodegen && cd ..
cd gltf-viewer && xcodegen && cd ..
cd hello-ar && xcodegen && cd ..
cd hello-gltf && xcodegen && cd ..
cd hello-pbr && xcodegen && cd ..
cd hello-triangle && xcodegen && cd ..
cd transparent-rendering && xcodegen && cd ..

View File

@@ -123,7 +123,7 @@
};
};
buildConfigurationList = 90327DE42AE5DF12C4BD2113 /* Build configuration list for PBXProject "gltf-viewer" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -219,7 +219,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -281,7 +281,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -336,7 +336,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -389,6 +389,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -431,7 +432,8 @@
"-lcivetweb",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.gltf-viewer";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -441,6 +443,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -483,7 +486,8 @@
"-lcivetweb",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.gltf-viewer";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -495,7 +499,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -555,6 +559,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -597,7 +602,8 @@
"-lcivetweb",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.gltf-viewer";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -607,6 +613,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -649,7 +656,8 @@
"-lcivetweb",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.gltf-viewer";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"

View File

@@ -56,6 +56,8 @@ using namespace ktxreader;
Texture* _iblTexture;
IndirectLight* _indirectLight;
Entity _sun;
UITapGestureRecognizer* _doubleTapRecognizer;
}
#pragma mark UIViewController methods
@@ -91,6 +93,10 @@ using namespace ktxreader;
_server = new viewer::RemoteServer();
_automation = viewer::AutomationEngine::createDefault();
_doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reloadModel)];
_doubleTapRecognizer.numberOfTapsRequired = 2;
[self.modelView addGestureRecognizer:_doubleTapRecognizer];
}
- (void)viewWillAppear:(BOOL)animated {
@@ -101,6 +107,8 @@ using namespace ktxreader;
[self stopDisplayLink];
}
#pragma mark Private
- (void)startDisplayLink {
[self stopDisplayLink];
@@ -116,8 +124,6 @@ using namespace ktxreader;
_displayLink = nil;
}
#pragma mark Private
- (void)createRenderablesFromPath:(NSString*)model {
// Retrieve the full path to the model in the documents directory.
NSString* documentPath = [NSSearchPathForDirectoriesInDomains(
@@ -257,6 +263,11 @@ using namespace ktxreader;
[self.modelView render];
}
- (void)reloadModel {
[self.modelView destroyModel];
[self createDefaultRenderables];
}
- (void)dealloc {
delete _server;
delete _automation;

View File

@@ -1,6 +1,6 @@
name: gltf-viewer
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
gltf-viewer:

View File

@@ -160,16 +160,17 @@
392626AFF36D607FEB6176BE /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1200;
TargetAttributes = {
};
};
buildConfigurationList = 281E763643B2F1F3B903864B /* Build configuration list for PBXProject "hello-ar" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 316A56C698744769908E6F88;
projectDirPath = "";
@@ -255,6 +256,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -279,6 +281,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -286,10 +289,11 @@
"-libl",
"-lfilameshio",
"-lmeshoptimizer",
"-lktxreader",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-ar";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-ar";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -299,6 +303,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -323,6 +328,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -330,10 +336,11 @@
"-libl",
"-lfilameshio",
"-lmeshoptimizer",
"-lktxreader",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-ar";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-ar";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -345,10 +352,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -364,6 +372,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -388,7 +397,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -402,6 +412,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -426,6 +437,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -433,10 +445,11 @@
"-libl",
"-lfilameshio",
"-lmeshoptimizer",
"-lktxreader",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-ar";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-ar";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -448,10 +461,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -467,6 +481,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -491,7 +506,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -507,10 +523,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -526,6 +543,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -544,11 +562,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "Metal Release";
};
@@ -556,6 +576,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -580,6 +601,7 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
@@ -587,10 +609,11 @@
"-libl",
"-lfilameshio",
"-lmeshoptimizer",
"-lktxreader",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-ar";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-ar";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -602,10 +625,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -621,6 +645,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -639,11 +664,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "OpenGL Release";
};

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:hello-ar.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "Metal Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Metal Release"

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "OpenGL Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "OpenGL Release"

View File

@@ -1,6 +1,6 @@
name: hello-ar
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
hello-ar:

View File

@@ -138,7 +138,7 @@
};
};
buildConfigurationList = C7514DDCFDA3C5F803E239F9 /* Build configuration list for PBXProject "hello-gltf" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -232,7 +232,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -292,6 +292,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -330,7 +331,8 @@
"-lktxreader",
"-lstb",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-gltf";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -340,6 +342,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -378,7 +381,8 @@
"-lktxreader",
"-lstb",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-gltf";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -390,7 +394,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -452,7 +456,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -507,7 +511,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -560,6 +564,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -598,7 +603,8 @@
"-lktxreader",
"-lstb",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-gltf";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -608,6 +614,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -646,7 +653,8 @@
"-lktxreader",
"-lstb",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-gltf";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"

View File

@@ -1,6 +1,6 @@
name: hello-gltf
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
hello-gltf:

View File

@@ -115,16 +115,17 @@
55C50CCB1B22D6D3E534BB59 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1200;
TargetAttributes = {
};
};
buildConfigurationList = 0479354FB8D9A411347D91A6 /* Build configuration list for PBXProject "hello-pbr" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 8B171658B4A1227B9CA44459;
projectDirPath = "";
@@ -212,10 +213,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -231,6 +233,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -255,7 +258,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -269,6 +273,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -293,17 +298,19 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
"-lktxreader",
"-lfilameshio",
"-lmeshoptimizer",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-pbr";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-pbr";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -313,6 +320,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -337,17 +345,19 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
"-lktxreader",
"-lfilameshio",
"-lmeshoptimizer",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-pbr";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-pbr";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -357,6 +367,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -381,17 +392,19 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
"-lktxreader",
"-lfilameshio",
"-lmeshoptimizer",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-pbr";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-pbr";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -403,10 +416,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -422,6 +436,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -440,11 +455,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "Metal Release";
};
@@ -454,10 +471,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -473,6 +491,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -491,11 +510,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "OpenGL Release";
};
@@ -503,6 +524,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -527,17 +549,19 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
"-lktxreader",
"-lfilameshio",
"-lmeshoptimizer",
"-limage",
"-lktxreader",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-pbr";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-pbr";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -549,10 +573,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -568,6 +593,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -592,7 +618,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:hello-pbr.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "Metal Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Metal Release"

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "OpenGL Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "OpenGL Release"

View File

@@ -1,6 +1,6 @@
name: hello-pbr
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
hello-pbr:

View File

@@ -102,16 +102,17 @@
72185E8715083808BEBA6666 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1200;
TargetAttributes = {
};
};
buildConfigurationList = 69A012BABE82CB5D1986B1BB /* Build configuration list for PBXProject "hello-triangle" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 5B3E3AAF5C75644908492410;
projectDirPath = "";
@@ -194,6 +195,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -218,13 +220,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-triangle";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-triangle";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -236,10 +240,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -255,6 +260,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -279,7 +285,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -293,6 +300,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -317,13 +325,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-triangle";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-triangle";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -333,6 +343,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -357,13 +368,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-triangle";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-triangle";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -375,10 +388,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -394,6 +408,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -412,11 +427,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "Metal Release";
};
@@ -426,10 +443,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -445,6 +463,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -463,11 +482,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "OpenGL Release";
};
@@ -475,6 +496,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -499,13 +521,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.hello-triangle";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-triangle";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -517,10 +541,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -536,6 +561,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -560,7 +586,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:hello-triangle.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "Metal Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Metal Release"

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "OpenGL Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "OpenGL Release"

View File

@@ -1,6 +1,6 @@
name: hello-triangle
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
hello-triangle:

View File

@@ -1,6 +1,6 @@
name: transparent-rendering
options:
bundleIdPrefix: google.filament
bundleIdPrefix: ${SAMPLE_CODE_DISAMBIGUATOR}.google.filament
include: ../app-template.yml
targets:
transparent-rendering:

View File

@@ -92,16 +92,17 @@
81DCFDD51562B31512E28C40 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1200;
TargetAttributes = {
};
};
buildConfigurationList = 8A22BD23DB15965551506480 /* Build configuration list for PBXProject "transparent-rendering" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 4C27390036DEB15C67D5A664;
projectDirPath = "";
@@ -186,10 +187,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -205,6 +207,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -223,11 +226,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "OpenGL Release";
};
@@ -237,10 +242,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -256,6 +262,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -280,7 +287,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -296,10 +304,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -315,6 +324,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -333,11 +343,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = "Metal Release";
};
@@ -345,6 +357,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -369,13 +382,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.transparent-rendering";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.transparent-rendering";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -385,6 +400,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -409,13 +425,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.transparent-rendering";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.transparent-rendering";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -425,6 +443,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -449,13 +468,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.transparent-rendering";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.transparent-rendering";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -465,6 +486,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -489,13 +511,15 @@
"-lfilament",
"-lbackend",
"-lfilaflat",
"-lktxreader",
"-lfilabridge",
"-lutils",
"-lsmol-v",
"-lgeometry",
"-libl",
);
PRODUCT_BUNDLE_IDENTIFIER = "google.filament.transparent-rendering";
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.transparent-rendering";
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -507,10 +531,11 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
@@ -526,6 +551,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -550,7 +576,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:transparent-rendering.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "Metal Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Metal Release"

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -41,10 +42,6 @@
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "OpenGL Debug"
@@ -68,8 +65,6 @@
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "OpenGL Release"

View File

@@ -177,7 +177,7 @@ public:
Bookmark getHomeBookmark() const override {
Bookmark bookmark;
bookmark.flight.position = Base::mProps.flightStartPosition;;
bookmark.flight.position = Base::mProps.flightStartPosition;
bookmark.flight.pitch = Base::mProps.flightStartPitch;
bookmark.flight.yaw = Base::mProps.flightStartYaw;
return bookmark;

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 22;
static constexpr size_t MATERIAL_VERSION = 23;
/**
* Supported shading models

View File

@@ -177,19 +177,22 @@ struct alignas(256) PerRenderableUib { // NOLINT(cppcoreguidelines-pro-type-memb
math::mat4f worldFromModelMatrix;
math::mat3f worldFromModelNormalMatrix; // this gets expanded to 48 bytes during the copy to the UBO
alignas(16) uint32_t morphTargetCount;
uint32_t flags; // see packFlags() below
uint32_t channels; // 0x000000ll
uint32_t flagsChannels; // see packFlags() below (0x00000fll)
uint32_t objectId; // used for picking
// TODO: We need a better solution, this currently holds the average local scale for the renderable
float userData;
math::float4 reserved[8];
static uint32_t packFlags(bool skinning, bool morphing, bool contactShadows) noexcept {
return (skinning ? 1 : 0) |
(morphing ? 2 : 0) |
(contactShadows ? 4 : 0);
static uint32_t packFlagsChannels(
bool skinning, bool morphing, bool contactShadows, uint8_t channels) noexcept {
return (skinning ? 0x100 : 0) |
(morphing ? 0x200 : 0) |
(contactShadows ? 0x400 : 0) |
channels;
}
};
static_assert(sizeof(PerRenderableUib) % 256 == 0, "sizeof(Transform) should be a multiple of 256");
static_assert(sizeof(PerRenderableUib) == 256, "sizeof(PerRenderableUib) must be 256 bytes");
struct LightsUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
static constexpr utils::StaticString _name{ "LightsUniforms" };

View File

@@ -152,10 +152,10 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept {
.add("worldFromModelMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("worldFromModelNormalMatrix", 1, UniformInterfaceBlock::Type::MAT3, Precision::HIGH)
.add("morphTargetCount", 1, UniformInterfaceBlock::Type::UINT)
.add("flags", 1, UniformInterfaceBlock::Type::UINT)
.add("channels", 1, UniformInterfaceBlock::Type::UINT)
.add("flagsChannels", 1, UniformInterfaceBlock::Type::UINT)
.add("objectId", 1, UniformInterfaceBlock::Type::UINT)
.add("userData", 1, UniformInterfaceBlock::Type::FLOAT)
.add("reserved", 8, UniformInterfaceBlock::Type::FLOAT4)
.build();
return uib;
}

View File

@@ -56,6 +56,25 @@ public:
*/
void updateBoneMatrices();
/**
* Applies a blended transform to the union of nodes affected by two animations.
* Used for cross-fading from a previous skinning-based animation or rigid body animation.
*
* First, this stashes the current transform hierarchy into a transient memory buffer.
*
* Next, this applies previousAnimIndex / previousAnimTime to the actual asset by internally
* calling applyAnimation().
*
* Finally, the stashed local transforms are lerped (via the scale / translation / rotation
* components) with their live counterparts, and the results are pushed to the asset.
*
* To achieve a cross fade effect with skinned models, clients will typically call animator
* methods in this order: (1) applyAnimation (2) applyCrossFade (3) updateBoneMatrices. The
* animation that clients pass to applyAnimation is the "current" animation corresponding to
* alpha=1, while the "previous" animation passed to applyCrossFade corresponds to alpha=0.
*/
void applyCrossFade(size_t previousAnimIndex, float previousAnimTime, float alpha);
/**
* Pass the identity matrix into all bone nodes, useful for returning to the T pose.
*

View File

@@ -46,7 +46,7 @@ namespace gltfio {
using TimeValues = map<float, size_t>;
using SourceValues = vector<float>;
using BoneVector = vector<filament::math::mat4f>;
using BoneVector = vector<mat4f>;
struct Sampler {
TimeValues times;
@@ -75,8 +75,11 @@ struct AnimatorImpl {
RenderableManager* renderableManager;
TransformManager* transformManager;
vector<float> weights;
FixedCapacityVector<mat4f> crossFade;
void addChannels(const NodeMap& nodeMap, const cgltf_animation& srcAnim, Animation& dst);
void applyAnimation(const Channel& channel, float t, size_t prevIndex, size_t nextIndex);
void stashCrossFade();
void applyCrossFade(float alpha);
};
static void createSampler(const cgltf_animation_sampler& src, Sampler& dst) {
@@ -221,6 +224,12 @@ Animator::Animator(FFilamentAsset* asset, FFilamentInstance* instance) {
}
}
void Animator::applyCrossFade(size_t previousAnimIndex, float previousAnimTime, float alpha) {
mImpl->stashCrossFade();
applyAnimation(previousAnimIndex, previousAnimTime);
mImpl->applyCrossFade(alpha);
}
void Animator::addInstance(FFilamentInstance* instance) {
const cgltf_data* srcAsset = mImpl->asset->mSourceAsset->hierarchy;
const cgltf_animation* srcAnims = srcAsset->animations;
@@ -367,6 +376,58 @@ const char* Animator::getAnimationName(size_t animationIndex) const {
return mImpl->animations[animationIndex].name.c_str();
}
void AnimatorImpl::stashCrossFade() {
using Instance = TransformManager::Instance;
auto& tm = *this->transformManager;
auto& stash = this->crossFade;
// Count the total number of transformable nodes to preallocate the stash memory.
// We considered caching this count, but the cache would need to be invalidated when entities
// are added into the hierarchy.
auto recursiveCount = [&tm](Instance node, size_t count, auto& fn) -> size_t {
++count;
for (auto iter = tm.getChildrenBegin(node); iter != tm.getChildrenEnd(node); ++iter) {
count = fn(*iter, count, fn);
}
return count;
};
auto recursiveStash = [&tm, &stash](Instance node, size_t index, auto& fn) -> size_t {
stash[index++] = tm.getTransform(node);
for (auto iter = tm.getChildrenBegin(node); iter != tm.getChildrenEnd(node); ++iter) {
index = fn(*iter, index, fn);
}
return index;
};
const Instance root = tm.getInstance(asset->mRoot);
const size_t count = recursiveCount(root, 0, recursiveCount);
crossFade.reserve(count);
crossFade.resize(count);
recursiveStash(root, 0, recursiveStash);
}
void AnimatorImpl::applyCrossFade(float alpha) {
using Instance = TransformManager::Instance;
auto& tm = *this->transformManager;
auto& stash = this->crossFade;
auto recursiveFn = [&tm, &stash, alpha](Instance node, size_t index, auto& fn) -> size_t {
float3 scale0, scale1;
quatf rotation0, rotation1;
float3 translation0, translation1;
decomposeMatrix(stash[index++], &translation1, &rotation1, &scale1);
decomposeMatrix(tm.getTransform(node), &translation0, &rotation0, &scale0);
const float3 scale = mix(scale0, scale1, alpha);
const quatf rotation = slerp(rotation0, rotation1, alpha);
const float3 translation = mix(translation0, translation1, alpha);
tm.setTransform(node, composeMatrix(translation, rotation, scale));
for (auto iter = tm.getChildrenBegin(node); iter != tm.getChildrenEnd(node); ++iter) {
index = fn(*iter, index, fn);
}
return index;
};
recursiveFn(tm.getInstance(asset->mRoot), 0, recursiveFn);
}
void AnimatorImpl::addChannels(const NodeMap& nodeMap, const cgltf_animation& srcAnim,
Animation& dst) {

View File

@@ -342,6 +342,11 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
SYSTRACE_CONTEXT();
SYSTRACE_ASYNC_END("addResourceData", 1);
// Clear our texture caches. Previous calls to loadResources may have populated these, but the
// Texture objects could have since been destroyed.
pImpl->mBufferTextureCache.clear();
pImpl->mFilepathTextureCache.clear();
if (asset->mResourcesLoaded) {
return false;
}

View File

@@ -32,7 +32,7 @@ add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
target_link_libraries(${TARGET} PUBLIC image math png tinyexr utils z stb basis_encoder)
target_link_libraries(${TARGET} PUBLIC image math png tinyexr utils z basis_encoder)
if (WIN32)
target_link_libraries(${TARGET} PRIVATE wsock32)
endif()

View File

@@ -509,7 +509,7 @@ private:
mSize = needed;
}
// this calculate the offset adjusted for all data alignment of a given array
// this calculates the offset adjusted for all data alignment of a given array
static inline size_t getOffset(size_t index, size_t capacity) noexcept {
auto offsets = getOffsets(capacity);
return offsets[index];

View File

@@ -196,7 +196,7 @@ struct LightSettings {
LightManager::ShadowOptions shadowOptions;
SoftShadowOptions softShadowOptions;
float sunlightIntensity = 100000.0f;
math::float3 sunlightDirection = {0.6, -1.0, -0.8};;
math::float3 sunlightDirection = {0.6, -1.0, -0.8};
math::float3 sunlightColor = filament::Color::toLinear<filament::ACCURATE>({ 0.98, 0.92, 0.89});
float iblIntensity = 30000.0f;
float iblRotation = 0.0f;

View File

@@ -255,7 +255,6 @@ private:
// Properties that can be changed from the UI.
int mCurrentAnimation = 1; // It is a 1-based index and 0 means not playing animation
int mCurrentVariant = 0;
bool mResetAnimation = true;
bool mEnableWireframe = false;
int mVsmMsaaSamplesLog2 = 1;
Settings mSettings;
@@ -269,6 +268,13 @@ private:
// 0 is the default "free camera". Additional cameras come from the gltf file (1-based index).
int mCurrentCamera = 0;
// Cross fade animation parameters.
float mCrossFadeDuration = 0.5f; // number of seconds to transition between animations
int mPreviousAnimation = 0; // one-based index of the previous animation
double mCurrentStartTime = 0.0f; // start time of most recent cross-fade (seconds)
double mPreviousStartTime = 0.0f; // start time of previous cross-fade (seconds)
bool mResetAnimation = true; // set when building ImGui widgets, honored in applyAnimation
// Color grading UI state.
float mToneMapPlot[1024];
float mRangePlot[1024 * 3];

View File

@@ -500,17 +500,20 @@ void ViewerGui::sceneSelectionUI() {
void ViewerGui::applyAnimation(double currentTime) {
assert_invariant(!isRemoteMode());
static double startTime = 0;
const size_t numAnimations = mAnimator->getAnimationCount();
if (mResetAnimation) {
startTime = currentTime;
for (size_t i = 0; i < numAnimations; i++) {
mAnimator->applyAnimation(i, 0);
}
mPreviousStartTime = mCurrentStartTime;
mCurrentStartTime = currentTime;
mResetAnimation = false;
}
const double elapsedSeconds = currentTime - mCurrentStartTime;
if (numAnimations > 0 && mCurrentAnimation > 0) {
mAnimator->applyAnimation(mCurrentAnimation - 1, currentTime - startTime);
mAnimator->applyAnimation(mCurrentAnimation - 1, elapsedSeconds);
if (elapsedSeconds < mCrossFadeDuration && mPreviousAnimation > 0) {
const double previousSeconds = currentTime - mPreviousStartTime;
const float lerpFactor = elapsedSeconds / mCrossFadeDuration;
mAnimator->applyCrossFade(mPreviousAnimation - 1, previousSeconds, lerpFactor);
}
}
if (mShowingRestPose) {
mAnimator->resetBoneMatrices();
@@ -1031,6 +1034,8 @@ void ViewerGui::updateUserInterface() {
ImGui::Indent();
int selectedAnimation = mCurrentAnimation;
ImGui::RadioButton("Disable", &selectedAnimation, 0);
ImGui::SliderFloat("Cross fade", &mCrossFadeDuration, 0.0f, 2.0f,
"%4.2f seconds", ImGuiSliderFlags_AlwaysClamp);
for (size_t i = 0, count = mAnimator->getAnimationCount(); i < count; ++i) {
std::string label = mAnimator->getAnimationName(i);
if (label.empty()) {
@@ -1039,6 +1044,7 @@ void ViewerGui::updateUserInterface() {
ImGui::RadioButton(label.c_str(), &selectedAnimation, i + 1);
}
if (selectedAnimation != mCurrentAnimation) {
mPreviousAnimation = mCurrentAnimation;
mCurrentAnimation = selectedAnimation;
mResetAnimation = true;
}

View File

@@ -45,7 +45,7 @@ fragment {
material.baseColor = bg;
} else {
uv.t = 1.0 - uv.t;
vec4 color = texture(materialParams_image, uv.st);
vec4 color = max(texture(materialParams_image, uv.st), 0.0);
color.rgb *= color.a;
// Manual, pre-multiplied srcOver with opaque destination optimization
material.baseColor.rgb = color.rgb + bg.rgb * (1.0 - color.a);

View File

@@ -69,9 +69,9 @@ highp vec2 uvToRenderTargetUV(highp vec2 uv) {
// TODO: below shouldn't be accessible from post-process materials
#define FILAMENT_OBJECT_SKINNING_ENABLED_BIT 0x1u
#define FILAMENT_OBJECT_MORPHING_ENABLED_BIT 0x2u
#define FILAMENT_OBJECT_CONTACT_SHADOWS_BIT 0x4u
#define FILAMENT_OBJECT_SKINNING_ENABLED_BIT 0x100u
#define FILAMENT_OBJECT_MORPHING_ENABLED_BIT 0x200u
#define FILAMENT_OBJECT_CONTACT_SHADOWS_BIT 0x400u
/** @public-api */
highp vec4 getResolution() {

View File

@@ -96,6 +96,7 @@ void morphPosition(inout vec4 p) {
}
void morphNormal(inout vec3 n) {
vec3 baseNormal = n;
ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
for (uint i = 0u; i < objectUniforms.morphTargetCount; ++i) {
float w = morphingUniforms.weights[i][0];
@@ -104,7 +105,7 @@ void morphNormal(inout vec3 n) {
ivec4 tangent = texelFetch(morphTargetBuffer_tangents, texcoord, 0);
vec3 normal;
toTangentFrame(float4(tangent) * (1.0 / 32767.0), normal);
n += w * normal;
n += w * (normal - baseNormal);
}
}
}
@@ -116,7 +117,7 @@ vec4 getPosition() {
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
if ((objectUniforms.flags & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) {
#if defined(LEGACY_MORPHING)
pos += morphingUniforms.weights[0] * mesh_custom0;
pos += morphingUniforms.weights[1] * mesh_custom1;
@@ -127,7 +128,7 @@ vec4 getPosition() {
#endif
}
if ((objectUniforms.flags & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) {
skinPosition(pos.xyz, mesh_bone_indices, mesh_bone_weights);
}

View File

@@ -36,7 +36,7 @@ void evaluateDirectionalLight(const MaterialInputs material,
Light light = getDirectionalLight();
uint channels = objectUniforms.channels & 0xFFu;
uint channels = objectUniforms.flagsChannels & 0xFFu;
if ((light.channels & channels) == 0u) {
return;
}
@@ -60,7 +60,7 @@ void evaluateDirectionalLight(const MaterialInputs material,
visibility = shadow(true, light_shadowMap, layer, 0u, cascade);
}
if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) {
if ((objectUniforms.flags & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
ssContactShadowOcclusion = screenSpaceContactShadow(light.l);
}
}

View File

@@ -194,7 +194,7 @@ void evaluatePunctualLights(const MaterialInputs material,
uint index = froxel.recordOffset;
uint end = index + froxel.count;
uint channels = objectUniforms.channels & 0xFFu;
uint channels = objectUniforms.flagsChannels & 0xFFu;
// Iterate point lights
for ( ; index < end; index++) {
@@ -217,7 +217,7 @@ void evaluatePunctualLights(const MaterialInputs material,
visibility = shadow(false, light_shadowMap, light.shadowLayer, light.shadowIndex, 0u);
}
if (light.contactShadows && visibility > 0.0) {
if ((objectUniforms.flags & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
visibility *= 1.0 - screenSpaceContactShadow(light.l);
}
}

View File

@@ -35,24 +35,25 @@ void main() {
toTangentFrame(mesh_tangents, material.worldNormal, vertex_worldTangent.xyz);
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
if ((objectUniforms.flags & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) {
#if defined(LEGACY_MORPHING)
vec3 normal0, normal1, normal2, normal3;
toTangentFrame(mesh_custom4, normal0);
toTangentFrame(mesh_custom5, normal1);
toTangentFrame(mesh_custom6, normal2);
toTangentFrame(mesh_custom7, normal3);
material.worldNormal += morphingUniforms.weights[0].xyz * normal0;
material.worldNormal += morphingUniforms.weights[1].xyz * normal1;
material.worldNormal += morphingUniforms.weights[2].xyz * normal2;
material.worldNormal += morphingUniforms.weights[3].xyz * normal3;
vec3 baseNormal = material.worldNormal;
material.worldNormal += morphingUniforms.weights[0].xyz * (normal0 - baseNormal);
material.worldNormal += morphingUniforms.weights[1].xyz * (normal1 - baseNormal);
material.worldNormal += morphingUniforms.weights[2].xyz * (normal2 - baseNormal);
material.worldNormal += morphingUniforms.weights[3].xyz * (normal3 - baseNormal);
#else
morphNormal(material.worldNormal);
material.worldNormal = normalize(material.worldNormal);
#endif
}
if ((objectUniforms.flags & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) {
skinNormal(material.worldNormal, mesh_bone_indices, mesh_bone_weights);
skinNormal(vertex_worldTangent.xyz, mesh_bone_indices, mesh_bone_weights);
}
@@ -70,9 +71,27 @@ void main() {
toTangentFrame(mesh_tangents, material.worldNormal);
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
if ((objectUniforms.flags & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) {
skinNormal(material.worldNormal, mesh_bone_indices, mesh_bone_weights);
}
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) {
#if defined(LEGACY_MORPHING)
vec3 normal0, normal1, normal2, normal3;
toTangentFrame(mesh_custom4, normal0);
toTangentFrame(mesh_custom5, normal1);
toTangentFrame(mesh_custom6, normal2);
toTangentFrame(mesh_custom7, normal3);
vec3 baseNormal = material.worldNormal;
material.worldNormal += morphingUniforms.weights[0].xyz * (normal0 - baseNormal);
material.worldNormal += morphingUniforms.weights[1].xyz * (normal1 - baseNormal);
material.worldNormal += morphingUniforms.weights[2].xyz * (normal2 - baseNormal);
material.worldNormal += morphingUniforms.weights[3].xyz * (normal3 - baseNormal);
#else
morphNormal(material.worldNormal);
material.worldNormal = normalize(material.worldNormal);
#endif
}
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) {
skinNormal(material.worldNormal, mesh_bone_indices, mesh_bone_weights);
}
#endif
material.worldNormal = objectUniforms.worldFromModelNormalMatrix * material.worldNormal;

View File

@@ -54,7 +54,7 @@ vec4 evaluateMaterial(const MaterialInputs material) {
visibility = shadow(true, light_shadowMap, layer, 0u, cascade);
}
if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) {
if ((objectUniforms.flags & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
if ((objectUniforms.flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
visibility *= (1.0 - screenSpaceContactShadow(frameUniforms.lightDirection));
}
}

View File

@@ -51,20 +51,23 @@ else()
set (BASIS_CONFIG ${BASIS_CONFIG} BASISD_SUPPORT_DXT5A=0 BASISD_SUPPORT_DXT1=0)
endif()
add_executable(basisu ${ENCODER_SRC} ${TRANSCODER_SRC} ../basisu_tool.cpp)
add_library(basis_encoder ${ENCODER_SRC} ${TRANSCODER_SRC})
add_library(basis_transcoder ${TRANSCODER_SRC})
target_include_directories(basis_encoder PUBLIC ../encoder)
target_include_directories(basis_transcoder PUBLIC ../transcoder)
target_compile_definitions(basisu PRIVATE ${BASIS_CONFIG})
target_compile_definitions(basis_encoder PRIVATE ${BASIS_CONFIG})
target_compile_definitions(basis_transcoder PRIVATE ${BASIS_CONFIG})
if (NOT MSVC AND NOT ANDROID)
target_link_libraries(basisu m pthread)
if (IS_HOST_PLATFORM)
add_executable(basisu ${ENCODER_SRC} ${TRANSCODER_SRC} ../basisu_tool.cpp)
target_compile_definitions(basisu PRIVATE ${BASIS_CONFIG})
install(TARGETS basisu DESTINATION bin)
if (NOT MSVC AND NOT ANDROID)
target_link_libraries(basisu m pthread)
endif()
endif()
install(TARGETS basisu DESTINATION bin)
install(TARGETS basis_transcoder ARCHIVE DESTINATION lib/${DIST_DIR})

View File

@@ -2,17 +2,17 @@
- [Description](#description)
- [Instructions](#instructions)
- [Input Limitations](#input-limitations)
- [Emitter Flags](#emitter-flags)
- [Input Files](#input-files)
- [Source Files](#source-files)
- [Output Files](#output-files)
- [Input Format](#input-format)
### Description
## Description
This Go program consumes C++ header file(s) and generates Java bindings, JavaScript bindings, and
C++ code that performs JSON serialization.
### Instructions
## Instructions
To install the Go compiler on macOS, just do:
@@ -22,19 +22,7 @@ To build and invoke the code generator, do:
cd tools/beamsplitter ; go run .
### Input Limitations
The source files must have very simple C++ syntax. Some of the limitations include:
- Only `enum class` is supported; no old-style enums.
- Opening braces for `enum` and `struct` must live at the end of a codeline.
- Enum values must be sequential and cannot have custom values.
- There are no namespaces other than the top-level namespace.
- Every struct field must supply a default value on a single codeline using the = operator.
- If the default value of a field is a vector, it must be in the form: `{ x, y, z }`.
- There must be no string literals that contain keywords.
### Emitter Flags
## Emitter Flags
Special directives in the form `%codegen_foo%` are called *emitter flags*. They are typically
embedded in a comment associated with a particular struct field.
@@ -43,14 +31,14 @@ flag | description
--------------------------- | ----
**codegen_skip_json** | Field is skipped when generating JSON serialization code.
**codegen_skip_javascript** | Field is skipped when generating JavaScript and TypeScript bindings.
**codegen_java_flatten** | Field is replaced with constituent sub-fields. (TBD)
**codegen_java_flatten** | Field is replaced with constituent sub-fields.
**codegen_java_float** | Field will be forced to have a `float` representation in Java.
### Input Files
## Source Files
- `filament/include/filament/Options.h`
### Output Files
## Output Files
The following files are created:
@@ -59,9 +47,98 @@ flag | description
- `web/filament-js/jsbindings_generated.cpp`
- `web/filament-js/jsenums_generated.cpp`
- `web/filament-js/extensions_generated.js`
- `android/filament-android/src/main/cpp/View_generated.cpp` (TBD)
Additionally, in-place edits are made to the following files:
- `web/filament-js/filament.d.ts`
- `android/filament-android/src/main/java/.../View.java` (TBD)
- `android/filament-android/src/main/java/.../View.java`
## Input Format
There are many ways in which the source file format is more restrictive than the full C++
language, but here are some of the highlights:
- All enums must be class enums.
- External headers pulled in with `#include` files are ignored.
- Expressions in the RHS of default value assignments are not parsed, they are just exposed by
the lexer as blobs.
- Struct fields, class fields, and method arguments must have fairly simple types. e.g. they cannot
have parentheses. If a type is C style callback, then it should be specified with an alias.
- Multiline strings and macro definitions are not allowed.
- Enum values must be sequential and cannot have custom values.
The following formal grammar describes the above limitations in greater detail, but with some
caveats:
- All C preprocessor directives are discarded during lexical analysis; they do not exist in the AST.
- Whitespace is similarly discarded, so there is no "space" concept in the AST.
- Macro invocations are also removed by the lexer if they are known Filament-specific macros (e.g.
`UTILS_PUBLIC` and `UTILS_DEPRECATED`).
- Comments are removed by the lexer and are generally not part of the resulting AST. However
the lexer proffers a mapping from line numbers to comments to allow for docstring extraction.
- Emitter flags in the form `%codegen_foo%` are detected in a post-processing phase and removed from
all comments.
### Grammar
```eBNF
root = namespace ;
namespace = "namespace" , [ ident ] , "{" , { block } , "}" ;
block = class | struct | enum | namespace | using | forward_declaration;
forward_declaration = ("class" | "struct" ) , ident , ";" ;
template = "template" , "TemplateArgs" ;
class = [template] , "class" , ident , [ ":" , [ "public" ] , "SimpleType" ]
, "{" , struct_body , "}" , ";" ;
struct = [template] , "struct" , ident , "{" , struct_body , "}" , ";" ;
enum = "enum" , "class" , ident , [ ":" , type ]
, "{" , , ident , { "," , ident } , [ "," ] , "}" , ";" ;
using = "using" , ident , "=", type , ";" ;
struct_body = { access_specifier | field | method | block } ;
access_specifier = ("public" | "private" | "protected" ) , ":" ;
method = [template] , { "constexpr" , "friend" } ,
, type , ident , "MethodArgs" , specifiers , ( ";" | "MethodBody" ) ;
specifiers = { "const" | "noexcept" } ;
field = type , ident , [ array ] , [ "=" , "DefaultValue" ] ";" ;
array = "[" , "ArrayLength", "]" ;
type = "SimpleType" ;
ident = "Identifier" ;
```
The above grammar uses the following notation:
- `" ... "` denotes a terminal
- `{ ... }` denotes zero or more repetition
- `[ ... ]` denotes an optional quantity
- `( ... )` is used for grouping
- `a | b` denotes a choice
- `a , b` denotes concatenation
- `;` terminates a production
Terminal name | Description
--------------------------- | ----
SimpleType (*) | examples: `Texture* const`, `uint8_t`, `BlendMode`
MethodBody | unparsed implementation of a function or method, including outer `{}`
MethodArgs | similar to above; an unparsed blob, but delimited with `()`
TemplateArgs | similar to above; an unparsed blob, but delimited with `<>`
DefaultValue (**) | an unparsed expression with certain restrictions
Identifier | `[A-Za-z_][A-Za-z0-9_]*`
ArrayLength | `[1-9][0-9]*`
(*) `SimpleType` should not contain parentheses or commas, so C callbacks are not allowed unless
you alias them first.
(**) If `DefaultValue` is a vector, it must be in the form: `{ x, y, z }`.
## References
Initially inspired by the following Rob Pike talk.
- https://www.youtube.com/watch?v=HxaD_trXwRE
Beamsplitter does not use the state machine described in the above prezo, but it does use a channel
for separating the parser from the lexer. The beamsplitter lexer is actually a recursive descent
parser with simple lookahead functionality. This makes it easy for the "real" parser to create a
coarse-grained AST.
The companion to the above talk is Go's template lexer, which can be studied here:
- https://cs.opensource.google/go/go/+/master:src/text/template/parse/lex.go
Wikipedia has a good example of recursive descent:
- https://en.wikipedia.org/wiki/Recursive_descent_parser

View File

@@ -0,0 +1,332 @@
/*
* Copyright (C) 2022 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.
*/
package database
import (
"beamsplitter/parse"
"bufio"
"io"
"log"
"regexp"
"strings"
)
func Create(root *parse.RootNode, contents string) []TypeDefinition {
context := gatherContext{}
context.compileRegexps()
// Line numbers are 1-based in beamsplitter, so we add an extra newline at the top.
context.codelines = strings.Split("\n"+contents, "\n")
context.commentBlocks = gatherCommentBlocks(strings.NewReader(contents))
// Recurse into the AST using pre-order traversal, gathering type information along the way.
context.gatherTypeDefinitions(root.Child, "", nil)
context.addTypeQualifiers()
return context.definitions
}
type TypeDefinition interface {
BaseName() string
QualifiedName() string
Parent() TypeDefinition
}
type StructField struct {
TypeString string
Name string
DefaultValue string
Description string
EmitterFlags map[string]struct{}
CustomType TypeDefinition
}
type StructDefinition struct {
name string
qualifier string
Fields []StructField
Description string
parent TypeDefinition
}
func (defn StructDefinition) BaseName() string { return defn.name }
func (defn StructDefinition) QualifiedName() string { return defn.qualifier + defn.name }
func (defn StructDefinition) Parent() TypeDefinition { return defn.parent }
type EnumValue struct {
Description string
Name string
}
type EnumDefinition struct {
name string
qualifier string
Values []EnumValue
Description string
parent TypeDefinition
}
func (defn EnumDefinition) BaseName() string { return defn.name }
func (defn EnumDefinition) QualifiedName() string { return defn.qualifier + defn.name }
func (defn EnumDefinition) Parent() TypeDefinition { return defn.parent }
type Documented interface{ GetDoc() string }
func (defn EnumDefinition) GetDoc() string { return defn.Description }
func (defn StructDefinition) GetDoc() string { return defn.Description }
func (field StructField) GetDoc() string { return field.Description }
func (value EnumValue) GetDoc() string { return value.Description }
type generalScope struct{}
type scope interface {
BaseName() string
QualifiedName() string
Parent() TypeDefinition
}
func (defn generalScope) BaseName() string { return "" }
func (defn generalScope) QualifiedName() string { return "" }
func (defn generalScope) Parent() TypeDefinition { return nil }
type gatherContext struct {
definitions []TypeDefinition
stack []scope
commentBlocks map[int]string
codelines []string
floatMatcher *regexp.Regexp
vectorMatcher *regexp.Regexp
fieldDocParser *regexp.Regexp
emitterFlagFinder *regexp.Regexp
}
// https://github.com/google/re2/wiki/Syntax
func (context *gatherContext) compileRegexps() {
context.floatMatcher = regexp.MustCompile(`(\-?[0-9]+\.[0-9]*)f?`)
context.vectorMatcher = regexp.MustCompile(`\{(\s*\-?[0-9\.]+\s*(,\s*\-?[0-9\.]+\s*){1,})\}`)
context.emitterFlagFinder = regexp.MustCompile(`\s*\%codegen_([a-zA-Z0-9_]+)\%\s*`)
context.fieldDocParser = regexp.MustCompile(`(?://\s*\!\<\s*(.*))`)
}
// Creates a mapping from line numbers to strings, where the strings are entire block comments
// and the line numbers correspond to the last line of each block comment.
func gatherCommentBlocks(sourceFile io.Reader) map[int]string {
comments := make(map[int]string)
scanner := bufio.NewScanner(sourceFile)
var comment = ""
var indention = 0
for lineNumber := 1; scanner.Scan(); lineNumber++ {
codeline := scanner.Text()
if strings.Contains(codeline, `/**`) {
indention = strings.Index(codeline, `/**`)
if strings.Contains(codeline, `*/`) {
comments[lineNumber] = codeline[indention:] + "\n"
continue
}
comment = codeline[indention:] + "\n"
continue
}
if comment != "" {
if len(codeline) > indention {
codeline = codeline[indention:]
}
comment += codeline + "\n"
if strings.Contains(codeline, `*/`) {
comments[lineNumber] = comment
comment = ""
}
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
return comments
}
// Annotates struct fields that have custom types (i.e. enums or structs).
func (context gatherContext) addTypeQualifiers() {
typeMap := make(map[string]TypeDefinition)
for _, defn := range context.definitions {
typeMap[defn.QualifiedName()] = defn
}
for _, defn := range context.definitions {
structDefn, isStruct := defn.(*StructDefinition)
if !isStruct {
continue
}
for fieldIndex, field := range structDefn.Fields {
// Extract the namespace prefix (if any) explicitly specified for this field type.
var namespace string
localTypeName := field.TypeString
if index := strings.LastIndex(field.TypeString, "::"); index > -1 {
namespace = field.TypeString[:index]
localTypeName = field.TypeString[index+2:]
}
// Prepend additional qualifiers to the type string by searching upward through
// the current namespace hierarchy, and looking for a match.
mutable := &structDefn.Fields[fieldIndex]
for ancestor := defn; ; ancestor = ancestor.Parent() {
var qualified string
if namespace != "" && strings.HasSuffix(ancestor.QualifiedName(), namespace) {
qualified = ancestor.QualifiedName() + "::" + localTypeName
} else {
qualified = ancestor.QualifiedName() + "::" + field.TypeString
}
if fieldType, found := typeMap[qualified]; found {
mutable.TypeString = qualified
mutable.CustomType = fieldType
break
}
if ancestor.Parent() == nil {
if fieldType, found := typeMap[field.TypeString]; found {
mutable.CustomType = fieldType
}
break
}
}
if mutable.CustomType == nil {
continue
}
// Prepend additional qualifiers to the value string if it is a known enum.
var fieldType TypeDefinition = structDefn.Fields[fieldIndex].CustomType
enumDefn, isEnum := fieldType.(*EnumDefinition)
if isEnum {
selectedEnum := field.DefaultValue
if index := strings.LastIndex(field.DefaultValue, "::"); index > -1 {
selectedEnum = field.DefaultValue[index+2:]
}
mutable.DefaultValue = enumDefn.QualifiedName() + "::" + selectedEnum
}
}
}
}
// Validates and transforms the RHS of an assignment.
// For vectors, this converts curly braces into square brackets.
func (context gatherContext) distillValue(cppvalue string, lineNumber int) string {
cppvalue = strings.TrimSpace(cppvalue)
// Remove trailing "f" from floats, which isn't allowed in JavaScript.
if context.floatMatcher.MatchString(cppvalue) {
cppvalue = context.floatMatcher.ReplaceAllString(cppvalue, "$1")
}
// There are many ways to declare vector values (multi-arg constructor, single-arg
// constructor, curly braces with type, curly braces without type), so just poop out if
// the syntax is anything other than "curly braces without type".
if strings.Contains(cppvalue, "math::") || strings.Contains(cppvalue, "Color") {
log.Fatalf("%d: vectors must have the form {x, y ...}", lineNumber)
}
// Assume it's a vector if there's a curly brace.
if strings.Contains(cppvalue, "{") {
if context.vectorMatcher.MatchString(cppvalue) {
cppvalue = context.vectorMatcher.ReplaceAllString(cppvalue, "[$1]")
} else {
log.Fatalf("%d: vectors must have the form {x, y ...}", lineNumber)
}
}
return cppvalue
}
func (context *gatherContext) getDescription(line int) string {
desc := context.commentBlocks[line-1]
if desc == "" {
codeline := context.codelines[line]
if matches := context.fieldDocParser.FindStringSubmatch(codeline); matches != nil {
desc = matches[1]
}
}
return context.emitterFlagFinder.ReplaceAllString(desc, "")
}
func (context *gatherContext) getEmitterFlags(line int) map[string]struct{} {
codeline := context.codelines[line]
if matches := context.emitterFlagFinder.FindAllStringSubmatch(codeline, -1); matches != nil {
result := make(map[string]struct{}, len(matches))
for _, flag := range matches {
result[flag[1]] = struct{}{}
}
return result
}
return nil
}
// Search for all enums and structs and gather them into a flat list of type definitions.
func (context *gatherContext) gatherTypeDefinitions(node parse.Node, prefix string, parent TypeDefinition) {
switch concrete := node.(type) {
case *parse.NamespaceNode:
// HACK: filament namespace is a special case, remove it from the type database.
if concrete.Name != "filament" {
prefix = prefix + concrete.Name + "::"
}
for _, child := range concrete.Children {
context.gatherTypeDefinitions(child, prefix, parent)
}
case *parse.EnumNode:
defn := &EnumDefinition{
name: concrete.Name,
qualifier: prefix,
Values: make([]EnumValue, len(concrete.Values)),
Description: context.getDescription(int(concrete.Line)),
parent: parent,
}
for i, val := range concrete.Values {
defn.Values[i] = EnumValue{
Name: val,
Description: context.getDescription(int(concrete.ValueLines[i])),
}
}
context.definitions = append(context.definitions, defn)
case *parse.StructNode:
defn := &StructDefinition{
name: concrete.Name,
qualifier: prefix,
Fields: make([]StructField, 0),
Description: context.getDescription(int(concrete.Line)),
parent: parent,
}
prefix = prefix + concrete.Name + "::"
for _, child := range concrete.Members {
switch member := child.(type) {
case *parse.StructNode, *parse.ClassNode, *parse.EnumNode, *parse.NamespaceNode:
context.gatherTypeDefinitions(child, prefix, defn)
case *parse.FieldNode:
defn.Fields = append(defn.Fields, StructField{
TypeString: member.Type,
Name: member.Name,
DefaultValue: context.distillValue(member.Rhs, int(member.Line)),
Description: context.getDescription(int(member.Line)),
EmitterFlags: context.getEmitterFlags(int(member.Line)),
})
}
}
context.definitions = append(context.definitions, defn)
case *parse.ClassNode:
prefix = prefix + concrete.Name + "::"
for _, child := range concrete.Members {
switch child.(type) {
case *parse.StructNode, *parse.ClassNode, *parse.EnumNode, *parse.NamespaceNode:
context.gatherTypeDefinitions(child, prefix, parent)
}
}
}
}

View File

@@ -0,0 +1,285 @@
/*
* Copyright (C) 2022 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.
*/
package emitters
import (
db "beamsplitter/database"
"bufio"
"bytes"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"text/template"
)
func EditJava(definitions []db.TypeDefinition, classname string, folder string) {
path := filepath.Join(folder, classname+".java")
var codelines []string
{
sourceFile, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
defer sourceFile.Close()
lineScanner := bufio.NewScanner(sourceFile)
foundMarker := false
for lineNumber := 1; lineScanner.Scan(); lineNumber++ {
codeline := lineScanner.Text()
if strings.Contains(codeline, kCodelineMarker) {
foundMarker = true
break
}
codelines = append(codelines, codeline)
}
if !foundMarker {
log.Fatal("Unable to find marker line in Java file.")
}
}
file, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
defer file.Close()
defer fmt.Println("Edited " + path)
for _, codeline := range codelines {
file.WriteString(codeline)
file.WriteString("\n")
}
file.WriteString(" // " + kCodelineMarker + "\n")
// Forward declarations for usage in a closure.
var flattener func(*db.StructDefinition) string
var sharedExtensions template.FuncMap
javifyType := func(field db.StructField) string {
if _, exists := field.EmitterFlags["java_float"]; exists {
return " float"
}
switch field.TypeString {
case "math::float2", "math::float3", "math::float4", "LinearColor", "LinearColorA":
return " float[]"
case "bool":
return " boolean"
case "uint8_t", "uint16_t", "uint32_t":
return " int"
}
result := strings.ReplaceAll(field.TypeString, "*", "")
result = strings.ReplaceAll(result, "::", ".")
return " " + result
}
javifyValue := func(field db.StructField) string {
// When forcing an array to be bound to a float, extract the first component and use
// that as the default value.
if _, exists := field.EmitterFlags["java_float"]; exists {
arrayContents := strings.Trim(field.DefaultValue, " []")
if comma := strings.Index(arrayContents, ","); comma > -1 {
arrayContents = arrayContents[:comma]
}
if !strings.HasSuffix(arrayContents, "f") {
arrayContents += "f"
}
return " " + arrayContents
}
if field.DefaultValue == "nullptr" {
return " null"
}
// Handle enums
value := strings.ReplaceAll(field.DefaultValue, "::", ".")
// Add "f" suffix for scalars
if field.TypeString == "float" {
value += "f"
} else if c := len(value); c > 1 && value[0] == '[' && value[c-1] == ']' {
// For vector types, replace [] with {} and add "f" suffix to components.
switch field.TypeString {
case "math::float2", "math::float3", "math::float4", "LinearColor", "LinearColorA":
slices := strings.Split(value[1:c-1], ",")
for i := range slices {
slices[i] = strings.TrimSpace(slices[i])
if !strings.HasSuffix(slices[i], "f") {
slices[i] += "f"
}
}
value = "{" + strings.Join(slices, ", ") + "}"
default:
value = "{" + value[1:c-1] + "}"
}
}
return " " + value
}
getDocBlock := func(defn db.Documented, depth int) string {
doc := defn.GetDoc()
if doc == "" {
return ""
}
indent := strings.Repeat(" ", depth)
if strings.Count(doc, "\n") > 0 {
return strings.ReplaceAll(doc, "\n", "\n"+indent)
}
return "/**\n" + indent + " * " + doc + "\n" + indent + " */\n" + indent
}
getFieldAnnotation := func(field db.StructField, depth int) string {
if _, exists := field.EmitterFlags["java_float"]; exists {
return ""
}
annotation := ""
switch {
case field.DefaultValue == "nullptr":
annotation = "@Nullable"
case field.TypeString == "math::float2":
annotation = "@NonNull @Size(min = 2)"
case field.TypeString == "math::float3" || field.TypeString == "LinearColor":
annotation = "@NonNull @Size(min = 3)"
case field.TypeString == "math::float4" || field.TypeString == "LinearColorA":
annotation = "@NonNull @Size(min = 4)"
case strings.Contains(field.DefaultValue, "::"):
annotation = "@NonNull"
default:
return ""
}
return annotation + "\n" + strings.Repeat(" ", depth)
}
flattenStruct := func(defn *db.StructDefinition) string {
prefix := strings.ToLower(defn.BaseName())
buf := &bytes.Buffer{}
for _, field := range defn.Fields {
doc := getDocBlock(defn, 0)
buf.WriteString(doc)
buf.WriteString(getFieldAnnotation(field, 0))
buf.WriteString("public")
buf.WriteString(javifyType(field))
buf.WriteByte(' ')
buf.WriteString(prefix + strings.ToUpper(field.Name[0:1]) + field.Name[1:])
buf.WriteString(" =")
buf.WriteString(javifyValue(field))
buf.WriteString(";\n")
}
return indent(buf.String(), 2)
}
flattener = flattenStruct
// These template extensions are used to transmogrify C++ symbols and value literals to Java.
customExtensions := template.FuncMap{
"docblock": getDocBlock,
"nested_type_declarations": func(parent db.TypeDefinition) string {
// Look for all fields that request flattening since we should skip their emission.
flattenedTypes := make(map[db.TypeDefinition]struct{})
if structDefn, isStruct := parent.(*db.StructDefinition); isStruct {
for _, field := range structDefn.Fields {
_, flatten := field.EmitterFlags["java_flatten"]
if flatten && field.CustomType != nil {
flattenedTypes[field.CustomType] = struct{}{}
}
}
}
// Find all child types and emit them.
generate := createJavaCodeGenerator(sharedExtensions)
buf := &bytes.Buffer{}
for _, definition := range definitions {
if definition.Parent() != parent {
continue
}
_, skip := flattenedTypes[definition]
if skip {
continue
}
switch definition.(type) {
case *db.StructDefinition:
generate(buf, "Struct", definition)
case *db.EnumDefinition:
generate(buf, "Enum", definition)
}
}
return indent(buf.String(), 1)
},
"annotation": getFieldAnnotation,
"java_type": javifyType,
"java_value": javifyValue,
"flatten": func(field *db.StructField) string {
if structDefn, isStruct := field.CustomType.(*db.StructDefinition); isStruct {
return strings.TrimLeft(flattener(structDefn), " ")
}
log.Fatal("Unexpected flatten flag.")
return ""
},
"flag": func(field *db.StructField, flag string) bool {
_, exists := field.EmitterFlags[flag]
return exists
},
}
sharedExtensions = customExtensions
generate := createJavaCodeGenerator(customExtensions)
for _, definition := range definitions {
if definition.Parent() != nil {
continue
}
switch definition.(type) {
case *db.StructDefinition:
generate(file, "Struct", definition)
case *db.EnumDefinition:
generate(file, "Enum", definition)
}
}
file.WriteString("}\n")
}
// Adds one level of indention to the given multi-line string.
// Isolated newlines are intentially not indented.
func indent(src string, depth int) string {
dst := &bytes.Buffer{}
buf := bytes.NewBufferString(src)
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
codeline := scanner.Text()
if codeline != "" {
dst.WriteString(strings.Repeat(" ", depth))
dst.WriteString(scanner.Text())
}
dst.WriteByte('\n')
}
return dst.String()
}
// Wrapper for ExecuteTemplate that performs error checking. Takes an output stream, a template name
// to invoke, and a template context object.
type templateFn = func(io.Writer, string, db.TypeDefinition)
func createJavaCodeGenerator(customExtensions template.FuncMap) templateFn {
templ := template.New("beamsplitter").Funcs(customExtensions)
templ = template.Must(templ.ParseFiles("emitters/java.template"))
return func(writer io.Writer, section string, definition db.TypeDefinition) {
err := templ.ExecuteTemplate(writer, section, definition)
if err != nil {
log.Fatal(err.Error())
}
}
}

View File

@@ -2,11 +2,16 @@
{{ docblock . 1 }}public static class {{ .BaseName }} {
{{- nested_type_declarations . }}
{{- range $index, $field := .Fields}}
{{ docblock . 2 }}{{ annotation $field 2 }}public
{{ docblock . 2 }}
{{- if flag $field "java_flatten" }}
{{- flatten $field }}
{{- else}}
{{- annotation $field 2 }}public
{{- java_type $field }} {{ $field.Name }} =
{{- java_value $field}};
{{- end }}
{{- end }}
};
}
{{ end }}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -16,5 +21,5 @@
{{- range .Values}}
{{ docblock . 2 }}{{ .Name }},
{{- end }}
};
}
{{ end }}

View File

@@ -14,10 +14,10 @@
* limitations under the License.
*/
package main
package emitters
import (
"beamsplitter/parse"
db "beamsplitter/database"
"bufio"
"fmt"
"log"
@@ -27,74 +27,9 @@ import (
"text/template"
)
// Returns a templating function that automatically checks for fatal errors. The returned function
// takes an output stream, a template name to invoke, and a template context object.
func createJsCodeGenerator(namespace string) func(*os.File, string, parse.TypeDefinition) {
jsPrefix := ""
classPrefix := ""
cppPrefix := ""
if namespace != "" {
jsPrefix = namespace + "$"
classPrefix = namespace + ".prototype."
cppPrefix = namespace + "::"
}
// These template extensions are used to transmogrify C++ symbols and value literals into
// JavaScript. We mostly don't need to do anything since the parser has already done some
// massaging and verification (e.g. it removed the trailing "f" from floating point literals).
// However enums need some special care here. Emscripten bindings are flat, so our own
// convention is to use $ for the scoping delimiter, which is a legal symbol character in JS.
// However we still use . to separate the enum value from the enum type, because emscripten has
// first-class support for class enums.
customExtensions := template.FuncMap{
"qualifiedtype": func(typename string) string {
typename = strings.ReplaceAll(typename, "::", "$")
return typename
},
"flag": func(field *parse.StructField, flag string) bool {
_, exists := field.EmitterFlags[flag]
return exists
},
"qualifiedvalue": func(name string) string {
count := strings.Count(name, "::")
if count > 0 {
name = "Filament." + jsPrefix + name
}
name = strings.Replace(name, "::", "$", count-1)
name = strings.Replace(name, "::", ".", 1)
return name
},
"tstype": func(cpptype string) string {
if strings.HasPrefix(cpptype, "math::") {
return cpptype[6:]
}
switch cpptype {
case "float", "uint8_t", "uint32_t", "uint16_t":
return "number"
case "bool":
return "boolean"
case "LinearColorA":
return "float4"
case "LinearColor":
return "float3"
}
return jsPrefix + strings.ReplaceAll(cpptype, "::", "$")
},
"jsprefix": func() string { return jsPrefix },
"cprefix": func() string { return cppPrefix },
"classprefix": func() string { return classPrefix },
}
const kCodelineMarker = "The remainder of this file is generated by beamsplitter"
templ := template.New("beamsplitter").Funcs(customExtensions)
templ = template.Must(templ.ParseFiles("javascript.template"))
return func(file *os.File, section string, definition parse.TypeDefinition) {
err := templ.ExecuteTemplate(file, section, definition)
if err != nil {
log.Fatal(err.Error())
}
}
}
func emitJavaScript(definitions []parse.TypeDefinition, namespace string, outputFolder string) {
func EmitJavaScript(definitions []db.TypeDefinition, namespace string, outputFolder string) {
generate := createJsCodeGenerator(namespace)
{
path := filepath.Join(outputFolder, "jsbindings_generated.cpp")
@@ -109,7 +44,7 @@ func emitJavaScript(definitions []parse.TypeDefinition, namespace string, output
for _, definition := range definitions {
switch definition.(type) {
case *parse.StructDefinition:
case *db.StructDefinition:
generate(file, "JsBindingsStruct", definition)
}
}
@@ -128,7 +63,7 @@ func emitJavaScript(definitions []parse.TypeDefinition, namespace string, output
for _, definition := range definitions {
switch definition.(type) {
case *parse.EnumDefinition:
case *db.EnumDefinition:
generate(file, "JsEnum", definition)
}
}
@@ -148,7 +83,7 @@ func emitJavaScript(definitions []parse.TypeDefinition, namespace string, output
for _, definition := range definitions {
switch definition.(type) {
case *parse.StructDefinition:
case *db.StructDefinition:
generate(file, "JsExtension", definition)
}
}
@@ -156,7 +91,7 @@ func emitJavaScript(definitions []parse.TypeDefinition, namespace string, output
}
}
func editTypeScript(definitions []parse.TypeDefinition, namespace string, folder string) {
func EditTypeScript(definitions []db.TypeDefinition, namespace string, folder string) {
path := filepath.Join(folder, "filament.d.ts")
var codelines []string
{
@@ -196,10 +131,88 @@ func editTypeScript(definitions []parse.TypeDefinition, namespace string, folder
generate := createJsCodeGenerator(namespace)
for _, definition := range definitions {
switch definition.(type) {
case *parse.StructDefinition:
case *db.StructDefinition:
generate(file, "TsStruct", definition)
case *parse.EnumDefinition:
case *db.EnumDefinition:
generate(file, "TsEnum", definition)
}
}
}
// Returns a templating function that automatically checks for fatal errors. The returned function
// takes an output stream, a template name to invoke, and a template context object.
func createJsCodeGenerator(namespace string) func(*os.File, string, db.TypeDefinition) {
jsPrefix := ""
classPrefix := ""
cppPrefix := ""
if namespace != "" {
jsPrefix = namespace + "$"
classPrefix = namespace + ".prototype."
cppPrefix = namespace + "::"
}
// These template extensions are used to transmogrify C++ symbols and value literals into
// JavaScript. We mostly don't need to do anything since the parser has already done some
// massaging and verification (e.g. it removed the trailing "f" from floating point literals).
// However enums need some special care here. Emscripten bindings are flat, so our own
// convention is to use $ for the scoping delimiter, which is a legal symbol character in JS.
// However we still use . to separate the enum value from the enum type, because emscripten has
// first-class support for class enums.
customExtensions := template.FuncMap{
"qualifiedtype": func(typename string) string {
typename = strings.ReplaceAll(typename, "::", "$")
return typename
},
"flag": func(field *db.StructField, flag string) bool {
_, exists := field.EmitterFlags[flag]
return exists
},
"qualifiedvalue": func(name string) string {
count := strings.Count(name, "::")
if count > 0 {
name = "Filament." + jsPrefix + name
}
name = strings.Replace(name, "::", "$", count-1)
name = strings.Replace(name, "::", ".", 1)
return name
},
"tstype": func(cpptype string) string {
if strings.HasPrefix(cpptype, "math::") {
return cpptype[6:]
}
switch cpptype {
case "float", "uint8_t", "uint32_t", "uint16_t":
return "number"
case "bool":
return "boolean"
case "LinearColorA":
return "float4"
case "LinearColor":
return "float3"
}
return jsPrefix + strings.ReplaceAll(cpptype, "::", "$")
},
"jsprefix": func() string { return jsPrefix },
"cprefix": func() string { return cppPrefix },
"classprefix": func() string { return classPrefix },
"docblock": func(defn db.Documented, depth int) string {
doc := defn.GetDoc()
if doc == "" {
return ""
}
indent := strings.Repeat(" ", depth)
if strings.Count(doc, "\n") > 0 {
return strings.ReplaceAll(doc, "\n", "\n"+indent)
}
return "/**\n" + indent + " * " + doc + "\n" + indent + " */\n" + indent
},
}
templ := template.New("beamsplitter").Funcs(customExtensions)
templ = template.Must(templ.ParseFiles("emitters/javascript.template"))
return func(file *os.File, section string, definition db.TypeDefinition) {
err := templ.ExecuteTemplate(file, section, definition)
if err != nil {
log.Fatal(err.Error())
}
}
}

View File

@@ -91,8 +91,8 @@ Filament.loadGeneratedExtensions = function() {
{{- if flag $field "skip_javascript" }}
// JavaScript binding for {{$field.Name}} is not yet supported, must use default value.
{{- else }}
{{ $field.Name }}?: {{ tstype $field.TypeString }};
{{- if $field.Description}} // {{ $field.Description }}{{end}}
{{ docblock $field 1 }}
{{- $field.Name }}?: {{ tstype $field.TypeString }};
{{- end }}
{{- end }}
}

View File

@@ -14,10 +14,10 @@
* limitations under the License.
*/
package main
package emitters
import (
"beamsplitter/parse"
db "beamsplitter/database"
"fmt"
"log"
"os"
@@ -25,7 +25,7 @@ import (
"text/template"
)
func emitSerializer(definitions []parse.TypeDefinition, outputFolder string) {
func EmitSerializer(definitions []db.TypeDefinition, outputFolder string) {
// The following template extensions make it possible to generate valid C++ code with
// fewer if-then-else blocks in the template file.
customExtensions := template.FuncMap{
@@ -35,7 +35,7 @@ func emitSerializer(definitions []parse.TypeDefinition, outputFolder string) {
}
return ","
},
"flag": func(field *parse.StructField, flag string) bool {
"flag": func(field *db.StructField, flag string) bool {
_, exists := field.EmitterFlags[flag]
return exists
},
@@ -63,9 +63,9 @@ func emitSerializer(definitions []parse.TypeDefinition, outputFolder string) {
}
codegen := template.New("beamsplitter").Funcs(customExtensions)
codegen = template.Must(codegen.ParseFiles("serializer.template"))
codegen = template.Must(codegen.ParseFiles("emitters/serializer.template"))
generate := func(file *os.File, section string, definition parse.TypeDefinition) {
generate := func(file *os.File, section string, definition db.TypeDefinition) {
err := codegen.ExecuteTemplate(file, section, definition)
if err != nil {
log.Fatal(err.Error())
@@ -83,10 +83,10 @@ func emitSerializer(definitions []parse.TypeDefinition, outputFolder string) {
generate(file, "CppHeader", nil)
for _, definition := range definitions {
switch definition.(type) {
case *parse.StructDefinition:
case *db.StructDefinition:
generate(file, "CppStructReader", definition)
generate(file, "CppStructWriter", definition)
case *parse.EnumDefinition:
case *db.EnumDefinition:
generate(file, "CppEnumReader", definition)
generate(file, "CppEnumWriter", definition)
}
@@ -104,9 +104,9 @@ func emitSerializer(definitions []parse.TypeDefinition, outputFolder string) {
generate(file, "HppHeader", nil)
for _, definition := range definitions {
switch definition.(type) {
case *parse.StructDefinition:
case *db.StructDefinition:
generate(file, "HppStruct", definition)
case *parse.EnumDefinition:
case *db.EnumDefinition:
generate(file, "HppEnum", definition)
}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (C) 2022 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.
*/
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"text/template"
"beamsplitter/parse"
)
// Adds one level of indention to the given multi-line string.
// Isolated newlines are intentially not indented.
func indent(src string) string {
dst := &bytes.Buffer{}
buf := bytes.NewBufferString(src)
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
codeline := scanner.Text()
if codeline != "" {
dst.WriteString(" ")
dst.WriteString(scanner.Text())
}
dst.WriteByte('\n')
}
return dst.String()
}
// Wrapper for ExecuteTemplate that performs error checking. Takes an output stream, a template name
// to invoke, and a template context object.
type templateFn = func(io.Writer, string, parse.TypeDefinition)
func createJavaCodeGenerator(customExtensions template.FuncMap) templateFn {
templ := template.New("beamsplitter").Funcs(customExtensions)
templ = template.Must(templ.ParseFiles("java.template"))
return func(writer io.Writer, section string, definition parse.TypeDefinition) {
err := templ.ExecuteTemplate(writer, section, definition)
if err != nil {
log.Fatal(err.Error())
}
}
}
func editJava(definitions []parse.TypeDefinition, classname string, folder string) {
path := filepath.Join(folder, classname+".java")
var codelines []string
{
sourceFile, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
defer sourceFile.Close()
lineScanner := bufio.NewScanner(sourceFile)
foundMarker := false
for lineNumber := 1; lineScanner.Scan(); lineNumber++ {
codeline := lineScanner.Text()
if strings.Contains(codeline, kCodelineMarker) {
foundMarker = true
break
}
codelines = append(codelines, codeline)
}
if !foundMarker {
log.Fatal("Unable to find marker line in Java file.")
}
}
file, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
defer file.Close()
defer fmt.Println("Edited " + path)
for _, codeline := range codelines {
file.WriteString(codeline)
file.WriteString("\n")
}
file.WriteString(" // " + kCodelineMarker + "\n")
// Declare a closure variable to handle nested types.
var sharedExtensions template.FuncMap
// These template extensions are used to transmogrify C++ symbols and value literals to Java.
customExtensions := template.FuncMap{
"docblock": func(defn parse.Documented, depth int) string {
doc := defn.GetDoc()
if doc == "" {
return ""
}
indent := strings.Repeat(" ", depth)
if strings.Count(doc, "\n") > 0 {
return strings.ReplaceAll(doc, "\n", "\n"+indent)
}
return "/**\n" + indent + " * " + doc + "\n" + indent + " */\n" + indent
},
"nested_type_declarations": func(parent parse.TypeDefinition) string {
generate := createJavaCodeGenerator(sharedExtensions)
buf := &bytes.Buffer{}
for _, definition := range definitions {
if definition.Parent() != parent {
continue
}
switch definition.(type) {
case *parse.StructDefinition:
generate(buf, "Struct", definition)
case *parse.EnumDefinition:
generate(buf, "Enum", definition)
}
}
return indent(buf.String())
},
"annotation": func(field parse.StructField, depth int) string {
if _, exists := field.EmitterFlags["java_float"]; exists {
return ""
}
annotation := ""
switch {
case field.DefaultValue == "nullptr":
annotation = "@Nullable"
case field.TypeString == "math::float2":
annotation = "@NonNull @Size(min = 2)"
case field.TypeString == "math::float3" || field.TypeString == "LinearColor":
annotation = "@NonNull @Size(min = 3)"
case field.TypeString == "math::float4" || field.TypeString == "LinearColorA":
annotation = "@NonNull @Size(min = 4)"
case strings.Contains(field.DefaultValue, "::"):
annotation = "@NonNull"
default:
return ""
}
return annotation + "\n" + strings.Repeat(" ", depth)
},
"java_type": func(field parse.StructField) string {
if _, exists := field.EmitterFlags["java_float"]; exists {
return " float"
}
switch field.TypeString {
case "math::float2", "math::float3", "math::float4", "LinearColor", "LinearColorA":
return " float[]"
case "bool":
return " boolean"
case "uint8_t", "uint16_t", "uint32_t":
return " int"
}
return " " + strings.ReplaceAll(field.TypeString, "*", "")
},
"java_value": func(field parse.StructField) string {
if _, exists := field.EmitterFlags["java_float"]; exists {
arrayContents := strings.Trim(field.DefaultValue, " []")
// If we're forcing an array to be bound to a flat, then extract the first component
// and use that as the default value.
if comma := strings.Index(arrayContents, ","); comma > -1 {
return " " + arrayContents[:comma]
}
return " " + arrayContents
}
if field.DefaultValue == "nullptr" {
return " null"
}
value := strings.ReplaceAll(field.DefaultValue, "::", ".")
if field.TypeString == "float" {
value += "f"
} else if c := len(value); c > 1 && value[0] == '[' && value[c-1] == ']' {
value = "{" + value[1:c-1] + "}"
}
return " " + value
},
}
sharedExtensions = customExtensions
generate := createJavaCodeGenerator(customExtensions)
for _, definition := range definitions {
if definition.Parent() != nil {
continue
}
switch definition.(type) {
case *parse.StructDefinition:
generate(file, "Struct", definition)
case *parse.EnumDefinition:
generate(file, "Enum", definition)
}
}
file.WriteString("}\n")
}

View File

@@ -23,10 +23,12 @@ import (
"path/filepath"
"runtime"
db "beamsplitter/database"
"beamsplitter/emitters"
"beamsplitter/parse"
)
const kCodelineMarker = "The remainder of this file is generated by beamsplitter"
var CHECK_API_SYNTAX = false
func findFilamentRoot() string {
var (
@@ -43,36 +45,25 @@ func main() {
log.SetPrefix(sourceFilename + ":")
root := findFilamentRoot()
sourcePath := filepath.Join(root, "filament", "include", "filament", sourceFilename)
definitions := parse.Parse(sourcePath)
// For diagnostic purposes, this dumps out the database that was gathered from
// the parsing phase.
if len(os.Args) > 1 && os.Args[1] == "--verbose" {
for _, defn := range definitions {
switch concrete := defn.(type) {
case *parse.StructDefinition:
fmt.Println("STRUCT:", concrete.QualifiedName())
for _, field := range concrete.Fields {
fmt.Println("\t", field.TypeString, "...", field.Name, "...", field.DefaultValue)
}
case *parse.EnumDefinition:
fmt.Println(" ENUM:", concrete.QualifiedName())
for _, value := range concrete.Values {
fmt.Println("\t", value)
}
}
}
data, err := os.ReadFile(sourcePath)
if err != nil {
log.Fatal(err)
}
emitSerializer(definitions, filepath.Join(root, "libs", "viewer", "src"))
contents := string(data)
ast := parse.Parse(contents)
definitions := db.Create(ast, contents)
emitters.EmitSerializer(definitions, filepath.Join(root, "libs", "viewer", "src"))
jsfolder := filepath.Join(root, "web", "filament-js")
emitJavaScript(definitions, "View", jsfolder)
editTypeScript(definitions, "View", jsfolder)
emitters.EmitJavaScript(definitions, "View", jsfolder)
emitters.EditTypeScript(definitions, "View", jsfolder)
javafolder := filepath.FromSlash("com/google/android/filament")
javafolder = filepath.Join(root, "android/filament-android/src/main/java", javafolder)
editJava(definitions, "View", javafolder)
emitters.EditJava(definitions, "View", javafolder)
fmt.Print(`
Note that this tool does not generate bindings for setter methods on
@@ -88,4 +79,22 @@ will likely need to manually modify the following files:
- android/filament-android/src/main/java/.../View.java
- android/filament-android/src/main/cpp/View.cpp
`)
if CHECK_API_SYNTAX {
sources := []string{}
apiPath := filepath.Join(root, "filament", "include", "filament")
entries, err := os.ReadDir(apiPath)
for _, entry := range entries {
sources = append(sources, filepath.Join(apiPath, entry.Name()))
}
for _, source := range sources {
log.SetPrefix(filepath.Base(source) + ":")
data, err = os.ReadFile(source)
if err != nil {
log.Fatal(err)
}
contents = string(data)
parse.Parse(contents)
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 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.
*/
package parse
type Node interface{}
type RootNode struct {
Line int
Child *NamespaceNode
}
type NamespaceNode struct {
Line int
Name string
Children []Node
}
type ClassNode struct {
Line int
Name string
Members []Node
IsTemplate bool
}
type StructNode struct {
Line int
Name string
Members []Node
IsTemplate bool
}
type EnumNode struct {
Line int
Name string
Values []string
ValueLines []int // used to find docstring for each enum value
}
type UsingNode struct {
Line int
Name string
Rhs string
}
type AccessSpecifierNode struct {
Line int
Access string
}
type MethodNode struct {
Line int
Name string
ReturnType string
Arguments string
Body string
IsTemplate bool
}
type FieldNode struct {
Line int
Name string
Type string
Rhs string
ArrayLength int
}

View File

@@ -0,0 +1,802 @@
/*
* Copyright (C) 2022 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.
*/
package parse
import (
"errors"
"fmt"
"log"
"strings"
"unicode"
"unicode/utf8"
)
var VERBOSE_LEXER = false
var KNOWN_MACROS = []string{"UTILS_PUBLIC"}
// item represents a token or text string returned from the scanner.
type item struct {
typ itemType // The type of this item.
pos int // The starting position, in bytes, of this item in the input string.
val string // The value of this item.
line int // The line number at the start of this item.
}
func (i item) String() string {
dbg := strings.ReplaceAll(i.val, "\n", "\\n")
switch {
case i.typ == itemEOF:
return "EOF"
case i.typ == itemError:
return i.val
case i.typ > itemKeywords_:
return dbg
case len(i.val) > 30:
return dbg[:10] + "..." + dbg[len(dbg)-10:]
}
return dbg
}
// itemType identifies the type of lex items.
type itemType int
const (
itemError itemType = iota // error occurred; value is text of error
itemSimpleType // examples: `Texture* const`, `uint8_t`, `BlendMode`
itemMethodBody // unparsed blob, includes outermost { and }
itemMethodArgs // unparsed blob, includes outermost ( and )
itemTemplateArgs // unparsed blob, includes outermost < and >
itemDefaultValue // the unparsed RHS of an expression
itemIdentifier // legal C++ identifier
itemEOF
itemCloseBrace
itemColon
itemComma
itemEquals
itemOpenBrace
itemSemicolon
itemOpenBracket
itemCloseBracket
itemArrayLength
itemKeywords_ // unused enum separator
itemClass
itemConst
itemConstexpr
itemEnum
itemFriend
itemNamespace
itemNoexcept
itemPrivate
itemProtected
itemPublic
itemStruct
itemTemplate
itemUsing
)
const eof = -1
// lexer holds the state of the scanner.
type lexer struct {
input string // the entire contents of the file being scanned
items chan item // channel of scanned items
line int // 1+number of newlines seen
startLine int // start line of this item
pos int // current position in the input
start int // start position of this item
atEOF bool // we have hit the end of input
lookahead []item
}
// next returns the next rune in the input.
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.atEOF = true
return eof
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.pos += w
if r == '\n' {
l.line++
}
return r
}
// backup steps back one rune.
func (l *lexer) backup() {
if !l.atEOF && l.pos > 0 {
r, w := utf8.DecodeLastRuneInString(l.input[:l.pos])
l.pos -= w
// Correct newline count.
if r == '\n' {
l.line--
}
}
}
func (lex *lexer) backupMultiple(count int) {
for i := 0; i < count; i++ {
lex.backup()
}
}
func (lex *lexer) backupKeyword() {
lex.backup()
for unicode.IsLetter(rune(lex.input[lex.pos])) {
lex.backup()
}
lex.next()
}
// Passes an item back to the parser and moves the start pointer to catch up to the cursor.
// The start pointer marks the beginning of the next item.
// It helps to imagine a crawling worm whose tail catches up with its head in one movement.
func (l *lexer) emit(t itemType) {
text := strings.TrimSpace(l.input[l.start:l.pos])
if text == "" {
column := l.pos - l.findLineStartPos()
// Can occur when calling emit() twice in a row without "accepting" anything in between.
log.Fatalf("%d: internal error at column %d\n", l.line, column)
}
token := item{t, l.start, text, l.startLine}
if l.lookahead != nil {
if VERBOSE_LEXER {
fmt.Printf("%03d Stashing %s\n", token.line, token.String())
}
l.lookahead = append(l.lookahead, token)
} else {
if VERBOSE_LEXER {
fmt.Printf("%03d Emitting %s\n", token.line, token.String())
}
l.items <- token
}
l.start = l.pos
l.startLine = l.line
l.discardOptionalSpace()
}
// Moves the start pointer to catch up to the cursor but does not pass the item to the parser.
// This function is like emit, except that it throws the item in the trash.
func (l *lexer) discard() {
if VERBOSE_LEXER {
dbg := l.input[l.start:l.pos]
dbg = strings.ReplaceAll(dbg, "\n", "\\n")
if len(dbg) > 30 {
dbg = dbg[:10] + "..." + dbg[len(dbg)-10:]
}
fmt.Printf("%03d Trashing [[%s]]\n", l.line, dbg)
}
l.start = l.pos
l.startLine = l.line
}
func (l *lexer) acceptAny(valid string) bool {
if strings.ContainsRune(valid, l.next()) {
return true
}
l.backup()
return false
}
func (lex *lexer) acceptAlphaNumeric() bool {
next := lex.next()
if isAlphaNumeric(next) {
return true
}
lex.backup()
return false
}
func (lex *lexer) acceptPositiveInteger() bool {
next := lex.next()
if !unicode.IsDigit(next) || next == '0' {
lex.backup()
return false
}
lex.acceptRun("123456789")
return true
}
// Consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
for strings.ContainsRune(valid, l.next()) {
}
l.backup()
}
func (lex *lexer) acceptSpace() bool {
return lex.acceptAny(" \t\n")
}
func (l *lexer) acceptRune(expected rune) bool {
if l.next() == expected {
return true
}
l.backup()
return false
}
func (lex *lexer) acceptString(expectedString string) bool {
for i, c := range expectedString {
if lex.next() != c {
lex.backupMultiple(i + 1)
return false
}
}
return true
}
func (lex *lexer) acceptIdentifier() bool {
next := lex.next()
if next != '_' && !unicode.IsLetter(next) {
lex.backup()
return false
}
for isAlphaNumeric(lex.next()) {
}
lex.backup()
return true
}
func (lex *lexer) acceptKeyword(keyword string) bool {
start := lex.pos
if !lex.acceptString(keyword) {
return false
}
if lex.eof() {
return true
}
if lex.acceptAlphaNumeric() {
lex.backupMultiple(lex.pos - start)
return false
}
return true
}
func (lex *lexer) acceptKeywords(keywords []string) bool {
for _, keyword := range keywords {
if lex.acceptKeyword(keyword) {
return true
}
}
return false
}
// Accepts a blob of unparsed text up until the given terminator, which is not included.
func (lex *lexer) acceptTerminatedBlob(term rune) bool {
previous := lex.pos
for {
if lex.next() == term {
lex.backup()
return true
}
if lex.atEOF {
lex.backupMultiple(lex.pos - previous)
return false
}
}
}
// Accepts a blob of unparsed text with the given delimiters, which can be nested.
func (lex *lexer) acceptDelimitedBlob(start rune, stop rune) bool {
previous := lex.pos
next := lex.next()
if next != start {
lex.backup()
return false
}
for depth := 1; next != eof; {
switch lex.next() {
case start:
depth++
case stop:
depth--
if depth == 0 {
return true
}
}
}
lex.backupMultiple(lex.pos - previous)
return false
}
// Discards whitespace, linefeeds, comments, and C preprocessor directives.
func (lex *lexer) discardOptionalSpace() {
for {
for _, macro := range KNOWN_MACROS {
if lex.acceptKeyword(macro) {
lex.discard()
}
}
switch {
case lex.acceptSpace():
lex.acceptRun(" \n\t")
lex.discard()
case lex.acceptString("/*"):
for !lex.acceptString("*/") {
lex.next()
}
lex.discard()
case lex.acceptString("//") || lex.acceptRune('#'):
for !lex.acceptRune('\n') {
lex.next()
}
lex.discard()
default:
return
}
}
}
// Returns the next item from the input.
// Called by the parser, not in the lexing goroutine.
func (l *lexer) nextItem() item {
return <-l.items
}
// Drains the output so the lexing goroutine will exit.
// Called by the parser, not in the lexing goroutine.
func (l *lexer) drain() {
for range l.items {
}
}
func (lex *lexer) eof() bool {
return lex.pos >= len(lex.input)
}
// Creates a new scanner for the input string.
func createLexer(input string) *lexer {
l := &lexer{
input: input,
items: make(chan item),
line: 1,
startLine: 1,
}
go l.run()
return l
}
func (lex *lexer) run() {
if err := lexRoot(lex); err != nil {
column := lex.pos - lex.findLineStartPos()
log.Fatalf("%d:%d: lexer expected %s", lex.line, column, err.Error())
}
close(lex.items)
}
func (lex *lexer) findLineStartPos() int {
lineNo := 1
for pos, c := range lex.input {
if c != '\n' {
continue
}
lineNo++
if lineNo == lex.line {
return pos
}
}
return 0
}
func isAlphaNumeric(r rune) bool {
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}
// Causes the lexer to enter a special "lookahead state" in which it stashes
// the lexer state and buffers all emitted items.
func lookahead(lex *lexer, cb func(*lexer) error) bool {
if lex.lookahead != nil {
log.Fatal("Nested lookahead.")
}
lex.lookahead = make([]item, 0)
previousStart := lex.start
previousStartLine := lex.startLine
previousPos := lex.pos
previousLine := lex.line
if err := cb(lex); err == nil {
for _, item := range lex.lookahead {
if VERBOSE_LEXER {
fmt.Printf("%03d Emitting %s\n", item.line, item.String())
}
lex.items <- item
}
lex.lookahead = nil
return true
}
lex.lookahead = nil
lex.start = previousStart
lex.startLine = previousStartLine
lex.line = previousLine
lex.pos = previousPos
return false
}
// -------------------------------------------------------------------------------------------------
// The remainder of this file has one function for each nonterminal in the BNF.
// The ordering should be consistent with the grammar in the README.
// These functions are all prefixed with "lex" and return an error or nil.
// Error strings are automatically prefixed with the line number and "lexer expected ".
// In lookahead situations, the returned error might be intentionally discarded.
// -------------------------------------------------------------------------------------------------
func lexRoot(lex *lexer) error {
if lex.eof() {
return nil
}
lex.discardOptionalSpace()
if lex.acceptKeyword("namespace") {
return lexNamespace(lex)
}
return errors.New("namespace")
}
// Assumptions: cursor is just past the namespace keyword
// Directly emits: keyword, the optional identifier, opening and closing braces
// Indirectly emits: content of the namespace
func lexNamespace(lex *lexer) error {
lex.emit(itemNamespace)
if lex.acceptIdentifier() {
lex.emit(itemIdentifier)
}
if !lex.acceptRune('{') {
return errors.New("{")
}
lex.emit(itemOpenBrace)
for !lex.acceptRune('}') {
if err := lexBlock(lex); err != nil {
return err
}
}
lex.emit(itemCloseBrace)
return nil
}
// Assumptions: cursor is just before one of the block keywords
// Directly emits: struct or class keywords
// Indirectly emits: the block keyword, its braces, contents, semicolons
func lexBlock(lex *lexer) error {
switch {
case lex.acceptKeyword("namespace"):
return lexNamespace(lex)
case lex.acceptKeyword("template"):
lex.emit(itemTemplate)
if !lex.acceptDelimitedBlob('<', '>') {
return errors.New("template arguments")
}
lex.emit(itemTemplateArgs)
if !lex.acceptKeywords([]string{"class", "struct"}) {
return errors.New("class or struct")
}
lex.backupKeyword()
return lexBlock(lex)
case lex.acceptKeyword("struct"):
lex.emit(itemStruct)
if lookahead(lex, lexForwardDeclaration) {
return nil
}
return lexStruct(lex)
case lex.acceptKeyword("class"):
lex.emit(itemClass)
if lookahead(lex, lexForwardDeclaration) {
return nil
}
return lexClass(lex)
case lex.acceptKeyword("using"):
return lexUsing(lex)
case lex.acceptKeyword("enum"):
return lexEnum(lex)
}
return errors.New("namespace, struct, class, enum, or using")
}
func lexForwardDeclaration(lex *lexer) error {
if !lex.acceptIdentifier() {
return errors.New("identifier")
}
lex.emit(itemIdentifier)
if !lex.acceptRune(';') {
return errors.New("; after forward declaration")
}
lex.emit(itemSemicolon)
return nil
}
// Assumptions: cursor is just past the class keyword
// Directly emits: identifier, opening and closing braces, semicolon
// Indirectly emits: content of the struct
func lexClass(lex *lexer) error {
if !lex.acceptIdentifier() {
return errors.New("does not allow anonymous classes")
}
lex.emit(itemIdentifier)
if lex.acceptRune(':') {
lex.emit(itemColon)
if lex.acceptKeyword("public") {
lex.emit(itemPublic)
}
if err := lexSimpleType(lex); err != nil {
return err
}
}
if !lex.acceptRune('{') {
return errors.New("{ before class body")
}
lex.emit(itemOpenBrace)
if err := lexStructBody(lex); err != nil {
return err
}
lex.emit(itemCloseBrace)
if !lex.acceptRune(';') {
return errors.New(": after class")
}
lex.emit(itemSemicolon)
return nil
}
// Assumptions: cursor is just past the struct keyword
// Directly emits: the optional identifier, opening and closing braces, semicolon
// Indirect emits: content of the struct
func lexStruct(lex *lexer) error {
if lex.acceptIdentifier() {
lex.emit(itemIdentifier)
}
if !lex.acceptRune('{') {
return errors.New("{ before struct body")
}
lex.emit(itemOpenBrace)
if err := lexStructBody(lex); err != nil {
return err
}
lex.emit(itemCloseBrace)
if !lex.acceptRune(';') {
return errors.New("; after struct body")
}
lex.emit(itemSemicolon)
return nil
}
// Assumptions: cursor is just past the enum keyword
// Directly emits: all parts of the enum, including the trailing semicolon
func lexEnum(lex *lexer) error {
lex.emit(itemEnum)
if !lex.acceptKeyword("class") {
return errors.New("class-style enum")
}
lex.emit(itemClass)
if !lex.acceptIdentifier() {
return errors.New("valid identifier (anonymous enums are not supported)")
}
lex.emit(itemIdentifier)
if lex.acceptRune(':') {
lex.emit(itemColon)
if err := lexSimpleType(lex); err != nil {
return err
}
}
if !lex.acceptRune('{') {
return errors.New("{ before enum definition")
}
lex.emit(itemOpenBrace)
if !lex.acceptIdentifier() {
return errors.New("at least one value in the enum")
}
lex.emit(itemIdentifier)
for !lex.acceptRune('}') {
if !lex.acceptRune(',') {
return errors.New(", between enum values")
}
lex.emit(itemComma)
if lex.acceptRune('}') {
break
}
if !lex.acceptIdentifier() {
return errors.New("valid identifier in enum")
}
lex.emit(itemIdentifier)
}
lex.emit(itemCloseBrace)
if !lex.acceptRune(';') {
return errors.New("; after enum definition")
}
lex.emit(itemSemicolon)
return nil
}
func lexUsing(lex *lexer) error {
lex.emit(itemUsing)
if !lex.acceptIdentifier() {
return errors.New("valid identifier in type alias")
}
lex.emit(itemIdentifier)
if !lex.acceptRune('=') {
return errors.New("= in type alias")
}
lex.emit(itemEquals)
if err := lexSimpleType(lex); err != nil {
return err
}
if !lex.acceptRune(';') {
return errors.New("; after type alias")
}
lex.emit(itemSemicolon)
return nil
}
// Assumptions: cursor is just past the opening brace of a struct or class
// Directly emits: nothing
// Indirect emits: entire content of the struct or class, but not the outer braces
func lexStructBody(lex *lexer) error {
accessKeywords := []string{"public", "private", "protected"}
blockKeywords := []string{"class", "struct", "enum", "using", "template", "namespace"}
for {
switch {
case lex.acceptRune('}'):
return nil
case lex.acceptKeywords(accessKeywords):
lex.backupKeyword()
if err := lexAccessSpecifier(lex); err != nil {
return err
}
case lex.acceptKeywords(blockKeywords):
lex.backupKeyword()
if err := lexBlock(lex); err != nil {
return err
}
default:
if lookahead(lex, lexMethod) {
continue
}
if err := lexField(lex); err != nil {
return err
}
}
}
}
// Assumptions: cursor is just before one of the access keywords
// Directly emits: the access keyword and the colon
func lexAccessSpecifier(lex *lexer) error {
switch {
case lex.acceptKeyword("public"):
lex.emit(itemPublic)
case lex.acceptKeyword("protected"):
lex.emit(itemProtected)
case lex.acceptKeyword("private"):
lex.emit(itemPrivate)
default:
return errors.New("legal access specifier")
}
if !lex.acceptRune(':') {
return errors.New(": after access specifier")
}
lex.emit(itemColon)
return nil
}
// Assumptions: cursor is just before a method declaration or implementation
// Directly emits: entire content of the method declaration or implementation
func lexMethod(lex *lexer) error {
if lex.acceptKeyword("template") {
lex.emit(itemTemplate)
if !lex.acceptDelimitedBlob('<', '>') {
return errors.New("template arguments")
}
lex.emit(itemTemplateArgs)
}
if lex.acceptKeyword("friend") {
lex.emit(itemFriend)
}
if lex.acceptKeyword("constexpr") {
lex.emit(itemConstexpr)
}
if err := lexSimpleType(lex); err != nil {
return err
}
if !lex.acceptIdentifier() {
return errors.New("valid identifier")
}
lex.emit(itemIdentifier)
if !lex.acceptDelimitedBlob('(', ')') {
return errors.New("function arguments")
}
lex.emit(itemMethodArgs)
if lex.acceptKeyword("const") {
lex.emit(itemConst)
}
if lex.acceptKeyword("noexcept") {
lex.emit(itemNoexcept)
}
if lex.acceptRune(';') {
lex.emit(itemSemicolon)
return nil
}
if !lex.acceptDelimitedBlob('{', '}') {
return errors.New("function body or ;")
}
lex.emit(itemMethodBody)
return nil
}
// Assumptions: cursor is just before a data field declaration in a class or struct
// Directly emits: entire content of the method declaration or implementation
func lexField(lex *lexer) error {
if err := lexSimpleType(lex); err != nil {
return err
}
if !lex.acceptIdentifier() {
return errors.New("valid identifier")
}
lex.emit(itemIdentifier)
if lex.acceptRune('[') {
lex.emit(itemOpenBracket)
if !lex.acceptPositiveInteger() {
return errors.New("positive integer")
}
lex.emit(itemArrayLength)
if !lex.acceptRune(']') {
return errors.New("valid array length")
}
lex.emit(itemCloseBracket)
}
if lex.acceptRune('=') {
lex.emit(itemEquals)
if !lex.acceptTerminatedBlob(';') {
return errors.New("right-hand side of assignment terminated by ;")
}
lex.emit(itemDefaultValue)
}
if !lex.acceptRune(';') {
return errors.New("; after field")
}
lex.emit(itemSemicolon)
return nil
}
// For now, SimpleType is a very restrictive subset of the C++ type expression language. It should
// not contain parentheses or commas, so C callbacks are not allowed unless you alias them first.
// Basically, a type is a bag of tokens that must include exactly one valid C identifier, along with
// a mix of spaces, "*", "&", "::", "const", "<", ">".
//
// Assumptions: cursor is just before a type identifier
// Directly emits: itemSimpleType
func lexSimpleType(lex *lexer) error {
encounteredIdentifier := false
for {
switch {
case lex.acceptString("::"), lex.acceptRune('<'):
encounteredIdentifier = false
continue
case lex.acceptAny("*& >\t\n"):
continue
case lex.acceptKeyword("const"):
continue
case encounteredIdentifier:
lex.emit(itemSimpleType)
return nil
case lex.acceptIdentifier():
encounteredIdentifier = true
default:
return errors.New("valid type identifier")
}
}
}

View File

@@ -17,424 +17,291 @@
package parse
import (
"bufio"
"log"
"os"
"regexp"
"strings"
"strconv"
)
// Consumes a C++ header file and produces a type database.
func Parse(sourcePath string) []TypeDefinition {
sourceFile, err := os.Open(sourcePath)
if err != nil {
log.Fatal(err)
// Consumes the entire content of a C++ header file and produces an abstract syntax tree.
func Parse(contents string) *RootNode {
lexer := createLexer(contents)
return parseRoot(lexer)
}
// Assumes that we have just consumed the open brace from the lexer.
// This consumes all lexemes in the entire struct, including the outer end brace.
// It does not consume anything after the end brace.
func parseStructBody(lex *lexer) []Node {
var members []Node
append := func(node Node) {
members = append(members, node)
}
defer sourceFile.Close()
// In the first pass, gather all block-style comments.
context := parserContext{}
context.commentBlocks = gatherCommentBlocks(sourceFile)
sourceFile.Seek(0, 0)
// In the second pass, pry apart each C++ codeline.
lineScanner := bufio.NewScanner(sourceFile)
for lineNumber := 1; lineScanner.Scan(); lineNumber++ {
context.scanCppCodeline(lineScanner.Text(), lineNumber)
}
if err := lineScanner.Err(); err != nil {
log.Fatal(err)
}
context.addTypeQualifiers()
return context.definitions
}
type TypeDefinition interface {
BaseName() string
QualifiedName() string
Parent() TypeDefinition
}
type StructField struct {
TypeString string
Name string
DefaultValue string
Description string
LineNumber int
EmitterFlags map[string]struct{}
CustomType TypeDefinition
}
type StructDefinition struct {
name string
qualifier string
Fields []StructField
Description string
parent TypeDefinition
}
func (defn StructDefinition) BaseName() string { return defn.name }
func (defn StructDefinition) QualifiedName() string { return defn.qualifier + defn.name }
func (defn StructDefinition) Parent() TypeDefinition { return defn.parent }
type EnumValue struct {
Description string
Name string
}
type EnumDefinition struct {
name string
qualifier string
Values []EnumValue
Description string
parent TypeDefinition
}
func (defn EnumDefinition) BaseName() string { return defn.name }
func (defn EnumDefinition) QualifiedName() string { return defn.qualifier + defn.name }
func (defn EnumDefinition) Parent() TypeDefinition { return defn.parent }
type Documented interface{ GetDoc() string }
func (defn EnumDefinition) GetDoc() string { return defn.Description }
func (defn StructDefinition) GetDoc() string { return defn.Description }
func (field StructField) GetDoc() string { return field.Description }
func (value EnumValue) GetDoc() string { return value.Description }
type generalScope struct{}
type scope interface {
BaseName() string
QualifiedName() string
Parent() TypeDefinition
}
func (defn generalScope) BaseName() string { return "" }
func (defn generalScope) QualifiedName() string { return "" }
func (defn generalScope) Parent() TypeDefinition { return nil }
type parserContext struct {
definitions []TypeDefinition
stack []scope
insideComment bool
commentBlocks map[int]string
cppTokenizer *regexp.Regexp
floatMatcher *regexp.Regexp
vectorMatcher *regexp.Regexp
fieldParser *regexp.Regexp
fieldDescParser *regexp.Regexp
customFlagFinder *regexp.Regexp
}
// https://github.com/google/re2/wiki/Syntax
func (context *parserContext) compileRegexps() {
context.cppTokenizer = regexp.MustCompile(`((?:/\*)|(?:\*/)|(?:;)|(?://)|(?:\})|(?:\{))`)
context.floatMatcher = regexp.MustCompile(`(\-?[0-9]+\.[0-9]*)f?`)
context.vectorMatcher = regexp.MustCompile(`\{(\s*\-?[0-9\.]+\s*(,\s*\-?[0-9\.]+\s*){1,})\}`)
context.customFlagFinder = regexp.MustCompile(`\s*\%codegen_([a-zA-Z0-9_]+)\%\s*`)
const kFieldType = `(?P<type>.*)`
const kFieldName = `(?P<name>[A-Za-z0-9_]+)`
const kFieldValue = `(?P<value>(.*?))`
const kFieldDesc = `(?://\s*\!\<\s*(?P<description>.*))?`
context.fieldParser = regexp.MustCompile(
`^\s*` + kFieldType + `\s+` + kFieldName + `\s*=\s*` + kFieldValue + `\s*;\s*` + kFieldDesc)
context.fieldDescParser = regexp.MustCompile(`(?://\s*\!\<\s*(.*))`)
}
// Creates a mapping from line numbers to strings, where the strings are entire block comments
// and the line numbers correspond to the last line of each block comment.
func gatherCommentBlocks(sourceFile *os.File) map[int]string {
comments := make(map[int]string)
scanner := bufio.NewScanner(sourceFile)
var comment = ""
var indention = 0
for lineNumber := 1; scanner.Scan(); lineNumber++ {
codeline := scanner.Text()
if strings.Contains(codeline, `/**`) {
indention = strings.Index(codeline, `/**`)
comment = codeline[indention:] + "\n"
continue
// Assumes that we have just consumed the end paren in the argument list.
parseMethod := func(name, returns, args item, isTemplate bool) *MethodNode {
item := lex.nextItem()
for item.typ == itemConst || item.typ == itemNoexcept {
item = lex.nextItem()
}
if comment != "" {
if len(codeline) > indention {
codeline = codeline[indention:]
}
comment += codeline + "\n"
if strings.Contains(codeline, `*/`) {
comments[lineNumber] = comment
comment = ""
}
for item.typ == itemConst || item.typ == itemNoexcept {
item = lex.nextItem()
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
return comments
}
func (context parserContext) generateQualifier() string {
qualifier := ""
for _, defn := range context.stack {
if defn.BaseName() != "" {
qualifier = qualifier + defn.BaseName() + "::"
method := &MethodNode{
Line: item.line,
Name: name.val,
ReturnType: returns.val,
Arguments: args.val,
Body: "",
IsTemplate: isTemplate,
}
}
return qualifier
}
func (context parserContext) findParent() TypeDefinition {
for i := len(context.stack) - 1; i >= 0; i-- {
switch defn := context.stack[i].(type) {
case *StructDefinition, *EnumDefinition:
var result TypeDefinition = defn
return result
switch item.typ {
case itemMethodBody:
method.Body = item.val
case itemSemicolon:
default:
panic(lex, item)
return nil
}
return method
}
return nil
}
// Annotates struct fields that have custom types (i.e. enums or structs).
func (context parserContext) addTypeQualifiers() {
typeMap := make(map[string]TypeDefinition)
for _, defn := range context.definitions {
typeMap[defn.QualifiedName()] = defn
}
for _, defn := range context.definitions {
structDefn, isStruct := defn.(*StructDefinition)
if !isStruct {
continue
}
for fieldIndex, field := range structDefn.Fields {
// Extract the namespace prefix (if any) explicitly specified for this field type.
var namespace string
localTypeName := field.TypeString
if index := strings.LastIndex(field.TypeString, "::"); index > -1 {
namespace = field.TypeString[:index]
localTypeName = field.TypeString[index+2:]
}
// Prepend additional qualifiers to the type string by searching upward through
// the current namespace hierarchy, and looking for a match.
mutable := &structDefn.Fields[fieldIndex]
for ancestor := defn; ; ancestor = ancestor.Parent() {
var qualified string
if namespace != "" && strings.HasSuffix(ancestor.QualifiedName(), namespace) {
qualified = ancestor.QualifiedName() + "::" + localTypeName
} else {
qualified = ancestor.QualifiedName() + "::" + field.TypeString
}
if fieldType, found := typeMap[qualified]; found {
mutable.TypeString = qualified
mutable.CustomType = fieldType
break
}
if ancestor.Parent() == nil {
if fieldType, found := typeMap[field.TypeString]; found {
mutable.CustomType = fieldType
}
break
}
}
if mutable.CustomType == nil {
continue
}
// Prepend additional qualifiers to the value string if it is a known enum.
var fieldType TypeDefinition = structDefn.Fields[fieldIndex].CustomType
enumDefn, isEnum := fieldType.(*EnumDefinition)
if isEnum {
selectedEnum := field.DefaultValue
if index := strings.LastIndex(field.DefaultValue, "::"); index > -1 {
selectedEnum = field.DefaultValue[index+2:]
}
mutable.DefaultValue = enumDefn.QualifiedName() + "::" + selectedEnum
}
}
}
}
// Validates and transforms the RHS of an assignment.
// For vectors, this converts curly braces into square brackets.
func (context parserContext) distillValue(cppvalue string, lineNumber int) string {
cppvalue = strings.TrimSpace(cppvalue)
// Remove trailing "f" from floats, which isn't allowed in JavaScript.
if context.floatMatcher.MatchString(cppvalue) {
cppvalue = context.floatMatcher.ReplaceAllString(cppvalue, "$1")
}
// There are many ways to declare vector values (multi-arg constructor, single-arg
// constructor, curly braces with type, curly braces without type), so just poop out if
// the syntax is anything other than "curly braces without type".
if strings.Contains(cppvalue, "math::") || strings.Contains(cppvalue, "Color") {
log.Fatalf("%d: vectors must have the form {x, y ...}", lineNumber)
}
// Assume it's a vector if there's a curly brace.
if strings.Contains(cppvalue, "{") {
if context.vectorMatcher.MatchString(cppvalue) {
cppvalue = context.vectorMatcher.ReplaceAllString(cppvalue, "[$1]")
} else {
log.Fatalf("%d: vectors must have the form {x, y ...}", lineNumber)
}
}
if len(cppvalue) == 0 {
log.Fatalf("%d: empty value specified", lineNumber)
}
return cppvalue
}
func (context *parserContext) scanCppCodeline(codeline string, lineNumber int) {
if context.cppTokenizer == nil {
context.compileRegexps()
}
codeline = context.cppTokenizer.ReplaceAllString(codeline, " $1 ")
scanner := bufio.NewScanner(strings.NewReader(codeline))
scanner.Split(bufio.ScanWords)
inPlaceDefinition := ""
scanStructField := func(defn *StructDefinition, firstWord string) {
var field = StructField{
LineNumber: lineNumber,
}
// Extract all custom flags into a string set. These are special backend-specific directives
// delimited by percent signs, e.g. %codegen_skip_javascript%
if matches := context.customFlagFinder.FindAllStringSubmatch(codeline, -1); matches != nil {
field.EmitterFlags = make(map[string]struct{}, len(matches))
for _, flag := range matches {
field.EmitterFlags[flag[1]] = struct{}{}
}
}
codeline = context.customFlagFinder.ReplaceAllString(codeline, "")
// Normally when we're inside a struct, the first word on each codeline is the field type,
// and the second word is the field name. However if a nested struct is defined, then the
// type is potentially anonymous and the first word is the field name.
if !scanner.Scan() {
log.Fatalf("%d: bad struct field", lineNumber)
}
// Check if this field type has an in place definition. For example:
// struct OuterType {
// int foo;
// struct Baz { int bar } baz;
// };
// In the above example, inPlaceDefinition == Baz.
if inPlaceDefinition != "" {
field.TypeString = inPlaceDefinition
field.Name = firstWord
defn.Fields = append(defn.Fields, field)
return
}
if !context.fieldParser.MatchString(codeline) {
log.Fatalf("%d: unexpected form in struct field declaration", lineNumber)
}
// To make the regex usage somewhat readable, extract the named subgroups into a map rather
// than referring to each result by index.
subexpList := context.fieldParser.FindStringSubmatch(codeline)
subexpMap := make(map[string]string)
for i, name := range context.fieldParser.SubexpNames() {
if i != 0 && name != "" {
subexpMap[name] = subexpList[i]
}
}
field.TypeString = subexpMap["type"]
field.Name = subexpMap["name"]
field.Description = subexpMap["description"]
field.DefaultValue = context.distillValue(subexpMap["value"], lineNumber)
defn.Fields = append(defn.Fields, field)
}
for scanner.Scan() {
depth := len(context.stack) - 1
token := scanner.Text()
for item := lex.nextItem(); item.typ != itemCloseBrace; item = lex.nextItem() {
switch {
case token == "//":
return
case token == "/*":
context.insideComment = true
case token == "*/":
if !context.insideComment {
log.Fatalf("%d: strange comment", lineNumber)
case item.val == "constexpr", item.val == "friend":
// do nothing for these annotations
case item.val == "enum":
append(parseEnum(lex))
case item.val == "struct":
append(parseStruct(lex))
case item.val == "class":
append(parseClass(lex))
case item.val == "namespace":
append(parseNamespace(lex))
case item.val == "using":
append(parseUsing(lex))
case item.val == "public", item.val == "private", item.val == "protected":
expect(lex, itemColon)
append(&AccessSpecifierNode{
Line: item.line,
Access: item.val,
})
case item.typ == itemCloseBrace:
break
case item.typ == itemSimpleType:
name := expect(lex, itemIdentifier)
nextItem := lex.nextItem()
arrayLength := 0
if nextItem.typ == itemOpenBracket {
arrayLength, _ = strconv.Atoi(expect(lex, itemArrayLength).val)
expect(lex, itemCloseBracket)
nextItem = lex.nextItem()
}
context.insideComment = false
case context.insideComment:
// Do nothing.
case token == ";":
// Do nothing.
case token == "{":
context.stack = append(context.stack, &generalScope{})
case token == "}":
if depth < 0 {
log.Fatalf("%d: bizarre nesting", lineNumber)
switch nextItem.typ {
case itemMethodArgs:
parseMethod(name, item, nextItem, false)
case itemSemicolon:
append(&FieldNode{
Line: item.line,
Name: name.val,
Type: item.val,
Rhs: "",
ArrayLength: arrayLength,
})
case itemEquals:
rhs := expect(lex, itemDefaultValue)
expect(lex, itemSemicolon)
append(&FieldNode{
Line: item.line,
Name: name.val,
Type: item.val,
Rhs: rhs.val,
ArrayLength: arrayLength,
})
}
switch defn := context.stack[depth].(type) {
case *StructDefinition, *EnumDefinition:
inPlaceDefinition = defn.BaseName()
context.definitions = append(context.definitions, defn)
}
context.stack = context.stack[:depth]
case token == "struct":
if !scanner.Scan() {
log.Fatalf("%d: bizarre struct", lineNumber)
}
if !strings.Contains(codeline, "{") || strings.Contains(codeline, "}") {
log.Fatalf("%d: bad formatting", lineNumber)
}
stackEntry := StructDefinition{
name: scanner.Text(),
qualifier: context.generateQualifier(),
Description: context.commentBlocks[lineNumber-1],
parent: context.findParent(),
}
context.stack = append(context.stack, &stackEntry)
return
case token == "enum":
if !scanner.Scan() || scanner.Text() != "class" || !scanner.Scan() {
log.Fatalf("%d: bad enum", lineNumber)
}
if !strings.Contains(codeline, "{") || strings.Contains(codeline, "}") {
log.Fatalf("%d: bad formatting", lineNumber)
}
stackEntry := EnumDefinition{
name: scanner.Text(),
qualifier: context.generateQualifier(),
Description: context.commentBlocks[lineNumber-1],
parent: context.findParent(),
}
context.stack = append(context.stack, &stackEntry)
return
case depth > 0:
switch defn := context.stack[depth].(type) {
case *StructDefinition:
scanStructField(defn, token)
case item.typ == itemTemplate:
expect(lex, itemTemplateArgs)
returns := expect(lex, itemSimpleType)
name := expect(lex, itemIdentifier)
args := expect(lex, itemMethodArgs)
append(parseMethod(name, returns, args, true))
default:
panic(lex, item)
}
}
return members
}
// Assumes that we have just consumed the "class" keyword from the lexer.
// Consumes everything up to (and including) the trailing semicolon.
func parseClass(lex *lexer) *ClassNode {
name := expect(lex, itemIdentifier)
item := lex.nextItem()
if item.typ == itemSemicolon {
// We don't have an AST node for forward declarations, just skip it.
return nil
}
if item.typ == itemColon {
// Only one base class is allowed.
item = lex.nextItem()
if item.typ == itemPublic {
item = lex.nextItem()
}
expect(lex, itemSimpleType)
item = lex.nextItem()
}
if item.typ != itemOpenBrace {
panic(lex, item)
}
members := parseStructBody(lex)
expect(lex, itemSemicolon)
return &ClassNode{
Line: name.line,
Name: name.val,
Members: members,
}
}
// Assumes that we have just consumed the "struct" keyword from the lexer.
// Consumes everything up to (and including) the trailing semicolon.
func parseStruct(lex *lexer) *StructNode {
name := expect(lex, itemIdentifier)
item := lex.nextItem()
if item.typ == itemSemicolon {
// We don't have an AST node for forward declarations, just skip it.
return nil
}
if item.typ != itemOpenBrace {
panic(lex, item)
}
members := parseStructBody(lex)
expect(lex, itemSemicolon)
return &StructNode{
Line: name.line,
Name: name.val,
Members: members,
}
}
// Assumes that we have just consumed the "enum" keyword from the lexer.
// Consumes everything up to (and including) the trailing semicolon.
func parseEnum(lex *lexer) *EnumNode {
expect(lex, itemClass)
name := expect(lex, itemIdentifier)
item := lex.nextItem()
if item.typ == itemColon {
expect(lex, itemSimpleType)
item = lex.nextItem()
}
if item.typ != itemOpenBrace {
panic(lex, item)
}
firstVal := expect(lex, itemIdentifier)
node := &EnumNode{
Name: name.val,
Line: name.line,
Values: []string{firstVal.val},
ValueLines: []int{firstVal.line},
}
for item = lex.nextItem(); item.typ != itemCloseBrace; {
if item.typ != itemComma {
panic(lex, item)
}
item = lex.nextItem()
if item.typ == itemCloseBrace {
break
}
if item.typ != itemIdentifier {
panic(lex, item)
}
node.Values = append(node.Values, item.val)
node.ValueLines = append(node.ValueLines, item.line)
item = lex.nextItem()
}
expect(lex, itemSemicolon)
return node
}
// Assumes that we have just consumed the "using" keyword from the lexer.
// Consumes everything up to (and including) the trailing semicolon.
func parseUsing(lex *lexer) *UsingNode {
name := expect(lex, itemIdentifier)
expect(lex, itemEquals)
rhs := expect(lex, itemSimpleType)
expect(lex, itemSemicolon)
return &UsingNode{name.line, name.val, rhs.val}
}
// Assumes that we have just consumed the "namespace" keyword from the lexer.
// Consumes everything up to (and including) the closing brace.
func parseNamespace(lex *lexer) *NamespaceNode {
name := expect(lex, itemIdentifier)
expect(lex, itemOpenBrace)
ns := &NamespaceNode{name.line, name.val, nil}
item := lex.nextItem()
// Filter out nil nodes (e.g. forward declarations)
// Note that checking for nil is tricky due to a classic Go gotcha.
append := func(child Node) {
switch concrete := child.(type) {
case *StructNode:
if concrete == nil {
return
case *EnumDefinition:
if strings.Contains(codeline, "=") {
log.Fatalf("%d: custom values are not allowed", lineNumber)
}
value := EnumValue{
Name: strings.Trim(token, ","),
}
if matches := context.fieldDescParser.FindStringSubmatch(codeline); matches != nil {
value.Description = matches[1]
}
defn.Values = append(defn.Values, value)
}
case *ClassNode:
if concrete == nil {
return
}
}
ns.Children = append(ns.Children, child)
}
if err := scanner.Err(); err != nil {
log.Fatalf("%d: %s", lineNumber, err)
for ; item.typ != itemCloseBrace; item = lex.nextItem() {
switch item.typ {
case itemTemplate:
expect(lex, itemTemplateArgs)
switch lex.nextItem().typ {
case itemClass:
node := parseClass(lex)
node.IsTemplate = true
append(node)
case itemStruct:
node := parseStruct(lex)
node.IsTemplate = true
append(node)
default:
panic(lex, item)
}
case itemClass:
append(parseClass(lex))
case itemStruct:
append(parseStruct(lex))
case itemEnum:
append(parseEnum(lex))
case itemUsing:
append(parseUsing(lex))
case itemNamespace:
append(parseNamespace(lex))
default:
panic(lex, item)
}
}
return ns
}
func parseRoot(lex *lexer) *RootNode {
expect(lex, itemNamespace)
ns := parseNamespace(lex)
return &RootNode{0, ns}
}
func expect(lex *lexer, expectedType itemType) item {
item := lex.nextItem()
if item.typ != expectedType {
panic(lex, item)
}
return item
}
func panic(lex *lexer, unexpected item) {
lex.drain()
// Very useful local hack: change this to Panicf to see a call stack.
log.Fatalf("%d: parser sees unexpected lexeme %s", unexpected.line, unexpected.String())
}

View File

@@ -182,18 +182,18 @@ bool MeshWriter::serialize(ostream& out, Mesh& mesh) {
header.strideTangents = sizeof(Vertex);
header.strideColor = sizeof(Vertex);
header.strideUV0 = sizeof(Vertex);
header.strideUV1 = numeric_limits<uint32_t>::max();;
header.strideUV1 = numeric_limits<uint32_t>::max();
} else {
header.offsetPosition = 0;
header.offsetTangents = mesh.vertexCount * sizeof(Vertex::position);
header.offsetColor = header.offsetTangents + mesh.vertexCount * sizeof(Vertex::tangents);
header.offsetUV0 = header.offsetColor + mesh.vertexCount * sizeof(Vertex::color);
header.offsetUV1 = numeric_limits<uint32_t>::max();;
header.offsetUV1 = numeric_limits<uint32_t>::max();
header.stridePosition = 0;
header.strideTangents = 0;
header.strideColor = 0;
header.strideUV0 = 0;
header.strideUV1 = numeric_limits<uint32_t>::max();;
header.strideUV1 = numeric_limits<uint32_t>::max();
if (hasUV1) {
header.offsetUV1 = header.offsetUV0 + mesh.vertexCount * sizeof(Vertex::uv0);
header.strideUV1 = 0;

View File

@@ -1756,15 +1756,15 @@ int main(int argc, char* argv[]) {
std::cout << "Rec.709 to LMSR matrix (4x3, column major):" << std::endl;
std::cout << std::fixed << std::setprecision(6) << l.r << ", ";
std::cout << std::fixed << std::setprecision(6) << l.g << ", ";
std::cout << std::fixed << std::setprecision(6) << l.b << ", ";;
std::cout << std::fixed << std::setprecision(6) << l.b << ", ";
std::cout << std::endl;
std::cout << std::fixed << std::setprecision(6) << m.r << ", ";
std::cout << std::fixed << std::setprecision(6) << m.g << ", ";
std::cout << std::fixed << std::setprecision(6) << m.b << ", ";;
std::cout << std::fixed << std::setprecision(6) << m.b << ", ";
std::cout << std::endl;
std::cout << std::fixed << std::setprecision(6) << s.r << ", ";
std::cout << std::fixed << std::setprecision(6) << s.g << ", ";
std::cout << std::fixed << std::setprecision(6) << s.b << ", ";;
std::cout << std::fixed << std::setprecision(6) << s.b << ", ";
std::cout << std::endl;
std::cout << std::fixed << std::setprecision(6) << r.r << ", ";
std::cout << std::fixed << std::setprecision(6) << r.g << ", ";

View File

@@ -4,8 +4,8 @@ Filament.loadGeneratedExtensions = function() {
Filament.View.prototype.setDynamicResolutionOptionsDefaults = function(overrides) {
const options = {
minScale: [ 0.5, 0.5 ],
maxScale: [ 1.0, 1.0 ],
minScale: [0.5, 0.5],
maxScale: [1.0, 1.0],
sharpness: 0.9,
enabled: false,
homogeneousScaling: false,
@@ -45,7 +45,7 @@ Filament.loadGeneratedExtensions = function() {
maximumOpacity: 1.0,
height: 0.0,
heightFalloff: 1.0,
color: [ 0.5, 0.5, 0.5 ],
color: [0.5, 0.5, 0.5],
density: 0.1,
inScatteringStart: 0.0,
inScatteringSize: -1.0,
@@ -76,7 +76,7 @@ Filament.loadGeneratedExtensions = function() {
midPoint: 0.5,
roundness: 0.5,
feather: 0.5,
color: [ 0.0, 0.0, 0.0, 1.0 ],
color: [0.0, 0.0, 0.0, 1.0],
enabled: false,
};
return Object.assign(options, overrides);
@@ -95,7 +95,7 @@ Filament.loadGeneratedExtensions = function() {
shadowDistance: 0.3,
contactDistanceMax: 1.0,
intensity: 0.8,
lightDirection: [ 0, -1, 0 ],
lightDirection: [ 0, -1, 0 ],
depthBias: 0.01,
depthSlopeBias: 0.01,
sampleCount: 4,

View File

@@ -598,6 +598,7 @@ export class gltfio$FilamentInstance {
export class gltfio$Animator {
public applyAnimation(index: number): void;
public applyCrossFade(previousAnimIndex: number, previousAnimTime: number, alpha: number): void;
public updateBoneMatrices(): void;
public resetBoneMatrices(): void;
public getAnimationCount(): number;
@@ -1088,11 +1089,36 @@ export enum View$BlendMode {
*
*/
export interface View$DynamicResolutionOptions {
minScale?: float2; // minimum scale factors in x and y
maxScale?: float2; // maximum scale factors in x and y
sharpness?: number; // sharpness when QualityLevel::MEDIUM or higher is used [0 (disabled), 1 (sharpest)]
enabled?: boolean; // enable or disable dynamic resolution
homogeneousScaling?: boolean; // set to true to force homogeneous scaling
/**
* minimum scale factors in x and y
*/
minScale?: float2;
/**
* maximum scale factors in x and y
*/
maxScale?: float2;
/**
* sharpness when QualityLevel::MEDIUM or higher is used [0 (disabled), 1 (sharpest)]
*/
sharpness?: number;
/**
* enable or disable dynamic resolution
*/
enabled?: boolean;
/**
* set to true to force homogeneous scaling
*/
homogeneousScaling?: boolean;
/**
* Upscaling quality
* LOW: bilinear filtered blit. Fastest, poor quality
* MEDIUM: AMD FidelityFX FSR1 w/ mobile optimizations
* HIGH: AMD FidelityFX FSR1 w/ mobile optimizations
* ULTRA: AMD FidelityFX FSR1
* FSR1 require a well anti-aliased (MSAA or TAA), noise free scene.
*
* The default upscaling quality is set to LOW.
*/
quality?: View$QualityLevel;
}
@@ -1136,39 +1162,120 @@ export enum View$BloomOptions$BlendMode {
export interface View$BloomOptions {
// JavaScript binding for dirt is not yet supported, must use default value.
// JavaScript binding for dirtStrength is not yet supported, must use default value.
strength?: number; // bloom's strength between 0.0 and 1.0
resolution?: number; // resolution of vertical axis (2^levels to 2048)
anamorphism?: number; // bloom x/y aspect-ratio (1/32 to 32)
levels?: number; // number of blur levels (3 to 11)
blendMode?: View$BloomOptions$BlendMode; // how the bloom effect is applied
threshold?: boolean; // whether to threshold the source
enabled?: boolean; // enable or disable bloom
highlight?: number; // limit highlights to this value before bloom [10, +inf]
lensFlare?: boolean; // enable screen-space lens flare
starburst?: boolean; // enable starburst effect on lens flare
chromaticAberration?: number; // amount of chromatic aberration
ghostCount?: number; // number of flare "ghosts"
ghostSpacing?: number; // spacing of the ghost in screen units [0, 1[
ghostThreshold?: number; // hdr threshold for the ghosts
haloThickness?: number; // thickness of halo in vertical screen units, 0 to disable
haloRadius?: number; // radius of halo in vertical screen units [0, 0.5]
haloThreshold?: number; // hdr threshold for the halo
/**
* bloom's strength between 0.0 and 1.0
*/
strength?: number;
/**
* resolution of vertical axis (2^levels to 2048)
*/
resolution?: number;
/**
* bloom x/y aspect-ratio (1/32 to 32)
*/
anamorphism?: number;
/**
* number of blur levels (3 to 11)
*/
levels?: number;
/**
* how the bloom effect is applied
*/
blendMode?: View$BloomOptions$BlendMode;
/**
* whether to threshold the source
*/
threshold?: boolean;
/**
* enable or disable bloom
*/
enabled?: boolean;
/**
* limit highlights to this value before bloom [10, +inf]
*/
highlight?: number;
/**
* enable screen-space lens flare
*/
lensFlare?: boolean;
/**
* enable starburst effect on lens flare
*/
starburst?: boolean;
/**
* amount of chromatic aberration
*/
chromaticAberration?: number;
/**
* number of flare "ghosts"
*/
ghostCount?: number;
/**
* spacing of the ghost in screen units [0, 1[
*/
ghostSpacing?: number;
/**
* hdr threshold for the ghosts
*/
ghostThreshold?: number;
/**
* thickness of halo in vertical screen units, 0 to disable
*/
haloThickness?: number;
/**
* radius of halo in vertical screen units [0, 0.5]
*/
haloRadius?: number;
/**
* hdr threshold for the halo
*/
haloThreshold?: number;
}
/**
* Options to control fog in the scene
*/
export interface View$FogOptions {
distance?: number; // distance in world units from the camera where the fog starts ( >= 0.0 )
maximumOpacity?: number; // fog's maximum opacity between 0 and 1
height?: number; // fog's floor in world units
heightFalloff?: number; // how fast fog dissipates with altitude
color?: float3; // fog's color (linear), see fogColorFromIbl
density?: number; // fog's density at altitude given by 'height'
inScatteringStart?: number; // distance in world units from the camera where in-scattering starts
inScatteringSize?: number; // size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
fogColorFromIbl?: boolean; // Fog color will be modulated by the IBL color in the view direction.
enabled?: boolean; // enable or disable fog
/**
* distance in world units from the camera where the fog starts ( >= 0.0 )
*/
distance?: number;
/**
* fog's maximum opacity between 0 and 1
*/
maximumOpacity?: number;
/**
* fog's floor in world units
*/
height?: number;
/**
* how fast fog dissipates with altitude
*/
heightFalloff?: number;
/**
* fog's color (linear), see fogColorFromIbl
*/
color?: float3;
/**
* fog's density at altitude given by 'height'
*/
density?: number;
/**
* distance in world units from the camera where in-scattering starts
*/
inScatteringStart?: number;
/**
* size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
*/
inScatteringSize?: number;
/**
* Fog color will be modulated by the IBL color in the view direction.
*/
fogColorFromIbl?: boolean;
/**
* enable or disable fog
*/
enabled?: boolean;
}
export enum View$DepthOfFieldOptions$Filter {
@@ -1187,15 +1294,64 @@ export enum View$DepthOfFieldOptions$Filter {
* @see Camera
*/
export interface View$DepthOfFieldOptions {
cocScale?: number; // circle of confusion scale factor (amount of blur)
maxApertureDiameter?: number; // maximum aperture diameter in meters (zero to disable rotation)
enabled?: boolean; // enable or disable depth of field effect
filter?: View$DepthOfFieldOptions$Filter; // filter to use for filling gaps in the kernel
nativeResolution?: boolean; // perform DoF processing at native resolution
foregroundRingCount?: number; // number of kernel rings for foreground tiles
backgroundRingCount?: number; // number of kernel rings for background tiles
fastGatherRingCount?: number; // number of kernel rings for fast tiles
/**
* circle of confusion scale factor (amount of blur)
*/
cocScale?: number;
/**
* maximum aperture diameter in meters (zero to disable rotation)
*/
maxApertureDiameter?: number;
/**
* enable or disable depth of field effect
*/
enabled?: boolean;
/**
* filter to use for filling gaps in the kernel
*/
filter?: View$DepthOfFieldOptions$Filter;
/**
* perform DoF processing at native resolution
*/
nativeResolution?: boolean;
/**
* 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.
*
* @{
*/
foregroundRingCount?: number;
/**
* number of kernel rings for background tiles
*/
backgroundRingCount?: number;
/**
* number of kernel rings for fast tiles
*/
fastGatherRingCount?: number;
/**
* 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.
*/
maxForegroundCOC?: number;
/**
* 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.
*/
maxBackgroundCOC?: number;
}
@@ -1203,11 +1359,26 @@ export interface View$DepthOfFieldOptions {
* Options to control the vignetting effect.
*/
export interface View$VignetteOptions {
midPoint?: number; // high values restrict the vignette closer to the corners, between 0 and 1
roundness?: number; // controls the shape of the vignette, from a rounded rectangle (0.0), to an oval (0.5), to a circle (1.0)
feather?: number; // softening amount of the vignette effect, between 0 and 1
color?: float4; // color of the vignette effect, alpha is currently ignored
enabled?: boolean; // enables or disables the vignette effect
/**
* high values restrict the vignette closer to the corners, between 0 and 1
*/
midPoint?: number;
/**
* controls the shape of the vignette, from a rounded rectangle (0.0), to an oval (0.5), to a circle (1.0)
*/
roundness?: number;
/**
* softening amount of the vignette effect, between 0 and 1
*/
feather?: number;
/**
* color of the vignette effect, alpha is currently ignored
*/
color?: float4;
/**
* enables or disables the vignette effect
*/
enabled?: boolean;
}
/**
@@ -1216,6 +1387,15 @@ export interface View$VignetteOptions {
* @see setRenderQuality, getRenderQuality
*/
export interface View$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).
*/
hdrColorBuffer?: View$QualityLevel;
}
@@ -1224,16 +1404,46 @@ export interface View$RenderQuality {
* Ambient shadows from dominant light
*/
export interface View$AmbientOcclusionOptions$Ssct {
lightConeRad?: number; // full cone angle in radian, between 0 and pi/2
shadowDistance?: number; // how far shadows can be cast
contactDistanceMax?: number; // max distance for contact
intensity?: number; // intensity
lightDirection?: float3; // light direction
depthBias?: number; // depth bias in world units (mitigate self shadowing)
depthSlopeBias?: number; // depth slope bias (mitigate self shadowing)
sampleCount?: number; // tracing sample count, between 1 and 255
rayCount?: number; // # of rays to trace, between 1 and 255
enabled?: boolean; // enables or disables SSCT
/**
* full cone angle in radian, between 0 and pi/2
*/
lightConeRad?: number;
/**
* how far shadows can be cast
*/
shadowDistance?: number;
/**
* max distance for contact
*/
contactDistanceMax?: number;
/**
* intensity
*/
intensity?: number;
/**
* light direction
*/
lightDirection?: float3;
/**
* depth bias in world units (mitigate self shadowing)
*/
depthBias?: number;
/**
* depth slope bias (mitigate self shadowing)
*/
depthSlopeBias?: number;
/**
* tracing sample count, between 1 and 255
*/
sampleCount?: number;
/**
* # of rays to trace, between 1 and 255
*/
rayCount?: number;
/**
* enables or disables SSCT
*/
enabled?: boolean;
}
/**
@@ -1241,18 +1451,54 @@ export interface View$AmbientOcclusionOptions$Ssct {
* @see setAmbientOcclusionOptions()
*/
export interface View$AmbientOcclusionOptions {
radius?: number; // Ambient Occlusion radius in meters, between 0 and ~10.
power?: number; // Controls ambient occlusion's contrast. Must be positive.
bias?: number; // Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
resolution?: number; // How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0.
intensity?: number; // Strength of the Ambient Occlusion effect.
bilateralThreshold?: number; // depth distance that constitute an edge for filtering
quality?: View$QualityLevel; // affects # of samples used for AO.
lowPassFilter?: View$QualityLevel; // affects AO smoothness
upsampling?: View$QualityLevel; // affects AO buffer upsampling quality
enabled?: boolean; // enables or disables screen-space ambient occlusion
bentNormals?: boolean; // enables bent normals computation from AO, and specular AO
minHorizonAngleRad?: number; // min angle in radian to consider
/**
* Ambient Occlusion radius in meters, between 0 and ~10.
*/
radius?: number;
/**
* Controls ambient occlusion's contrast. Must be positive.
*/
power?: number;
/**
* Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
*/
bias?: number;
/**
* How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0.
*/
resolution?: number;
/**
* Strength of the Ambient Occlusion effect.
*/
intensity?: number;
/**
* depth distance that constitute an edge for filtering
*/
bilateralThreshold?: number;
/**
* affects # of samples used for AO.
*/
quality?: View$QualityLevel;
/**
* affects AO smoothness
*/
lowPassFilter?: View$QualityLevel;
/**
* affects AO buffer upsampling quality
*/
upsampling?: View$QualityLevel;
/**
* enables or disables screen-space ambient occlusion
*/
enabled?: boolean;
/**
* enables bent normals computation from AO, and specular AO
*/
bentNormals?: boolean;
/**
* min angle in radian to consider
*/
minHorizonAngleRad?: number;
// JavaScript binding for ssct is not yet supported, must use default value.
}
@@ -1261,8 +1507,21 @@ export interface View$AmbientOcclusionOptions {
* @see setMultiSampleAntiAliasingOptions()
*/
export interface View$MultiSampleAntiAliasingOptions {
enabled?: boolean; // enables or disables msaa
/**
* enables or disables msaa
*/
enabled?: boolean;
/**
* sampleCount 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.
*/
sampleCount?: number;
/**
* custom resolve improves quality for HDR scenes, but may impact performance.
*/
customResolve?: boolean;
}
@@ -1271,9 +1530,18 @@ export interface View$MultiSampleAntiAliasingOptions {
* @see setTemporalAntiAliasingOptions()
*/
export interface View$TemporalAntiAliasingOptions {
filterWidth?: number; // reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
feedback?: number; // history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
enabled?: boolean; // enables or disables temporal anti-aliasing
/**
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
*/
filterWidth?: number;
/**
* history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
*/
feedback?: number;
/**
* enables or disables temporal anti-aliasing
*/
enabled?: boolean;
}
/**
@@ -1281,10 +1549,22 @@ export interface View$TemporalAntiAliasingOptions {
* @see setScreenSpaceReflectionsOptions()
*/
export interface View$ScreenSpaceReflectionsOptions {
thickness?: number; // ray thickness, in world units
bias?: number; // bias, in world units, to prevent self-intersections
maxDistance?: number; // maximum distance, in world units, to raycast
stride?: number; // stride, in texels, for samples along the ray.
/**
* ray thickness, in world units
*/
thickness?: number;
/**
* bias, in world units, to prevent self-intersections
*/
bias?: number;
/**
* maximum distance, in world units, to raycast
*/
maxDistance?: number;
/**
* stride, in texels, for samples along the ray.
*/
stride?: number;
enabled?: boolean;
}
@@ -1332,9 +1612,24 @@ export enum View$ShadowType {
* @warning This API is still experimental and subject to change.
*/
export interface View$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.
*/
anisotropy?: number;
/**
* Whether to generate mipmaps for all VSM shadow maps.
*/
mipmapping?: boolean;
/**
* VSM minimum variance scale, must be positive.
*/
minVarianceScale?: number;
/**
* VSM light bleeding reduction amount, between 0 and 1.
*/
lightBleedReduction?: number;
}
@@ -1344,6 +1639,16 @@ export interface View$VsmShadowOptions {
* @warning This API is still experimental and subject to change.
*/
export interface View$SoftShadowOptions {
/**
* Globally scales the penumbra of all DPCF and PCSS shadows
* Acceptable values are greater than 0
*/
penumbraScale?: number;
/**
* Globally scales the computed penumbra ratio of all DPCF and PCSS shadows.
* This effectively controls the strength of contact hardening effect and is useful for
* artistic purposes. Higher values make the shadows become softer faster.
* Acceptable values are equal to or greater than 1.
*/
penumbraRatioScale?: number;
}

View File

@@ -1707,6 +1707,7 @@ class_<SurfaceOrientation>("SurfaceOrientation")
class_<Animator>("gltfio$Animator")
.function("applyAnimation", &Animator::applyAnimation)
.function("updateBoneMatrices", &Animator::updateBoneMatrices)
.function("applyCrossFade", &Animator::applyCrossFade)
.function("resetBoneMatrices", &Animator::resetBoneMatrices)
.function("getAnimationCount", &Animator::getAnimationCount)
.function("getAnimationDuration", &Animator::getAnimationDuration)

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.22.2",
"version": "1.23.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",