Skip to content

Commit

Permalink
Introduce SurfaceOrientation helper class.
Browse files Browse the repository at this point in the history
This moves our existing VertexBuffer utility into a new "geometry"
library and adds new functionality for computing tangents based on UV's.
The UV-based method comes from Eric Lengyel.

We considered mikktspace (which thankfully has a zlib-style license) but
it would require re-indexing via meshoptimizer and is therefore a bit
heavyweight.

The new library has no dependencies and will add only 7 KB to Filament's
Android aar file.

Fixes #858 and preps for #528.
  • Loading branch information
prideout committed Feb 26, 2019
1 parent e40935d commit 2e29bc7
Show file tree
Hide file tree
Showing 9 changed files with 480 additions and 53 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ add_subdirectory(${EXTERNAL}/libgtest/tnt)
add_subdirectory(${LIBRARIES}/filabridge)
add_subdirectory(${LIBRARIES}/filaflat)
add_subdirectory(${LIBRARIES}/filameshio)
add_subdirectory(${LIBRARIES}/geometry)
add_subdirectory(${LIBRARIES}/image)
add_subdirectory(${LIBRARIES}/math)
add_subdirectory(${LIBRARIES}/utils)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ and tools.
- `filagui`: Helper library for [Dear ImGui](https://github.com/ocornut/imgui)
- `filamat`: Material generation library
- `filameshio`: Tiny mesh parsing library (see also `tools/filamesh`)
- `geometry`: Mesh-related utilities
- `image`: Image filtering and simple transforms
- `imageio`: Image file reading / writing, only intended for internal use
- `math`: Math library
Expand Down
5 changes: 5 additions & 0 deletions android/filament-android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)

add_library(geometry STATIC IMPORTED)
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)

add_library(filabridge STATIC IMPORTED)
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
Expand Down Expand Up @@ -74,6 +78,7 @@ target_link_libraries(filament-jni
filament
filaflat
filabridge
geometry
utils
log
GLESv3
Expand Down
1 change: 1 addition & 0 deletions filament/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ endif()

target_link_libraries(${TARGET} PUBLIC math)
target_link_libraries(${TARGET} PUBLIC utils)
target_link_libraries(${TARGET} PUBLIC geometry) # TODO: remove this dependency after deprecating VertexBuffer::populateTangentQuaternions
target_link_libraries(${TARGET} PUBLIC filaflat)
target_link_libraries(${TARGET} PUBLIC filabridge)
target_link_libraries(${TARGET} PUBLIC image_headers)
Expand Down
7 changes: 3 additions & 4 deletions filament/include/filament/VertexBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI {
* Convenience function that consumes normal vectors (and, optionally, tangent vectors) and
* produces quaternions that can be passed into a TANGENTS buffer.
*
* We may deprecate this method in the future. See also filament::geometry::SurfaceOrientation,
* which has additional capabilities.
*
* The given output buffer must be preallocated with at least quatCount * outStride bytes.
*
* Normals are required but tangents are optional, in which case this function tries to generate
Expand All @@ -141,10 +144,6 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI {
* If supplied, the tangent vectors should be unit length and should be orthogonal to the
* normals. The w component of the tangent is a sign (-1 or +1) indicating handedness of the
* basis.
*
* Note that some applications and file formats (e.g. Blender and glTF) use mikktspace, which
* consumes full topology information and produces an unindexed mesh, so it cannot be used here.
* This function exists for simple use cases only.
*/
static void populateTangentQuaternions(const QuatTangentContext& ctx);
};
Expand Down
62 changes: 13 additions & 49 deletions filament/src/VertexBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "FilamentAPI-impl.h"

#include <geometry/SurfaceOrientation.h>

#include <math/mat3.h>
#include <math/norm.h>
#include <math/quat.h>
Expand Down Expand Up @@ -206,65 +208,27 @@ void VertexBuffer::setBufferAt(Engine& engine, uint8_t bufferIndex,
}

void VertexBuffer::populateTangentQuaternions(const QuatTangentContext& ctx) {
if (!ASSERT_PRECONDITION_NON_FATAL(ctx.normals, "Normals must be provided")) {
return;
}
auto quats = geometry::SurfaceOrientation::Builder()
.vertexCount(ctx.quatCount)
.normals(ctx.normals)
.normalStride(ctx.normalsStride)
.tangents(ctx.tangents)
.tangentStride(ctx.tangentsStride)
.build();

// Define a small lambda that converts fp32 into the desired output format.
size_t outStride = ctx.outStride;
void (*writeQuat)(quatf, uint8_t*);
switch (ctx.quatType) {
case HALF4:
writeQuat = [] (quatf inquat, uint8_t* outquat) {
*((quath*) outquat) = quath(inquat);
};
outStride = outStride ? outStride : sizeof(half4);
quats->getQuats((quath*) ctx.outBuffer, ctx.quatCount, ctx.outStride);
break;
case SHORT4:
writeQuat = [] (quatf inquat, uint8_t* outquat) {
*((short4*) outquat) = packSnorm16(inquat.xyzw);
};
outStride = outStride ? outStride : sizeof(short4);
quats->getQuats((short4*) ctx.outBuffer, ctx.quatCount, ctx.outStride);
break;
case FLOAT4:
writeQuat = [] (quatf inquat, uint8_t* outquat) {
*((quatf*) outquat) = inquat;
};
outStride = outStride ? outStride : sizeof(float4);
quats->getQuats((quatf*) ctx.outBuffer, ctx.quatCount, ctx.outStride);
break;
}

const float3* normal = ctx.normals;
const size_t nstride = ctx.normalsStride ? ctx.normalsStride : sizeof(float3);
uint8_t* outquat = (uint8_t*) ctx.outBuffer;

// If tangents are not provided, simply cross N with arbitrary vector (1, 0, 0)
if (!ctx.tangents) {
for (size_t qindex = 0, qcount = ctx.quatCount; qindex < qcount; ++qindex) {
float3 n = *normal;
float3 b = normalize(cross(n, float3{1, 0, 0}));
float3 t = cross(n, b);
writeQuat(mat3f::packTangentFrame({t, b, n}), outquat);
normal = (const float3*) (((const uint8_t*) normal) + nstride);
outquat += outStride;
}
return;
}

const float3* tanvec = &ctx.tangents->xyz;
const float* tandir = &ctx.tangents->w;
const size_t tstride = ctx.tangentsStride ? ctx.tangentsStride : sizeof(float4);

for (size_t qindex = 0, qcount = ctx.quatCount; qindex < qcount; ++qindex) {
float3 n = *normal;
float3 t = *tanvec;
float3 b = *tandir > 0 ? cross(t, n) : cross(n, t);
writeQuat(mat3f::packTangentFrame({t, b, n}), outquat);
normal = (const float3*) (((const uint8_t*) normal) + nstride);
tanvec = (const float3*) (((const uint8_t*) tanvec) + tstride);
tandir = (const float*) (((const uint8_t*) tandir) + tstride);
outquat += outStride;
}
geometry::SurfaceOrientation::destroy(quats);
}

} // namespace filament
48 changes: 48 additions & 0 deletions libs/geometry/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.1)
project(geometry)

