Merge branch 'rc/1.17.0' into release
This commit is contained in:
+5
-1
@@ -123,6 +123,10 @@ if (ANDROID OR WEBGL OR IOS OR FILAMENT_LINUX_IS_MOBILE)
|
||||
set(IS_MOBILE_TARGET TRUE)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
add_definitions(-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE)
|
||||
set(IS_HOST_PLATFORM TRUE)
|
||||
endif()
|
||||
@@ -295,7 +299,7 @@ if (LINUX)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Werror=unguarded-availability")
|
||||
endif()
|
||||
|
||||
if (CYGWIN)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.16.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.17.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -52,7 +52,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.16.1'
|
||||
pod 'Filament', '~> 1.17.0'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
+9
-1
@@ -3,7 +3,15 @@
|
||||
This file contains one line summaries of commits that are worthy of mentioning in release notes.
|
||||
A new header is inserted each time a *tag* is created.
|
||||
|
||||
## v1.16.2 (currently main branch)
|
||||
## v1.17.1 (currently main branch)
|
||||
|
||||
## v1.17.0
|
||||
|
||||
- engine: Add experimental glossy screen-space reflections.
|
||||
- engine: Add support for GPU morphing and 128 morph targets.
|
||||
- engine: Fix crash with non-shadow receiving renderables and VSM.
|
||||
- engine: Bring back the 3x3 filter for PCF shadows.
|
||||
- engine: Correct AABB calculation for skinned glTF models.
|
||||
|
||||
## v1.16.1
|
||||
|
||||
|
||||
+8
-8
@@ -114,14 +114,14 @@ public class MaterialBuilder {
|
||||
BONE_INDICES, // indices of 4 bones (uvec4)
|
||||
BONE_WEIGHTS, // weights of the 4 bones (normalized float4)
|
||||
UNUSED, // reserved for future use
|
||||
CUSTOM0, // custom or MORPH_POSITION_0
|
||||
CUSTOM1, // custom or MORPH_POSITION_1
|
||||
CUSTOM2, // custom or MORPH_POSITION_2
|
||||
CUSTOM3, // custom or MORPH_POSITION_3
|
||||
CUSTOM4, // custom or MORPH_TANGENTS_0
|
||||
CUSTOM5, // custom or MORPH_TANGENTS_1
|
||||
CUSTOM6, // custom or MORPH_TANGENTS_2
|
||||
CUSTOM7 // custom or MORPH_TANGENTS_3
|
||||
CUSTOM0, // custom
|
||||
CUSTOM1, // custom
|
||||
CUSTOM2, // custom
|
||||
CUSTOM3, // custom
|
||||
CUSTOM4, // custom
|
||||
CUSTOM5, // custom
|
||||
CUSTOM6, // custom
|
||||
CUSTOM7 // custom
|
||||
}
|
||||
|
||||
public enum BlendingMode {
|
||||
|
||||
@@ -270,9 +270,9 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env,
|
||||
jlong nativeRenderableManager, jint instance, jfloatArray weights) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
jfloat* vec = env->GetFloatArrayElements(weights, NULL);
|
||||
math::float4 floatvec(vec[0], vec[1], vec[2], vec[3]);
|
||||
jsize count = env->GetArrayLength(weights);
|
||||
rm->setMorphWeights((RenderableManager::Instance)instance, vec, count);
|
||||
env->ReleaseFloatArrayElements(weights, vec, JNI_ABORT);
|
||||
rm->setMorphWeights((RenderableManager::Instance)instance, floatvec);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
|
||||
@@ -49,8 +49,8 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateGenericToneMapper(JNIEnv*, jclass,
|
||||
jfloat contrast, jfloat shoulder, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) {
|
||||
return (jlong) new GenericToneMapper(contrast, shoulder, midGrayIn, midGrayOut, hdrMax);
|
||||
jfloat contrast, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) {
|
||||
return (jlong) new GenericToneMapper(contrast, midGrayIn, midGrayOut, hdrMax);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jfloat JNICALL
|
||||
@@ -58,11 +58,6 @@ Java_com_google_android_filament_ToneMapper_nGenericGetContrast(JNIEnv*, jclass,
|
||||
return ((GenericToneMapper*) nativeObject)->getContrast();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jfloat JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nGenericGetShoulder(JNIEnv*, jclass, jlong nativeObject) {
|
||||
return ((GenericToneMapper*) nativeObject)->getShoulder();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jfloat JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nGenericGetMidGrayIn(JNIEnv*, jclass, jlong nativeObject) {
|
||||
return ((GenericToneMapper*) nativeObject)->getMidGrayIn();
|
||||
@@ -84,12 +79,6 @@ Java_com_google_android_filament_ToneMapper_nGenericSetContrast(JNIEnv*, jclass,
|
||||
((GenericToneMapper*) nativeObject)->setContrast(contrast);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nGenericSetShoulder(JNIEnv*, jclass,
|
||||
jlong nativeObject, jfloat shoulder) {
|
||||
((GenericToneMapper*) nativeObject)->setShoulder(shoulder);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nGenericSetMidGrayIn(JNIEnv*, jclass,
|
||||
jlong nativeObject, jfloat midGrayIn) {
|
||||
|
||||
+1
-7
@@ -402,9 +402,6 @@ public class RenderableManager {
|
||||
/**
|
||||
* Controls if the renderable has vertex morphing targets, false by default.
|
||||
*
|
||||
* <p>This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer
|
||||
* must provide data in the appropriate VertexAttribute slots (<code>MORPH_POSITION_0</code> etc).</p>
|
||||
*
|
||||
* <p>See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis
|
||||
* to advance the animation.</p>
|
||||
*/
|
||||
@@ -497,14 +494,11 @@ public class RenderableManager {
|
||||
/**
|
||||
* Updates the vertex morphing weights on a renderable, all zeroes by default.
|
||||
*
|
||||
* <p>This is specified using a 4-tuple, one float per morph target. If the renderable has fewer
|
||||
* than 4 morph targets, then clients should fill the unused components with zeroes.</p>
|
||||
*
|
||||
* <p>The renderable must be built with morphing enabled.</p>
|
||||
*
|
||||
* @see Builder#morphing
|
||||
*/
|
||||
public void setMorphWeights(@EntityInstance int i, @NonNull @Size(min = 4) float[] weights) {
|
||||
public void setMorphWeights(@EntityInstance int i, @NonNull float[] weights) {
|
||||
nSetMorphWeights(mNativeObject, i, weights);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,6 @@ public class ToneMapper {
|
||||
* The tone mapping curve is defined by 5 parameters:
|
||||
* <ul>
|
||||
* <li>contrast: controls the contrast of the curve</li>
|
||||
* <li>shoulder: controls the shoulder of the curve, i.e. how quickly scene
|
||||
* referred values map to output white</li>
|
||||
* <li>midGrayIn: sets the input middle gray</li>
|
||||
* <li>midGrayOut: sets the output middle gray</li>
|
||||
@@ -123,15 +122,14 @@ public class ToneMapper {
|
||||
* the {@link ACESLegacy} tone mapper. The default values are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>contrast = 1.585f</li>
|
||||
* <li>shoulder = 0.5f</li>
|
||||
* <li>contrast = 1.55f</li>
|
||||
* <li>midGrayIn = 0.18f</li>
|
||||
* <li>midGrayOut = 0.268f</li>
|
||||
* <li>midGrayOut = 0.215f</li>
|
||||
* <li>hdrMax = 10.0f</li>
|
||||
* </ul>
|
||||
*/
|
||||
public Generic() {
|
||||
this(1.585f, 0.5f, 0.18f, 0.268f, 10.0f);
|
||||
this(1.55f, 0.18f, 0.215f, 10.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,16 +137,14 @@ public class ToneMapper {
|
||||
*
|
||||
* @param contrast: controls the contrast of the curve, must be > 0.0, values
|
||||
* in the range 0.5..2.0 are recommended.
|
||||
* @param shoulder: controls the shoulder of the curve, i.e. how quickly scene
|
||||
* referred values map to output white, between 0.0 and 1.0.
|
||||
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
|
||||
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
|
||||
* @param hdrMax: defines the maximum input value that will be mapped to
|
||||
* output white. Must be >= 1.0.
|
||||
*/
|
||||
public Generic(
|
||||
float contrast, float shoulder, float midGrayIn, float midGrayOut, float hdrMax) {
|
||||
super(nCreateGenericToneMapper(contrast, shoulder, midGrayIn, midGrayOut, hdrMax));
|
||||
float contrast, float midGrayIn, float midGrayOut, float hdrMax) {
|
||||
super(nCreateGenericToneMapper(contrast, midGrayIn, midGrayOut, hdrMax));
|
||||
}
|
||||
|
||||
/** Returns the contrast of the curve as a strictly positive value. */
|
||||
@@ -161,16 +157,6 @@ public class ToneMapper {
|
||||
nGenericSetContrast(getNativeObject(), contrast);
|
||||
}
|
||||
|
||||
/** Returns how fast scene referred values map to output white as a value between 0.0 and 1.0. */
|
||||
public float getShoulder() {
|
||||
return nGenericGetShoulder(getNativeObject());
|
||||
}
|
||||
|
||||
/** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */
|
||||
public void setShoulder(float shoulder) {
|
||||
nGenericSetShoulder(getNativeObject(), shoulder);
|
||||
}
|
||||
|
||||
/** Returns the middle gray point for input values as a value between 0.0 and 1.0. */
|
||||
public float getMidGrayIn() {
|
||||
return nGenericGetMidGrayIn(getNativeObject());
|
||||
@@ -209,17 +195,15 @@ public class ToneMapper {
|
||||
private static native long nCreateACESLegacyToneMapper();
|
||||
private static native long nCreateFilmicToneMapper();
|
||||
private static native long nCreateGenericToneMapper(
|
||||
float contrast, float shoulder, float midGrayIn, float midGrayOut, float hdrMax);
|
||||
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
|
||||
|
||||
// Generic tone mappper
|
||||
private static native float nGenericGetContrast(long nativeObject);
|
||||
private static native float nGenericGetShoulder(long nativeObject);
|
||||
private static native float nGenericGetMidGrayIn(long nativeObject);
|
||||
private static native float nGenericGetMidGrayOut(long nativeObject);
|
||||
private static native float nGenericGetHdrMax(long nativeObject);
|
||||
|
||||
private static native void nGenericSetContrast(long nativeObject, float contrast);
|
||||
private static native void nGenericSetShoulder(long nativeObject, float shoulder);
|
||||
private static native void nGenericSetMidGrayIn(long nativeObject, float midGrayIn);
|
||||
private static native void nGenericSetMidGrayOut(long nativeObject, float midGrayOut);
|
||||
private static native void nGenericSetHdrMax(long nativeObject, float hdrMax);
|
||||
|
||||
+8
-8
@@ -66,14 +66,14 @@ public class VertexBuffer {
|
||||
BONE_INDICES, // indices of 4 bones (uvec4)
|
||||
BONE_WEIGHTS, // weights of the 4 bones (normalized float4)
|
||||
UNUSED, // reserved for future use
|
||||
CUSTOM0, // custom or MORPH_POSITION_0
|
||||
CUSTOM1, // custom or MORPH_POSITION_1
|
||||
CUSTOM2, // custom or MORPH_POSITION_2
|
||||
CUSTOM3, // custom or MORPH_POSITION_3
|
||||
CUSTOM4, // custom or MORPH_TANGENTS_0
|
||||
CUSTOM5, // custom or MORPH_TANGENTS_1
|
||||
CUSTOM6, // custom or MORPH_TANGENTS_2
|
||||
CUSTOM7 // custom or MORPH_TANGENTS_3
|
||||
CUSTOM0, // custom
|
||||
CUSTOM1, // custom
|
||||
CUSTOM2, // custom
|
||||
CUSTOM3, // custom
|
||||
CUSTOM4, // custom
|
||||
CUSTOM5, // custom
|
||||
CUSTOM6, // custom
|
||||
CUSTOM7 // custom
|
||||
}
|
||||
|
||||
public enum AttributeType {
|
||||
|
||||
@@ -1337,21 +1337,26 @@ public class View {
|
||||
*
|
||||
* <p>Post-processing includes:</p>
|
||||
* <ul>
|
||||
* <li>Tone-mapping & gamma encoding</li>
|
||||
* <li>Depth-of-field</li>
|
||||
* <li>Bloom</li>
|
||||
* <li>Vignetting</li>
|
||||
* <li>Temporal Anti-aliasing (TAA)</li>
|
||||
* <li>Color grading & gamma encoding</li>
|
||||
* <li>Dithering</li>
|
||||
* <li>MSAA</li>
|
||||
* <li>FXAA</li>
|
||||
* <li>Dynamic scaling</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Disabling post-processing forgoes color correctness as well as anti-aliasing and
|
||||
* should only be used experimentally (e.g., for UI overlays).
|
||||
* Disabling post-processing forgoes color correctness as well as some anti-aliasing techniques
|
||||
* and should only be used for debugging, UI overlays or when using custom render targets
|
||||
* (see RenderTarget).
|
||||
* </p>
|
||||
*
|
||||
* @param enabled true enables post processing, false disables it
|
||||
*
|
||||
* @see #setToneMapping
|
||||
* @see #setBloomOptions
|
||||
* @see #setColorGrading
|
||||
* @see #setAntiAliasing
|
||||
* @see #setDithering
|
||||
* @see #setSampleCount
|
||||
|
||||
+23
-10
@@ -58,7 +58,10 @@ private const val kSensitivity = 100f
|
||||
*
|
||||
* See `sample-gltf-viewer` for a usage example.
|
||||
*/
|
||||
class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
class ModelViewer(
|
||||
val engine: Engine,
|
||||
private val uiHelper: UiHelper
|
||||
) : android.view.View.OnTouchListener {
|
||||
var asset: FilamentAsset? = null
|
||||
private set
|
||||
|
||||
@@ -71,6 +74,7 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
|
||||
var normalizeSkinningWeights = true
|
||||
var recomputeBoundingBoxes = false
|
||||
var ignoreBindTransform = false
|
||||
|
||||
var cameraFocalLength = 28f
|
||||
set(value) {
|
||||
@@ -84,7 +88,6 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
val renderer: Renderer
|
||||
@Entity val light: Int
|
||||
|
||||
private val uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
|
||||
private lateinit var displayHelper: DisplayHelper
|
||||
private lateinit var cameraManipulator: Manipulator
|
||||
private lateinit var gestureDetector: GestureDetector
|
||||
@@ -111,7 +114,7 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
view.camera = camera
|
||||
|
||||
assetLoader = AssetLoader(engine, UbershaderLoader(engine), EntityManager.get())
|
||||
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes)
|
||||
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes, ignoreBindTransform)
|
||||
|
||||
// Always add a direct light source since it is required for shadowing.
|
||||
// We highly recommend adding an indirect light as well.
|
||||
@@ -129,7 +132,12 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
scene.addEntity(light)
|
||||
}
|
||||
|
||||
constructor(surfaceView: SurfaceView, engine: Engine = Engine.create(), manipulator: Manipulator? = null) : this(engine) {
|
||||
constructor(
|
||||
surfaceView: SurfaceView,
|
||||
engine: Engine = Engine.create(),
|
||||
uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK),
|
||||
manipulator: Manipulator? = null
|
||||
) : this(engine, uiHelper) {
|
||||
cameraManipulator = manipulator ?: Manipulator.Builder()
|
||||
.targetPosition(kDefaultObjectPosition.x, kDefaultObjectPosition.y, kDefaultObjectPosition.z)
|
||||
.viewport(surfaceView.width, surfaceView.height)
|
||||
@@ -144,7 +152,12 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
constructor(textureView: TextureView, engine: Engine = Engine.create(), manipulator: Manipulator? = null) : this(engine) {
|
||||
constructor(
|
||||
textureView: TextureView,
|
||||
engine: Engine = Engine.create(),
|
||||
uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK),
|
||||
manipulator: Manipulator? = null
|
||||
) : this(engine, uiHelper) {
|
||||
cameraManipulator = manipulator ?: Manipulator.Builder()
|
||||
.targetPosition(kDefaultObjectPosition.x, kDefaultObjectPosition.y, kDefaultObjectPosition.z)
|
||||
.viewport(textureView.width, textureView.height)
|
||||
@@ -215,8 +228,8 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
fun transformToUnitCube(centerPoint: Float3 = kDefaultObjectPosition) {
|
||||
asset?.let { asset ->
|
||||
val tm = engine.transformManager
|
||||
var center = asset.boundingBox.center.let { v-> Float3(v[0], v[1], v[2]) }
|
||||
val halfExtent = asset.boundingBox.halfExtent.let { v-> Float3(v[0], v[1], v[2]) }
|
||||
var center = asset.boundingBox.center.let { v -> Float3(v[0], v[1], v[2]) }
|
||||
val halfExtent = asset.boundingBox.halfExtent.let { v -> Float3(v[0], v[1], v[2]) }
|
||||
val maxExtent = 2.0f * max(halfExtent)
|
||||
val scaleFactor = 2.0f / maxExtent
|
||||
center -= centerPoint / scaleFactor
|
||||
@@ -284,9 +297,9 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
private fun populateScene(asset: FilamentAsset) {
|
||||
val rcm = engine.renderableManager
|
||||
var count = 0
|
||||
val popRenderables = {count = asset.popRenderables(readyRenderables); count != 0}
|
||||
val popRenderables = { count = asset.popRenderables(readyRenderables); count != 0 }
|
||||
while (popRenderables()) {
|
||||
for (i in 0..count-1) {
|
||||
for (i in 0..count - 1) {
|
||||
val ri = rcm.getInstance(readyRenderables[i])
|
||||
rcm.setScreenSpaceContactShadows(ri, true)
|
||||
}
|
||||
@@ -383,4 +396,4 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
companion object {
|
||||
private val kDefaultObjectPosition = Float3(0.0f, 0.0f, -4.0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,10 +35,11 @@ static void destroy(void*, size_t, void *userData) {
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateResourceLoader(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jboolean normalizeSkinningWeights, jboolean recomputeBoundingBoxes) {
|
||||
jlong nativeEngine, jboolean normalizeSkinningWeights, jboolean recomputeBoundingBoxes,
|
||||
jboolean ignoreBindTransform) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) new ResourceLoader({ engine, {}, (bool) normalizeSkinningWeights,
|
||||
(bool) recomputeBoundingBoxes });
|
||||
(bool) recomputeBoundingBoxes, (bool) ignoreBindTransform});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
|
||||
+5
-4
@@ -45,7 +45,7 @@ public class ResourceLoader {
|
||||
*/
|
||||
public ResourceLoader(@NonNull Engine engine) {
|
||||
long nativeEngine = engine.getNativeObject();
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false, false);
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,14 +54,15 @@ public class ResourceLoader {
|
||||
* @param engine the engine that gets passed to all builder methods
|
||||
* @param normalizeSkinningWeights scale non-conformant skinning weights so they sum to 1
|
||||
* @param recomputeBoundingBoxes use computed bounding boxes rather than the ones in the asset
|
||||
* @param ignoreBindTransform ignore skinned primitives bind transform when compute bounding boxes
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public ResourceLoader(@NonNull Engine engine, boolean normalizeSkinningWeights,
|
||||
boolean recomputeBoundingBoxes) {
|
||||
boolean recomputeBoundingBoxes, boolean ignoreBindTransform) {
|
||||
long nativeEngine = engine.getNativeObject();
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights,
|
||||
recomputeBoundingBoxes);
|
||||
recomputeBoundingBoxes, ignoreBindTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +167,7 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
private static native long nCreateResourceLoader(long nativeEngine,
|
||||
boolean normalizeSkinningWeights, boolean recomputeBoundingBoxes);
|
||||
boolean normalizeSkinningWeights, boolean recomputeBoundingBoxes, boolean ignoreBindTransform);
|
||||
private static native void nDestroyResourceLoader(long nativeLoader);
|
||||
private static native void nAddResourceData(long nativeLoader, String url, Buffer buffer,
|
||||
int remaining);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.16.1
|
||||
VERSION_NAME=1.17.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+1761
-1975
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -713,7 +713,8 @@ Filament.loadClassExtensions = function() {
|
||||
const interval = asyncInterval || 30;
|
||||
const defaults = {
|
||||
normalizeSkinningWeights: true,
|
||||
recomputeBoundingBoxes: false
|
||||
recomputeBoundingBoxes: false,
|
||||
ignoreBindTransform: false
|
||||
};
|
||||
config = Object.assign(defaults, config || {});
|
||||
|
||||
@@ -736,7 +737,8 @@ Filament.loadClassExtensions = function() {
|
||||
// Construct a resource loader and start decoding after all textures are fetched.
|
||||
const resourceLoader = new Filament.gltfio$ResourceLoader(engine,
|
||||
config.normalizeSkinningWeights,
|
||||
config.recomputeBoundingBoxes);
|
||||
config.recomputeBoundingBoxes,
|
||||
config.ignoreBindTransform);
|
||||
const onComplete = () => {
|
||||
resourceLoader.asyncBeginLoad(asset);
|
||||
|
||||
|
||||
@@ -307,6 +307,7 @@ class FilamentViewer extends LitElement {
|
||||
const config = {
|
||||
normalizeSkinningWeights: true,
|
||||
recomputeBoundingBoxes: false,
|
||||
ignoreBindTransform: false,
|
||||
asyncInterval: 30
|
||||
};
|
||||
|
||||
@@ -333,7 +334,8 @@ class FilamentViewer extends LitElement {
|
||||
|
||||
const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine,
|
||||
config.normalizeSkinningWeights,
|
||||
config.recomputeBoundingBoxes);
|
||||
config.recomputeBoundingBoxes,
|
||||
config.ignoreBindTransform);
|
||||
|
||||
let remaining = Object.keys(this.srcBlobResources).length;
|
||||
for (const name in this.srcBlobResources) {
|
||||
|
||||
@@ -713,7 +713,8 @@ Filament.loadClassExtensions = function() {
|
||||
const interval = asyncInterval || 30;
|
||||
const defaults = {
|
||||
normalizeSkinningWeights: true,
|
||||
recomputeBoundingBoxes: false
|
||||
recomputeBoundingBoxes: false,
|
||||
ignoreBindTransform: false
|
||||
};
|
||||
config = Object.assign(defaults, config || {});
|
||||
|
||||
@@ -736,7 +737,8 @@ Filament.loadClassExtensions = function() {
|
||||
// Construct a resource loader and start decoding after all textures are fetched.
|
||||
const resourceLoader = new Filament.gltfio$ResourceLoader(engine,
|
||||
config.normalizeSkinningWeights,
|
||||
config.recomputeBoundingBoxes);
|
||||
config.recomputeBoundingBoxes,
|
||||
config.ignoreBindTransform);
|
||||
const onComplete = () => {
|
||||
resourceLoader.asyncBeginLoad(asset);
|
||||
|
||||
|
||||
@@ -1438,14 +1438,6 @@ This defines the following functions:</p>
|
||||
<li>CUSTOM5</li>
|
||||
<li>CUSTOM6</li>
|
||||
<li>CUSTOM7</li>
|
||||
<li>MORPH_POSITION_0</li>
|
||||
<li>MORPH_POSITION_1</li>
|
||||
<li>MORPH_POSITION_2</li>
|
||||
<li>MORPH_POSITION_3</li>
|
||||
<li>MORPH_TANGENTS_0</li>
|
||||
<li>MORPH_TANGENTS_1</li>
|
||||
<li>MORPH_TANGENTS_2</li>
|
||||
<li>MORPH_TANGENTS_3</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
|
||||
@@ -28,6 +28,7 @@ set(PUBLIC_HDRS
|
||||
include/filament/LightManager.h
|
||||
include/filament/Material.h
|
||||
include/filament/MaterialInstance.h
|
||||
include/filament/MorphTargetBuffer.h
|
||||
include/filament/Options.h
|
||||
include/filament/RenderTarget.h
|
||||
include/filament/RenderableManager.h
|
||||
@@ -69,6 +70,7 @@ set(SRCS
|
||||
src/Material.cpp
|
||||
src/MaterialInstance.cpp
|
||||
src/MaterialParser.cpp
|
||||
src/MorphTargetBuffer.cpp
|
||||
src/PerViewUniforms.cpp
|
||||
src/PostProcessManager.cpp
|
||||
src/RenderPass.cpp
|
||||
@@ -140,6 +142,7 @@ set(PRIVATE_HDRS
|
||||
src/details/IndirectLight.h
|
||||
src/details/Material.h
|
||||
src/details/MaterialInstance.h
|
||||
src/details/MorphTargetBuffer.h
|
||||
src/details/RenderTarget.h
|
||||
src/details/Renderer.h
|
||||
src/details/Scene.h
|
||||
|
||||
@@ -809,10 +809,10 @@ using AttributeArray = std::array<Attribute, MAX_VERTEX_ATTRIBUTE_COUNT>;
|
||||
//! Raster state descriptor
|
||||
struct RasterState {
|
||||
|
||||
using CullingMode = CullingMode;
|
||||
using DepthFunc = SamplerCompareFunc;
|
||||
using BlendEquation = BlendEquation;
|
||||
using BlendFunction = BlendFunction;
|
||||
using CullingMode = backend::CullingMode;
|
||||
using DepthFunc = backend::SamplerCompareFunc;
|
||||
using BlendEquation = backend::BlendEquation;
|
||||
using BlendFunction = backend::BlendFunction;
|
||||
|
||||
RasterState() noexcept { // NOLINT
|
||||
static_assert(sizeof(RasterState) == sizeof(uint32_t),
|
||||
|
||||
@@ -95,33 +95,25 @@ void ExternalTextureManagerAndroid::destroy(ExternalTextureManagerAndroid* pExte
|
||||
// called on gl thread
|
||||
ExternalTextureManagerAndroid::ExternalTextureManagerAndroid() noexcept
|
||||
: mVm(VirtualMachineEnv::get()) {
|
||||
// if we compile for API 26 (Oreo) and above, we're guaranteed to have AHardwareBuffer
|
||||
// in all other cases, we need to get them at runtime.
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
// the following dlsym() calls don't work on API 19
|
||||
if (api_level() >= 21) {
|
||||
loadSymbol(AHardwareBuffer_allocate, "AHardwareBuffer_allocate");
|
||||
loadSymbol(AHardwareBuffer_release, "AHardwareBuffer_release");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// not quite sure on which thread this is going to be called
|
||||
ExternalTextureManagerAndroid::~ExternalTextureManagerAndroid() noexcept {
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
if (mGraphicBufferClass) {
|
||||
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
|
||||
if (env) {
|
||||
env->DeleteGlobalRef(mGraphicBufferClass);
|
||||
if (__builtin_available(android 26, *)) {
|
||||
} else {
|
||||
if (mGraphicBufferClass) {
|
||||
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
|
||||
if (env) {
|
||||
env->DeleteGlobalRef(mGraphicBufferClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// called on gl thread
|
||||
backend::Platform::ExternalTexture* ExternalTextureManagerAndroid::createExternalTexture() noexcept {
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
if (!AHardwareBuffer_allocate) {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
} else {
|
||||
// initialize java stuff on-demand
|
||||
if (!mGraphicBufferClass) {
|
||||
JNIEnv* env = mVm.getEnvironment();
|
||||
@@ -134,7 +126,6 @@ backend::Platform::ExternalTexture* ExternalTextureManagerAndroid::createExterna
|
||||
mGraphicBufferClass = static_cast<jclass>(env->NewGlobalRef(mGraphicBufferClass));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
EGLExternalTexture* ets = new EGLExternalTexture;
|
||||
return ets;
|
||||
}
|
||||
@@ -181,9 +172,7 @@ void ExternalTextureManagerAndroid::alloc(
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
if (AHardwareBuffer_allocate) {
|
||||
#endif
|
||||
if (__builtin_available(android 26, *)) {
|
||||
// allocate new storage...
|
||||
AHardwareBuffer* buffer = nullptr;
|
||||
if (AHardwareBuffer_allocate(&desc, &buffer) < 0) {
|
||||
@@ -191,7 +180,6 @@ void ExternalTextureManagerAndroid::alloc(
|
||||
return;
|
||||
}
|
||||
ets->hardwareBuffer = buffer;
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
} else {
|
||||
// note: This is called on the application thread (not the GL thread)
|
||||
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
|
||||
@@ -209,23 +197,17 @@ void ExternalTextureManagerAndroid::alloc(
|
||||
ets->clientBuffer = static_cast<ANativeWindowBuffer*>(gb);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// called on gl thread
|
||||
void ExternalTextureManagerAndroid::destroyStorage(backend::Platform::ExternalTexture* t) noexcept {
|
||||
EGLExternalTexture* ets = static_cast<EGLExternalTexture*>(t);
|
||||
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
if (AHardwareBuffer_allocate) {
|
||||
#endif
|
||||
if (__builtin_available(android 26, *)) {
|
||||
// destroy the current storage if any
|
||||
if (ets->hardwareBuffer) {
|
||||
AHardwareBuffer_release(ets->hardwareBuffer);
|
||||
ets->hardwareBuffer = nullptr;
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
} else {
|
||||
if (ets->graphicBufferWrapper) {
|
||||
JNIEnv* env = VirtualMachineEnv::get().getEnvironment();
|
||||
@@ -235,7 +217,6 @@ void ExternalTextureManagerAndroid::destroyStorage(backend::Platform::ExternalTe
|
||||
ets->graphicBufferWrapper = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
#include <android/api-level.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#if __ANDROID_API__ >= 26
|
||||
# define PLATFORM_HAS_HARDWAREBUFFER
|
||||
#endif
|
||||
|
||||
namespace filament {
|
||||
|
||||
/*
|
||||
@@ -78,16 +74,9 @@ private:
|
||||
|
||||
VirtualMachineEnv& mVm;
|
||||
|
||||
#ifndef PLATFORM_HAS_HARDWAREBUFFER
|
||||
// if we compile for API 26 (Oreo) and above, we're guaranteed to have AHardwareBuffer
|
||||
// in all other cases, we need to get them at runtime.
|
||||
int (*AHardwareBuffer_allocate)(const AHardwareBuffer_Desc*, AHardwareBuffer**) = nullptr;
|
||||
void (*AHardwareBuffer_release)(AHardwareBuffer*) = nullptr;
|
||||
|
||||
jclass mGraphicBufferClass = nullptr;
|
||||
jmethodID mGraphicBuffer_nCreateGraphicBuffer = nullptr;
|
||||
jmethodID mGraphicBuffer_nDestroyGraphicBuffer = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -272,6 +272,9 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) noexcept {
|
||||
for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) {
|
||||
BlockInfo blockInfo = blockInfos[i];
|
||||
HwSamplerGroup const * const UTILS_RESTRICT hwsb = samplerBindings[blockInfo.binding];
|
||||
if (UTILS_UNLIKELY(!hwsb)) {
|
||||
continue;
|
||||
}
|
||||
SamplerGroup const& UTILS_RESTRICT sb = *(hwsb->sb);
|
||||
SamplerGroup::Sampler const* const UTILS_RESTRICT samplers = sb.getSamplers();
|
||||
for (uint8_t j = 0, m = blockInfo.count ; j <= m; ++j, ++tmu) { // "<=" on purpose here
|
||||
|
||||
@@ -47,18 +47,7 @@ void ExternalStreamManagerAndroid::destroy(ExternalStreamManagerAndroid* pExtern
|
||||
|
||||
ExternalStreamManagerAndroid::ExternalStreamManagerAndroid() noexcept
|
||||
: mVm(VirtualMachineEnv::get()) {
|
||||
|
||||
// the following dlsym() calls don't work on API 19
|
||||
if (api_level() >= 21) {
|
||||
loadSymbol(ASurfaceTexture_fromSurfaceTexture, "ASurfaceTexture_fromSurfaceTexture");
|
||||
loadSymbol(ASurfaceTexture_release, "ASurfaceTexture_release");
|
||||
loadSymbol(ASurfaceTexture_attachToGLContext, "ASurfaceTexture_attachToGLContext");
|
||||
loadSymbol(ASurfaceTexture_detachFromGLContext, "ASurfaceTexture_detachFromGLContext");
|
||||
loadSymbol(ASurfaceTexture_updateTexImage, "ASurfaceTexture_updateTexImage");
|
||||
loadSymbol(ASurfaceTexture_getTimestamp, "ASurfaceTexture_getTimestamp");
|
||||
}
|
||||
|
||||
if (ASurfaceTexture_fromSurfaceTexture) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
slog.d << "Using ASurfaceTexture" << io::endl;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +84,7 @@ Stream* ExternalStreamManagerAndroid::acquire(jobject surfaceTexture) noexcept {
|
||||
}
|
||||
EGLStream* stream = new EGLStream();
|
||||
stream->jSurfaceTexture = env->NewGlobalRef(surfaceTexture);
|
||||
if (ASurfaceTexture_fromSurfaceTexture) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
stream->nSurfaceTexture = ASurfaceTexture_fromSurfaceTexture(env, surfaceTexture);
|
||||
}
|
||||
return stream;
|
||||
@@ -103,7 +92,7 @@ Stream* ExternalStreamManagerAndroid::acquire(jobject surfaceTexture) noexcept {
|
||||
|
||||
void ExternalStreamManagerAndroid::release(Stream* handle) noexcept {
|
||||
EGLStream* stream = static_cast<EGLStream*>(handle);
|
||||
if (ASurfaceTexture_fromSurfaceTexture) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
ASurfaceTexture_release(stream->nSurfaceTexture);
|
||||
}
|
||||
JNIEnv* const env = getEnvironment();
|
||||
@@ -114,7 +103,7 @@ void ExternalStreamManagerAndroid::release(Stream* handle) noexcept {
|
||||
|
||||
void ExternalStreamManagerAndroid::attach(Stream* handle, intptr_t tname) noexcept {
|
||||
EGLStream* stream = static_cast<EGLStream*>(handle);
|
||||
if (ASurfaceTexture_fromSurfaceTexture) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
// associate our GL texture to the SurfaceTexture
|
||||
ASurfaceTexture* const aSurfaceTexture = stream->nSurfaceTexture;
|
||||
if (UTILS_UNLIKELY(ASurfaceTexture_attachToGLContext(aSurfaceTexture, (uint32_t)tname))) {
|
||||
@@ -152,7 +141,7 @@ void ExternalStreamManagerAndroid::attach(Stream* handle, intptr_t tname) noexce
|
||||
|
||||
void ExternalStreamManagerAndroid::detach(Stream* handle) noexcept {
|
||||
EGLStream* stream = static_cast<EGLStream*>(handle);
|
||||
if (ASurfaceTexture_fromSurfaceTexture) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
ASurfaceTexture_detachFromGLContext(stream->nSurfaceTexture);
|
||||
} else {
|
||||
JNIEnv* const env = mVm.getEnvironment();
|
||||
@@ -164,14 +153,9 @@ void ExternalStreamManagerAndroid::detach(Stream* handle) noexcept {
|
||||
|
||||
void ExternalStreamManagerAndroid::updateTexImage(Stream* handle, int64_t* timestamp) noexcept {
|
||||
EGLStream* stream = static_cast<EGLStream*>(handle);
|
||||
if (ASurfaceTexture_fromSurfaceTexture) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
ASurfaceTexture_updateTexImage(stream->nSurfaceTexture);
|
||||
if (ASurfaceTexture_getTimestamp) {
|
||||
*timestamp = ASurfaceTexture_getTimestamp(stream->nSurfaceTexture);
|
||||
} else {
|
||||
// if we're not at least on API 28, we may not have getTimestamp()
|
||||
*timestamp = (std::chrono::steady_clock::now().time_since_epoch().count());
|
||||
}
|
||||
*timestamp = ASurfaceTexture_getTimestamp(stream->nSurfaceTexture);
|
||||
} else {
|
||||
JNIEnv* const env = mVm.getEnvironment();
|
||||
assert_invariant(env); // we should have called attach() by now
|
||||
|
||||
@@ -84,13 +84,6 @@ private:
|
||||
jmethodID mSurfaceTextureClass_getTimestamp{};
|
||||
jmethodID mSurfaceTextureClass_attachToGLContext{};
|
||||
jmethodID mSurfaceTextureClass_detachFromGLContext{};
|
||||
|
||||
ASurfaceTexture* (*ASurfaceTexture_fromSurfaceTexture)(JNIEnv*, jobject){};
|
||||
void (*ASurfaceTexture_release)(ASurfaceTexture*){};
|
||||
int (*ASurfaceTexture_attachToGLContext)(ASurfaceTexture*, uint32_t){};
|
||||
int (*ASurfaceTexture_detachFromGLContext)(ASurfaceTexture*){};
|
||||
int (*ASurfaceTexture_updateTexImage)(ASurfaceTexture*){};
|
||||
int64_t (*ASurfaceTexture_getTimestamp)(ASurfaceTexture*){}; // available since api 28
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -39,6 +39,7 @@ class SkinningBuffer;
|
||||
class IndirectLight;
|
||||
class Material;
|
||||
class MaterialInstance;
|
||||
class MorphTargetBuffer;
|
||||
class Renderer;
|
||||
class RenderTarget;
|
||||
class Scene;
|
||||
@@ -421,6 +422,7 @@ public:
|
||||
bool destroy(const Fence* p); //!< Destroys a Fence object.
|
||||
bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object.
|
||||
bool destroy(const SkinningBuffer* p); //!< Destroys a SkinningBuffer object.
|
||||
bool destroy(const MorphTargetBuffer* p); //!< Destroys a MorphTargetBuffer object.
|
||||
bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object.
|
||||
|
||||
/**
|
||||
|
||||
@@ -193,9 +193,12 @@ public:
|
||||
//! Returns the refraction mode used by this material.
|
||||
RefractionMode getRefractionMode() const noexcept;
|
||||
|
||||
// Return the refraction type used by this material.
|
||||
//! Return the refraction type used by this material.
|
||||
RefractionType getRefractionType() const noexcept;
|
||||
|
||||
//! Returns the reflection mode used by this material.
|
||||
ReflectionMode getReflectionMode() const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the number of parameters declared by this material.
|
||||
* The returned value can be 0.
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_MORPHTARGETBUFFER_H
|
||||
#define TNT_FILAMENT_MORPHTARGETBUFFER_H
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
|
||||
namespace filament {
|
||||
|
||||
/**
|
||||
* MorphTargetBuffer is used to hold morphing data (positions and tangents).
|
||||
*/
|
||||
class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
Builder(Builder const& rhs) noexcept;
|
||||
Builder(Builder&& rhs) noexcept;
|
||||
~Builder() noexcept;
|
||||
Builder& operator=(Builder const& rhs) noexcept;
|
||||
Builder& operator=(Builder&& rhs) noexcept;
|
||||
|
||||
/**
|
||||
* Size of the morph targets in vertex counts.
|
||||
* @param vertexCount Number of vertex counts the morph targets can hold.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& vertexCount(size_t vertexCount) noexcept;
|
||||
|
||||
/**
|
||||
* Size of the morph targets in targets.
|
||||
* @param count Number of targets the morph targets can hold.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& count(size_t count) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the MorphTargetBuffer object and returns a pointer to it.
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this MorphTargetBuffer with.
|
||||
*
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*
|
||||
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
|
||||
* memory or other resources.
|
||||
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
|
||||
*/
|
||||
MorphTargetBuffer* build(Engine& engine);
|
||||
private:
|
||||
friend class FMorphTargetBuffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the position of morph target at the index.
|
||||
* @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
|
||||
*/
|
||||
void setPositionsAt(Engine& engine, size_t targetIndex, math::float3 const* positions, size_t count);
|
||||
|
||||
/**
|
||||
* Updates the position of morph target at the index.
|
||||
* @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
|
||||
*/
|
||||
void setPositionsAt(Engine& engine, size_t targetIndex, math::float4 const* positions, size_t count);
|
||||
|
||||
/**
|
||||
* Updates the position of morph target at the index.
|
||||
* @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
|
||||
*/
|
||||
void setTangentsAt(Engine& engine, size_t targetIndex, math::short4 const* tangents, size_t count);
|
||||
|
||||
/**
|
||||
* Returns the vertex count of this MorphTargetBuffer.
|
||||
* @return The number of vertices the MorphTargetBuffer holds.
|
||||
*/
|
||||
size_t getVertexCount() const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the target count of this MorphTargetBuffer.
|
||||
* @return The number of targets the MorphTargetBuffer holds.
|
||||
*/
|
||||
size_t getCount() const noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif //TNT_FILAMENT_MORPHTARGETBUFFER_H
|
||||
@@ -316,6 +316,18 @@ struct TemporalAntiAliasingOptions {
|
||||
bool enabled = false; //!< enables or disables temporal anti-aliasing
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for Screen-space Reflections.
|
||||
* @see setScreenSpaceReflectionsOptions()
|
||||
*/
|
||||
struct ScreenSpaceReflectionsOptions {
|
||||
float thickness = 0.5f; //!< ray thickness, in world units
|
||||
float bias = 0.01f; //!< bias, in world units, to prevent self-intersections
|
||||
float maxDistance = 3.0f; //!< maximum distance, in world units, to raycast
|
||||
float stride = 1.0f; //!< stride, in texels, for samples along the ray.
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of available post-processing anti-aliasing techniques.
|
||||
* @see setAntiAliasing, getAntiAliasing, setSampleCount
|
||||
|
||||
@@ -37,6 +37,12 @@ class Texture;
|
||||
* An offscreen render target that can be associated with a View and contains
|
||||
* weak references to a set of attached Texture objects.
|
||||
*
|
||||
* RenderTarget is intended to be used with the View's post-processing disabled for the most part.
|
||||
* especially when a DEPTH attachment is also used (see Builder::texture()).
|
||||
*
|
||||
* Custom RenderTarget are ultimately intended to render into textures that might be used during
|
||||
* the main render pass.
|
||||
*
|
||||
* Clients are responsible for the lifetime of all associated Texture attachments.
|
||||
*
|
||||
* @see View
|
||||
@@ -87,6 +93,14 @@ public:
|
||||
*
|
||||
* All RenderTargets must have a non-null COLOR attachment.
|
||||
*
|
||||
* When using a DEPTH attachment, it is important to always disable post-processing
|
||||
* in the View. Failing to do so will cause the DEPTH attachment to be ignored in most
|
||||
* cases.
|
||||
*
|
||||
* When the intention is to keep the content of the DEPTH attachment after rendering,
|
||||
* Usage::SAMPLEABLE must be set on the DEPTH attachment, otherwise the content of the
|
||||
* DEPTH buffer may be discarded.
|
||||
*
|
||||
* @param attachment The attachment point of the texture.
|
||||
* @param texture The associated texture object.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
|
||||
@@ -41,9 +41,11 @@ class Engine;
|
||||
class IndexBuffer;
|
||||
class Material;
|
||||
class MaterialInstance;
|
||||
class MorphTargetBuffer;
|
||||
class Renderer;
|
||||
class SkinningBuffer;
|
||||
class VertexBuffer;
|
||||
class Texture;
|
||||
|
||||
class FEngine;
|
||||
class FRenderPrimitive;
|
||||
@@ -300,9 +302,6 @@ public:
|
||||
/**
|
||||
* Controls if the renderable has vertex morphing targets, false by default.
|
||||
*
|
||||
* This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer
|
||||
* must provide data in the appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc).
|
||||
*
|
||||
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
|
||||
* to advance the animation.
|
||||
*/
|
||||
@@ -456,12 +455,16 @@ public:
|
||||
/**
|
||||
* Updates the vertex morphing weights on a renderable, all zeroes by default.
|
||||
*
|
||||
* This is specified using a 4-tuple, one float per morph target. If the renderable has fewer
|
||||
* than 4 morph targets, then clients should fill the unused components with zeroes.
|
||||
*
|
||||
* The renderable must be built with morphing enabled, see Builder::morphing().
|
||||
*/
|
||||
void setMorphWeights(Instance instance, math::float4 const& weights) noexcept;
|
||||
void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Associates a MorphTargetBuffer to the given primitive.
|
||||
*/
|
||||
void setMorphTargetBufferAt(Instance instance,
|
||||
size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer) noexcept;
|
||||
|
||||
/**
|
||||
* Gets the bounding box used for frustum culling.
|
||||
|
||||
@@ -123,8 +123,6 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
|
||||
*
|
||||
* The tone mapping curve is defined by 5 parameters:
|
||||
* - contrast: controls the contrast of the curve
|
||||
* - shoulder: controls the shoulder of the curve, i.e. how quickly scene
|
||||
* referred values map to output white
|
||||
* - midGrayIn: sets the input middle gray
|
||||
* - midGrayOut: sets the output middle gray
|
||||
* - hdrMax: defines the maximum input value that will be mapped to
|
||||
@@ -138,18 +136,15 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
|
||||
*
|
||||
* @param contrast: controls the contrast of the curve, must be > 0.0, values
|
||||
* in the range 0.5..2.0 are recommended.
|
||||
* @param shoulder: controls the shoulder of the curve, i.e. how quickly scene
|
||||
* referred values map to output white, between 0.0 and 1.0.
|
||||
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
|
||||
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
|
||||
* @param hdrMax: defines the maximum input value that will be mapped to
|
||||
* output white. Must be >= 1.0.
|
||||
*/
|
||||
explicit GenericToneMapper(
|
||||
float contrast = 1.585f,
|
||||
float shoulder = 0.5f,
|
||||
float contrast = 1.55f,
|
||||
float midGrayIn = 0.18f,
|
||||
float midGrayOut = 0.268f,
|
||||
float midGrayOut = 0.215f,
|
||||
float hdrMax = 10.0f
|
||||
) noexcept;
|
||||
~GenericToneMapper() noexcept final;
|
||||
@@ -179,9 +174,6 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
|
||||
/** Sets the contrast of the curve, must be > 0.0, values in the range 0.5..2.0 are recommended. */
|
||||
void setContrast(float contrast) noexcept;
|
||||
|
||||
/** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */
|
||||
void setShoulder(float shoulder) noexcept;
|
||||
|
||||
/** Sets the input middle gray, between 0.0 and 1.0. */
|
||||
void setMidGrayIn(float midGrayIn) noexcept;
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
using MultiSampleAntiAliasingOptions = MultiSampleAntiAliasingOptions;
|
||||
using VsmShadowOptions = VsmShadowOptions;
|
||||
using SoftShadowOptions = SoftShadowOptions;
|
||||
using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions;
|
||||
|
||||
/**
|
||||
* Sets the View's name. Only useful for debugging.
|
||||
@@ -330,6 +331,20 @@ public:
|
||||
*/
|
||||
TemporalAntiAliasingOptions const& getTemporalAntiAliasingOptions() const noexcept;
|
||||
|
||||
/**
|
||||
* Enables or disable screen-space reflections. Disabled by default.
|
||||
*
|
||||
* @param options screen-space reflections options
|
||||
*/
|
||||
void setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept;
|
||||
|
||||
/**
|
||||
* Returns screen-space reflections options.
|
||||
*
|
||||
* @return screen-space reflections options
|
||||
*/
|
||||
ScreenSpaceReflectionsOptions const& getScreenSpaceReflectionsOptions() const noexcept;
|
||||
|
||||
/**
|
||||
* Enables or disable multi-sample anti-aliasing (MSAA). Disabled by default.
|
||||
*
|
||||
@@ -564,15 +579,18 @@ public:
|
||||
* Enables or disables post processing. Enabled by default.
|
||||
*
|
||||
* Post-processing includes:
|
||||
* - Depth-of-field
|
||||
* - Bloom
|
||||
* - Tone-mapping & gamma encoding
|
||||
* - Vignetting
|
||||
* - Temporal Anti-aliasing (TAA)
|
||||
* - Color grading & gamma encoding
|
||||
* - Dithering
|
||||
* - MSAA
|
||||
* - FXAA
|
||||
* - Dynamic scaling
|
||||
*
|
||||
* Disabling post-processing forgoes color correctness as well as anti-aliasing and
|
||||
* should only be used experimentally (e.g., for UI overlays).
|
||||
* Disabling post-processing forgoes color correctness as well as some anti-aliasing techniques
|
||||
* and should only be used for debugging, UI overlays or when using custom render targets
|
||||
* (see RenderTarget).
|
||||
*
|
||||
* @param enabled true enables post processing, false disables it.
|
||||
*
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
#include "private/backend/BackendUtils.h"
|
||||
|
||||
#ifndef FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB
|
||||
# define FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB 2
|
||||
# define FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB 3
|
||||
#endif
|
||||
|
||||
#ifndef FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB
|
||||
# define FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB 1
|
||||
# define FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB 2
|
||||
#endif
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -400,7 +400,7 @@ constexpr mat3f adaptationTransform(float2 whiteBalance) noexcept {
|
||||
float y = chromaticityCoordinateIlluminantD(x) + t * 0.066f;
|
||||
|
||||
float3 lms = XYZ_to_CIECAT16 * xyY_to_XYZ({x, y, 1.0f});
|
||||
return LMS_CAT16_to_REC2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * REC2020_to_LMS_CAT16;
|
||||
return LMS_CAT16_to_Rec2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * Rec2020_to_LMS_CAT16;
|
||||
}
|
||||
|
||||
UTILS_ALWAYS_INLINE
|
||||
@@ -575,21 +575,21 @@ static mat3f selectColorGradingTransformIn(ColorGrading::ToneMapping toneMapping
|
||||
if (toneMapping == ColorGrading::ToneMapping::FILMIC) {
|
||||
return mat3f{};
|
||||
}
|
||||
return sRGB_to_REC2020;
|
||||
return sRGB_to_Rec2020;
|
||||
}
|
||||
|
||||
static mat3f selectColorGradingTransformOut(ColorGrading::ToneMapping toneMapping) noexcept {
|
||||
if (toneMapping == ColorGrading::ToneMapping::FILMIC) {
|
||||
return mat3f{};
|
||||
}
|
||||
return REC2020_to_sRGB;
|
||||
return Rec2020_to_sRGB;
|
||||
}
|
||||
|
||||
static float3 selectColorGradingLuminance(ColorGrading::ToneMapping toneMapping) noexcept {
|
||||
if (toneMapping == ColorGrading::ToneMapping::FILMIC) {
|
||||
return LUMINANCE_REC709;
|
||||
return LUMINANCE_Rec709;
|
||||
}
|
||||
return LUMINANCE_REC2020;
|
||||
return LUMINANCE_Rec2020;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
|
||||
+41
-17
@@ -52,13 +52,13 @@ constexpr mat3f sRGB_to_XYZ{
|
||||
0.1804380f, 0.0721750f, 0.9503040f
|
||||
};
|
||||
|
||||
constexpr mat3f REC2020_to_XYZ{
|
||||
constexpr mat3f Rec2020_to_XYZ{
|
||||
0.6369530f, 0.2626983f, 0.0000000f,
|
||||
0.1446169f, 0.6780088f, 0.0280731f,
|
||||
0.1688558f, 0.0592929f, 1.0608272f
|
||||
};
|
||||
|
||||
constexpr mat3f XYZ_to_REC2020{
|
||||
constexpr mat3f XYZ_to_Rec2020{
|
||||
1.7166634f, -0.6666738f, 0.0176425f,
|
||||
-0.3556733f, 1.6164557f, -0.0427770f,
|
||||
-0.2533681f, 0.0157683f, 0.9422433f
|
||||
@@ -130,6 +130,18 @@ constexpr mat3f sRGB_to_OkLab_LMS{
|
||||
0.0514459929f, 0.1073969566f, 0.6299787005f
|
||||
};
|
||||
|
||||
constexpr mat3f XYZ_to_OkLab_LMS{
|
||||
0.8189330101f, 0.3618667424f, -0.1288597137f,
|
||||
0.0329845436f, 0.9293118715f, 0.0361456387f,
|
||||
0.0482003018f, 0.2643662691f, 0.6338517070f
|
||||
};
|
||||
|
||||
constexpr mat3f OkLab_LMS_to_XYZ{
|
||||
1.227014f, -0.557800f, 0.281256f,
|
||||
-0.040580f, 1.112257f, -0.071677f,
|
||||
-0.076381f, -0.421482f, 1.586163f
|
||||
};
|
||||
|
||||
constexpr mat3f OkLab_LMS_to_OkLab{
|
||||
0.2104542553f, 1.9779984951f, 0.0259040371f,
|
||||
0.7936177850f, -2.4285922050f, 0.7827717662f,
|
||||
@@ -148,21 +160,25 @@ constexpr mat3f OkLab_LMS_to_sRGB{
|
||||
0.2309699292f, -0.3413193965f, 1.7076147010f
|
||||
};
|
||||
|
||||
constexpr mat3f sRGB_to_REC2020 = XYZ_to_REC2020 * sRGB_to_XYZ;
|
||||
constexpr mat3f sRGB_to_Rec2020 = XYZ_to_Rec2020 * sRGB_to_XYZ;
|
||||
|
||||
constexpr mat3f REC2020_to_sRGB = XYZ_to_sRGB * REC2020_to_XYZ;
|
||||
constexpr mat3f Rec2020_to_sRGB = XYZ_to_sRGB * Rec2020_to_XYZ;
|
||||
|
||||
constexpr mat3f sRGB_to_LMS_CAT16 = XYZ_to_CIECAT16 * sRGB_to_XYZ;
|
||||
|
||||
constexpr mat3f LMS_CAT16_to_sRGB = XYZ_to_sRGB * CIECAT16_to_XYZ;
|
||||
|
||||
constexpr mat3f REC2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * REC2020_to_XYZ;
|
||||
constexpr mat3f Rec2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * Rec2020_to_XYZ;
|
||||
|
||||
constexpr mat3f LMS_CAT16_to_REC2020 = XYZ_to_REC2020 * CIECAT16_to_XYZ;
|
||||
constexpr mat3f LMS_CAT16_to_Rec2020 = XYZ_to_Rec2020 * CIECAT16_to_XYZ;
|
||||
|
||||
constexpr mat3f REC2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * REC2020_to_XYZ;
|
||||
constexpr mat3f Rec2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * Rec2020_to_XYZ;
|
||||
|
||||
constexpr mat3f AP1_to_REC2020 = XYZ_to_REC2020 * AP1_to_XYZ;
|
||||
constexpr mat3f AP1_to_Rec2020 = XYZ_to_Rec2020 * AP1_to_XYZ;
|
||||
|
||||
constexpr mat3f Rec2020_to_OkLab_LMS = XYZ_to_OkLab_LMS * Rec2020_to_XYZ;
|
||||
|
||||
constexpr mat3f OkLab_LMS_to_Rec2020 = XYZ_to_Rec2020 * OkLab_LMS_to_XYZ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
@@ -175,17 +191,17 @@ constexpr float3 ILLUMINANT_D65_xyY{0.31271f, 0.32902f, 1.0f};
|
||||
// Result of: XYZ_to_CIECAT16 * xyY_to_XYZ(ILLUMINANT_D65_xyY);
|
||||
constexpr float3 ILLUMINANT_D65_LMS_CAT16{0.975533f, 1.016483f, 1.084837f};
|
||||
|
||||
// RGB to luminance coefficients for Rec.2020, from REC2020_to_XYZ
|
||||
constexpr float3 LUMINANCE_REC2020{0.2627002f, 0.6779981f, 0.0593017f};
|
||||
// RGB to luminance coefficients for Rec.2020, from Rec2020_to_XYZ
|
||||
constexpr float3 LUMINANCE_Rec2020{0.2627002f, 0.6779981f, 0.0593017f};
|
||||
|
||||
// RGB to luminance coefficients for ACEScg (AP1), from AP1_to_XYZ
|
||||
constexpr float3 LUMINANCE_AP1{0.272229f, 0.674082f, 0.0536895f};
|
||||
|
||||
// RGB to luminance coefficients for Rec.709, from sRGB_to_XYZ
|
||||
constexpr float3 LUMINANCE_REC709{0.2126730f, 0.7151520f, 0.0721750f};
|
||||
constexpr float3 LUMINANCE_Rec709{0.2126730f, 0.7151520f, 0.0721750f};
|
||||
|
||||
// RGB to luminance coefficients for Rec.709 with HK-like weighting
|
||||
constexpr float3 LUMINANCE_HK_REC709{0.13913043f, 0.73043478f, 0.13043478f};
|
||||
constexpr float3 LUMINANCE_HK_Rec709{0.13913043f, 0.73043478f, 0.13043478f};
|
||||
|
||||
constexpr float MIDDLE_GRAY_ACEScg = 0.18f;
|
||||
|
||||
@@ -212,21 +228,29 @@ inline constexpr XYZ xyY_to_XYZ(xyY v) noexcept {
|
||||
}
|
||||
|
||||
inline constexpr xyY XYZ_to_xyY(XYZ v) noexcept {
|
||||
return float3(v.xy / max(v.x + v.y + v.z, 1e-5f), v.y);
|
||||
}
|
||||
|
||||
inline float3 sRGB_to_OkLab(float3 x) noexcept {
|
||||
return OkLab_LMS_to_OkLab * cbrt(sRGB_to_OkLab_LMS * x);
|
||||
return {v.xy / max(v.x + v.y + v.z, 1e-5f), v.y};
|
||||
}
|
||||
|
||||
inline constexpr float3 pow3(float3 x) noexcept {
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
inline float3 sRGB_to_OkLab(float3 x) noexcept {
|
||||
return OkLab_LMS_to_OkLab * cbrt(sRGB_to_OkLab_LMS * x);
|
||||
}
|
||||
|
||||
inline float3 Rec2020_to_OkLab(float3 x) noexcept {
|
||||
return OkLab_LMS_to_OkLab * cbrt(Rec2020_to_OkLab_LMS * x);
|
||||
}
|
||||
|
||||
inline float3 OkLab_to_sRGB(float3 x) noexcept {
|
||||
return OkLab_LMS_to_sRGB * pow3(OkLab_to_OkLab_LMS * x);
|
||||
}
|
||||
|
||||
inline float3 OkLab_to_Rec2020(float3 x) noexcept {
|
||||
return OkLab_LMS_to_Rec2020 * pow3(OkLab_to_OkLab_LMS * x);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Conversion functions and encoding/decoding
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -333,6 +333,7 @@ void FEngine::shutdown() {
|
||||
|
||||
cleanupResourceList(mBufferObjects);
|
||||
cleanupResourceList(mIndexBuffers);
|
||||
cleanupResourceList(mMorphTargetBuffers);
|
||||
cleanupResourceList(mSkinningBuffers);
|
||||
cleanupResourceList(mVertexBuffers);
|
||||
cleanupResourceList(mTextures);
|
||||
@@ -567,6 +568,10 @@ FSkinningBuffer* FEngine::createSkinningBuffer(const SkinningBuffer::Builder& bu
|
||||
return create(mSkinningBuffers, builder);
|
||||
}
|
||||
|
||||
FMorphTargetBuffer* FEngine::createMorphTargetBuffer(const MorphTargetBuffer::Builder& builder) noexcept {
|
||||
return create(mMorphTargetBuffers, builder);
|
||||
}
|
||||
|
||||
FTexture* FEngine::createTexture(const Texture::Builder& builder) noexcept {
|
||||
return create(mTextures, builder);
|
||||
}
|
||||
@@ -753,6 +758,10 @@ bool FEngine::destroy(const FSkinningBuffer* p) {
|
||||
return terminateAndDestroy(p, mSkinningBuffers);
|
||||
}
|
||||
|
||||
bool FEngine::destroy(const FMorphTargetBuffer* p) {
|
||||
return terminateAndDestroy(p, mMorphTargetBuffers);
|
||||
}
|
||||
|
||||
inline bool FEngine::destroy(const FRenderer* p) {
|
||||
return terminateAndDestroy(p, mRenderers);
|
||||
}
|
||||
@@ -966,6 +975,10 @@ bool Engine::destroy(const SkinningBuffer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const MorphTargetBuffer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const IndirectLight* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace filament {
|
||||
struct FrameHistoryEntry {
|
||||
FrameGraphTexture color;
|
||||
FrameGraphTexture::Descriptor colorDesc;
|
||||
math::mat4f projection;
|
||||
math::mat4f projection; // world space to clip space
|
||||
math::float2 jitter{};
|
||||
uint32_t frameId = 0;
|
||||
|
||||
@@ -60,6 +60,10 @@ public:
|
||||
return mCurrentEntry;
|
||||
}
|
||||
|
||||
const T& getCurrent() const noexcept {
|
||||
return mCurrentEntry;
|
||||
}
|
||||
|
||||
// This pushes the current frame info to the FIFO, effectively destroying
|
||||
// the oldest state (note: only the structure is destroyed, handles stored in it may
|
||||
// have to be destroyed prior to calling this).
|
||||
|
||||
@@ -189,6 +189,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
parser->getRequiredAttributes(&mRequiredAttributes);
|
||||
parser->getRefractionMode(&mRefractionMode);
|
||||
parser->getRefractionType(&mRefractionType);
|
||||
parser->getReflectionMode(&mReflectionMode);
|
||||
|
||||
if (mBlendingMode == BlendingMode::MASKED) {
|
||||
parser->getMaskThreshold(&mMaskThreshold);
|
||||
@@ -378,6 +379,10 @@ Handle<HwProgram> FMaterial::getSurfaceProgramSlow(uint8_t variantKey) const noe
|
||||
|
||||
if (Variant(variantKey).hasSkinningOrMorphing()) {
|
||||
pb.setUniformBlock(BindingPoints::PER_RENDERABLE_BONES, PerRenderableUibBone::_name);
|
||||
pb.setUniformBlock(BindingPoints::PER_RENDERABLE_MORPHING, PerRenderableMorphingUib::_name);
|
||||
|
||||
addSamplerGroup(pb, BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
SibGenerator::getPerRenderPrimitiveMorphingSib(variantKey), mSamplerBindings);
|
||||
}
|
||||
|
||||
addSamplerGroup(pb, BindingPoints::PER_VIEW, SibGenerator::getPerViewSib(variantKey), mSamplerBindings);
|
||||
@@ -649,6 +654,10 @@ RefractionType Material::getRefractionType() const noexcept {
|
||||
return upcast(this)->getRefractionType();
|
||||
}
|
||||
|
||||
ReflectionMode Material::getReflectionMode() const noexcept {
|
||||
return upcast(this)->getReflectionMode();
|
||||
}
|
||||
|
||||
bool Material::hasParameter(const char* name) const noexcept {
|
||||
return upcast(this)->hasParameter(name);
|
||||
}
|
||||
|
||||
@@ -269,6 +269,12 @@ bool MaterialParser::getRefractionType(RefractionType* value) const noexcept {
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialRefractionType, (uint8_t*)value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getReflectionMode(ReflectionMode* value) const noexcept {
|
||||
static_assert(sizeof(ReflectionMode) == sizeof(uint8_t),
|
||||
"ReflectionMode expected size is wrong");
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialReflectionMode, (uint8_t*)value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getShader(ShaderBuilder& shader,
|
||||
ShaderModel shaderModel, uint8_t variant, ShaderType stage) noexcept {
|
||||
return mImpl.mMaterialChunk.getShader(shader,
|
||||
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
bool getRequiredAttributes(AttributeBitset*) const noexcept;
|
||||
bool getRefractionMode(RefractionMode* value) const noexcept;
|
||||
bool getRefractionType(RefractionType* value) const noexcept;
|
||||
bool getReflectionMode(ReflectionMode* value) const noexcept;
|
||||
bool hasCustomDepthShader(bool* value) const noexcept;
|
||||
bool hasSpecularAntiAliasing(bool* value) const noexcept;
|
||||
bool getSpecularAntiAliasingVariance(float* value) const noexcept;
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "details/MorphTargetBuffer.h"
|
||||
|
||||
#include "private/filament/SibGenerator.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <math/norm.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
struct MorphTargetBuffer::BuilderDetails {
|
||||
size_t mVertexCount = 0;
|
||||
size_t mCount = 0;
|
||||
};
|
||||
|
||||
using BuilderType = MorphTargetBuffer;
|
||||
BuilderType::Builder::Builder() noexcept = default;
|
||||
BuilderType::Builder::~Builder() noexcept = default;
|
||||
BuilderType::Builder::Builder(BuilderType::Builder const& rhs) noexcept = default;
|
||||
BuilderType::Builder::Builder(BuilderType::Builder&& rhs) noexcept = default;
|
||||
BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder const& rhs) noexcept = default;
|
||||
BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder&& rhs) noexcept = default;
|
||||
|
||||
MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::vertexCount(size_t vertexCount) noexcept {
|
||||
mImpl->mVertexCount = vertexCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::count(size_t count) noexcept {
|
||||
mImpl->mCount = count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MorphTargetBuffer* MorphTargetBuffer::Builder::build(Engine& engine) {
|
||||
return upcast(engine).createMorphTargetBuffer(*this);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// This value is limited by ES3.0, ES3.0 only guarantees 2048.
|
||||
// When you change this value, you must change MAX_MORPH_TARGET_BUFFER_WIDTH at getters.vs
|
||||
constexpr size_t MAX_MORPH_TARGET_BUFFER_WIDTH = 2048;
|
||||
|
||||
static const auto FREE_CALLBACK = [](void* mem, size_t, void*) { ::free(mem); };
|
||||
|
||||
static inline size_t getWidth(size_t vertexCount) noexcept {
|
||||
return std::min(vertexCount, MAX_MORPH_TARGET_BUFFER_WIDTH);
|
||||
}
|
||||
|
||||
static inline size_t getHeight(size_t vertexCount) noexcept {
|
||||
return (vertexCount + MAX_MORPH_TARGET_BUFFER_WIDTH) / MAX_MORPH_TARGET_BUFFER_WIDTH;
|
||||
}
|
||||
|
||||
static inline size_t getDepth(size_t targetCount) noexcept {
|
||||
return targetCount * 2;
|
||||
}
|
||||
|
||||
static inline size_t getSize(size_t vertexCount) noexcept {
|
||||
const size_t stride = getWidth(vertexCount) * sizeof(float4);
|
||||
return Texture::PixelBufferDescriptor::computeDataSize(
|
||||
Texture::PixelBufferDescriptor::PixelDataFormat::RGBA,
|
||||
Texture::PixelBufferDescriptor::PixelDataType::FLOAT,
|
||||
stride, getHeight(vertexCount), 1);
|
||||
}
|
||||
|
||||
static inline size_t getPositionIndex(size_t targetIndex) noexcept {
|
||||
return targetIndex * 2 + 0;
|
||||
}
|
||||
|
||||
static inline size_t getTangentIndex(size_t targetIndex) noexcept {
|
||||
return targetIndex * 2 + 1;
|
||||
}
|
||||
|
||||
FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
|
||||
: mSBuffer(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT),
|
||||
mVertexCount(builder->mVertexCount),
|
||||
mCount(builder->mCount) {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
mSbHandle = driver.createSamplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT);
|
||||
mTbHandle = driver.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1, TextureFormat::RGBA32F, 1,
|
||||
getWidth(mVertexCount), getHeight(mVertexCount), getDepth(mCount),
|
||||
TextureUsage::DEFAULT);
|
||||
|
||||
SamplerParams samplerParams{};
|
||||
samplerParams.filterMin = SamplerMinFilter::NEAREST;
|
||||
samplerParams.filterMag = SamplerMagFilter::NEAREST;
|
||||
mSBuffer.setSampler(PerRenderPrimitiveMorphingSib::TARGETS, mTbHandle, samplerParams);
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::terminate(FEngine& engine) {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
driver.destroySamplerGroup(mSbHandle);
|
||||
driver.destroyTexture(mTbHandle);
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex, math::float3 const* positions, size_t count) {
|
||||
ASSERT_PRECONDITION(targetIndex < mCount, "targetIndex must be < count");
|
||||
|
||||
auto size = getSize(mVertexCount);
|
||||
|
||||
ASSERT_PRECONDITION((int)sizeof(math::float3) * count <= size,
|
||||
"MorphTargetBuffer (size=%lu) overflow (size=%lu)",
|
||||
size, sizeof(math::float3) * count);
|
||||
|
||||
// We could use a pool instead of malloc() directly.
|
||||
auto* out = (float4*) malloc(size);
|
||||
std::transform(positions, positions + count, out,
|
||||
[](const float3& p) { return float4(p, 1.0f); });
|
||||
|
||||
Texture::PixelBufferDescriptor buffer(out, size, Texture::Format::RGBA, Texture::Type::FLOAT,
|
||||
FREE_CALLBACK);
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
driver.update3DImage(mTbHandle, 0, 0, 0, getPositionIndex(targetIndex),
|
||||
getWidth(mVertexCount), getHeight(mVertexCount), 1,
|
||||
std::move(buffer));
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex, math::float4 const* positions, size_t count) {
|
||||
ASSERT_PRECONDITION(targetIndex < mCount, "targetIndex must be < count");
|
||||
|
||||
auto size = getSize(mVertexCount);
|
||||
|
||||
ASSERT_PRECONDITION((int)sizeof(math::float4) * count <= size,
|
||||
"MorphTargetBuffer (size=%lu) overflow (size=%lu)",
|
||||
size, sizeof(math::float4) * count);
|
||||
|
||||
// We could use a pool instead of malloc() directly.
|
||||
auto* out = (float4*) malloc(size);
|
||||
memcpy(out, positions, sizeof(math::float4) * count);
|
||||
|
||||
Texture::PixelBufferDescriptor buffer(out, size, Texture::Format::RGBA, Texture::Type::FLOAT,
|
||||
FREE_CALLBACK);
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
driver.update3DImage(mTbHandle, 0, 0, 0, getPositionIndex(targetIndex),
|
||||
getWidth(mVertexCount), getHeight(mVertexCount), 1,
|
||||
std::move(buffer));
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t targetIndex, math::short4 const* tangents, size_t count) {
|
||||
ASSERT_PRECONDITION(targetIndex < mCount, "targetIndex must be < count");
|
||||
|
||||
auto size = getSize(mVertexCount);
|
||||
|
||||
ASSERT_PRECONDITION((int)sizeof(math::short4) * count <= size,
|
||||
"MorphTargetBuffer (size=%lu) overflow (size=%lu)",
|
||||
size, sizeof(math::short4) * count);
|
||||
|
||||
// We could use a pool instead of malloc() directly.
|
||||
auto* out = (float4*) malloc(size);
|
||||
std::transform(tangents, tangents + count, out,
|
||||
[](const short4& t) { return unpackSnorm16(t); });
|
||||
|
||||
Texture::PixelBufferDescriptor buffer(out, size, Texture::Format::RGBA, Texture::Type::FLOAT,
|
||||
FREE_CALLBACK);
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
driver.update3DImage(mTbHandle, 0, 0, 0, getTangentIndex(targetIndex),
|
||||
getWidth(mVertexCount), getHeight(mVertexCount), 1,
|
||||
std::move(buffer));
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::commit(FEngine& engine) const noexcept {
|
||||
if (mSBuffer.isDirty()) {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
driver.updateSamplerGroup(mSbHandle, std::move(mSBuffer.toCommandStream()));
|
||||
}
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::bind(backend::DriverApi& driver) const noexcept {
|
||||
driver.bindSamplers(BindingPoints::PER_RENDERABLE_MORPHING, mSbHandle);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Trampoline calling into private implementation
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex, math::float3 const* positions, size_t count) {
|
||||
upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count);
|
||||
}
|
||||
|
||||
void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex, math::float4 const* positions, size_t count) {
|
||||
upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count);
|
||||
}
|
||||
|
||||
void MorphTargetBuffer::setTangentsAt(Engine& engine, size_t targetIndex, math::short4 const* tangents, size_t count) {
|
||||
upcast(this)->setTangentsAt(upcast(engine), targetIndex, tangents, count);
|
||||
}
|
||||
|
||||
size_t MorphTargetBuffer::getVertexCount() const noexcept {
|
||||
return upcast(this)->getVertexCount();
|
||||
}
|
||||
|
||||
size_t MorphTargetBuffer::getCount() const noexcept {
|
||||
return upcast(this)->getCount();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -177,6 +177,31 @@ void PerViewUniforms::prepareSSR(Handle<HwTexture> ssr, float refractionLodOffse
|
||||
s.refractionLodOffset = refractionLodOffset;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareSSReflections(TextureHandle ssr, math::mat4f const& historyProjection,
|
||||
math::mat4f const& projectToPixelMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
|
||||
mPerViewSb.setSampler(PerViewSib::SSR, ssr, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
auto& s = mPerViewUb.edit();
|
||||
s.ssrReprojection = historyProjection;
|
||||
s.ssrProjectToPixelMatrix = projectToPixelMatrix;
|
||||
s.ssrThickness = ssrOptions.thickness;
|
||||
s.ssrBias = ssrOptions.bias;
|
||||
s.ssrDistance = ssrOptions.maxDistance;
|
||||
s.ssrStride = ssrOptions.stride;
|
||||
}
|
||||
|
||||
void PerViewUniforms::disableSSReflections() noexcept {
|
||||
// We must bind a placeholder texture, even if reflections are disabled, to avoid a
|
||||
// "no texture unit bound" warning.
|
||||
Handle<backend::HwTexture> placeholder = mEngine.getPostProcessManager().getOneTexture();
|
||||
auto& s = mPerViewUb.edit();
|
||||
mPerViewSb.setSampler(PerViewSib::SSR, placeholder, {});
|
||||
s.ssrDistance = 0.0f;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareStructure(Handle<HwTexture> structure) noexcept {
|
||||
// sampler must be NEAREST
|
||||
mPerViewSb.setSampler(PerViewSib::STRUCTURE, structure, {});
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace filament {
|
||||
struct AmbientOcclusionOptions;
|
||||
struct DynamicResolutionOptions;
|
||||
struct FogOptions;
|
||||
struct ScreenSpaceReflectionsOptions;
|
||||
struct SoftShadowOptions;
|
||||
struct TemporalAntiAliasingOptions;
|
||||
struct VsmShadowOptions;
|
||||
@@ -72,6 +73,10 @@ public:
|
||||
void prepareStructure(TextureHandle structure) noexcept;
|
||||
void prepareSSAO(TextureHandle ssao, AmbientOcclusionOptions const& options) noexcept;
|
||||
void prepareSSR(TextureHandle ssr, float refractionLodOffset) noexcept;
|
||||
void prepareSSReflections(TextureHandle ssr, math::mat4f const& historyProjection,
|
||||
math::mat4f const& projectToPixelMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
|
||||
void disableSSReflections() noexcept;
|
||||
void prepareShadowMapping(ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
VsmShadowOptions const& options) noexcept;
|
||||
|
||||
|
||||
@@ -317,7 +317,8 @@ void PostProcessManager::commitAndRender(FrameGraphResources::RenderPassInfo con
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::structure(FrameGraph& fg,
|
||||
RenderPass const& pass, uint32_t width, uint32_t height,
|
||||
RenderPass const& pass, uint8_t structureRenderFlags,
|
||||
uint32_t width, uint32_t height,
|
||||
StructurePassConfig const& config) noexcept {
|
||||
|
||||
const float scale = config.scale;
|
||||
@@ -367,10 +368,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::structure(FrameGraph& fg,
|
||||
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
[=, renderPass = pass](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) mutable {
|
||||
auto out = resources.getRenderPassInfo();
|
||||
pass.execute(resources.getPassName(), out.target, out.params);
|
||||
renderPass.setRenderFlags(structureRenderFlags);
|
||||
renderPass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
|
||||
renderPass.sortCommands();
|
||||
renderPass.execute(resources.getPassName(), out.target, out.params);
|
||||
});
|
||||
|
||||
auto depth = structurePass->depth;
|
||||
@@ -2167,12 +2171,8 @@ void PostProcessManager::prepareTaa(FrameHistory& frameHistory,
|
||||
auto& current = frameHistory.getCurrent();
|
||||
// get sample position within a pixel [-0.5, 0.5]
|
||||
const float2 jitter = halton(previous.frameId) - 0.5f;
|
||||
// compute the world-space to clip-space matrix for this frame
|
||||
current.projection = cameraInfo.projection * (cameraInfo.view * cameraInfo.worldOrigin);
|
||||
// save this frame's sample position
|
||||
current.jitter = jitter;
|
||||
// update frame id
|
||||
current.frameId = previous.frameId + 1;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
|
||||
@@ -70,7 +70,8 @@ public:
|
||||
// methods below are ordered relative to their position in the pipeline (as much as possible)
|
||||
|
||||
// structure (depth) pass
|
||||
FrameGraphId<FrameGraphTexture> structure(FrameGraph& fg, RenderPass const& pass,
|
||||
FrameGraphId<FrameGraphTexture> structure(FrameGraph& fg,
|
||||
RenderPass const& pass, uint8_t structureRenderFlags,
|
||||
uint32_t width, uint32_t height, StructurePassConfig const& config) noexcept;
|
||||
|
||||
// SSAO
|
||||
|
||||
@@ -381,6 +381,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
FMaterialInstance const* const mi = primitive.getMaterialInstance();
|
||||
if constexpr (isColorPass) {
|
||||
cmdColor.primitive.primitiveHandle = primitive.getHwHandle();
|
||||
cmdColor.primitive.morphTargetBuffer = primitive.getMorphTargetBuffer();
|
||||
cmdColor.primitive.materialVariant = materialVariant;
|
||||
RenderPass::setupColorCommand(cmdColor, mi, inverseFrontFaces);
|
||||
|
||||
@@ -470,6 +471,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
// unconditionally write the command
|
||||
cmdDepth.primitive.primitiveHandle = primitive.getHwHandle();
|
||||
cmdDepth.primitive.mi = mi;
|
||||
cmdDepth.primitive.morphTargetBuffer = primitive.getMorphTargetBuffer();
|
||||
cmdDepth.primitive.rasterState.culling = mi->getCullingMode();
|
||||
|
||||
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
|
||||
@@ -529,6 +531,7 @@ void RenderPass::Executor::recordDriverCommands(backend::DriverApi& driver,
|
||||
SYSTRACE_VALUE32("commandCount", last - first);
|
||||
|
||||
auto const* const UTILS_RESTRICT soaSkinning = soa.data<FScene::SKINNING_BUFFER>();
|
||||
auto const* const UTILS_RESTRICT soaMorphing = soa.data<FScene::MORPHING_BUFFER>();
|
||||
|
||||
PolygonOffset dummyPolyOffset;
|
||||
PipelineState pipeline{ .polygonOffset = mPolygonOffset };
|
||||
@@ -577,6 +580,18 @@ void RenderPass::Executor::recordDriverCommands(backend::DriverApi& driver,
|
||||
skinning.offset * sizeof(PerRenderableUibBone),
|
||||
CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone));
|
||||
}
|
||||
|
||||
auto morphTargetBuffer = info.morphTargetBuffer;
|
||||
if (UTILS_UNLIKELY(morphTargetBuffer)) {
|
||||
// Instead of using a UBO per primitive, we could also have a single UBO for all primitives
|
||||
// and use bindUniformBufferRange which might be more efficient.
|
||||
auto morphing = soaMorphing[info.index];
|
||||
assert_invariant(morphing.handle);
|
||||
driver.bindUniformBuffer(BindingPoints::PER_RENDERABLE_MORPHING, morphing.handle);
|
||||
assert_invariant(morphing.count <= morphTargetBuffer->getCount());
|
||||
morphTargetBuffer->bind(driver);
|
||||
}
|
||||
|
||||
driver.draw(pipeline, info.primitiveHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,19 +208,20 @@ public:
|
||||
return boolish ? std::numeric_limits<uint64_t>::max() : uint64_t(0);
|
||||
}
|
||||
|
||||
struct PrimitiveInfo { // 24 bytes
|
||||
struct PrimitiveInfo { // 32 bytes
|
||||
FMaterialInstance const* mi = nullptr; // 8 bytes (4)
|
||||
FMorphTargetBuffer const* morphTargetBuffer = nullptr; // 8 bytes (4)
|
||||
backend::Handle<backend::HwRenderPrimitive> primitiveHandle; // 4 bytes
|
||||
backend::RasterState rasterState; // 4 bytes
|
||||
uint16_t index = 0; // 2 bytes
|
||||
Variant materialVariant; // 1 byte
|
||||
uint8_t reserved[13 - sizeof(void*)] = {}; // 5 byte (9)
|
||||
uint8_t reserved[21 - sizeof(void*) - sizeof(void*)] = {}; // 5 bytes (13)
|
||||
};
|
||||
static_assert(sizeof(PrimitiveInfo) == 24);
|
||||
static_assert(sizeof(PrimitiveInfo) == 32);
|
||||
|
||||
struct alignas(8) Command { // 32 bytes
|
||||
struct alignas(8) Command { // 40 bytes
|
||||
CommandKey key = 0; // 8 bytes
|
||||
PrimitiveInfo primitive; // 24 bytes
|
||||
PrimitiveInfo primitive; // 32 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) {
|
||||
@@ -228,7 +229,7 @@ public:
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Command) == 32);
|
||||
static_assert(sizeof(Command) == 40);
|
||||
static_assert(std::is_trivially_destructible_v<Command>,
|
||||
"Command isn't trivially destructible");
|
||||
|
||||
|
||||
@@ -83,4 +83,8 @@ void FRenderPrimitive::set(FEngine& engine, RenderableManager::PrimitiveType typ
|
||||
mPrimitiveType = type;
|
||||
}
|
||||
|
||||
void FRenderPrimitive::set(FMorphTargetBuffer* morphTargetBuffer) noexcept {
|
||||
mMorphTargetBuffer = morphTargetBuffer;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace filament {
|
||||
class FEngine;
|
||||
class FVertexBuffer;
|
||||
class FIndexBuffer;
|
||||
class FMorphTargetBuffer;
|
||||
class FRenderer;
|
||||
|
||||
class FRenderPrimitive {
|
||||
@@ -45,6 +46,8 @@ public:
|
||||
void set(FEngine& engine, RenderableManager::PrimitiveType type,
|
||||
size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept;
|
||||
|
||||
void set(FMorphTargetBuffer* morphTargetBuffer) noexcept;
|
||||
|
||||
// frees driver resources, object becomes invalid
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
@@ -53,6 +56,7 @@ public:
|
||||
backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; }
|
||||
AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; }
|
||||
uint16_t getBlendOrder() const noexcept { return mBlendOrder; }
|
||||
FMorphTargetBuffer* getMorphTargetBuffer() const noexcept { return mMorphTargetBuffer; }
|
||||
|
||||
void setMaterialInstance(FMaterialInstance const* mi) noexcept { mMaterialInstance = mi; }
|
||||
void setBlendOrder(uint16_t order) noexcept {
|
||||
@@ -65,6 +69,7 @@ private:
|
||||
backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::NONE;
|
||||
AttributeBitset mEnabledAttributes;
|
||||
uint16_t mBlendOrder = 0;
|
||||
FMorphTargetBuffer* mMorphTargetBuffer = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+99
-19
@@ -240,6 +240,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
auto taaOptions = view.getTemporalAntiAliasingOptions();
|
||||
auto vignetteOptions = view.getVignetteOptions();
|
||||
auto colorGrading = view.getColorGrading();
|
||||
auto ssReflectionsOptions = view.getScreenSpaceReflectionsOptions();
|
||||
if (!hasPostProcess) {
|
||||
// disable all effects that are part of post-processing
|
||||
dofOptions.enabled = false;
|
||||
@@ -384,7 +385,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
.msaa = msaaSampleCount,
|
||||
.clearFlags = clearFlags,
|
||||
.clearColor = clearColor,
|
||||
.hasContactShadows = scene.hasContactShadows()
|
||||
.hasContactShadows = scene.hasContactShadows(),
|
||||
.hasScreenSpaceReflections = ssReflectionsOptions.enabled
|
||||
};
|
||||
|
||||
// asSubpass is disabled with TAA (although it's supported) because performance was degraded
|
||||
@@ -416,6 +418,9 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
// of RenderPass::setCamera / RenderPass::setGeometry calls.
|
||||
view.updatePrimitivesLod(engine, cameraInfo,
|
||||
scene.getRenderableData(), view.getVisibleRenderables());
|
||||
// updatePrimitivesMorphTargetBuffer must be run after updatePrimitivesLod.
|
||||
view.updatePrimitivesMorphTargetBuffer(engine, cameraInfo,
|
||||
scene.getRenderableData(), view.getVisibleRenderables());
|
||||
|
||||
pass.setCamera(cameraInfo);
|
||||
pass.setGeometry(scene.getRenderableData(), view.getVisibleRenderables(), scene.getRenderableUBO());
|
||||
@@ -433,14 +438,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
// Currently it consists of a simple depth pass.
|
||||
// This is normally used by SSAO and contact-shadows
|
||||
|
||||
// TODO: ideally this should be a FrameGraph pass to participate to automatic culling
|
||||
RenderPass structurePass(pass);
|
||||
structurePass.setRenderFlags(structureRenderFlags);
|
||||
structurePass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
|
||||
structurePass.sortCommands();
|
||||
|
||||
// TODO: the scaling should depends on all passes that need the structure pass
|
||||
ppm.structure(fg, structurePass, svp.width, svp.height, {
|
||||
ppm.structure(fg, pass, structureRenderFlags, svp.width, svp.height, {
|
||||
.scale = aoOptions.resolution,
|
||||
.picking = view.hasPicking()
|
||||
});
|
||||
@@ -467,6 +466,17 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
});
|
||||
}
|
||||
|
||||
// Store this frame's camera projection in the frame history.
|
||||
if (UTILS_UNLIKELY(taaOptions.enabled || ssReflectionsOptions.enabled)) {
|
||||
auto projection = cameraInfo.projection * (cameraInfo.view * cameraInfo.worldOrigin);
|
||||
setHistoryProjection(view, projection);
|
||||
// update frame id
|
||||
auto& history = view.getFrameHistory();
|
||||
auto const& previous = history[0];
|
||||
auto& current = history.getCurrent();
|
||||
current.frameId = previous.frameId + 1;
|
||||
}
|
||||
|
||||
// Apply the TAA jitter to everything after the structure pass, starting with the color pass.
|
||||
if (taaOptions.enabled) {
|
||||
auto& history = view.getFrameHistory();
|
||||
@@ -494,7 +504,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Color passes
|
||||
|
||||
// TODO: ideally this should be a FrameGraph pass to participate to automatic culling
|
||||
// This one doesn't need to be a FrameGraph pass because it always happens by construction
|
||||
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
|
||||
pass.setRenderFlags(colorRenderFlags);
|
||||
pass.appendCommands(RenderPass::COLOR);
|
||||
pass.sortCommands();
|
||||
@@ -533,7 +544,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
colorGradingConfigForColor.asSubpass = colorGradingConfigForColor.asSubpass && !taaOptions.enabled;
|
||||
|
||||
if (colorGradingConfigForColor.asSubpass) {
|
||||
// append colorgrading subpass after all other passes
|
||||
// append color grading subpass after all other passes
|
||||
pass.appendCustomCommand(
|
||||
RenderPass::Pass::BLENDED,
|
||||
RenderPass::CustomCommand::EPILOG,
|
||||
@@ -596,7 +607,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
struct ExportColorHistoryData {
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
};
|
||||
if (taaOptions.enabled) {
|
||||
if (taaOptions.enabled || ssReflectionsOptions.enabled) {
|
||||
fg.addPass<ExportColorHistoryData>("Export color history", [&](FrameGraph::Builder& builder,
|
||||
auto& data) {
|
||||
// We need to use sideEffect here to ensure this pass won't be culled. The "output" of
|
||||
@@ -829,12 +840,25 @@ FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg, const char*
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
FrameGraphId<FrameGraphTexture> ssr;
|
||||
FrameGraphId<FrameGraphTexture> ssr; // either screen-space reflections or refractions
|
||||
FrameGraphId<FrameGraphTexture> structure;
|
||||
float4 clearColor{};
|
||||
TargetBufferFlags clearFlags{};
|
||||
};
|
||||
|
||||
FrameGraphId<FrameGraphTexture> colorHistory;
|
||||
mat4f historyProjection;
|
||||
if (config.hasScreenSpaceReflections) {
|
||||
colorHistory = getColorHistory(fg, view.getFrameHistory());
|
||||
if (UTILS_UNLIKELY(!colorHistory)) {
|
||||
// if we don't have a history yet, don't render reflections this frame
|
||||
} else {
|
||||
const FrameHistory& frameHistory = view.getFrameHistory();
|
||||
FrameHistoryEntry const& entry = frameHistory[0];
|
||||
historyProjection = entry.projection;
|
||||
}
|
||||
}
|
||||
|
||||
auto& colorPass = fg.addPass<ColorPassData>(name,
|
||||
[&](FrameGraph::Builder& builder, ColorPassData& data) {
|
||||
|
||||
@@ -843,12 +867,24 @@ FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg, const char*
|
||||
TargetBufferFlags clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
|
||||
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
|
||||
data.color = blackboard.get<FrameGraphTexture>("color");
|
||||
data.depth = blackboard.get<FrameGraphTexture>("depth");
|
||||
|
||||
if (config.hasContactShadows) {
|
||||
// TODO: we currently only allow either screen-space reflections or refractions,
|
||||
// but not both. If SS reflections are enabled, we prefer those.
|
||||
if (config.hasScreenSpaceReflections && colorHistory) {
|
||||
// Screen-space reflections
|
||||
data.ssr = builder.sample(colorHistory);
|
||||
} else {
|
||||
// Screen-space refractions
|
||||
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
|
||||
if (data.ssr) {
|
||||
data.ssr = builder.sample(data.ssr);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.hasContactShadows || config.hasScreenSpaceReflections) {
|
||||
data.structure = blackboard.get<FrameGraphTexture>("structure");
|
||||
assert_invariant(data.structure);
|
||||
data.structure = builder.sample(data.structure);
|
||||
@@ -858,10 +894,6 @@ FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg, const char*
|
||||
data.shadows = builder.sample(data.shadows);
|
||||
}
|
||||
|
||||
if (data.ssr) {
|
||||
data.ssr = builder.sample(data.ssr);
|
||||
}
|
||||
|
||||
if (data.ssao) {
|
||||
data.ssao = builder.sample(data.ssao);
|
||||
}
|
||||
@@ -939,8 +971,32 @@ FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg, const char*
|
||||
view.prepareStructure(data.structure ?
|
||||
resources.getTexture(data.structure) : ppm.getOneTexture());
|
||||
|
||||
// set screen-space reflections or screen-space refractions
|
||||
if (data.ssr) {
|
||||
view.prepareSSR(resources.getTexture(data.ssr), config.refractionLodOffset);
|
||||
// We currently only allow either SS reflections or refractions.
|
||||
auto const& ssrOptions = view.getScreenSpaceReflectionsOptions();
|
||||
const bool reflections = config.hasScreenSpaceReflections && ssrOptions.enabled;
|
||||
const bool refractions = !config.hasScreenSpaceReflections;
|
||||
|
||||
if (reflections) {
|
||||
const auto& cameraInfo = view.getCameraInfo();
|
||||
auto reprojection =
|
||||
getClipSpaceToTextureSpaceMatrix() *
|
||||
historyProjection *
|
||||
inverse(cameraInfo.view * cameraInfo.worldOrigin);
|
||||
|
||||
auto projectToPixelMatrix =
|
||||
getClipSpaceToTextureSpaceMatrix() *
|
||||
cameraInfo.projection;
|
||||
|
||||
view.prepareSSReflections(resources.getTexture(data.ssr), reprojection,
|
||||
projectToPixelMatrix, ssrOptions);
|
||||
} else if (refractions) { // TODO: support both
|
||||
view.prepareSSR(resources.getTexture(data.ssr), config.refractionLodOffset);
|
||||
}
|
||||
} else {
|
||||
// Screen-space reflections must be explicitly disabled.
|
||||
view.disableSSReflections();
|
||||
}
|
||||
|
||||
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport));
|
||||
@@ -1266,6 +1322,30 @@ void FRenderer::initializeClearFlags() {
|
||||
| TargetBufferFlags::DEPTH_AND_STENCIL;
|
||||
}
|
||||
|
||||
math::mat4f FRenderer::getClipSpaceToTextureSpaceMatrix() const noexcept {
|
||||
// Compute a clip-space [-1 to 1] to texture space [0 to 1] matrix, taking into account
|
||||
// API-level differences.
|
||||
const bool textureSpaceYFlipped =
|
||||
mEngine.getBackend() == Backend::METAL || mEngine.getBackend() == Backend::VULKAN;
|
||||
return mat4f(textureSpaceYFlipped ? mat4f::row_major_init{
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, -0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
} : mat4f::row_major_init{
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
});
|
||||
}
|
||||
|
||||
void FRenderer::setHistoryProjection(FView& view, math::mat4f const& projection) {
|
||||
auto& history = view.getFrameHistory();
|
||||
auto& current = history.getCurrent();
|
||||
current.projection = projection;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> FRenderer::getColorHistory(FrameGraph& fg,
|
||||
FrameHistory const& frameHistory) noexcept {
|
||||
// Here we import the previous frame's color output and cache it in the blackboard. This ensures
|
||||
|
||||
@@ -138,9 +138,9 @@ void FScene::prepare(const mat4& worldOriginTransform, bool shadowReceiversAreCa
|
||||
worldTransform, // WORLD_TRANSFORM
|
||||
visibility, // VISIBILITY_STATE
|
||||
rcm.getSkinningBufferInfo(ri), // SKINNING_BUFFER
|
||||
rcm.getMorphingBufferInfo(ri), // MORPHING_BUFFER
|
||||
worldAABB.center, // WORLD_AABB_CENTER
|
||||
0, // VISIBLE_MASK
|
||||
rcm.getMorphWeights(ri), // MORPH_WEIGHTS
|
||||
rcm.getChannels(ri), // CHANNELS
|
||||
rcm.getLayerMask(ri), // LAYERS
|
||||
worldAABB.halfExtent, // WORLD_AABB_EXTENT
|
||||
@@ -254,8 +254,8 @@ void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Hand
|
||||
visibility.screenSpaceContactShadows));
|
||||
|
||||
UniformBuffer::setUniform(buffer,
|
||||
offset + offsetof(PerRenderableUib, morphWeights),
|
||||
sceneData.elementAt<MORPH_WEIGHTS>(i));
|
||||
offset + offsetof(PerRenderableUib, morphTargetCount),
|
||||
sceneData.elementAt<MORPHING_BUFFER>(i).count);
|
||||
|
||||
UniformBuffer::setUniform(buffer,
|
||||
offset + offsetof(PerRenderableUib, channels),
|
||||
|
||||
@@ -264,6 +264,8 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
|
||||
|
||||
// updatePrimitivesLod must be run before RenderPass::appendCommands.
|
||||
view.updatePrimitivesLod(engine, cameraInfo, scene->getRenderableData(), entry.range);
|
||||
// updatePrimitivesMorphTargetBuffer must be run after updatePrimitivesLod.
|
||||
view.updatePrimitivesMorphTargetBuffer(engine, cameraInfo, scene->getRenderableData(), entry.range);
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
RenderPass entryPass(pass);
|
||||
|
||||
+15
-50
@@ -111,7 +111,7 @@ inline float3 darkSurround_to_dimSurround(float3 linearCV) {
|
||||
float3 XYZ = AP1_to_XYZ * linearCV;
|
||||
float3 xyY = XYZ_to_xyY(XYZ);
|
||||
|
||||
xyY.z = clamp(xyY.z, 0.0f, (float)std::numeric_limits<half>::max());
|
||||
xyY.z = clamp(xyY.z, 0.0f, (float) std::numeric_limits<half>::max());
|
||||
xyY.z = std::pow(xyY.z, DIM_SURROUND_GAMMA);
|
||||
|
||||
XYZ = xyY_to_XYZ(xyY);
|
||||
@@ -135,7 +135,7 @@ float3 ACES(float3 color, float brightness) noexcept {
|
||||
constexpr float RRT_SAT_FACTOR = 0.96f;
|
||||
constexpr float ODT_SAT_FACTOR = 0.93f;
|
||||
|
||||
float3 ap0 = REC2020_to_AP0 * color;
|
||||
float3 ap0 = Rec2020_to_AP0 * color;
|
||||
|
||||
// Glow module
|
||||
float saturation = rgb_2_saturation(ap0);
|
||||
@@ -177,7 +177,7 @@ float3 ACES(float3 color, float brightness) noexcept {
|
||||
// Apply desaturation to compensate for luminance difference
|
||||
linearCV = mix(float3(dot(linearCV, LUMINANCE_AP1)), linearCV, ODT_SAT_FACTOR);
|
||||
|
||||
return AP1_to_REC2020 * linearCV;
|
||||
return AP1_to_Rec2020 * linearCV;
|
||||
}
|
||||
|
||||
} // namespace aces
|
||||
@@ -261,7 +261,7 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept {
|
||||
// The 5th color in the array (cyan) represents middle gray (18%)
|
||||
// Every stop above or below middle gray causes a color shift
|
||||
// TODO: This should depend on the working color grading color space
|
||||
float v = log2(dot(c, LUMINANCE_REC2020) / 0.18f);
|
||||
float v = log2(dot(c, LUMINANCE_Rec2020) / 0.18f);
|
||||
v = clamp(v + 5.0f, 0.0f, 15.0f);
|
||||
|
||||
size_t index = size_t(v);
|
||||
@@ -272,71 +272,52 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept {
|
||||
// Generic tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Lottes, 2016,"Advanced Techniques and Optimization of VDR Color Pipelines":
|
||||
// https://gpuopen.com/wp-content/uploads/2016/03/GdcVdrLottes.pdf
|
||||
// Includes fix from Bart Wronski:
|
||||
// https://bartwronski.com/2016/09/01/dynamic-range-and-evs/
|
||||
|
||||
struct GenericToneMapper::Options {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
void setParameters(
|
||||
float contrast,
|
||||
float shoulder,
|
||||
float midGrayIn,
|
||||
float midGrayOut,
|
||||
float hdrMax
|
||||
) {
|
||||
contrast = max(contrast, 1e-5f);
|
||||
shoulder = saturate(shoulder);
|
||||
midGrayIn = clamp(midGrayIn, 1e-5f, 1.0f);
|
||||
midGrayOut = clamp(midGrayOut, 1e-5f, 1.0f);
|
||||
hdrMax = max(hdrMax, 1.0f);
|
||||
|
||||
this->contrast = contrast;
|
||||
this->shoulder = shoulder;
|
||||
this->midGrayIn = midGrayIn;
|
||||
this->midGrayOut = midGrayOut;
|
||||
this->hdrMax = hdrMax;
|
||||
|
||||
// remap shoulder
|
||||
d = 0.8f + 0.4f * shoulder;
|
||||
float a = pow(midGrayIn, contrast);
|
||||
float b = pow(hdrMax, contrast);
|
||||
float c = a - midGrayOut * b;
|
||||
|
||||
float mc = std::pow(midGrayIn, contrast);
|
||||
float mcs = std::pow(mc, d);
|
||||
|
||||
float hc = std::pow(hdrMax, contrast);
|
||||
float hcs = std::pow(hc, d);
|
||||
|
||||
float u = (hcs - mcs) * midGrayOut;
|
||||
float v = mcs * midGrayOut;
|
||||
|
||||
b = -((-mc + (midGrayOut * (hcs * mc - hc * v)) / u) / v);
|
||||
c = (hcs * mc - hc * v) / u;
|
||||
inputScale = (a * b * (midGrayOut - 1.0f)) / c;
|
||||
outputScale = midGrayOut * (a - b) / c;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
float contrast;
|
||||
float shoulder;
|
||||
float midGrayIn;
|
||||
float midGrayOut;
|
||||
float hdrMax;
|
||||
|
||||
// Computed fields, do not modify
|
||||
float b;
|
||||
float c;
|
||||
float d;
|
||||
// TEMP
|
||||
float inputScale;
|
||||
float outputScale;
|
||||
};
|
||||
|
||||
GenericToneMapper::GenericToneMapper(
|
||||
float contrast,
|
||||
float shoulder,
|
||||
float midGrayIn,
|
||||
float midGrayOut,
|
||||
float hdrMax
|
||||
) noexcept {
|
||||
mOptions = new Options();
|
||||
mOptions->setParameters(contrast, shoulder, midGrayIn, midGrayOut, hdrMax);
|
||||
mOptions->setParameters(contrast, midGrayIn, midGrayOut, hdrMax);
|
||||
}
|
||||
|
||||
GenericToneMapper::~GenericToneMapper() noexcept {
|
||||
@@ -354,12 +335,11 @@ GenericToneMapper& GenericToneMapper::operator=(GenericToneMapper&& rhs) noexcep
|
||||
}
|
||||
|
||||
float3 GenericToneMapper::operator()(math::float3 x) const noexcept {
|
||||
float3 xc = pow(clamp(x, 0.0f, mOptions->hdrMax), mOptions->contrast);
|
||||
return saturate(xc / (pow(xc, mOptions->d) * mOptions->b + mOptions->c));
|
||||
x = pow(x, mOptions->contrast);
|
||||
return mOptions->outputScale * x / (x + mOptions->inputScale);
|
||||
}
|
||||
|
||||
float GenericToneMapper::getContrast() const noexcept { return mOptions->contrast; }
|
||||
float GenericToneMapper::getShoulder() const noexcept { return mOptions->shoulder; }
|
||||
float GenericToneMapper::getMidGrayIn() const noexcept { return mOptions->midGrayIn; }
|
||||
float GenericToneMapper::getMidGrayOut() const noexcept { return mOptions->midGrayOut; }
|
||||
float GenericToneMapper::getHdrMax() const noexcept { return mOptions->hdrMax; }
|
||||
@@ -367,27 +347,14 @@ float GenericToneMapper::getHdrMax() const noexcept { return mOptions->hdrMax;
|
||||
void GenericToneMapper::setContrast(float contrast) noexcept {
|
||||
mOptions->setParameters(
|
||||
contrast,
|
||||
mOptions->shoulder,
|
||||
mOptions->midGrayIn,
|
||||
mOptions->midGrayOut,
|
||||
mOptions->hdrMax
|
||||
);
|
||||
}
|
||||
|
||||
void GenericToneMapper::setShoulder(float shoulder) noexcept {
|
||||
mOptions->setParameters(
|
||||
mOptions->contrast,
|
||||
shoulder,
|
||||
mOptions->midGrayIn,
|
||||
mOptions->midGrayOut,
|
||||
mOptions->hdrMax
|
||||
);
|
||||
}
|
||||
|
||||
void GenericToneMapper::setMidGrayIn(float midGrayIn) noexcept {
|
||||
mOptions->setParameters(
|
||||
mOptions->contrast,
|
||||
mOptions->shoulder,
|
||||
midGrayIn,
|
||||
mOptions->midGrayOut,
|
||||
mOptions->hdrMax
|
||||
@@ -397,7 +364,6 @@ void GenericToneMapper::setMidGrayIn(float midGrayIn) noexcept {
|
||||
void GenericToneMapper::setMidGrayOut(float midGrayOut) noexcept {
|
||||
mOptions->setParameters(
|
||||
mOptions->contrast,
|
||||
mOptions->shoulder,
|
||||
mOptions->midGrayIn,
|
||||
midGrayOut,
|
||||
mOptions->hdrMax
|
||||
@@ -407,7 +373,6 @@ void GenericToneMapper::setMidGrayOut(float midGrayOut) noexcept {
|
||||
void GenericToneMapper::setHdrMax(float hdrMax) noexcept {
|
||||
mOptions->setParameters(
|
||||
mOptions->contrast,
|
||||
mOptions->shoulder,
|
||||
mOptions->midGrayIn,
|
||||
mOptions->midGrayOut,
|
||||
hdrMax
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Culler.h"
|
||||
#include "DFG.h"
|
||||
#include "Froxelizer.h"
|
||||
#include "RenderPrimitive.h"
|
||||
#include "ResourceAllocator.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
@@ -657,9 +658,23 @@ void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
|
||||
}
|
||||
|
||||
void FView::prepareSSR(Handle<HwTexture> ssr, float refractionLodOffset) const noexcept {
|
||||
// If screen-space refractions are enabled, make sure to disable screen-space reflections.
|
||||
// disableSSReflections binds a dummy texture to light_ssr, but this is overridden by prepareSSR.
|
||||
// TODO: support simultaneous screen-space refractions and reflections.
|
||||
mPerViewUniforms.disableSSReflections();
|
||||
mPerViewUniforms.prepareSSR(ssr, refractionLodOffset);
|
||||
}
|
||||
|
||||
void FView::disableSSReflections() const noexcept {
|
||||
mPerViewUniforms.disableSSReflections();
|
||||
}
|
||||
|
||||
void FView::prepareSSReflections(backend::Handle<backend::HwTexture> ssr,
|
||||
math::mat4f historyProjection, math::mat4f projectToPixelMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept {
|
||||
mPerViewUniforms.prepareSSReflections(ssr, historyProjection, projectToPixelMatrix, ssrOptions);
|
||||
}
|
||||
|
||||
void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
|
||||
// sampler must be NEAREST
|
||||
mPerViewUniforms.prepareStructure(structure);
|
||||
@@ -868,6 +883,19 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
|
||||
}
|
||||
}
|
||||
|
||||
void FView::updatePrimitivesMorphTargetBuffer(FEngine& engine, const CameraInfo&,
|
||||
FScene::RenderableSoa& renderableData, Range visible) noexcept {
|
||||
for (uint32_t index : visible) {
|
||||
Slice<FRenderPrimitive> primitives = renderableData.elementAt<FScene::PRIMITIVES>(index);
|
||||
for (auto& primitive : primitives) {
|
||||
auto morphTargetBuffer = primitive.getMorphTargetBuffer();
|
||||
if (morphTargetBuffer) {
|
||||
morphTargetBuffer->commit(engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FView::renderShadowMaps(FrameGraph& fg, FEngine& engine, FEngine::DriverApi& driver,
|
||||
RenderPass const& pass) noexcept {
|
||||
mShadowMapManager.render(fg, engine, driver, pass, *this);
|
||||
@@ -1016,6 +1044,14 @@ const View::MultiSampleAntiAliasingOptions& View::getMultiSampleAntiAliasingOpti
|
||||
return upcast(this)->getMultiSampleAntiAliasingOptions();
|
||||
}
|
||||
|
||||
void View::setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept {
|
||||
upcast(this)->setScreenSpaceReflectionsOptions(options);
|
||||
}
|
||||
|
||||
const View::ScreenSpaceReflectionsOptions& View::getScreenSpaceReflectionsOptions() const noexcept {
|
||||
return upcast(this)->getScreenSpaceReflectionsOptions();
|
||||
}
|
||||
|
||||
void View::setColorGrading(ColorGrading* colorGrading) noexcept {
|
||||
return upcast(this)->setColorGrading(upcast(colorGrading));
|
||||
}
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
#include "details/Engine.h"
|
||||
#include "details/VertexBuffer.h"
|
||||
#include "details/IndexBuffer.h"
|
||||
#include "details/Texture.h"
|
||||
#include "details/Material.h"
|
||||
|
||||
#include "private/filament/SibGenerator.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
@@ -316,7 +319,6 @@ void FRenderableManager::create(
|
||||
setCulling(ci, builder->mCulling);
|
||||
setSkinning(ci, false);
|
||||
setMorphing(ci, builder->mMorphingEnabled);
|
||||
setMorphWeights(ci, {0, 0, 0, 0});
|
||||
mManager[ci].channels = builder->mChannels;
|
||||
|
||||
const uint32_t count = builder->mSkinningBoneCount;
|
||||
@@ -372,6 +374,18 @@ void FRenderableManager::create(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (builder->mMorphingEnabled) {
|
||||
// Instead of using a UBO per primitive, we could also have a single UBO for all primitives
|
||||
// and use bindUniformBufferRange which might be more efficient.
|
||||
MorphWeights& morphWeights = manager[ci].morphWeights;
|
||||
morphWeights = MorphWeights {
|
||||
.handle = driver.createBufferObject(
|
||||
sizeof(PerRenderableMorphingUib),
|
||||
BufferObjectBinding::UNIFORM,
|
||||
backend::BufferUsage::DYNAMIC),
|
||||
.count = 0 };
|
||||
}
|
||||
}
|
||||
engine.flushIfNeeded();
|
||||
}
|
||||
@@ -416,6 +430,12 @@ void FRenderableManager::destroyComponent(Instance ci) noexcept {
|
||||
if (bones.handle && !bones.skinningBufferMode) {
|
||||
driver.destroyBufferObject(bones.handle);
|
||||
}
|
||||
|
||||
// destroy the weights structures if any
|
||||
MorphWeights const& morphWeights = manager[ci].morphWeights;
|
||||
if (morphWeights.handle) {
|
||||
driver.destroyBufferObject(morphWeights.handle);
|
||||
}
|
||||
}
|
||||
|
||||
void FRenderableManager::destroyComponentPrimitives(
|
||||
@@ -562,9 +582,38 @@ void FRenderableManager::setSkinningBuffer(FRenderableManager::Instance ci,
|
||||
bones.offset = uint16_t(offset);
|
||||
}
|
||||
|
||||
void FRenderableManager::setMorphWeights(Instance ci, const float4& weights) noexcept {
|
||||
if (ci) {
|
||||
mManager[ci].morphWeights = weights;
|
||||
static void updateMorphWeights(FEngine& engine, backend::Handle<backend::HwBufferObject> handle,
|
||||
float const* weights, size_t count) noexcept {
|
||||
auto& driver = engine.getDriverApi();
|
||||
auto size = sizeof(PerRenderableMorphingUib);
|
||||
auto* UTILS_RESTRICT out = (PerRenderableMorphingUib*)driver.allocate(size);
|
||||
memset(out, 0, size);
|
||||
std::transform(weights, weights + count, out->weights,
|
||||
[](float value) { return float4(value); });
|
||||
driver.updateBufferObject(handle, { out, size }, 0);
|
||||
}
|
||||
|
||||
void FRenderableManager::setMorphWeights(Instance instance, float const* weights, size_t count) noexcept {
|
||||
if (instance) {
|
||||
MorphWeights& morphWeights = mManager[instance].morphWeights;
|
||||
morphWeights.count = count;
|
||||
|
||||
ASSERT_PRECONDITION(count < CONFIG_MAX_MORPH_TARGET_COUNT,
|
||||
"Only %d morph targets are supported (count=%d)", CONFIG_MAX_MORPH_TARGET_COUNT, count);
|
||||
|
||||
if (morphWeights.handle) {
|
||||
updateMorphWeights(mEngine, morphWeights.handle, weights, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FRenderableManager::setMorphTargetBufferAt(Instance instance,
|
||||
size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer) noexcept {
|
||||
if (instance) {
|
||||
Slice<FRenderPrimitive>& primitives = getRenderPrimitives(instance, 0);
|
||||
if (primitiveIndex < primitives.size()) {
|
||||
primitives[primitiveIndex].set(morphTargetBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,15 +742,20 @@ void RenderableManager::setBones(Instance instance,
|
||||
upcast(this)->setBones(instance, transforms, boneCount, offset);
|
||||
}
|
||||
|
||||
void RenderableManager::setMorphWeights(Instance instance, float4 const& weights) noexcept {
|
||||
upcast(this)->setMorphWeights(instance, weights);
|
||||
}
|
||||
|
||||
void RenderableManager::setSkinningBuffer(Instance instance,
|
||||
SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept {
|
||||
upcast(this)->setSkinningBuffer(instance, upcast(skinningBuffer), count, offset);
|
||||
}
|
||||
|
||||
void RenderableManager::setMorphWeights(Instance instance, float const* weights, size_t count) noexcept {
|
||||
upcast(this)->setMorphWeights(instance, weights, count);
|
||||
}
|
||||
|
||||
void RenderableManager::setMorphTargetBufferAt(Instance instance,
|
||||
size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer) noexcept {
|
||||
upcast(this)->setMorphTargetBufferAt(instance, primitiveIndex, upcast(morphTargetBuffer));
|
||||
}
|
||||
|
||||
void RenderableManager::setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept {
|
||||
upcast(this)->setLightChannel(instance, channel, enable);
|
||||
}
|
||||
|
||||
@@ -40,9 +40,11 @@ namespace filament {
|
||||
class FBufferObject;
|
||||
class FIndexBuffer;
|
||||
class FMaterialInstance;
|
||||
class FMorphTargetBuffer;
|
||||
class FRenderPrimitive;
|
||||
class FSkinningBuffer;
|
||||
class FVertexBuffer;
|
||||
class FTexture;
|
||||
|
||||
class FRenderableManager : public RenderableManager {
|
||||
public:
|
||||
@@ -106,9 +108,11 @@ public:
|
||||
inline void setPrimitives(Instance instance, utils::Slice<FRenderPrimitive> const& primitives) noexcept;
|
||||
inline void setBones(Instance instance, Bone const* transforms, size_t boneCount, size_t offset = 0) noexcept;
|
||||
inline void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount, size_t offset = 0) noexcept;
|
||||
inline void setMorphWeights(Instance instance, const math::float4& weights) noexcept;
|
||||
inline void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer,
|
||||
size_t count, size_t offset) noexcept;
|
||||
void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept;
|
||||
void setMorphTargetBufferAt(Instance instance,
|
||||
size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer) noexcept;
|
||||
inline void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept;
|
||||
|
||||
inline bool getLightChannel(Instance instance, unsigned int channel) const noexcept;
|
||||
@@ -123,7 +127,6 @@ public:
|
||||
inline Visibility getVisibility(Instance instance) const noexcept;
|
||||
inline uint8_t getLayerMask(Instance instance) const noexcept;
|
||||
inline uint8_t getPriority(Instance instance) const noexcept;
|
||||
inline math::float4 getMorphWeights(Instance instance) const noexcept;
|
||||
inline uint8_t getChannels(Instance instance) const noexcept;
|
||||
|
||||
struct SkinningBindingInfo {
|
||||
@@ -134,6 +137,12 @@ public:
|
||||
inline SkinningBindingInfo getSkinningBufferInfo(Instance instance) const noexcept;
|
||||
inline uint32_t getBoneCount(Instance instance) const noexcept;
|
||||
|
||||
struct MorphingBindingInfo {
|
||||
backend::Handle<backend::HwBufferObject> handle;
|
||||
uint32_t count;
|
||||
};
|
||||
inline MorphingBindingInfo getMorphingBufferInfo(Instance instance) const noexcept;
|
||||
inline int getMorphTargetCount(Instance instance) const noexcept;
|
||||
|
||||
utils::Entity getEntity(Instance instance) const noexcept {
|
||||
return mManager.getEntity(instance);
|
||||
@@ -165,13 +174,18 @@ private:
|
||||
uint16_t offset = 0;
|
||||
bool skinningBufferMode = false;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Bones) == 12);
|
||||
|
||||
struct MorphWeights {
|
||||
backend::Handle<backend::HwBufferObject> handle;
|
||||
uint32_t count = 0;
|
||||
};
|
||||
static_assert(sizeof(MorphWeights) == 8);
|
||||
|
||||
enum {
|
||||
AABB, // user data
|
||||
LAYERS, // user data
|
||||
MORPH_WEIGHTS, // user data
|
||||
MORPH_WEIGHTS, // filament data, UBO storing a pointer to the morph weights information
|
||||
CHANNELS, // user data
|
||||
VISIBILITY, // user data
|
||||
PRIMITIVES, // user data
|
||||
@@ -181,7 +195,7 @@ private:
|
||||
using Base = utils::SingleInstanceComponentManager<
|
||||
Box, // AABB
|
||||
uint8_t, // LAYERS
|
||||
math::float4, // MORPH_WEIGHTS
|
||||
MorphWeights, // MORPH_WEIGHTS
|
||||
uint8_t, // CHANNELS
|
||||
Visibility, // VISIBILITY
|
||||
utils::Slice<FRenderPrimitive>, // PRIMITIVES
|
||||
@@ -325,10 +339,6 @@ uint8_t FRenderableManager::getPriority(Instance instance) const noexcept {
|
||||
return getVisibility(instance).priority;
|
||||
}
|
||||
|
||||
math::float4 FRenderableManager::getMorphWeights(Instance instance) const noexcept {
|
||||
return mManager[instance].morphWeights;
|
||||
}
|
||||
|
||||
uint8_t FRenderableManager::getChannels(Instance instance) const noexcept {
|
||||
return mManager[instance].channels;
|
||||
}
|
||||
@@ -348,6 +358,17 @@ inline uint32_t FRenderableManager::getBoneCount(Instance instance) const noexce
|
||||
return bones.count;
|
||||
}
|
||||
|
||||
FRenderableManager::MorphingBindingInfo
|
||||
FRenderableManager::getMorphingBufferInfo(Instance instance) const noexcept {
|
||||
MorphWeights const& morphWeights = mManager[instance].morphWeights;
|
||||
return { morphWeights.handle, morphWeights.count };
|
||||
}
|
||||
|
||||
int FRenderableManager::getMorphTargetCount(Instance instance) const noexcept {
|
||||
MorphWeights const& morphWeights = mManager[instance].morphWeights;
|
||||
return morphWeights.count;
|
||||
}
|
||||
|
||||
utils::Slice<FRenderPrimitive> const& FRenderableManager::getRenderPrimitives(
|
||||
Instance instance, uint8_t level) const noexcept {
|
||||
return mManager[instance].primitives;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "details/IndexBuffer.h"
|
||||
#include "details/RenderTarget.h"
|
||||
#include "details/SkinningBuffer.h"
|
||||
#include "details/MorphTargetBuffer.h"
|
||||
#include "details/Skybox.h"
|
||||
|
||||
#include "private/backend/CommandBufferQueue.h"
|
||||
@@ -234,6 +235,7 @@ public:
|
||||
FVertexBuffer* createVertexBuffer(const VertexBuffer::Builder& builder) noexcept;
|
||||
FIndexBuffer* createIndexBuffer(const IndexBuffer::Builder& builder) noexcept;
|
||||
FSkinningBuffer* createSkinningBuffer(const SkinningBuffer::Builder& builder) noexcept;
|
||||
FMorphTargetBuffer* createMorphTargetBuffer(const MorphTargetBuffer::Builder& builder) noexcept;
|
||||
FIndirectLight* createIndirectLight(const IndirectLight::Builder& builder) noexcept;
|
||||
FMaterial* createMaterial(const Material::Builder& builder) noexcept;
|
||||
FTexture* createTexture(const Texture::Builder& builder) noexcept;
|
||||
@@ -265,6 +267,7 @@ public:
|
||||
bool destroy(const FFence* p);
|
||||
bool destroy(const FIndexBuffer* p);
|
||||
bool destroy(const FSkinningBuffer* p);
|
||||
bool destroy(const FMorphTargetBuffer* p);
|
||||
bool destroy(const FIndirectLight* p);
|
||||
bool destroy(const FMaterial* p);
|
||||
bool destroy(const FMaterialInstance* p);
|
||||
@@ -376,6 +379,7 @@ private:
|
||||
ResourceList<FStream> mStreams{ "Stream" };
|
||||
ResourceList<FIndexBuffer> mIndexBuffers{ "IndexBuffer" };
|
||||
ResourceList<FSkinningBuffer> mSkinningBuffers{ "SkinningBuffer" };
|
||||
ResourceList<FMorphTargetBuffer> mMorphTargetBuffers{ "MorphTargetBuffer" };
|
||||
ResourceList<FVertexBuffer> mVertexBuffers{ "VertexBuffer" };
|
||||
ResourceList<FIndirectLight> mIndirectLights{ "IndirectLight" };
|
||||
ResourceList<FMaterial> mMaterials{ "Material" };
|
||||
|
||||
@@ -118,6 +118,7 @@ public:
|
||||
AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; }
|
||||
RefractionMode getRefractionMode() const noexcept { return mRefractionMode; }
|
||||
RefractionType getRefractionType() const noexcept { return mRefractionType; }
|
||||
ReflectionMode getReflectionMode() const noexcept { return mReflectionMode; }
|
||||
|
||||
bool hasSpecularAntiAliasing() const noexcept { return mSpecularAntiAliasing; }
|
||||
float getSpecularAntiAliasingVariance() const noexcept { return mSpecularAntiAliasingVariance; }
|
||||
@@ -188,6 +189,7 @@ private:
|
||||
AttributeBitset mRequiredAttributes;
|
||||
RefractionMode mRefractionMode = RefractionMode::NONE;
|
||||
RefractionType mRefractionType = RefractionType::SOLID;
|
||||
ReflectionMode mReflectionMode = ReflectionMode::DEFAULT;
|
||||
uint64_t mMaterialProperties = 0;
|
||||
|
||||
float mMaskThreshold = 0.4f;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DETAILS_MORPHTARGETBUFFER_H
|
||||
#define TNT_FILAMENT_DETAILS_MORPHTARGETBUFFER_H
|
||||
|
||||
#include "upcast.h"
|
||||
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
|
||||
#include "private/backend/DriverApiForward.h"
|
||||
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FEngine;
|
||||
|
||||
class FMorphTargetBuffer : public MorphTargetBuffer {
|
||||
public:
|
||||
FMorphTargetBuffer(FEngine& engine, const Builder& builder);
|
||||
|
||||
// frees driver resources, object becomes invalid
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void setPositionsAt(FEngine& engine, size_t targetIndex, math::float3 const* positions, size_t count);
|
||||
void setPositionsAt(FEngine& engine, size_t targetIndex, math::float4 const* positions, size_t count);
|
||||
void setTangentsAt(FEngine& engine, size_t targetIndex, math::short4 const* tangents, size_t count);
|
||||
inline size_t getVertexCount() const noexcept { return mVertexCount; }
|
||||
inline size_t getCount() const noexcept { return mCount; }
|
||||
|
||||
private:
|
||||
friend class FView;
|
||||
friend class RenderPass;
|
||||
|
||||
void commit(FEngine& engine) const noexcept;
|
||||
void bind(backend::DriverApi& driver) const noexcept;
|
||||
|
||||
backend::Handle<backend::HwSamplerGroup> mSbHandle;
|
||||
backend::Handle<backend::HwTexture> mTbHandle;
|
||||
backend::SamplerGroup mSBuffer;
|
||||
size_t mVertexCount;
|
||||
size_t mCount;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(MorphTargetBuffer)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif //TNT_FILAMENT_DETAILS_MORPHTARGETBUFFER_H
|
||||
@@ -152,6 +152,7 @@ private:
|
||||
math::float4 clearColor = {};
|
||||
float refractionLodOffset;
|
||||
bool hasContactShadows;
|
||||
bool hasScreenSpaceReflections;
|
||||
};
|
||||
|
||||
FrameGraphId<FrameGraphTexture> colorPass(FrameGraph& fg, const char* name,
|
||||
@@ -187,6 +188,10 @@ private:
|
||||
return clock::now() - getUserEpoch();
|
||||
}
|
||||
|
||||
math::mat4f getClipSpaceToTextureSpaceMatrix() const noexcept;
|
||||
|
||||
void setHistoryProjection(FView& view, math::mat4f const& projection);
|
||||
|
||||
static FrameGraphId<FrameGraphTexture> getColorHistory(FrameGraph& fg,
|
||||
FrameHistory const& frameHistory) noexcept;
|
||||
|
||||
|
||||
@@ -105,9 +105,9 @@ public:
|
||||
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
|
||||
MORPHING_BUFFER, // 8 | weights uniform buffer handle, count
|
||||
WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable
|
||||
VISIBLE_MASK, // 2 | each bit represents a visibility in a pass
|
||||
MORPH_WEIGHTS, // 4 | floats for morphing
|
||||
CHANNELS, // 1 | currently light channels only
|
||||
|
||||
// These are not needed anymore after culling
|
||||
@@ -127,9 +127,9 @@ public:
|
||||
math::mat4f, // WORLD_TRANSFORM
|
||||
FRenderableManager::Visibility, // VISIBILITY_STATE
|
||||
FRenderableManager::SkinningBindingInfo, // SKINNING_BUFFER
|
||||
FRenderableManager::MorphingBindingInfo, // MORPHING_BUFFER
|
||||
math::float3, // WORLD_AABB_CENTER
|
||||
VisibleMaskType, // VISIBLE_MASK
|
||||
math::float4, // MORPH_WEIGHTS
|
||||
uint8_t, // CHANNELS
|
||||
uint8_t, // LAYERS
|
||||
math::float3, // WORLD_AABB_EXTENT
|
||||
|
||||
@@ -172,6 +172,10 @@ public:
|
||||
|
||||
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
|
||||
void prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset) const noexcept;
|
||||
void disableSSReflections() const noexcept;
|
||||
void prepareSSReflections(backend::Handle<backend::HwTexture> ssr,
|
||||
math::mat4f historyProjection, math::mat4f projectToPixelMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
|
||||
void prepareStructure(backend::Handle<backend::HwTexture> structure) const noexcept;
|
||||
void prepareShadow(backend::Handle<backend::HwTexture> structure) const noexcept;
|
||||
void prepareShadowMap() const noexcept;
|
||||
@@ -198,6 +202,10 @@ public:
|
||||
FEngine& engine, const CameraInfo& camera,
|
||||
FScene::RenderableSoa& renderableData, Range visible) noexcept;
|
||||
|
||||
void updatePrimitivesMorphTargetBuffer(
|
||||
FEngine& engine, const CameraInfo&,
|
||||
FScene::RenderableSoa& renderableData, Range visible) noexcept;
|
||||
|
||||
void setShadowingEnabled(bool enabled) noexcept { mShadowingEnabled = enabled; }
|
||||
|
||||
bool isShadowingEnabled() const noexcept { return mShadowingEnabled; }
|
||||
@@ -255,6 +263,18 @@ public:
|
||||
return mMultiSampleAntiAliasingOptions;
|
||||
}
|
||||
|
||||
void setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept {
|
||||
options.thickness = std::max(0.0f, options.thickness);
|
||||
options.bias = std::max(0.0f, options.bias);
|
||||
options.maxDistance = std::max(0.0f, options.maxDistance);
|
||||
options.stride = std::max(1.0f, options.stride);
|
||||
mScreenSpaceReflectionsOptions = options;
|
||||
}
|
||||
|
||||
const ScreenSpaceReflectionsOptions& getScreenSpaceReflectionsOptions() const noexcept {
|
||||
return mScreenSpaceReflectionsOptions;
|
||||
}
|
||||
|
||||
void setColorGrading(FColorGrading* colorGrading) noexcept {
|
||||
mColorGrading = colorGrading == nullptr ? mDefaultColorGrading : colorGrading;
|
||||
}
|
||||
@@ -570,6 +590,7 @@ private:
|
||||
VignetteOptions mVignetteOptions;
|
||||
TemporalAntiAliasingOptions mTemporalAntiAliasingOptions;
|
||||
MultiSampleAntiAliasingOptions mMultiSampleAntiAliasingOptions;
|
||||
ScreenSpaceReflectionsOptions mScreenSpaceReflectionsOptions;
|
||||
BlendMode mBlendMode = BlendMode::OPAQUE;
|
||||
const FColorGrading* mColorGrading = nullptr;
|
||||
const FColorGrading* mDefaultColorGrading = nullptr;
|
||||
|
||||
@@ -21,7 +21,7 @@ highp float linearizeDepth(highp float depth) {
|
||||
// Our far plane is at infinity, which causes a division by zero below, which in turn
|
||||
// causes some issues on some GPU. We workaround it by replacing "infinity" by the closest
|
||||
// value representable in a 24 bit depth buffer.
|
||||
const float preventDiv0 = 1.0 / 16777216.0;
|
||||
const highp float preventDiv0 = 1.0 / 16777216.0;
|
||||
mat4 p = getViewFromClipMatrix();
|
||||
// this works with perspective and ortho projections, for a perspective projection
|
||||
// this resolves to -near/depth, for an ortho projection this resolves to depth*(far - near) - far
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.16.1"
|
||||
spec.version = "1.17.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.16.1/filament-v1.16.1-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.17.0/filament-v1.17.0-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -62,6 +62,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialDomain = charTo64bitNum("MAT_DOMN"),
|
||||
MaterialRefraction = charTo64bitNum("MAT_REFM"),
|
||||
MaterialRefractionType = charTo64bitNum("MAT_REFT"),
|
||||
MaterialReflectionMode = charTo64bitNum("MAT_REFL"),
|
||||
|
||||
MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"),
|
||||
MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"),
|
||||
|
||||
@@ -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 = 16;
|
||||
static constexpr size_t MATERIAL_VERSION = 17;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
@@ -136,20 +136,10 @@ enum VertexAttribute : uint8_t {
|
||||
CUSTOM6 = 14,
|
||||
CUSTOM7 = 15,
|
||||
|
||||
// Aliases for vertex morphing.
|
||||
MORPH_POSITION_0 = CUSTOM0,
|
||||
MORPH_POSITION_1 = CUSTOM1,
|
||||
MORPH_POSITION_2 = CUSTOM2,
|
||||
MORPH_POSITION_3 = CUSTOM3,
|
||||
MORPH_TANGENTS_0 = CUSTOM4,
|
||||
MORPH_TANGENTS_1 = CUSTOM5,
|
||||
MORPH_TANGENTS_2 = CUSTOM6,
|
||||
MORPH_TANGENTS_3 = CUSTOM7,
|
||||
|
||||
// this is limited by driver::MAX_VERTEX_ATTRIBUTE_COUNT
|
||||
};
|
||||
|
||||
static constexpr size_t MAX_MORPH_TARGETS = 4;
|
||||
static constexpr size_t MAX_MORPH_TARGETS = 128; // this is limited by filament::CONFIG_MAX_MORPH_TARGET_COUNT
|
||||
static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8;
|
||||
|
||||
/**
|
||||
@@ -186,6 +176,14 @@ enum class RefractionType : uint8_t {
|
||||
THIN = 1, //!< refraction through thin objects (e.g. window)
|
||||
};
|
||||
|
||||
/**
|
||||
* Reflection mode
|
||||
*/
|
||||
enum class ReflectionMode : uint8_t {
|
||||
DEFAULT = 0, //! reflections sample from the scene's IBL only
|
||||
SCREEN_SPACE = 1, //! reflections sample from screen space, and fallback to the scene's IBL
|
||||
};
|
||||
|
||||
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
|
||||
using AttributeBitset = utils::bitset32;
|
||||
|
||||
|
||||
@@ -33,14 +33,15 @@ enum class PostProcessVariant : uint8_t {
|
||||
// Binding points for uniform buffers and sampler buffers.
|
||||
// Effectively, these are just names.
|
||||
namespace BindingPoints {
|
||||
constexpr uint8_t PER_VIEW = 0; // uniforms/samplers updated per view
|
||||
constexpr uint8_t PER_RENDERABLE = 1; // uniforms/samplers updated per renderable
|
||||
constexpr uint8_t PER_RENDERABLE_BONES = 2; // bones data, per renderable
|
||||
constexpr uint8_t LIGHTS = 3; // lights data array
|
||||
constexpr uint8_t SHADOW = 4; // punctual shadow data
|
||||
constexpr uint8_t FROXEL_RECORDS = 5;
|
||||
constexpr uint8_t PER_MATERIAL_INSTANCE = 6; // uniforms/samplers updates per material
|
||||
constexpr uint8_t COUNT = 7;
|
||||
constexpr uint8_t PER_VIEW = 0; // uniforms/samplers updated per view
|
||||
constexpr uint8_t PER_RENDERABLE = 1; // uniforms/samplers updated per renderable
|
||||
constexpr uint8_t PER_RENDERABLE_BONES = 2; // bones data, per renderable
|
||||
constexpr uint8_t PER_RENDERABLE_MORPHING = 3; // morphing uniform/sampler updated per render primitive
|
||||
constexpr uint8_t LIGHTS = 4; // lights data array
|
||||
constexpr uint8_t SHADOW = 5; // punctual shadow data
|
||||
constexpr uint8_t FROXEL_RECORDS = 6;
|
||||
constexpr uint8_t PER_MATERIAL_INSTANCE = 7; // uniforms/samplers updates per material
|
||||
constexpr uint8_t COUNT = 8;
|
||||
// These are limited by Program::UNIFORM_BINDING_COUNT (currently 8)
|
||||
}
|
||||
|
||||
@@ -66,6 +67,11 @@ constexpr size_t CONFIG_MAX_SHADOW_CASCADES = 4;
|
||||
// We store 64 bytes per bone.
|
||||
constexpr size_t CONFIG_MAX_BONE_COUNT = 256;
|
||||
|
||||
// The maximum number of morph target count.
|
||||
// This value is limited by ES3.0, ES3.0 only guarantees 256 layers in an array texture.
|
||||
// For morphing, 128 layers are used for the positions and others are used for tangents.
|
||||
constexpr int CONFIG_MAX_MORPH_TARGET_COUNT = 128;
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_driver/EngineEnums.h
|
||||
|
||||
@@ -30,6 +30,7 @@ class SamplerInterfaceBlock;
|
||||
class SibGenerator {
|
||||
public:
|
||||
static SamplerInterfaceBlock const& getPerViewSib(uint8_t variantKey) noexcept;
|
||||
static SamplerInterfaceBlock const& getPerRenderPrimitiveMorphingSib(uint8_t variantKey) noexcept;
|
||||
static SamplerInterfaceBlock const* getSib(uint8_t bindingPoint, uint8_t variantKey) noexcept;
|
||||
// When adding a sampler block here, make sure to also update
|
||||
// FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed
|
||||
@@ -48,5 +49,14 @@ struct PerViewSib {
|
||||
static constexpr size_t SAMPLER_COUNT = 7;
|
||||
};
|
||||
|
||||
struct PerRenderPrimitiveMorphingSib {
|
||||
// Morph positions and normals are packed into one texture.
|
||||
// Because some materials exceed MAX_SAMPLER_COUNT(16)
|
||||
// when morph positions and normals are bound to samplers separately.
|
||||
static constexpr size_t TARGETS = 0; // morph positions and normals
|
||||
|
||||
static constexpr size_t SAMPLER_COUNT = 1;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
|
||||
@@ -136,8 +136,16 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
float nearOverFarMinusNear; // n / (f-n), always positive
|
||||
float temporalNoise; // noise [0,1] when TAA is used, 0 otherwise
|
||||
|
||||
// Screen-space reflections
|
||||
math::mat4f ssrReprojection;
|
||||
math::mat4f ssrProjectToPixelMatrix;
|
||||
float ssrThickness; // ssr thickness, in world units
|
||||
float ssrBias; // ssr bias, in world units
|
||||
float ssrDistance; // ssr world raycast distance, 0 when ssr is off
|
||||
float ssrStride; // ssr texel stride, >= 1.0
|
||||
|
||||
// bring PerViewUib to 2 KiB
|
||||
math::float4 padding2[58];
|
||||
math::float4 padding3[49];
|
||||
};
|
||||
|
||||
// 2 KiB == 128 float4s
|
||||
@@ -149,7 +157,7 @@ struct alignas(256) PerRenderableUib { // NOLINT(cppcoreguidelines-pro-type-memb
|
||||
static constexpr utils::StaticString _name{ "ObjectUniforms" };
|
||||
math::mat4f worldFromModelMatrix;
|
||||
math::mat3f worldFromModelNormalMatrix; // this gets expanded to 48 bytes during the copy to the UBO
|
||||
alignas(16) math::float4 morphWeights; // morph weights (we could easily have 8 using half)
|
||||
alignas(16) uint32_t morphTargetCount;
|
||||
uint32_t flags; // see packFlags() below
|
||||
uint32_t channels; // 0x000000ll
|
||||
uint32_t objectId; // used for picking
|
||||
@@ -227,6 +235,14 @@ struct PerRenderableUibBone { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
static_assert(CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone) <= 16384,
|
||||
"PerRenderableUibBone exceed max UBO size");
|
||||
|
||||
struct alignas(16) PerRenderableMorphingUib {
|
||||
static constexpr utils::StaticString _name{ "MorphingUniforms" };
|
||||
// The array stride(the bytes between array elements) is always rounded up to the size of a vec4 in std140.
|
||||
math::float4 weights[CONFIG_MAX_MORPH_TARGET_COUNT];
|
||||
};
|
||||
static_assert(sizeof(PerRenderableMorphingUib) <= 16384,
|
||||
"PerRenderableMorphingUib exceed max UBO size");
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILABRIDGE_UIBSTRUCTS_H
|
||||
|
||||
@@ -185,6 +185,10 @@ namespace filament {
|
||||
if (isValidDepthVariant(variantKey)) {
|
||||
return variantKey;
|
||||
}
|
||||
// if shadow receiver is disabled, turn off VSM
|
||||
if (isLit && !(variantKey & SHADOW_RECEIVER)) {
|
||||
return variantKey & ~VSM;
|
||||
}
|
||||
// when the shading mode is unlit, remove all the lighting variants
|
||||
return isLit ? variantKey : (variantKey & UNLIT_MASK);
|
||||
}
|
||||
|
||||
@@ -66,12 +66,27 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(uint8_t variantKey) noe
|
||||
return v.hasVsm() ? sibVsm : sibPcf;
|
||||
}
|
||||
|
||||
SamplerInterfaceBlock const& SibGenerator::getPerRenderPrimitiveMorphingSib(uint8_t variantKey) noexcept {
|
||||
using Type = SamplerInterfaceBlock::Type;
|
||||
using Format = SamplerInterfaceBlock::Format;
|
||||
using Precision = SamplerInterfaceBlock::Precision;
|
||||
|
||||
static SamplerInterfaceBlock sib = SamplerInterfaceBlock::Builder()
|
||||
.name("Morphing")
|
||||
.add("targets", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH)
|
||||
.build();
|
||||
|
||||
return sib;
|
||||
}
|
||||
|
||||
SamplerInterfaceBlock const* SibGenerator::getSib(uint8_t bindingPoint, uint8_t variantKey) noexcept {
|
||||
switch (bindingPoint) {
|
||||
case BindingPoints::PER_VIEW:
|
||||
return &getPerViewSib(variantKey);
|
||||
case BindingPoints::PER_RENDERABLE:
|
||||
return nullptr;
|
||||
case BindingPoints::PER_RENDERABLE_MORPHING:
|
||||
return &getPerRenderPrimitiveMorphingSib(variantKey);
|
||||
case BindingPoints::LIGHTS:
|
||||
return nullptr;
|
||||
default:
|
||||
|
||||
@@ -190,6 +190,7 @@ public:
|
||||
using MaterialDomain = filament::MaterialDomain;
|
||||
using RefractionMode = filament::RefractionMode;
|
||||
using RefractionType = filament::RefractionType;
|
||||
using ReflectionMode = filament::ReflectionMode;
|
||||
using VertexAttribute = filament::VertexAttribute;
|
||||
|
||||
using ShaderQuality = filament::ShaderQuality;
|
||||
@@ -461,6 +462,9 @@ public:
|
||||
//! Specify the refraction type
|
||||
MaterialBuilder& refractionType(RefractionType refractionType) noexcept;
|
||||
|
||||
//! Specifies how reflections should be rendered (default is DEFAULT).
|
||||
MaterialBuilder& reflectionMode(ReflectionMode mode) noexcept;
|
||||
|
||||
//! Specifies how transparent objects should be rendered (default is DEFAULT).
|
||||
MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept;
|
||||
|
||||
@@ -702,6 +706,7 @@ private:
|
||||
MaterialDomain mMaterialDomain = MaterialDomain::SURFACE;
|
||||
RefractionMode mRefractionMode = RefractionMode::NONE;
|
||||
RefractionType mRefractionType = RefractionType::SOLID;
|
||||
ReflectionMode mReflectionMode = ReflectionMode::DEFAULT;
|
||||
Interpolation mInterpolation = Interpolation::SMOOTH;
|
||||
VertexDomain mVertexDomain = VertexDomain::OBJECT;
|
||||
TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT;
|
||||
|
||||
@@ -374,6 +374,11 @@ MaterialBuilder& MaterialBuilder::transparencyMode(TransparencyMode mode) noexce
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::reflectionMode(ReflectionMode mode) noexcept {
|
||||
mReflectionMode = mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::platform(Platform platform) noexcept {
|
||||
mPlatform = platform;
|
||||
return *this;
|
||||
@@ -480,6 +485,7 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
|
||||
info.specularAOSet = mSpecularAOSet;
|
||||
info.refractionMode = mRefractionMode;
|
||||
info.refractionType = mRefractionType;
|
||||
info.reflectionMode = mReflectionMode;
|
||||
info.quality = mShaderQuality;
|
||||
info.hasCustomSurfaceShading = mCustomSurfaceShading;
|
||||
}
|
||||
@@ -1026,6 +1032,7 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialBlendingMode, static_cast<uint8_t>(mBlendingMode));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialTransparencyMode, static_cast<uint8_t>(mTransparencyMode));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialReflectionMode, static_cast<uint8_t>(mReflectionMode));
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialDepthWriteSet, mDepthWriteSet);
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialColorWrite, mColorWrite);
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialDepthWrite, mDepthWrite);
|
||||
|
||||
@@ -117,8 +117,16 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
|
||||
.add("nearOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
|
||||
.add("temporalNoise", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
|
||||
|
||||
// Screen-space reflection parameters
|
||||
.add("ssrReprojection", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
|
||||
.add("ssrProjectToPixelMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
|
||||
.add("ssrThickness", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("ssrBias", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("ssrDistance", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("ssrStride", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
|
||||
// bring PerViewUib to 2 KiB
|
||||
.add("padding2", 58, UniformInterfaceBlock::Type::FLOAT4)
|
||||
.add("padding3", 49, UniformInterfaceBlock::Type::FLOAT4)
|
||||
.build();
|
||||
return uib;
|
||||
}
|
||||
@@ -128,7 +136,7 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept {
|
||||
.name(PerRenderableUib::_name)
|
||||
.add("worldFromModelMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
|
||||
.add("worldFromModelNormalMatrix", 1, UniformInterfaceBlock::Type::MAT3, Precision::HIGH)
|
||||
.add("morphWeights", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
|
||||
.add("morphTargetCount", 1, UniformInterfaceBlock::Type::UINT)
|
||||
.add("flags", 1, UniformInterfaceBlock::Type::UINT)
|
||||
.add("channels", 1, UniformInterfaceBlock::Type::UINT)
|
||||
.add("objectId", 1, UniformInterfaceBlock::Type::UINT)
|
||||
@@ -161,6 +169,14 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableBonesUib() noexcept {
|
||||
return uib;
|
||||
}
|
||||
|
||||
UniformInterfaceBlock const& UibGenerator::getPerRenderableMorphingUib() noexcept {
|
||||
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
|
||||
.name(PerRenderableMorphingUib::_name)
|
||||
.add("weights", CONFIG_MAX_MORPH_TARGET_COUNT, UniformInterfaceBlock::Type::FLOAT4)
|
||||
.build();
|
||||
return uib;
|
||||
}
|
||||
|
||||
UniformInterfaceBlock const& UibGenerator::getFroxelRecordUib() noexcept {
|
||||
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
|
||||
.name(FroxelRecordUib::_name)
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
static UniformInterfaceBlock const& getLightsUib() noexcept;
|
||||
static UniformInterfaceBlock const& getShadowUib() noexcept;
|
||||
static UniformInterfaceBlock const& getPerRenderableBonesUib() noexcept;
|
||||
static UniformInterfaceBlock const& getPerRenderableMorphingUib() noexcept;
|
||||
static UniformInterfaceBlock const& getFroxelRecordUib() noexcept;
|
||||
// When adding an UBO here, make sure to also update
|
||||
// FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed
|
||||
|
||||
@@ -611,6 +611,7 @@ io::sstream& CodeGenerator::generateShaderLit(io::sstream& out, ShaderType type,
|
||||
|
||||
if (shading != Shading::UNLIT) {
|
||||
out << SHADERS_AMBIENT_OCCLUSION_FS_DATA;
|
||||
out << SHADERS_LIGHT_REFLECTIONS_FS_DATA;
|
||||
out << SHADERS_LIGHT_INDIRECT_FS_DATA;
|
||||
}
|
||||
if (variant.hasDirectionalLighting()) {
|
||||
|
||||
@@ -48,6 +48,7 @@ struct UTILS_PUBLIC MaterialInfo {
|
||||
filament::SpecularAmbientOcclusion specularAO;
|
||||
filament::RefractionMode refractionMode;
|
||||
filament::RefractionType refractionType;
|
||||
filament::ReflectionMode reflectionMode;
|
||||
filament::AttributeBitset requiredAttributes;
|
||||
filament::BlendingMode blendingMode;
|
||||
filament::BlendingMode postLightingBlendingMode;
|
||||
|
||||
@@ -218,14 +218,6 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
|
||||
if (variant.hasSkinningOrMorphing()) {
|
||||
attributes.set(VertexAttribute::BONE_INDICES);
|
||||
attributes.set(VertexAttribute::BONE_WEIGHTS);
|
||||
attributes.set(VertexAttribute::MORPH_POSITION_0);
|
||||
attributes.set(VertexAttribute::MORPH_POSITION_1);
|
||||
attributes.set(VertexAttribute::MORPH_POSITION_2);
|
||||
attributes.set(VertexAttribute::MORPH_POSITION_3);
|
||||
attributes.set(VertexAttribute::MORPH_TANGENTS_0);
|
||||
attributes.set(VertexAttribute::MORPH_TANGENTS_1);
|
||||
attributes.set(VertexAttribute::MORPH_TANGENTS_2);
|
||||
attributes.set(VertexAttribute::MORPH_TANGENTS_3);
|
||||
}
|
||||
CodeGenerator::generateShaderInputs(vs, ShaderType::VERTEX, attributes, interpolation);
|
||||
|
||||
@@ -247,6 +239,12 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_RENDERABLE_BONES,
|
||||
UibGenerator::getPerRenderableBonesUib());
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
UibGenerator::getPerRenderableMorphingUib());
|
||||
cg.generateSamplers(vs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_RENDERABLE_MORPHING),
|
||||
SibGenerator::getPerRenderPrimitiveMorphingSib(variantKey));
|
||||
}
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
@@ -310,6 +308,11 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
|
||||
auto specularAO = material.specularAOSet ? material.specularAO : defaultSpecularAO;
|
||||
CodeGenerator::generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO));
|
||||
|
||||
// Currently, we only support either screen-space refractions or reflections.
|
||||
// The HAS_REFRACTION/HAS_REFLECTIONS defines signify if refraction/reflections are supported by
|
||||
// the material, yet only one technique will be selected at runtime.
|
||||
// TODO: support simultaneous screen-space refractions and reflections.
|
||||
|
||||
CodeGenerator::generateDefine(fs, "HAS_REFRACTION", material.refractionMode != RefractionMode::NONE);
|
||||
if (material.refractionMode != RefractionMode::NONE) {
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_MODE_CUBEMAP", uint32_t(RefractionMode::CUBEMAP));
|
||||
@@ -337,6 +340,14 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
|
||||
}
|
||||
}
|
||||
|
||||
CodeGenerator::generateDefine(fs, "HAS_REFLECTIONS", material.reflectionMode != ReflectionMode::DEFAULT);
|
||||
if (material.reflectionMode != ReflectionMode::DEFAULT) {
|
||||
CodeGenerator::generateDefine(fs, "REFLECTION_MODE_SCREEN_SPACE", uint32_t(ReflectionMode::SCREEN_SPACE));
|
||||
if (material.reflectionMode == ReflectionMode::SCREEN_SPACE) {
|
||||
CodeGenerator::generateDefine(fs, "REFLECTION_MODE", "REFLECTION_MODE_SCREEN_SPACE");
|
||||
}
|
||||
}
|
||||
|
||||
bool multiBounceAO = material.multiBounceAOSet ?
|
||||
material.multiBounceAO : !isMobileTarget(shaderModel);
|
||||
CodeGenerator::generateDefine(fs, "MULTI_BOUNCE_AMBIENT_OCCLUSION", multiBounceAO ? 1u : 0u);
|
||||
|
||||
@@ -203,6 +203,16 @@ public:
|
||||
*/
|
||||
Animator* getAnimator() noexcept;
|
||||
|
||||
/**
|
||||
* Updates the morphing weights in the given entity.
|
||||
*/
|
||||
void setMorphWeights(utils::Entity entity, const float* weights, size_t count);
|
||||
|
||||
/**
|
||||
* Gets the number of morphing in the given entity.
|
||||
*/
|
||||
int getMorphTargetCount(utils::Entity entity) noexcept;
|
||||
|
||||
/**
|
||||
* Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy
|
||||
* for diagnostic purposes. The wireframe is owned by the asset so clients should not delete it.
|
||||
|
||||
@@ -52,6 +52,10 @@ struct ResourceConfiguration {
|
||||
//! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files
|
||||
//! do not need this, but it is useful for robustness.
|
||||
bool recomputeBoundingBoxes;
|
||||
|
||||
//! If true, ignore skinned primitives bind transform when compute bounding box. Implicitly true
|
||||
//! for instanced asset. Only applicable when recomputeBoundingBoxes is set to true
|
||||
bool ignoreBindTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,7 @@ material {
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
reflections : screenspace,
|
||||
parameters : [
|
||||
|
||||
// Base Color
|
||||
|
||||
@@ -9,6 +9,7 @@ material {
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
reflections : screenspace,
|
||||
parameters : [
|
||||
|
||||
// Base Color
|
||||
|
||||
@@ -10,6 +10,7 @@ material {
|
||||
specularAntiAliasing : true,
|
||||
refractionMode: screenspace,
|
||||
refractionType: thin,
|
||||
reflections: screenspace,
|
||||
parameters : [
|
||||
|
||||
// Base Color
|
||||
|
||||
@@ -9,6 +9,7 @@ material {
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
clearCoatIorChange : false,
|
||||
reflections : screenspace,
|
||||
parameters : [
|
||||
|
||||
{ type : float3, name : specularFactor },
|
||||
|
||||
@@ -10,6 +10,7 @@ material {
|
||||
specularAntiAliasing : true,
|
||||
refractionMode: screenspace,
|
||||
refractionType: solid,
|
||||
reflections: screenspace,
|
||||
parameters : [
|
||||
|
||||
// Base Color
|
||||
|
||||
@@ -176,7 +176,6 @@ Animator::Animator(FFilamentAsset* asset, FFilamentInstance* instance) {
|
||||
mImpl->instance = instance;
|
||||
mImpl->renderableManager = &asset->mEngine->getRenderableManager();
|
||||
mImpl->transformManager = &asset->mEngine->getTransformManager();
|
||||
mImpl->morpher = new MorphHelper(asset, instance);
|
||||
|
||||
const cgltf_data* srcAsset = asset->mSourceAsset->hierarchy;
|
||||
const cgltf_animation* srcAnims = srcAsset->animations;
|
||||
@@ -222,6 +221,8 @@ Animator::Animator(FFilamentAsset* asset, FFilamentInstance* instance) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mImpl->morpher = asset->mMorpher;
|
||||
}
|
||||
|
||||
void Animator::addInstance(FFilamentInstance* instance) {
|
||||
@@ -235,7 +236,6 @@ void Animator::addInstance(FFilamentInstance* instance) {
|
||||
}
|
||||
|
||||
Animator::~Animator() {
|
||||
delete mImpl->morpher;
|
||||
delete mImpl;
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
|
||||
}
|
||||
}
|
||||
|
||||
morpher->applyWeights(channel.targetEntity, weights.data(), weights.size());
|
||||
morpher->setWeights(channel.targetEntity, weights.data(), weights.size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/LightManager.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
@@ -520,14 +521,15 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const
|
||||
// node weights are provided, they override the ones specified on the mesh.
|
||||
if (numMorphTargets > 0) {
|
||||
RenderableManager::Instance renderable = mRenderableManager.getInstance(entity);
|
||||
float4 weights(0, 0, 0, 0);
|
||||
for (cgltf_size i = 0; i < std::min(MAX_MORPH_TARGETS, mesh->weights_count); ++i) {
|
||||
const auto size = std::min(MAX_MORPH_TARGETS, std::max(mesh->weights_count, node->weights_count));
|
||||
std::vector<float> weights(size);
|
||||
for (cgltf_size i = 0, c = std::min(size, mesh->weights_count); i < c; ++i) {
|
||||
weights[i] = mesh->weights[i];
|
||||
}
|
||||
for (cgltf_size i = 0; i < std::min(MAX_MORPH_TARGETS, node->weights_count); ++i) {
|
||||
for (cgltf_size i = 0, c = std::min(size, node->weights_count); i < c; ++i) {
|
||||
weights[i] = node->weights[i];
|
||||
}
|
||||
mRenderableManager.setMorphWeights(renderable, weights);
|
||||
mRenderableManager.setMorphWeights(renderable, weights.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,15 +690,11 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out
|
||||
|
||||
cgltf_size targetsCount = inPrim->targets_count;
|
||||
|
||||
// There is no need to emit a warning if there are more than 4 targets. This is only the base
|
||||
// VertexBuffer and more might be created by MorphHelper.
|
||||
if (targetsCount > MAX_MORPH_TARGETS) {
|
||||
utils::slog.w << "WARNING: Exceeded max morph target count of " << MAX_MORPH_TARGETS << utils::io::endl;
|
||||
targetsCount = MAX_MORPH_TARGETS;
|
||||
}
|
||||
|
||||
constexpr int baseTangentsAttr = (int) VertexAttribute::MORPH_TANGENTS_0;
|
||||
constexpr int basePositionAttr = (int) VertexAttribute::MORPH_POSITION_0;
|
||||
|
||||
for (cgltf_size targetIndex = 0; targetIndex < targetsCount; targetIndex++) {
|
||||
const cgltf_morph_target& morphTarget = inPrim->targets[targetIndex];
|
||||
for (cgltf_size aindex = 0; aindex < morphTarget.attributes_count; aindex++) {
|
||||
@@ -705,17 +703,8 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out
|
||||
const cgltf_attribute_type atype = attribute.type;
|
||||
const int morphId = targetIndex + 1;
|
||||
|
||||
// The glTF tangent data is ignored here, but honored in ResourceLoader.
|
||||
if (atype == cgltf_attribute_type_tangent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (atype == cgltf_attribute_type_normal) {
|
||||
VertexAttribute attr = (VertexAttribute) (baseTangentsAttr + targetIndex);
|
||||
vbb.attribute(attr, slot, VertexBuffer::AttributeType::SHORT4);
|
||||
vbb.normalized(attr);
|
||||
outPrim->morphTangents[targetIndex] = slot;
|
||||
addBufferSlot({&mResult->mGenerateTangents, atype, slot++, morphId});
|
||||
// The glTF normal and tangent data are ignored here, but honored in ResourceLoader.
|
||||
if (atype == cgltf_attribute_type_normal || atype == cgltf_attribute_type_tangent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -736,13 +725,6 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out
|
||||
slog.e << "Unsupported accessor type in " << name << io::endl;
|
||||
return false;
|
||||
}
|
||||
const int stride = (fatype == actualType) ? accessor->stride : 0;
|
||||
|
||||
VertexAttribute attr = (VertexAttribute) (basePositionAttr + targetIndex);
|
||||
vbb.attribute(attr, slot, fatype, 0, stride);
|
||||
vbb.normalized(attr, accessor->normalized);
|
||||
outPrim->morphPositions[targetIndex] = slot;
|
||||
addBufferSlot({accessor, atype, slot++, morphId});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,13 +65,13 @@ namespace gltfio {
|
||||
|
||||
class Animator;
|
||||
class Wireframe;
|
||||
class MorphHelper;
|
||||
|
||||
// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer().
|
||||
struct BufferSlot {
|
||||
const cgltf_accessor* accessor;
|
||||
cgltf_attribute_type attribute;
|
||||
int bufferIndex; // for vertex buffers only
|
||||
int morphTarget; // 0 if no morphing, otherwise 1-based index
|
||||
filament::VertexBuffer* vertexBuffer;
|
||||
filament::IndexBuffer* indexBuffer;
|
||||
};
|
||||
@@ -97,8 +97,6 @@ struct Primitive {
|
||||
filament::IndexBuffer* indices = nullptr;
|
||||
filament::Aabb aabb; // object-space bounding box
|
||||
UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes)
|
||||
uint8_t morphPositions[4] = {}; // Buffer indices for MORPH_POSITION_0, MORPH_POSITION_1 etc.
|
||||
uint8_t morphTangents[4] = {}; // Buffer indices for MORPH_TANGENTS_0, MORPH_TANGENTS_1, etc.
|
||||
};
|
||||
using MeshCache = tsl::robin_map<const cgltf_mesh*, std::vector<Primitive>>;
|
||||
|
||||
@@ -194,6 +192,10 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
|
||||
Animator* getAnimator() noexcept;
|
||||
|
||||
void setMorphWeights(utils::Entity entity , const float* weights, size_t count) noexcept;
|
||||
|
||||
int getMorphTargetCount(utils::Entity entity) noexcept;
|
||||
|
||||
utils::Entity getWireframe() noexcept;
|
||||
|
||||
filament::Engine* getEngine() const noexcept {
|
||||
@@ -243,6 +245,7 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
std::vector<FFilamentInstance*> mInstances;
|
||||
SkinVector mSkins; // unused for instanced assets
|
||||
Animator* mAnimator = nullptr;
|
||||
MorphHelper* mMorpher = nullptr;
|
||||
Wireframe* mWireframe = nullptr;
|
||||
bool mResourcesLoaded = false;
|
||||
DependencyGraph mDependencyGraph;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/NameComponentManager.h>
|
||||
|
||||
#include "MorphHelper.h"
|
||||
#include "Wireframe.h"
|
||||
|
||||
using namespace filament;
|
||||
@@ -41,6 +42,7 @@ FFilamentAsset::~FFilamentAsset() {
|
||||
|
||||
delete mAnimator;
|
||||
delete mWireframe;
|
||||
delete mMorpher;
|
||||
|
||||
mEngine->destroy(mRoot);
|
||||
mEntityManager->destroy(mRoot);
|
||||
@@ -95,6 +97,19 @@ Animator* FFilamentAsset::getAnimator() noexcept {
|
||||
return mAnimator;
|
||||
}
|
||||
|
||||
void FFilamentAsset::setMorphWeights(utils::Entity entity, const float* weights, size_t count) noexcept {
|
||||
if (mResourcesLoaded) {
|
||||
mMorpher->setWeights(entity, weights, count);
|
||||
}
|
||||
}
|
||||
|
||||
int FFilamentAsset::getMorphTargetCount(utils::Entity entity) noexcept {
|
||||
if (mResourcesLoaded) {
|
||||
return mMorpher->getTargetCount(entity);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Entity FFilamentAsset::getWireframe() noexcept {
|
||||
if (!mWireframe) {
|
||||
mWireframe = new Wireframe(this);
|
||||
@@ -274,6 +289,14 @@ Animator* FilamentAsset::getAnimator() noexcept {
|
||||
return upcast(this)->getAnimator();
|
||||
}
|
||||
|
||||
void FilamentAsset::setMorphWeights(utils::Entity entity, const float* weights, size_t count) {
|
||||
return upcast(this)->setMorphWeights(entity, weights, count);
|
||||
}
|
||||
|
||||
int FilamentAsset::getMorphTargetCount(utils::Entity entity) noexcept {
|
||||
return upcast(this)->getMorphTargetCount(entity);
|
||||
}
|
||||
|
||||
Entity FilamentAsset::getWireframe() noexcept {
|
||||
return upcast(this)->getWireframe();
|
||||
}
|
||||
|
||||
@@ -324,6 +324,7 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
|
||||
.transparencyMode(config.doubleSided ?
|
||||
MaterialBuilder::TransparencyMode::TWO_PASSES_TWO_SIDES :
|
||||
MaterialBuilder::TransparencyMode::DEFAULT)
|
||||
.reflectionMode(MaterialBuilder::ReflectionMode::SCREEN_SPACE)
|
||||
.targetApi(filamat::targetApiFromBackend(engine->getBackend()));
|
||||
|
||||
if (!optimizeShaders) {
|
||||
|
||||
+41
-101
@@ -16,11 +16,12 @@
|
||||
|
||||
#include "MorphHelper.h"
|
||||
|
||||
#include <filament/BufferObject.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <math/norm.h>
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
|
||||
#include "GltfEnums.h"
|
||||
#include "TangentsJob.h"
|
||||
|
||||
using namespace filament;
|
||||
@@ -45,14 +46,6 @@ static bool isSubsetOf(ubyte4 a, ubyte4 b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int indexOf(int a, ubyte4 b) {
|
||||
if (a == b.x) return 0;
|
||||
if (a == b.y) return 1;
|
||||
if (a == b.z) return 2;
|
||||
if (a == b.w) return 3;
|
||||
return -1;
|
||||
}
|
||||
|
||||
MorphHelper::MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst) : mAsset(asset),
|
||||
mInstance(inst) {
|
||||
NodeMap& sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap : asset->mNodeMap;
|
||||
@@ -62,7 +55,7 @@ MorphHelper::MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst) : mAsse
|
||||
if (mesh) {
|
||||
cgltf_primitive const* prims = mesh->primitives;
|
||||
for (cgltf_size pi = 0, count = mesh->primitives_count; pi < count; ++pi) {
|
||||
addPrimitive(mesh, pi, &mMorphTable[pair.second]);
|
||||
addPrimitive(mesh, pi, pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,96 +65,50 @@ MorphHelper::~MorphHelper() {
|
||||
auto engine = mAsset->mEngine;
|
||||
for (auto& entry : mMorphTable) {
|
||||
for (auto& prim : entry.second.primitives) {
|
||||
for (auto& target : prim.targets) {
|
||||
engine->destroy(target.bufferObject);
|
||||
if (prim.targets) {
|
||||
engine->destroy(prim.targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MorphHelper::applyWeights(Entity entity, float const* weights, size_t count) noexcept {
|
||||
void MorphHelper::setWeights(Entity entity, float const* weights, int count) noexcept {
|
||||
auto& engine = *mAsset->mEngine;
|
||||
auto renderableManager = &engine.getRenderableManager();
|
||||
auto renderable = renderableManager->getInstance(entity);
|
||||
auto& rcm = engine.getRenderableManager();
|
||||
rcm.setMorphWeights(rcm.getInstance(entity), weights, count);
|
||||
}
|
||||
|
||||
// If there are 4 or fewer targets, we can simply re-use the original VertexBuffer.
|
||||
if (count <= 4) {
|
||||
float4 vec{};
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
vec[i] = weights[i];
|
||||
}
|
||||
renderableManager->setMorphWeights(renderable, vec);
|
||||
return;
|
||||
}
|
||||
|
||||
// We honor up to 255 weights because our set representation is a 4-tuple of bytes, with one
|
||||
// slot reserved for a sentinel value. Note that 255 is much more than the glTF min spec of 4.
|
||||
count = std::min(count, size_t(255));
|
||||
|
||||
// Make a copy of the weights because we want to re-order them.
|
||||
auto& sorted = mPartiallySortedWeights;
|
||||
sorted.clear();
|
||||
sorted.insert(sorted.begin(), weights, weights + count);
|
||||
|
||||
// Find the four highest weights in O(n) by doing a partial sort.
|
||||
std::nth_element(sorted.begin(), sorted.begin() + 4, sorted.end(), [](float a, float b) {
|
||||
return a > b;
|
||||
});
|
||||
|
||||
// Find the "primary indices" which are the indices of the four highest weights. This is O(n).
|
||||
ubyte4 primaryIndices = {kUnused, kUnused, kUnused, kUnused};
|
||||
for (size_t index = 0, primary = 0; index < count && primary < 4; ++index) {
|
||||
const float w = weights[index];
|
||||
if (w > 0 && (w == sorted[0] || w == sorted[1] || w == sorted[2] || w == sorted[3])) {
|
||||
primaryIndices[primary++] = index;
|
||||
int MorphHelper::getTargetCount(Entity entity) const noexcept {
|
||||
if (mMorphTable.count(entity)) {
|
||||
auto& primitive = mMorphTable.at(entity).primitives;
|
||||
if (!primitive.empty() && primitive[0].targets) {
|
||||
return primitive[0].targets->getCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Swap out the buffer objects for the primary indices.
|
||||
for (const auto& prim : mMorphTable[entity].primitives) {
|
||||
for (const auto& target : prim.targets) {
|
||||
const int index = indexOf(target.morphTargetIndex, primaryIndices);
|
||||
if (index > -1) {
|
||||
assert_invariant(primaryIndices[index] == target.morphTargetIndex);
|
||||
VertexBuffer* vb = prim.vertexBuffer;
|
||||
const int bufferObjectSlot = target.type == cgltf_attribute_type_position ?
|
||||
prim.positions[index] : prim.tangents[index];
|
||||
|
||||
// Slot 0 is always used for the base position so if it's getting clobbered, then
|
||||
// something is seriously wrong. Should never occur, assert just in case.
|
||||
assert_invariant(bufferObjectSlot != 0);
|
||||
|
||||
vb->setBufferObjectAt(engine, bufferObjectSlot, target.bufferObject);
|
||||
|
||||
// Do not break out early because there could be more than one target entry for this
|
||||
// particular target index (e.g. positions + tangents).
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, set the 4-tuple uniform for the weight values by derefing the primary indices.
|
||||
// Note that we first create a "safe set" by replacing the unused sentinel with zero.
|
||||
float4 highest;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
highest[i] = (primaryIndices[i] == kUnused) ? 0 : weights[primaryIndices[i]];
|
||||
}
|
||||
renderableManager->setMorphWeights(renderable, highest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This method copies various morphing-related data from the FilamentAsset MeshCache primitive
|
||||
// (which lives in transient memory) into the MorphHelper primitive (which will stay resident).
|
||||
void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, TableEntry* entry) {
|
||||
void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Entity entity){
|
||||
auto& entry = mMorphTable[entity];
|
||||
auto& engine = *mAsset->mEngine;
|
||||
const cgltf_primitive& prim = mesh->primitives[primitiveIndex];
|
||||
const auto& gltfioPrim = mAsset->mMeshCache.at(mesh)[primitiveIndex];
|
||||
VertexBuffer* vertexBuffer = gltfioPrim.vertices;
|
||||
entry.primitives.push_back({ nullptr });
|
||||
|
||||
entry->primitives.push_back({ vertexBuffer });
|
||||
auto& morphHelperPrim = entry->primitives.back();
|
||||
auto& morphHelperPrim = entry.primitives.back();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
morphHelperPrim.positions[i] = gltfioPrim.morphPositions[i];
|
||||
morphHelperPrim.tangents[i] = gltfioPrim.morphTangents[i];
|
||||
if (prim.targets_count) {
|
||||
morphHelperPrim.targets = MorphTargetBuffer::Builder()
|
||||
.vertexCount(vertexBuffer->getVertexCount())
|
||||
.count(prim.targets_count)
|
||||
.build(engine);
|
||||
|
||||
auto& rcm = engine.getRenderableManager();
|
||||
rcm.setMorphTargetBufferAt(rcm.getInstance(entity),
|
||||
primitiveIndex, morphHelperPrim.targets);
|
||||
}
|
||||
|
||||
const cgltf_accessor* previous = nullptr;
|
||||
@@ -175,19 +122,15 @@ void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Table
|
||||
continue;
|
||||
}
|
||||
if (atype == cgltf_attribute_type_normal) {
|
||||
|
||||
// TODO: use JobSystem for this, like what we do for non-morph tangents.
|
||||
TangentsJob job;
|
||||
TangentsJob::Params params = { .in = { &prim, targetIndex } };
|
||||
TangentsJob::run(¶ms);
|
||||
|
||||
if (params.out.results) {
|
||||
const size_t size = params.out.vertexCount * sizeof(short4);
|
||||
BufferObject* bufferObject = BufferObject::Builder().size(size).build(engine);
|
||||
VertexBuffer::BufferDescriptor bd(params.out.results, size, FREE_CALLBACK);
|
||||
bufferObject->setBuffer(engine, std::move(bd));
|
||||
params.out.results = nullptr;
|
||||
morphHelperPrim.targets.push_back({bufferObject, targetIndex, atype});
|
||||
morphHelperPrim.targets->setTangentsAt(engine, targetIndex,
|
||||
params.out.results, params.out.vertexCount);
|
||||
free(params.out.results);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -201,19 +144,16 @@ void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Table
|
||||
if (accessor->buffer_view) {
|
||||
auto bufferData = (const uint8_t*) accessor->buffer_view->buffer->data;
|
||||
assert_invariant(bufferData);
|
||||
const uint8_t* data = computeBindingOffset(accessor) + bufferData;
|
||||
const uint32_t size = computeBindingSize(accessor);
|
||||
auto* data = computeBindingOffset(accessor) + bufferData;
|
||||
|
||||
// This creates a copy because we don't know when the user will free the cgltf
|
||||
// source data. For non-morphed vertex buffers, we use a sharing mechanism to
|
||||
// prevent copies, but here we just want to keep it as simple as possible.
|
||||
uint8_t* clone = (uint8_t*) malloc(size);
|
||||
memcpy(clone, data, size);
|
||||
|
||||
BufferObject* bufferObject = BufferObject::Builder().size(size).build(engine);
|
||||
VertexBuffer::BufferDescriptor bd(clone, size, FREE_CALLBACK);
|
||||
bufferObject->setBuffer(engine, std::move(bd));
|
||||
morphHelperPrim.targets.push_back({bufferObject, targetIndex, atype});
|
||||
if (accessor->type == cgltf_type_vec3) {
|
||||
morphHelperPrim.targets->setPositionsAt(engine, targetIndex,
|
||||
(const float3*) data, vertexBuffer->getVertexCount());
|
||||
} else {
|
||||
assert_invariant(accessor->type == cgltf_type_vec4);
|
||||
morphHelperPrim.targets->setPositionsAt(engine, targetIndex,
|
||||
(const float4*) data, vertexBuffer->getVertexCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_MORPHHELPER_H
|
||||
#define GLTFIO_MORPHHELPER_H
|
||||
|
||||
#include "FFilamentAsset.h"
|
||||
#include "FFilamentInstance.h"
|
||||
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
@@ -44,35 +49,25 @@ public:
|
||||
MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst);
|
||||
~MorphHelper();
|
||||
|
||||
/**
|
||||
* Picks the 4 most influential weights and applies them to the target entity.
|
||||
*/
|
||||
void applyWeights(Entity targetEntity, float const* weights, size_t count) noexcept;
|
||||
void setWeights(Entity entity, float const* weights, int count) noexcept;
|
||||
int getTargetCount(Entity entity) const noexcept;
|
||||
|
||||
private:
|
||||
struct GltfTarget {
|
||||
filament::BufferObject* bufferObject;
|
||||
int morphTargetIndex;
|
||||
cgltf_attribute_type type;
|
||||
};
|
||||
|
||||
struct GltfPrimitive {
|
||||
filament::VertexBuffer* vertexBuffer;
|
||||
uint8_t positions[4];
|
||||
uint8_t tangents[4];
|
||||
std::vector<GltfTarget> targets; // TODO: flatten this?
|
||||
filament::MorphTargetBuffer* targets;
|
||||
};
|
||||
|
||||
struct TableEntry {
|
||||
std::vector<GltfPrimitive> primitives; // TODO: flatten this?
|
||||
};
|
||||
|
||||
void addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, TableEntry* entry);
|
||||
void addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Entity entity);
|
||||
|
||||
std::vector<float> mPartiallySortedWeights;
|
||||
tsl::robin_map<Entity, TableEntry> mMorphTable;
|
||||
const FFilamentAsset* mAsset;
|
||||
const FFilamentInstance* mInstance;
|
||||
};
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
#endif // GLTFIO_MORPHHELPER_H
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "GltfEnums.h"
|
||||
#include "FFilamentAsset.h"
|
||||
#include "TangentsJob.h"
|
||||
#include "MorphHelper.h"
|
||||
#include "upcast.h"
|
||||
|
||||
#include <filament/BufferObject.h>
|
||||
@@ -86,13 +87,19 @@ struct ResourceLoader::Impl {
|
||||
mEngine = config.engine;
|
||||
mNormalizeSkinningWeights = config.normalizeSkinningWeights;
|
||||
mRecomputeBoundingBoxes = config.recomputeBoundingBoxes;
|
||||
mIgnoreBindTransform = config.ignoreBindTransform;
|
||||
}
|
||||
|
||||
Engine* mEngine;
|
||||
bool mNormalizeSkinningWeights;
|
||||
bool mRecomputeBoundingBoxes;
|
||||
bool mIgnoreBindTransform;
|
||||
std::string mGltfPath;
|
||||
|
||||
// This is used to calculate skinIndex when updateBoundingBoxes, so that the correspondency between
|
||||
// cgltf_node* and FFilamentInstance::Skin can be retrieved. This pointer doesn't need to be freed.
|
||||
cgltf_skin* cgltfSkinBaseAddress;
|
||||
|
||||
// User-provided resource data with URI string keys, populated with addResourceData().
|
||||
// This is used on platforms without traditional file systems, such as Android, iOS, and WebGL.
|
||||
UriDataCache mUriDataCache;
|
||||
@@ -458,6 +465,11 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
}
|
||||
|
||||
if (pImpl->mRecomputeBoundingBoxes) {
|
||||
// asset->mSkins is unused for instanced assets
|
||||
if (!pImpl->mIgnoreBindTransform) {
|
||||
pImpl->mIgnoreBindTransform = asset->isInstanced();
|
||||
}
|
||||
pImpl->cgltfSkinBaseAddress = &gltf->skins[0];
|
||||
updateBoundingBoxes(asset);
|
||||
}
|
||||
|
||||
@@ -861,15 +873,10 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
|
||||
|
||||
// Collect all TANGENT vertex attribute slots that need to be populated.
|
||||
tsl::robin_map<VertexBuffer*, uint8_t> baseTangents;
|
||||
tsl::robin_map<VertexBuffer*, uint8_t> morphTangents[4];
|
||||
for (auto slot : asset->mBufferSlots) {
|
||||
if (slot.accessor != kGenerateTangents && slot.accessor != kGenerateNormals) {
|
||||
continue;
|
||||
}
|
||||
if (slot.morphTarget) {
|
||||
morphTangents[slot.morphTarget - 1][slot.vertexBuffer] = slot.bufferIndex;
|
||||
continue;
|
||||
}
|
||||
baseTangents[slot.vertexBuffer] = slot.bufferIndex;
|
||||
}
|
||||
|
||||
@@ -885,13 +892,6 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
|
||||
if (iter != baseTangents.end()) {
|
||||
jobParams.emplace_back(Params {{ pair.first }, {vb, iter->second }});
|
||||
}
|
||||
for (int morphTarget = 0; morphTarget < 4; morphTarget++) {
|
||||
const auto& tangents = morphTangents[morphTarget];
|
||||
auto iter = tangents.find(vb);
|
||||
if (iter != tangents.end()) {
|
||||
jobParams.emplace_back(Params {{ pair.first, morphTarget }, {vb, iter->second }});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kick off jobs for computing tangent frames.
|
||||
@@ -912,6 +912,8 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
|
||||
params.out.results, bo->getByteCount(), FREE_CALLBACK));
|
||||
params.context.vb->setBufferObjectAt(*mEngine, params.context.slot, bo);
|
||||
}
|
||||
|
||||
asset->mMorpher = new MorphHelper(asset, nullptr);
|
||||
}
|
||||
|
||||
ResourceLoader::Impl::~Impl() {
|
||||
@@ -1005,27 +1007,99 @@ void ResourceLoader::updateBoundingBoxes(FFilamentAsset* asset) const {
|
||||
*result = aabb;
|
||||
};
|
||||
|
||||
// Collect all mesh primitives that we wish to find bounds for.
|
||||
std::vector<cgltf_primitive const*> prims;
|
||||
const size_t posAttrSize = cgltf_num_components(cgltf_type_vec3);
|
||||
const size_t skinningAttrSize = cgltf_num_components(cgltf_type_vec4);
|
||||
auto computeBoundingBoxSkinned = [&](const cgltf_primitive* prim, const Skin* skin, Aabb* result) {
|
||||
Aabb aabb;
|
||||
std::vector<mat4f> inverseGlobalTransforms(skin->targets.size());
|
||||
for (size_t i = 0; i < skin->targets.size(); i++) {
|
||||
auto xformable = tm.getInstance(skin->targets[i]);
|
||||
if (xformable) {
|
||||
inverseGlobalTransforms[i] = inverse(tm.getWorldTransform(xformable));
|
||||
}
|
||||
}
|
||||
std::vector<float> verts;
|
||||
std::vector<float> rawJoints;
|
||||
std::vector<float> weights;
|
||||
for (cgltf_size slot = 0; slot < prim->attributes_count; slot++) {
|
||||
const cgltf_attribute& attr = prim->attributes[slot];
|
||||
const cgltf_accessor* accessor = attr.data;
|
||||
if (attr.type == cgltf_attribute_type_position && cgltf_num_components(accessor->type) >= posAttrSize) {
|
||||
verts.resize(accessor->count * posAttrSize);
|
||||
cgltf_accessor_unpack_floats(accessor, &verts[0], accessor->count * posAttrSize);
|
||||
}
|
||||
if (attr.type == cgltf_attribute_type_joints && cgltf_num_components(accessor->type) >= skinningAttrSize) {
|
||||
rawJoints.resize(accessor->count * skinningAttrSize);
|
||||
cgltf_accessor_unpack_floats(accessor, &rawJoints[0], accessor->count * skinningAttrSize);
|
||||
}
|
||||
if (attr.type == cgltf_attribute_type_weights && cgltf_num_components(accessor->type) >= skinningAttrSize) {
|
||||
weights.resize(accessor->count * skinningAttrSize);
|
||||
cgltf_accessor_unpack_floats(accessor, &weights[0], accessor->count * skinningAttrSize);
|
||||
}
|
||||
}
|
||||
std::vector<size_t> jointIndices(rawJoints.begin(), rawJoints.end());
|
||||
auto primitiveCount = static_cast<size_t>(verts.size() / posAttrSize);
|
||||
for (size_t i = 0; i < primitiveCount; i++) {
|
||||
float3 point(verts[posAttrSize * i], verts[posAttrSize * i + 1], verts[posAttrSize * i + 2]);
|
||||
mat4f tmp = mat4f(0.0f);
|
||||
for (size_t j = 0; j < skinningAttrSize; j++) {
|
||||
size_t jointIndex = jointIndices[skinningAttrSize * i + j];
|
||||
float jointWeight = weights[skinningAttrSize * i + j];
|
||||
Entity jointEntity = skin->joints[jointIndex];
|
||||
mat4f globalJointTransform = tm.getWorldTransform(tm.getInstance(jointEntity));
|
||||
mat4f inverseBindMatrix = skin->inverseBindMatrices[jointIndex];
|
||||
tmp += jointWeight * globalJointTransform * inverseBindMatrix;
|
||||
}
|
||||
for (const auto& inverseGlobalTransform: inverseGlobalTransforms) {
|
||||
mat4f skinMatrix = inverseGlobalTransform * tmp;
|
||||
if (!pImpl->mNormalizeSkinningWeights) {
|
||||
skinMatrix /= skinMatrix[3].w;
|
||||
}
|
||||
float3 skinnedPoint = (point.x * skinMatrix[0] + point.y * skinMatrix[1] + point.z * skinMatrix[2] + skinMatrix[3]).xyz;
|
||||
aabb.min = min(aabb.min, skinnedPoint);
|
||||
aabb.max = max(aabb.max, skinnedPoint);
|
||||
}
|
||||
}
|
||||
*result = aabb;
|
||||
};
|
||||
|
||||
// Collect all mesh primitives that we wish to find bounds for. For each mesh primitive, we also
|
||||
// collect the skin it bound to (nullptr if not skinned) for bounds computation.
|
||||
std::vector<std::pair<cgltf_primitive*, const Skin*>> primitives;
|
||||
for (auto iter : nodeMap) {
|
||||
const cgltf_mesh* mesh = iter.first->mesh;
|
||||
if (mesh) {
|
||||
for (cgltf_size index = 0, nprims = mesh->primitives_count; index < nprims; ++index) {
|
||||
prims.push_back(&mesh->primitives[index]);
|
||||
const Skin* skin = nullptr;
|
||||
cgltf_skin* const cgltfSkin = iter.first->skin;
|
||||
if (cgltfSkin) {
|
||||
// importSkins unpacked cgltfSkin into FFilamentInstance::SkinVector bijectively so that
|
||||
// the unpacked Skin can be retrieved given cgltfSkin index
|
||||
int skinIndex = cgltfSkin - pImpl->cgltfSkinBaseAddress;
|
||||
skin = &asset->mSkins[skinIndex];
|
||||
}
|
||||
const cgltf_mesh* cgltfMesh = iter.first->mesh;
|
||||
if (cgltfMesh) {
|
||||
for (cgltf_size index = 0, nprims = cgltfMesh->primitives_count; index < nprims; ++index) {
|
||||
primitives.push_back({&cgltfMesh->primitives[index], skin});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kick off a bounding box job for every primitive.
|
||||
std::vector<Aabb> bounds(prims.size());
|
||||
std::vector<Aabb> bounds(primitives.size());
|
||||
JobSystem* js = &pImpl->mEngine->getJobSystem();
|
||||
JobSystem::Job* parent = js->createJob();
|
||||
for (size_t i = 0; i < prims.size(); ++i) {
|
||||
cgltf_primitive const* prim = prims[i];
|
||||
for (size_t i = 0; i < primitives.size(); ++i) {
|
||||
Aabb* result = &bounds[i];
|
||||
js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] {
|
||||
computeBoundingBox(prim, result);
|
||||
}));
|
||||
if (pImpl->mIgnoreBindTransform || !primitives[i].second) {
|
||||
cgltf_primitive const* prim = primitives[i].first;
|
||||
js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] {
|
||||
computeBoundingBox(prim, result);
|
||||
}));
|
||||
} else {
|
||||
std::pair<cgltf_primitive*, const Skin*> skinnedPrimitive = primitives[i];
|
||||
js->run(jobs::createJob(*js, parent, [skinnedPrimitive, result, computeBoundingBoxSkinned] {
|
||||
computeBoundingBoxSkinned(skinnedPrimitive.first, skinnedPrimitive.second, result);
|
||||
}));
|
||||
}
|
||||
}
|
||||
js->runAndWait(parent);
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ Material* UbershaderLoader::getMaterial(const MaterialKey& config) const {
|
||||
case MATINDEX(LIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_OPAQUE); break;
|
||||
#endif
|
||||
|
||||
#if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_BLEND_DATA)
|
||||
#if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_FADE_DATA)
|
||||
case MATINDEX(LIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_FADE); break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -477,6 +477,13 @@ private:
|
||||
return v;
|
||||
}
|
||||
|
||||
friend inline VECTOR<T> MATH_PURE sign(VECTOR<T> v) {
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
v[i] = std::copysign(T(1), v[i]);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, T p) {
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
v[i] = std::pow(v[i], p);
|
||||
|
||||
@@ -73,6 +73,9 @@ set(SRCS
|
||||
if (WIN32)
|
||||
list(APPEND SRCS src/win32/Path.cpp)
|
||||
endif()
|
||||
if (ANDROID)
|
||||
list(APPEND SRCS src/android/ThermalManager.cpp)
|
||||
endif()
|
||||
if (LINUX OR ANDROID)
|
||||
list(APPEND SRCS src/linux/Condition.cpp)
|
||||
list(APPEND SRCS src/linux/Mutex.cpp)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_THERMALMANAGER_H
|
||||
#define TNT_UTILS_THERMALMANAGER_H
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <utils/android/ThermalManager.h>
|
||||
#else
|
||||
#include <utils/generic/ThermalManager.h>
|
||||
#endif
|
||||
|
||||
#endif // TNT_UTILS_THERMALMANAGER_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user