set(TARGET geometry)
set(PUBLIC_HDR_DIR include)

# ==================================================================================================
# Sources and headers
# ==================================================================================================
set(PUBLIC_HDRS
include/geometry/SurfaceOrientation.h
)

set(SRCS
src/SurfaceOrientation.cpp
)

# ==================================================================================================
# Include and target definitions
# ==================================================================================================
include_directories(${PUBLIC_HDR_DIR})

add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})

target_link_libraries(${TARGET} PUBLIC math utils)

target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})

# ==================================================================================================
# Compiler flags
# ==================================================================================================
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)

if (MSVC OR CLANG_CL)
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:/fp:fast>)
else()
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:-ffast-math>)
endif()

if (LINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif()

# ==================================================================================================
# Installation
# ==================================================================================================
install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR})
install(DIRECTORY ${PUBLIC_HDR_DIR}/geometry DESTINATION include)
118 changes: 118 additions & 0 deletions libs/geometry/include/geometry/SurfaceOrientation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef TNT_GEOMETRY_SURFACEORIENTATION_H
#define TNT_GEOMETRY_SURFACEORIENTATION_H

#include <math/quat.h>
#include <math/vec3.h>
#include <math/vec4.h>

namespace filament {
namespace geometry {

struct OrientationBuilderImpl;
struct OrientationImpl;

/**
* The surface orientation helper can be used to populate Filament-style TANGENTS buffers.
*/
class SurfaceOrientation {
public:

/**
* Constructs an immutable surface orientation helper.
*
* At a minimum, clients must supply a vertex count and normals buffer. They can supply data in
* any of the following three combinations:
*
* 1. vec3 normals only (not recommended)
* 2. vec3 normals + vec4 tangents (sign of W determines bitangent orientation)
* 3. vec3 normals + vec2 uvs + vec3 positions + uint3 indices
*/
class Builder {
public:
Builder();
~Builder() noexcept;

/**
* These two attributes are required. They are not passed into the constructor to force
* calling code to be self-documenting.
* @{
*/
Builder& vertexCount(size_t vertexCount) noexcept;
Builder& normals(const filament::math::float3*) noexcept;
/** @} */

Builder& tangents(const filament::math::float4*) noexcept;
Builder& uvs(const filament::math::float2*) noexcept;
Builder& positions(const filament::math::float3*) noexcept;

Builder& triangleCount(size_t triangleCount) noexcept;
Builder& triangles(const filament::math::uint3*) noexcept;
Builder& triangles(const filament::math::ushort3*) noexcept;

/**
* Supplying explicit stride values is optional, they each default to the size of their
* respective input attributes, e.g. sizeof(float3) for normals.
* @{
*/
Builder& normalStride(size_t numBytes) noexcept;
Builder& tangentStride(size_t numBytes) noexcept;
Builder& uvStride(size_t numBytes) noexcept;
Builder& positionStride(size_t numBytes) noexcept;
/** @} */

/**
* Generates quats or panics if the submitted data is an incomplete combination.
*/
SurfaceOrientation* build();

private:
OrientationBuilderImpl* mImpl;
Builder(const Builder&);
Builder& operator=(const Builder&);
};

static void destroy(SurfaceOrientation* so);

/**
* Returns the vertex count.
*/
size_t getVertexCount() const noexcept;

/**
* Converts quaternions into the desired output format and writes up to "nquats"
* to the given output pointer. Normally nquats should be equal to the vertex count.
* The optional stride is the desired quat-to-quat stride in bytes.
* @{
*/
void getQuats(filament::math::quatf* out, size_t nquats, size_t stride = 0) const noexcept;
void getQuats(filament::math::short4* out, size_t nquats, size_t stride = 0) const noexcept;
void getQuats(filament::math::quath* out, size_t nquats, size_t stride = 0) const noexcept;
/** @} */

private:
SurfaceOrientation(OrientationImpl*);
~SurfaceOrientation();
OrientationImpl* mImpl;
friend struct OrientationBuilderImpl;
};

} // namespace geometry
} // namespace filament

#endif // TNT_GEOMETRY_SURFACEORIENTATION_H
Loading

0 comments on commit 2e29bc7

Please sign in to comment.