Merge branch 'master' of github.com:nmfisher/polyvox_filament

This commit is contained in:
Nick Fisher
2023-08-20 23:24:09 +08:00
91 changed files with 4653 additions and 4314 deletions

Binary file not shown.

View File

@@ -225,6 +225,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:vector_math/vector_math.dart' as v;
import 'package:polyvox_filament/filament_controller.dart';
@@ -21,15 +22,32 @@ class MyApp extends StatefulWidget {
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return MaterialApp(
// showPerformanceOverlay: true,
color: Colors.white,
home: Scaffold(backgroundColor: Colors.white, body: ExampleWidget()));
}
}
class ExampleWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ExampleWidgetState();
}
}
class _ExampleWidgetState extends State<ExampleWidget> {
late FilamentController _filamentController;
FilamentEntity? _cube;
FilamentEntity? _flightHelmet;
List<String>? _animations;
FilamentEntity? _light;
final weights = List.filled(255, 0.0);
List<String> _targetNames = [];
List<String> _animationNames = [];
bool _loop = false;
bool _vertical = false;
bool _rendering = false;
@@ -38,174 +56,99 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
_filamentController = FilamentController(this);
_filamentController = FilamentController();
}
void onClick(int index) async {
switch (index) {
case -1:
await _filamentController.initialize();
break;
case -2:
bool _initialized = false;
bool _coneHidden = false;
Widget _item(void Function() onTap, String text) {
return GestureDetector(
onTap: onTap,
child: Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
child: Text(text)));
}
@override
Widget build(BuildContext context) {
var children = [
_initialized
? Container()
: _item(() async {
await _filamentController.initialize();
setState(() {
_initialized = true;
});
}, "initialize"),
_item(() {
_filamentController.render();
break;
case -4:
}, "render"),
_item(() {
setState(() {
_rendering = !_rendering;
_filamentController.setRendering(_rendering);
});
break;
case -5:
}, "Rendering: $_rendering "),
_item(() {
setState(() {
_framerate = _framerate == 60 ? 30 : 60;
_filamentController.setFrameRate(_framerate);
});
break;
case 0:
}, "$_framerate fps"),
_item(() {
_filamentController.setBackgroundColor(Color(0xFF73C9FA));
}, "set background color"),
_item(() {
_filamentController.setBackgroundImage('assets/background.ktx');
break;
case 1:
}, "load background image"),
_item(() {
_filamentController
.loadSkybox('assets/default_env/default_env_skybox.ktx');
break;
case -3:
}, 'load skybox'),
_item(() {
_filamentController.loadIbl('assets/default_env/default_env_ibl.ktx');
break;
case 2:
_filamentController.removeSkybox();
break;
case 3:
_cube = _filamentController.loadGlb('assets/cube.glb');
_animationNames = _filamentController.getAnimationNames(_cube!);
break;
case 4:
}, 'load IBL'),
_item(
() {
_filamentController.removeSkybox();
},
'remove skybox',
),
_item(() async {
_cube = await _filamentController.loadGlb('assets/cube.glb');
_animations = await _filamentController.getAnimationNames(_cube!);
setState(() {});
}, 'load cube GLB'),
_item(() async {
if (_coneHidden) {
_filamentController.reveal(_cube!, "Cone");
} else {
_filamentController.hide(_cube!, "Cone");
}
setState(() {
_coneHidden = !_coneHidden;
});
}, _coneHidden ? 'show cone' : 'hide cone'),
_item(() async {
if (_cube != null) {
_filamentController.removeAsset(_cube!);
}
_cube = _filamentController.loadGltf('assets/cube.gltf', 'assets');
break;
case 5:
_flightHelmet ??= _filamentController.loadGltf(
'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet');
break;
case 6:
_filamentController.removeAsset(_cube!);
break;
case 7:
_filamentController.setMorphTargetWeights(
_cube!, "Cube.001", List.filled(8, 1.0));
break;
case 8:
_filamentController.setMorphTargetWeights(
_cube!, "Cube.001", List.filled(8, 0));
break;
case 9:
for (int i = 0; i < _animationNames.length; i++) {
print("Playing animation ${_animationNames[i]}");
_filamentController.playAnimation(_cube!, i, loop: _loop);
}
break;
case 10:
_filamentController.stopAnimation(_cube!, 0);
break;
case 11:
setState(() {
_loop = !_loop;
});
break;
case 14:
_filamentController.setCamera(_cube!, "Camera_Orientation");
break;
case 15:
final animation = AnimationBuilder(
controller: _filamentController,
asset: _cube!,
framerate: 30,
meshName: "Cube.001")
.setDuration(4)
.interpolateMorphWeights(0, 4, 0, 1)
// .interpolateBoneTransform(
// "Bone.001",
// "Cube.001",
// 2,
// 4,
// v.Vector3.zero(),
// v.Vector3.zero(),
// // Vec3(x: 1, y: 1, z: 1),
// v.Quaternion(0, 0, 0, 1),
// v.Quaternion(1, 1, 1, 1))
// Quaternion(x: 1, y: 1, z: 1, w: 1))
.set();
break;
case 16:
_targetNames = _filamentController.getMorphTargetNames(_cube!, "Cube");
setState(() {});
break;
case 17:
_animationNames = _filamentController.getAnimationNames(_cube!);
setState(() {});
break;
case 18:
_filamentController.panStart(1, 1);
_filamentController.panUpdate(1, 2);
_filamentController.panEnd();
break;
case 19:
_filamentController.panStart(1, 1);
_filamentController.panUpdate(0, 0);
_filamentController.panEnd();
break;
case 20:
_filamentController.clearAssets();
break;
case 21:
_cube =
await _filamentController.loadGltf('assets/cube.gltf', 'assets');
}, 'load cube GLTF'),
_item(() async {
_filamentController.setTexture(_cube!, "assets/background.png");
break;
case 22:
}, 'swap cube texture'),
_item(() async {
_filamentController.transformToUnitCube(_cube!);
break;
case 23:
}, 'transform to unit cube'),
_item(() async {
_filamentController.setPosition(_cube!, 1.0, 1.0, -1.0);
break;
case 24:
_filamentController.setRotation(_cube!, pi / 2, 0.0, 1.0, 0.0);
break;
case 25:
setState(() {
_vertical = !_vertical;
});
break;
case 26:
_filamentController.setCameraPosition(0, 0, 3);
_filamentController.setCameraRotation(0, 0, 1, 0);
break;
case 27:
_framerate = _framerate == 60 ? 30 : 60;
_filamentController.setFrameRate(_framerate);
break;
case 28:
_filamentController.setBackgroundImagePosition(25, 25);
break;
case 29:
_light = _filamentController.addLight(
1, 6500, 15000000, 0, 1, 0, 0, -1, 0, true);
_light = _filamentController.addLight(
2, 6500, 15000000, 0, 0, 1, 0, 0, -1, true);
break;
case 30:
if (_light != null) {
_filamentController.removeLight(_light!);
}
break;
case 31:
_filamentController.clearLights();
break;
case 32:
}, 'set position to 1, 1, -1'),
_item(() async {
var frameData = Float32List.fromList(
List<double>.generate(120, (i) => i / 120).expand((x) {
var vals = List<double>.filled(7, x);
@@ -225,114 +168,268 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
// "Cube.001",
// BoneTransform([Vec3(x: 0, y: 0.0, z: 0.0)],
// [Quaternion(x: 1, y: 1, z: 1, w: 1)]));
// break;
break;
case 33:
if (_coneHidden) {
_filamentController.reveal(_cube!, "Cone");
} else {
_filamentController.hide(_cube!, "Cone");
}
setState(() {
_coneHidden = !_coneHidden;
});
}, 'construct bone animation'),
_item(() async {
_filamentController.removeAsset(_cube!);
}, 'remove cube'),
_item(() async {
_filamentController.clearAssets();
}, 'clear all assets'),
_item(() async {
var names =
await _filamentController.getMorphTargetNames(_cube!, "Cylinder");
await showDialog(
context: context,
builder: (ctx) {
return Container(
height: 100,
width: 100,
color: Colors.white,
child: Text(names.join(",")));
});
}, "show morph target names for Cylinder"),
_item(() {
_filamentController.setMorphTargetWeights(
_cube!, "Cylinder", List.filled(4, 1.0));
}, "set Cylinder morph weights to 1"),
_item(() {
_filamentController.setMorphTargetWeights(
_cube!, "Cylinder", List.filled(4, 0.0));
}, "set Cylinder morph weights to 0.0"),
_item(() async {
var morphs =
await _filamentController.getMorphTargetNames(_cube!, "Cylinder");
final animation = AnimationBuilder(
availableMorphs: morphs, framerate: 30, meshName: "Cylinder")
.setDuration(4)
.setMorphTargets(["Key 1", "Key 2"])
.interpolateMorphWeights(0, 4, 0, 1)
.build();
_filamentController.setMorphAnimationData(_cube!, animation);
}, "animate morph weights #1 and #2"),
_item(() async {
var morphs =
await _filamentController.getMorphTargetNames(_cube!, "Cylinder");
final animation = AnimationBuilder(
availableMorphs: morphs, framerate: 30, meshName: "Cylinder")
.setDuration(4)
.setMorphTargets(["Key 3", "Key 4"])
.interpolateMorphWeights(0, 4, 0, 1)
.build();
_filamentController.setMorphAnimationData(_cube!, animation);
}, "animate morph weights #3 and #4"),
_item(() {
_filamentController.setMaterialColor(_cube!, "Cone", 0, Colors.purple);
}, "set cone material color to purple")
];
if (_animations != null) {
children.addAll(_animations!.map((a) => _item(() {
_filamentController.playAnimation(_cube!, _animations!.indexOf(a),
replaceActive: true, crossfade: 0.5);
}, "play animation ${_animations!.indexOf(a)} (replace/fade)")));
children.addAll(_animations!.map((a) => _item(() {
_filamentController.playAnimation(_cube!, _animations!.indexOf(a),
replaceActive: false);
}, "play animation ${_animations!.indexOf(a)} (noreplace)")));
}
}
bool _coneHidden = false;
Widget _item({int value = 0, Widget? child = null}) {
return GestureDetector(
onTap: () {
onClick(value);
},
child: Container(
margin: EdgeInsets.symmetric(vertical: 10), child: child));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
// showPerformanceOverlay: true,
color: Colors.white,
home: Scaffold(
backgroundColor: Colors.white,
body: Row(children: [
SingleChildScrollView(
return Padding(
padding: EdgeInsets.only(top: 20, left: 20),
child: Row(children: [
Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Target names : ${_targetNames.join(",")}, Animation names : ${_animationNames.join(",")}"),
_item(value: -1, child: Text("initialize")),
_item(value: -2, child: Text("render")),
_item(value: -4, child: Text("Rendering: $_rendering ")),
_item(value: -5, child: Text("$_framerate fps")),
_item(value: 0, child: Text("load background image")),
_item(
value: 1,
child: Text('load skybox'),
),
_item(
value: -3,
child: Text('load IBL'),
),
_item(
value: 2,
child: Text('remove skybox'),
),
_item(value: 3, child: Text('load cube GLB')),
_item(
value: 33,
child: Text(_coneHidden ? 'show cone' : 'hide cone')),
_item(value: 4, child: Text('load cube GLTF')),
_item(value: 21, child: Text('swap cube texture')),
_item(value: 22, child: Text('transform to unit cube')),
_item(value: 23, child: Text('set position to 1, 1, -1')),
_item(value: 32, child: Text('construct bone animation')),
_item(value: 24, child: Text('rotate by pi around Y axis')),
_item(value: 5, child: Text('load flight helmet')),
_item(value: 6, child: Text('remove cube')),
_item(value: 20, child: Text('clear all assets')),
_item(value: 7, child: Text('set all weights to 1')),
_item(value: 8, child: Text('set all weights to 0')),
_item(value: 9, child: Text('play all animations')),
_item(value: 10, child: Text('stop animations')),
_item(
value: 11,
child: Text(
_loop ? "don't loop animation" : "loop animation")),
_item(value: 14, child: Text('set camera')),
_item(value: 15, child: Text('animate weights')),
_item(value: 16, child: Text('get target names')),
_item(value: 17, child: Text('get animation names')),
_item(value: 18, child: Text('pan left')),
_item(value: 19, child: Text('pan right')),
_item(
value: 25,
child: Text(
_vertical ? 'set horizontal' : 'set vertical')),
_item(value: 26, child: Text('set camera pos to 0,0,3')),
_item(value: 27, child: Text('toggle framerate')),
_item(value: 28, child: Text('set bg image pos')),
_item(value: 29, child: Text('add light')),
_item(value: 30, child: Text('remove light')),
_item(value: 31, child: Text('clear all lights')),
_item(value: 32, child: Text('set camera model matrix')),
])),
Container(
width: _vertical ? 200 : 400,
height: _vertical ? 400 : 200,
alignment: Alignment.center,
child: SizedBox(
child: FilamentGestureDetector(
showControlOverlay: true,
controller: _filamentController,
child: FilamentWidget(
controller: _filamentController,
)),
)),
])));
children: children
// _item(24 () async { 'rotate by pi around Y axis'),
// _item(5 () async { 'load flight helmet'),
// _item(7 () async { 'set all weights to 1'),
// _item(8 () async { 'set all weights to 0'),
// _item(9 () async { 'play all animations'),
// _item(34 () async { 'play animation 0'),
// _item(34 () async { 'play animation 0 (noreplace)'),
// _item(35 () async { 'play animation 1'),
// _item(34 () async { 'play animation 0 (noreplace)'),
// _item(36 () async { 'play animation 2'),
// _item(34 () async { 'play animation 0 (noreplace)'),
// _item(36 () async { 'play animation 3'),
// _item(34 () async { 'play animation 3 (noreplace)'),
// _item(37 () async { 'stop animation 0'),
// _item(11 () async {
// Text(
// _loop ? "don't loop animation" : "loop animation")),
// _item(14 () async { 'set camera'),
// _item(15 () async { 'animate weights'),
// _item(16 () async { 'get target names'),
// _item(17 () async { 'get animation names'),
// _item(18 () async { 'pan left'),
// _item(19 () async { 'pan right'),
// _item(25 () async {
// Text(_vertical ? 'set horizontal' : 'set vertical')),
// _item(26 () async { 'set camera pos to 0,0,3'),
// _item(27 () async { 'toggle framerate'),
// _item(28 () async { 'set bg image pos'),
// _item(29 () async { 'add light'),
// _item(30 () async { 'remove light'),
// _item(31 () async { 'clear all lights'),
// _item(32 () async { 'set camera model matrix'),
))),
Container(
width: _vertical ? 200 : 400,
height: _vertical ? 400 : 200,
alignment: Alignment.center,
child: FilamentGestureDetector(
showControlOverlay: true,
controller: _filamentController,
child: FilamentWidget(
controller: _filamentController,
)),
),
]));
}
}
// case -1:
// break;
// case -2:
// _filamentController.render();
// break;
// case -4:
// setState(() {
// _rendering = !_rendering;
// _filamentController.setRendering(_rendering);
// });
// break;
// case -5:
// setState(() {
// _framerate = _framerate == 60 ? 30 : 60;
// _filamentController.setFrameRate(_framerate);
// });
// break;
// case -6:
// _filamentController.setBackgroundColor(Color(0xFF73C9FA));
// break;
// case 5:
// _flightHelmet ??= await _filamentController.loadGltf(
// 'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet');
// break;
// case 11:
// setState(() {
// _loop = !_loop;
// });
// break;
// case 14:
// _filamentController.setCamera(_cube!, "Camera_Orientation");
// break;
// case 15:
// break;
// case 17:
// var animationNames =
// await _filamentController.getAnimationNames(_cube!);
// await showDialog(
// context: context,
// builder: (ctx) {
// return Container(
// height: 100,
// width: 100,
// color: Colors.white,
// child: Text(animationNames.join(",")));
// });
// break;
// case 18:
// _filamentController.panStart(1, 1);
// _filamentController.panUpdate(1, 2);
// _filamentController.panEnd();
// break;
// case 19:
// _filamentController.panStart(1, 1);
// _filamentController.panUpdate(0, 0);
// _filamentController.panEnd();
// break;
// case 20:
// _filamentController.clearAssets();
// break;
// case 21:
// break;
// case 22:
// break;
// case 23:
// break;
// case 24:
// _filamentController.setRotation(_cube!, pi / 2, 0.0, 1.0, 0.0);
// break;
// case 25:
// setState(() {
// _vertical = !_vertical;
// });
// break;
// case 26:
// _filamentController.setCameraPosition(0, 0, 3);
// _filamentController.setCameraRotation(0, 0, 1, 0);
// break;
// case 27:
// _framerate = _framerate == 60 ? 30 : 60;
// _filamentController.setFrameRate(_framerate);
// break;
// case 28:
// _filamentController.setBackgroundImagePosition(25, 25);
// break;
// case 29:
// _light = await _filamentController.addLight(
// 1, 6500, 15000000, 0, 1, 0, 0, -1, 0, true);
// break;
// case 30:
// if (_light != null) {
// _filamentController.removeLight(_light!);
// _light = null;
// }
// break;
// case 31:
// _filamentController.clearLights();
// break;
// case 32:
// // break;
// break;
// case 33:
// break;
// case 34:
// var duration =
// await _filamentController.getAnimationDuration(_cube!, 0);
// _filamentController.playAnimation(_cube!, 0,
// loop: false, crossfade: 0.5);
// await Future.delayed(
// Duration(milliseconds: (duration * 1000.0).toInt()));
// print("animation complete");
// // showDialog(
// // context: context,
// // builder: (context) {
// // return Container(
// // width: 100,
// // height: 100,
// // color: Colors.white,
// // child: "animation complete!");
// // });
// break;
// case 35:
// _filamentController.playAnimation(_cube!, 1,
// loop: false, crossfade: 0.5);
// break;
// case 36:
// _filamentController.playAnimation(_cube!, 2,
// loop: false, crossfade: 0.5);
// break;
// case 37:
// _filamentController.stopAnimation(_cube!, 0);
// break;

View File

@@ -6,7 +6,7 @@ description: Demonstrates how to use the polyvox_filament plugin.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ">=2.12.0 <3.0.0"
sdk: ">=2.12.0 <4.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions

View File

@@ -3,11 +3,11 @@ import UIKit
import GLKit
public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture {
var registrar : FlutterPluginRegistrar
var flutterTextureId: Int64?
var registry: FlutterTextureRegistry
var pixelBuffer: CVPixelBuffer?;
var createdAt = Date()
@@ -18,121 +18,126 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
kCVPixelBufferOpenGLESCompatibilityKey: kCFBooleanTrue,
kCVPixelBufferIOSurfacePropertiesKey: [:]
] as CFDictionary
var resources:NSMutableDictionary = [:]
var viewer:UnsafeRawPointer? = nil
var displayLink:CADisplayLink? = nil
var rendering:Bool = false
static var messenger : FlutterBinaryMessenger? = nil;
var loadResource : @convention(c) (UnsafePointer<Int8>?, UnsafeMutableRawPointer?) -> ResourceBuffer = { uri, resourcesPtr in
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(resourcesPtr!).takeUnretainedValue()
let uriString = String(cString:uri!)
var path:String? = nil
// check for hot-reloaded asset
var found : URL? = nil
if(uriString.hasPrefix("asset://")) {
let assetPath = String(uriString.dropFirst(8))
print("Searching for hot reloaded asset under path : \(assetPath)")
let appFolder = Bundle.main.resourceURL
let dirPaths = NSSearchPathForDirectoriesInDomains(.applicationDirectory,
.userDomainMask, true)
let supportDirPaths = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory,
.userDomainMask, true)
let devFsPath = URL(fileURLWithPath: supportDirPaths.first!, isDirectory:true).deletingLastPathComponent().deletingLastPathComponent().appendingPathComponent("tmp")
let orderedURLs = try? FileManager.default.enumerator(at: devFsPath, includingPropertiesForKeys: [ .pathKey, .creationDateKey], options: .skipsHiddenFiles)
var path:String? = nil
for case let fileURL as URL in orderedURLs! {
if !(fileURL.path.hasSuffix(assetPath)) {
continue
}
print("Found hot reloaded asset : \(fileURL)")
if found == nil {
found = fileURL
} else {
do {
let c1 = try found!.resourceValues(forKeys: [.creationDateKey]).creationDate
let c2 = try fileURL.resourceValues(forKeys: [.creationDateKey]).creationDate
// check for hot-reloaded asset
var found : URL? = nil
if(uriString.hasPrefix("asset://")) {
let assetPath = String(uriString.dropFirst(8))
print("Searching for hot reloaded asset under path : \(assetPath)")
let appFolder = Bundle.main.resourceURL
let dirPaths = NSSearchPathForDirectoriesInDomains(.applicationDirectory,
.userDomainMask, true)
let supportDirPaths = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory,
.userDomainMask, true)
let devFsPath = URL(fileURLWithPath: supportDirPaths.first!, isDirectory:true).deletingLastPathComponent().deletingLastPathComponent().appendingPathComponent("tmp")
if c1! < c2! {
found = fileURL
print("\(fileURL) is newer, replacing")
} else {
print("Ignoring older asset")
}
} catch {
}
}
}
}
do {
if let cd = try found?.resourceValues(forKeys:[.creationDateKey]).creationDate {
if cd > instance.createdAt {
print("Using hot reloaded asset : \(found)")
path = found!.path
}
}
} catch {
}
if path == nil {
if(uriString.hasPrefix("file://")) {
path = String(uriString.dropFirst(7))
} else if(uriString.hasPrefix("asset://")) {
let key = instance.registrar.lookupKey(forAsset:String(uriString.dropFirst(8)))
path = Bundle.main.path(forResource: key, ofType:nil)
print("Found path \(path) for uri \(uriString)")
guard path != nil else {
print("File not present in bundle : \(uri)")
return ResourceBuffer()
}
} else {
let key = instance.registrar.lookupKey(forAsset:uriString)
path = Bundle.main.path(forResource: key, ofType:nil)
print("Found path \(path) for uri \(uriString)")
guard path != nil else {
print("File not present in bundle : \(uri)")
return ResourceBuffer()
let orderedURLs = try? FileManager.default.enumerator(at: devFsPath, includingPropertiesForKeys: [ .pathKey, .creationDateKey], options: .skipsHiddenFiles)
for case let fileURL as URL in orderedURLs! {
if !(fileURL.path.hasSuffix(assetPath)) {
continue
}
print("Found hot reloaded asset : \(fileURL)")
if found == nil {
found = fileURL
} else {
do {
let c1 = try found!.resourceValues(forKeys: [.creationDateKey]).creationDate
let c2 = try fileURL.resourceValues(forKeys: [.creationDateKey]).creationDate
if c1! < c2! {
found = fileURL
print("\(fileURL) is newer, replacing")
} else {
print("Ignoring older asset")
}
} catch {
}
}
}
}
}
do {
print("Opening data from path \(path)")
let data = try Data(contentsOf: URL(fileURLWithPath:path!))
let resId = instance.resources.count
let nsData = data as NSData
instance.resources[resId] = nsData
let rawPtr = nsData.bytes
return ResourceBuffer(data:rawPtr, size:UInt32(nsData.count), id:UInt32(resId))
} catch {
print("Error opening file: \(error)")
}
return ResourceBuffer()
do {
if let cd = try found?.resourceValues(forKeys:[.creationDateKey]).creationDate {
if cd > instance.createdAt {
print("Using hot reloaded asset : \(found)")
path = found!.path
}
}
} catch {
}
if path == nil {
if(uriString.hasPrefix("file://")) {
path = String(uriString.dropFirst(7))
} else if(uriString.hasPrefix("asset://")) {
let key = instance.registrar.lookupKey(forAsset:String(uriString.dropFirst(8)))
path = Bundle.main.path(forResource: key, ofType:nil)
print("Found path \(path) for uri \(uriString)")
guard path != nil else {
print("File not present in bundle : \(uri)")
return ResourceBuffer()
}
} else {
let key = instance.registrar.lookupKey(forAsset:uriString)
path = Bundle.main.path(forResource: key, ofType:nil)
print("Found path \(path) for uri \(uriString)")
guard path != nil else {
print("File not present in bundle : \(uri)")
return ResourceBuffer()
}
}
}
do {
print("Opening data from path \(path)")
let data = try Data(contentsOf: URL(fileURLWithPath:path!))
let resId = instance.resources.count
let nsData = data as NSData
instance.resources[resId] = nsData
let rawPtr = nsData.bytes
return ResourceBuffer(data:rawPtr, size:UInt32(nsData.count), id:UInt32(resId))
} catch {
print("Error opening file: \(error)")
}
return ResourceBuffer()
}
var freeResource : @convention(c) (ResourceBuffer,UnsafeMutableRawPointer?) -> () = { rbuf, resourcesPtr in
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(resourcesPtr!).takeUnretainedValue()
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(resourcesPtr!).takeUnretainedValue()
instance.resources.removeObject(forKey:rbuf.id)
}
@objc func doRender() {
if(viewer != nil && rendering) {
render(viewer, 0)
self.registry.textureFrameAvailable(flutterTextureId!)
}
}
func createDisplayLink() {
displayLink = CADisplayLink(target: self,
selector: #selector(doRender))
displayLink!.add(to: .current, forMode: RunLoop.Mode.default)
displayLink = CADisplayLink(target: self,
selector: #selector(doRender))
displayLink!.add(to: .current, forMode: RunLoop.Mode.default)
}
public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
@@ -141,7 +146,7 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
}
return Unmanaged.passRetained(pixelBuffer!);
}
public func onTextureUnregistered(_ texture:FlutterTexture) {
print("Texture unregistered")
}
@@ -150,57 +155,505 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
let _messenger = registrar.messenger();
messenger = _messenger;
let channel = FlutterMethodChannel(name: "app.polyvox.filament/event", binaryMessenger: _messenger)
let instance = SwiftPolyvoxFilamentPlugin(textureRegistry: registrar.textures(), registrar:registrar)
let instance = SwiftPolyvoxFilamentPlugin(textureRegistry: registrar.textures(), registrar:registrar)
registrar.addMethodCallDelegate(instance, channel: channel)
}
init(textureRegistry: FlutterTextureRegistry, registrar:FlutterPluginRegistrar) {
self.registry = textureRegistry;
self.registrar = registrar
}
private func createPixelBuffer(width:Int, height:Int) {
if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height),
kCVPixelFormatType_32BGRA, pixelBufferAttrs, &pixelBuffer) != kCVReturnSuccess) {
print("Error allocating pixel buffer")
}
self.flutterTextureId = self.registry.register(self)
if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height),
kCVPixelFormatType_32BGRA, pixelBufferAttrs, &pixelBuffer) != kCVReturnSuccess) {
print("Error allocating pixel buffer")
}
self.flutterTextureId = self.registry.register(self)
}
private func resize(width:Int32, height:Int32) {
if(self.flutterTextureId != nil) {
self.registry.unregisterTexture(self.flutterTextureId!)
}
createPixelBuffer(width: Int(width), height:Int(height))
if(self.flutterTextureId != nil) {
self.registry.unregisterTexture(self.flutterTextureId!)
}
createPixelBuffer(width: Int(width), height:Int(height))
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let methodName = call.method;
print(methodName)
switch methodName {
let methodName = call.method;
switch methodName {
case "createTexture":
let args = call.arguments as! Array<Int32>
createPixelBuffer(width:Int(args[0]), height:Int(args[1]))
// we no longer need to call createDisplayLink() because we drive our render ticker from the Dart side, not the platform side
result(self.flutterTextureId)
case "getResourceLoader":
let callback = make_resource_loader(loadResource, freeResource, Unmanaged.passUnretained(self).toOpaque())
result(unsafeBitCast(callback, to:Int64.self))
case "getGlTextureId":
result(FlutterMethodNotImplemented)
case "getSurface":
var pixelBufferTextureId = Int64(Int(bitPattern:unsafeBitCast(pixelBuffer!, to: UnsafeMutableRawPointer.self)))
result(pixelBufferTextureId)
case "getContext":
result(0) //nullptr
let args = call.arguments as! Array<Int32>
createPixelBuffer(width:Int(args[0]), height:Int(args[1]))
createDisplayLink()
result(self.flutterTextureId)
case "resize":
result(self.flutterTextureId);
case "tick":
self.registry.textureFrameAvailable(flutterTextureId!)
result(true)
if(viewer == nil) {
print("Error: cannot resize before a viewer has been created")
result(nil);
}
rendering = false
destroy_swap_chain(viewer)
let args = call.arguments as! [Any]
resize(width:args[0] as! Int32, height:args[1] as! Int32)
var pixelBufferTextureId = unsafeBitCast(pixelBuffer!, to: UnsafeRawPointer.self)
create_swap_chain(viewer, pixelBufferTextureId, UInt32(args[0] as! Int64), UInt32(args[1] as! Int64))
update_viewport_and_camera_projection(viewer, Int32(args[0] as! Int64), Int32(args[1] as! Int64), Float(args[2] as! Double))
rendering = true
print("Resized to \(args[0])x\(args[1])")
result(self.flutterTextureId);
case "createFilamentViewer":
let callback = make_resource_loader(loadResource, freeResource, Unmanaged.passUnretained(self).toOpaque())
let args = call.arguments as! [Any]
let width = args[0] as! Int64
let height = args[1] as! Int64
viewer = create_filament_viewer(nil, callback)
var pixelBufferTextureId = unsafeBitCast(pixelBuffer!, to: UnsafeRawPointer.self)
create_swap_chain(viewer, pixelBufferTextureId, UInt32(width), UInt32(height))
update_viewport_and_camera_projection(viewer, Int32(args[0] as! Int64), Int32(args[1] as! Int64), 1.0)
result(unsafeBitCast(viewer, to:Int64.self))
case "deleteFilamentViewer":
delete_filament_viewer(viewer)
viewer = nil
result(true)
case "getAssetManager":
let assetManager = get_asset_manager(viewer)
result(unsafeBitCast(assetManager, to:Int64.self))
case "clearBackgroundImage":
clear_background_image(viewer)
result(true)
case "setBackgroundImage":
set_background_image(viewer, call.arguments as! String)
result(true)
case "setBackgroundImagePosition":
let args = call.arguments as! [Any]
set_background_image_position(viewer, Float(args[0] as! Double), Float(args[1] as! Double), args[2] as! Bool)
result(true)
case "setBackgroundColor":
guard let args = call.arguments as? [Double], args.count == 4 else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected RGBA values for setBackgroundColor", details: nil))
return
}
set_background_color(viewer, Float(args[0]), Float(args[1]), Float(args[2]), Float(args[3]))
result(true)
case "loadSkybox":
load_skybox(viewer, call.arguments as! String)
result(true)
case "loadIbl":
let args = call.arguments as! [Any]
load_ibl(viewer, args[0] as! String, args[1] as! Float)
result(true)
case "removeSkybox":
remove_skybox(viewer)
result(true)
case "removeIbl":
remove_ibl(viewer)
result(true)
case "addLight":
guard let args = call.arguments as? [Any], args.count == 10,
let type = args[0] as? Int32,
let colour = args[1] as? Double,
let intensity = args[2] as? Double,
let posX = args[3] as? Double,
let posY = args[4] as? Double,
let posZ = args[5] as? Double,
let dirX = args[6] as? Double,
let dirY = args[7] as? Double,
let dirZ = args[8] as? Double,
let shadows = args[9] as? Bool else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected viewer and light parameters for addLight", details: nil))
return
}
let entityId = add_light(viewer, UInt8(type), Float(colour), Float(intensity),Float(posX), Float(posY), Float(posZ), Float(dirX), Float(dirY), Float(dirZ), shadows)
result(entityId)
case "removeLight":
remove_light(viewer, Int32(call.arguments as! Int64))
result(true)
case "clearLights":
clear_lights(viewer)
result(true)
case "loadGlb":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let assetPath = args[1] as? String,
let unlit = args[2] as? Bool else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected assetManager, assetPath, and unlit for load_glb", details: nil))
return
}
let entityId = load_glb(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), assetPath, unlit)
result(entityId)
case "loadGltf":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let assetPath = args[1] as? String,
let relativePath = args[2] as? String else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected assetManager, assetPath, and relativePath for load_gltf", details: nil))
return
}
let entityId = load_gltf(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), assetPath, relativePath)
result(entityId)
case "transformToUnitCube":
let args = call.arguments as! [Any]
transform_to_unit_cube(unsafeBitCast(args[0] as! Int64, to:UnsafeMutableRawPointer.self), args[1] as! EntityId)
result(true)
case "render":
render(viewer, 0)
result(true)
case "setRendering":
rendering = call.arguments as! Bool
result(true)
case "setFrameInterval":
let interval = call.arguments as! Double
displayLink!.preferredFramesPerSecond = Int(1 / interval)
print("Set preferred frames er second to \(displayLink!.preferredFramesPerSecond)")
let fInterval = Float(interval)
print("Set filament interval to \(fInterval)")
set_frame_interval(viewer, fInterval)
result(true)
case "updateViewportAndCameraProjection":
guard let args = call.arguments as? [Any], args.count == 3,
let width = args[0] as? Int,
let height = args[1] as? Int,
let scaleFactor = args[2] as? Float else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected viewer, width, height, and scaleFactor for update_viewport_and_camera_projection", details: nil))
return
}
update_viewport_and_camera_projection(viewer, Int32(width), Int32(height), scaleFactor)
result(true)
case "scrollBegin":
scroll_begin(viewer)
result(true)
case "scrollUpdate":
guard let args = call.arguments as? [Any], args.count == 3,
let x = args[0] as? Double,
let y = args[1] as? Double,
let z = args[2] as? Double else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected viewer, x, y, and z for scroll_update", details: nil))
return
}
scroll_update(viewer, Float(x), Float(y), Float(z))
result(true)
case "scrollEnd":
scroll_end(viewer)
result(true)
case "grabBegin":
guard let args = call.arguments as? [Any], args.count == 3,
let x = args[0] as? Double,
let y = args[1] as? Double,
let pan = args[2] as? Bool else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected viewer, x, y, and pan for grab_begin", details: nil))
return
}
grab_begin(viewer, Float(x), Float(y), pan)
result(true)
case "grabUpdate":
guard let args = call.arguments as? [Any], args.count == 2,
let x = args[0] as? Float,
let y = args[1] as? Float else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected viewer, x, and y for grab_update", details: nil))
return
}
grab_update(viewer, x, y)
result(true)
case "grabEnd":
grab_end(viewer)
result(true)
case "applyWeights":
guard let args = call.arguments as? [Any], args.count == 5,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let entityName = args[2] as? String,
let weights = args[3] as? [Float],
let count = args[4] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for apply_weights", details: nil))
return
}
// apply_weights(assetManager, asset, entityName, UnsafeMutablePointer(&weights), Int32(count))
result(true)
case "setMorphTargetWeights":
guard let args = call.arguments as? [Any], args.count == 5,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let entityName = args[2] as? String,
let morphData = args[3] as? [Double],
let numMorphWeights = args[4] as? Int32 else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for setMorphTargetWeights", details: nil))
return
}
set_morph_target_weights(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, entityName, morphData.map { Float($0) }, Int32(numMorphWeights))
result(true)
case "setMorphAnimation":
guard let args = call.arguments as? [Any], args.count == 8,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let entityName = args[2] as? String,
let morphData = args[3] as? [Double],
let morphIndices = args[4] as? [Int32],
let numMorphTargets = args[5] as? Int32,
let numFrames = args[6] as? Int32,
let frameLengthInMs = args[7] as? Double else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Incorrect arguments provided for setMorphAnimation", details: nil))
return
}
let frameData = morphData.map { Float($0) }
let am = unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self)
let success = set_morph_animation(
am,
asset,
entityName,
frameData,
morphIndices,
Int32(numMorphTargets),
Int32(numFrames),
Float(frameLengthInMs))
result(success)
case "setBoneAnimation":
guard let args = call.arguments as? [Any], args.count == 9,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let frameData = args[2] as? [Float],
let numFrames = args[3] as? Int,
let numBones = args[4] as? Int,
let boneNames = args[5] as? [String],
let meshName = args[6] as? [String],
let numMeshTargets = args[7] as? Int,
let frameLengthInMs = args[8] as? Float else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for set_bone_animation", details: nil))
return
}
// // Convert boneNames and meshName to C-style strings array.
// var cBoneNames: [UnsafePointer<CChar>?] = boneNames.map { $0.cString(using: .utf8) }
// var cMeshName: [UnsafePointer<CChar>?] = meshName.map { $0.cString(using: .utf8) }
//
// set_bone_animation(assetManager, asset, UnsafeMutablePointer(&frameData), numFrames, numBones, &cBoneNames, &cMeshName, numMeshTargets, frameLengthInMs)
// // Clean up after conversion
// for cStr in cBoneNames { free(UnsafeMutablePointer(mutating: cStr)) }
// for cStr in cMeshName { free(UnsafeMutablePointer(mutating: cStr)) }
result(true)
case "playAnimation":
guard let args = call.arguments as? [Any], args.count == 7,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let index = args[2] as? Int,
let loop = args[3] as? Bool,
let reverse = args[4] as? Bool,
let replaceActive = args[5] as? Bool,
let crossfade = args[6] as? Double else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for play_animation", details: nil))
return
}
play_animation(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, Int32(index), loop, reverse, replaceActive, Float(crossfade))
result(true)
case "getAnimationDuration":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let animationIndex = args[2] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for getAnimationDuration", details: nil))
return
}
let dur = get_animation_duration(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, Int32(animationIndex))
result(dur)
case "setAnimationFrame":
guard let args = call.arguments as? [Any], args.count == 4,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let animationIndex = args[2] as? Int,
let animationFrame = args[3] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for set_animation_frame", details: nil))
return
}
set_animation_frame(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, Int32(animationIndex), Int32(animationFrame))
result(true)
case "stopAnimation":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let index = args[2] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for stop_animation", details: nil))
return
}
stop_animation(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, Int32(index))
result(true)
case "getAnimationCount":
guard let args = call.arguments as? [Any], args.count == 2,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for get_animation_count", details: nil))
return
}
let count = get_animation_count(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset)
result(count)
case "getAnimationNames":
guard let args = call.arguments as? [Any], args.count == 2,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for get_animation_name", details: nil))
return
}
var names:[String] = [];
var count = get_animation_count(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset)
var buffer = [CChar](repeating: 0, count: 256) // Assuming max name length of 256 for simplicity
for i in 0...count - 1 {
get_animation_name(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, &buffer, Int32(i))
let name = String(cString: buffer)
names.append(name)
}
result(names)
case "getAnimationName":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let index = args[2] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for get_animation_name", details: nil))
return
}
var buffer = [CChar](repeating: 0, count: 256) // Assuming max name length of 256 for simplicity
get_animation_name(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, &buffer, Int32(index))
let name = String(cString: buffer)
result(name)
case "getMorphTargetName":
guard let args = call.arguments as? [Any], args.count == 4,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let meshName = args[2] as? String,
let index = args[3] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for get_morph_target_name", details: nil))
return
}
var buffer = [CChar](repeating: 0, count: 256) // Assuming max name length of 256 for simplicity
get_morph_target_name(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName, &buffer, Int32(index))
let targetName = String(cString: buffer)
result(targetName)
case "getMorphTargetNames":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let meshName = args[2] as? String else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for get_morph_target_name", details: nil))
return
}
let count = get_morph_target_name_count(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName)
var names:[String] = []
if count > 0 {
for i in 0...count - 1 {
var buffer = [CChar](repeating: 0, count: 256) // Assuming max name length of 256 for simplicity
get_morph_target_name(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName, &buffer, Int32(i))
names.append(String(cString:buffer))
}
}
result(names)
case "getMorphTargetNameCount":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let meshName = args[2] as? String else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for get_morph_target_name_count", details: nil))
return
}
let count = get_morph_target_name_count(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName)
result(count)
case "removeAsset":
remove_asset(viewer, call.arguments as! EntityId)
result(true)
case "clearAssets":
clear_assets(viewer)
result(true)
case "setCamera":
guard let args = call.arguments as? [Any], args.count == 2,
let asset = args[0] as? EntityId,
let nodeName = args[1] as? String? else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected asset and nodeName for set_camera", details: nil))
return
}
let success = set_camera(viewer, asset, nodeName)
result(success)
case "setCameraPosition":
let args = call.arguments as! [Any]
set_camera_position(viewer, Float(args[0] as! Double), Float(args[1] as! Double), Float(args[2] as! Double))
result(true)
case "setCameraRotation":
let args = call.arguments as! [Any]
set_camera_rotation(viewer, Float(args[0] as! Double), Float(args[1] as! Double), Float(args[2] as! Double), Float(args[3] as! Double))
result(true)
case "setCameraModelMatrix":
guard let matrix = call.arguments as? [Float], matrix.count == 16 else { // Assuming a 4x4 matrix
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for set_camera_model_matrix", details: nil))
return
}
set_camera_model_matrix(viewer, matrix)
result(true)
case "setCameraFocalLength":
set_camera_focal_length(viewer, call.arguments as! Float)
result(true)
case "setCameraFocusDistance":
set_camera_focus_distance(viewer, call.arguments as! Float)
result(true)
case "setMaterialColor":
guard let args = call.arguments as? [Any], args.count == 5,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let meshName = args[2] as? String,
let materialIndex = args[3] as? Int32,
let color = args[4] as? [Double] else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for setMaterialColor", details: nil))
return
}
set_material_color(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName, materialIndex, Float(color[0]), Float(color[1]), Float(color[2]), Float(color[3]))
result(true)
case "hideMesh":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let meshName = args[2] as? String else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for hide_mesh", details: nil))
return
}
let status = hide_mesh(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName)
result(status)
case "revealMesh":
guard let args = call.arguments as? [Any], args.count == 3,
let assetManager = args[0] as? Int64,
let asset = args[1] as? EntityId,
let meshName = args[2] as? String else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for reveal_mesh", details: nil))
return
}
let status = reveal_mesh(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, meshName)
result(status)
default:
result(FlutterMethodNotImplemented)
}
result(FlutterMethodNotImplemented)
}
}
}

View File

@@ -17,7 +17,7 @@ namespace polyvox {
class AssetManager {
public:
AssetManager(ResourceLoaderWrapper* loader,
AssetManager(const ResourceLoaderWrapper* const loader,
NameComponentManager *ncm,
Engine *engine,
Scene *scene);
@@ -28,6 +28,7 @@ namespace polyvox {
void remove(EntityId entity);
void destroyAll();
unique_ptr<vector<string>> getAnimationNames(EntityId entity);
float getAnimationDuration(EntityId entity, int animationIndex);
unique_ptr<vector<string>> getMorphTargetNames(EntityId entity, const char *meshName);
void transformToUnitCube(EntityId e);
inline void updateTransform(EntityId e);
@@ -39,15 +40,17 @@ namespace polyvox {
const utils::Entity* getLightEntities(EntityId e) const noexcept;
size_t getLightEntityCount(EntityId e) const noexcept;
void updateAnimations();
bool setMaterialColor(EntityId e, const char* meshName, int materialInstance, const float r, const float g, const float b, const float a);
bool setMorphAnimationBuffer(
EntityId entityId,
const char* entityName,
const float* const morphData,
int numMorphWeights,
const int* const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs);
void setMorphTargetWeights(EntityId entityId, const char* const entityName, const float* const weights, int count);
bool setBoneAnimationBuffer(
@@ -59,7 +62,7 @@ namespace polyvox {
const char** const meshName,
int numMeshTargets,
float frameLengthInMs);
void playAnimation(EntityId e, int index, bool loop, bool reverse);
void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f);
void stopAnimation(EntityId e, int index);
void setMorphTargetWeights(const char* const entityName, float *weights, int count);
void loadTexture(EntityId entity, const char* resourcePath, int renderableIndex);
@@ -69,7 +72,7 @@ namespace polyvox {
private:
AssetLoader* _assetLoader = nullptr;
ResourceLoaderWrapper* _resourceLoaderWrapper;
const ResourceLoaderWrapper* const _resourceLoaderWrapper;
NameComponentManager* _ncm = nullptr;
Engine* _engine;
Scene* _scene;
@@ -77,6 +80,8 @@ namespace polyvox {
MaterialProvider* _ubershaderProvider = nullptr;
gltfio::ResourceLoader* _gltfResourceLoader = nullptr;
gltfio::TextureProvider* _stbDecoder = nullptr;
gltfio::TextureProvider* _ktxDecoder = nullptr;
vector<SceneAsset> _assets;
tsl::robin_map<EntityId, int> _entityIdLookup;

View File

@@ -44,8 +44,7 @@ typedef int32_t EntityId;
namespace polyvox {
class FilamentViewer {
public:
// FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource);
FilamentViewer(void* context, ResourceLoaderWrapper* resourceLoaderWrapper);
FilamentViewer(const void* context, const ResourceLoaderWrapper* const resourceLoaderWrapper);
~FilamentViewer();
void loadSkybox(const char* const skyboxUri);
@@ -64,7 +63,7 @@ namespace polyvox {
bool setCamera(EntityId asset, const char* nodeName);
void createSwapChain(void* surface, uint32_t width, uint32_t height);
void createSwapChain(const void* surface, uint32_t width, uint32_t height);
void destroySwapChain();
void createRenderTarget(uint32_t glTextureId, uint32_t width,uint32_t height);
@@ -108,7 +107,7 @@ namespace polyvox {
math::mat4f _cameraPosition;
math::mat4f _cameraRotation;
ResourceLoaderWrapper* _resourceLoaderWrapper;
const ResourceLoaderWrapper* const _resourceLoaderWrapper;
Scene* _scene;
View* _view;

View File

@@ -3,43 +3,38 @@
#include "ResourceBuffer.hpp"
#include <stddef.h>
typedef int32_t EntityId;
void* create_filament_viewer(void *context, ResourceLoaderWrapper* loader);
const void* create_filament_viewer(const void* const context, const ResourceLoaderWrapper* const loader);
ResourceLoaderWrapper* make_resource_loader(LoadResourceFromOwner loadFn, FreeResourceFromOwner freeFn, void* owner);
void delete_filament_viewer(void *viewer);
void* get_asset_manager(void* viewer);
void create_render_target(void *viewer, uint32_t textureId, uint32_t width, uint32_t height);
void clear_background_image(void *viewer);
void set_background_image(void *viewer, const char *path);
void set_background_image_position(void *viewer, float x, float y, bool clamp);
void set_background_color(void *viewer, const float r, const float g, const float b, const float a);
void load_skybox(void *viewer, const char *skyboxPath);
void load_ibl(void *viewer, const char *iblPath, float intensity);
void remove_skybox(void *viewer);
void remove_ibl(void *viewer);
EntityId add_light(void *viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows);
void remove_light(void *viewer, EntityId entityId);
void clear_lights(void *viewer);
void delete_filament_viewer(const void* const viewer);
void* get_asset_manager(const void* const viewer);
void create_render_target(const void* const viewer, uint32_t textureId, uint32_t width, uint32_t height);
void clear_background_image(const void* const viewer);
void set_background_image(const void* const viewer, const char *path);
void set_background_image_position(const void* const viewer, float x, float y, bool clamp);
void set_background_color(const void* const viewer, const float r, const float g, const float b, const float a);
void load_skybox(const void* const viewer, const char *skyboxPath);
void load_ibl(const void* const viewer, const char *iblPath, float intensity);
void remove_skybox(const void* const viewer);
void remove_ibl(const void* const viewer);
EntityId add_light(const void* const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows);
void remove_light(const void* const viewer, EntityId entityId);
void clear_lights(const void* const viewer);
EntityId load_glb(void *assetManager, const char *assetPath, bool unlit);
EntityId load_gltf(void *assetManager, const char *assetPath, const char *relativePath);
bool set_camera(void *viewer, EntityId asset, const char *nodeName);
void render(void *viewer, uint64_t frameTimeInNanos);
void create_swap_chain(void *viewer, void *surface, uint32_t width, uint32_t height);
void destroy_swap_chain(void *viewer);
void set_frame_interval(void *viewer, float interval);
void* get_renderer(void *viewer);
void update_viewport_and_camera_projection(void *viewer, int width, int height, float scaleFactor);
void scroll_begin(void *viewer);
void scroll_update(void *viewer, float x, float y, float z);
void scroll_end(void *viewer);
void grab_begin(void *viewer, float x, float y, bool pan);
void grab_update(void *viewer, float x, float y);
void grab_end(void *viewer);
bool set_camera(const void* const viewer, EntityId asset, const char *nodeName);
void render(const void* const viewer, uint64_t frameTimeInNanos);
void create_swap_chain(const void* const viewer, const void* const surface, uint32_t width, uint32_t height);
void destroy_swap_chain(const void* const viewer);
void set_frame_interval(const void* const viewer, float interval);
void update_viewport_and_camera_projection(const void* const viewer, int width, int height, float scaleFactor);
void scroll_begin(const void* const viewer);
void scroll_update(const void* const viewer, float x, float y, float z);
void scroll_end(const void* const viewer);
void grab_begin(const void* const viewer, float x, float y, bool pan);
void grab_update(const void* const viewer, float x, float y);
void grab_end(const void* const viewer);
void apply_weights(
void* assetManager,
EntityId asset,
@@ -47,7 +42,6 @@ void apply_weights(
float *const weights,
int count
);
void set_morph_target_weights(
void* assetManager,
EntityId asset,
@@ -55,13 +49,13 @@ void set_morph_target_weights(
const float *const morphData,
int numWeights
);
bool set_morph_animation(
void* assetManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
int numMorphWeights,
const int* const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs);
@@ -76,29 +70,32 @@ void set_bone_animation(
int numMeshTargets,
float frameLengthInMs);
void play_animation(void* assetManager, EntityId asset, int index, bool loop, bool reverse);
void play_animation(void* assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade);
void set_animation_frame(void* assetManager, EntityId asset, int animationIndex, int animationFrame);
void stop_animation(void* assetManager, EntityId asset, int index);
int get_animation_count(void* assetManager, EntityId asset);
void get_animation_name(void* assetManager, EntityId asset, char *const outPtr, int index);
float get_animation_duration(void* assetManager, EntityId asset, int index);
void get_morph_target_name(void* assetManager, EntityId asset, const char *meshName, char *const outPtr, int index);
int get_morph_target_name_count(void* assetManager, EntityId asset, const char *meshName);
void remove_asset(void *viewer, EntityId asset);
void clear_assets(void *viewer);
void remove_asset(const void* const viewer, EntityId asset);
void clear_assets(const void* const viewer);
void load_texture(void* assetManager, EntityId asset, const char *assetPath, int renderableIndex);
void set_texture(void* assetManager, EntityId asset);
bool set_material_color(void* assetManager, EntityId asset, const char* meshName, int materialIndex, const float r, const float g, const float b, const float a);
void transform_to_unit_cube(void* assetManager, EntityId asset);
void set_position(void* assetManager, EntityId asset, float x, float y, float z);
void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z);
void set_scale(void* assetManager, EntityId asset, float scale);
void set_camera_exposure(void *viewer, float aperture, float shutterSpeed, float sensitivity);
void set_camera_position(void *viewer, float x, float y, float z);
void set_camera_rotation(void *viewer, float rads, float x, float y, float z);
void set_camera_model_matrix(void *viewer, const float *const matrix);
void set_camera_focal_length(void *viewer, float focalLength);
void set_camera_focus_distance(void *viewer, float focusDistance);
void set_camera_exposure(const void* const viewer, float aperture, float shutterSpeed, float sensitivity);
void set_camera_position(const void* const viewer, float x, float y, float z);
void set_camera_rotation(const void* const viewer, float rads, float x, float y, float z);
void set_camera_model_matrix(const void* const viewer, const float *const matrix);
void set_camera_focal_length(const void* const viewer, float focalLength);
void set_camera_focus_distance(const void* const viewer, float focusDistance);
int hide_mesh(void* assetManager, EntityId asset, const char* meshName);
int reveal_mesh(void* assetManager, EntityId asset, const char* meshName);
void ios_dummy();
#endif

View File

@@ -51,14 +51,14 @@ extern "C" {
};
ResourceBuffer load(const char* uri) {
ResourceBuffer load(const char* uri) const {
if(mLoadResourceFromOwner) {
return mLoadResourceFromOwner(uri, mOwner);
}
return mLoadResource(uri);
}
void free(ResourceBuffer rb) {
void free(ResourceBuffer rb) const {
if(mFreeResourceFromOwner) {
mFreeResourceFromOwner(rb, mOwner);
} else {

View File

@@ -31,12 +31,17 @@ namespace polyvox {
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
enum AnimationType {
MORPH, BONE, GLTF
};
struct AnimationStatus {
time_point_t mStart = time_point_t::max();
bool mLoop = false;
bool mReverse = false;
float mDuration = 0;
bool mAnimating = false;
float mDuration = 0;
AnimationType type;
int gltfIndex = -1;
};
//
@@ -47,7 +52,7 @@ namespace polyvox {
int mNumFrames = -1;
float mFrameLengthInMs = 0;
vector<float> mFrameData;
int mNumMorphWeights = 0;
vector<int> mMorphIndices;
};
//
@@ -73,10 +78,14 @@ namespace polyvox {
FilamentAsset* mAsset = nullptr;
Animator* mAnimator = nullptr;
// fixed-sized vector containing the status of the morph, bone and GLTF animations.
// entries 0 and 1 are the morph/bone animations.
// subsequent entries are the GLTF animations.
// vector containing AnimationStatus structs for the morph, bone and/or glTF animations.
vector<AnimationStatus> mAnimations;
// the index of the last active glTF animation,
// used to cross-fade
int fadeGltfAnimationIndex = -1;
float fadeDuration = 0.0f;
float fadeOutAnimationStart = 0.0f;
MorphAnimationBuffer mMorphAnimationBuffer;
BoneAnimationBuffer mBoneAnimationBuffer;
@@ -96,12 +105,6 @@ namespace polyvox {
FilamentAsset* asset
) : mAsset(asset) {
mAnimator = mAsset->getInstance()->getAnimator();
mAnimations.resize(2 + mAnimator->getAnimationCount());
for(int i=0; i < mAnimations.size() - 2; i++) {
mAnimations[i].mDuration = mAnimator->getAnimationDuration(i);
}
}
};
}
}

View File

@@ -106,7 +106,8 @@ static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guarantee
* Defines the backend's feature levels.
*/
enum class FeatureLevel : uint8_t {
FEATURE_LEVEL_1 = 1, //!< OpenGL ES 3.0 features (default)
FEATURE_LEVEL_0 = 0, //!< OpenGL ES 2.0 features
FEATURE_LEVEL_1, //!< OpenGL ES 3.0 features (default)
FEATURE_LEVEL_2, //!< OpenGL ES 3.1 features + 16 textures units + cubemap arrays
FEATURE_LEVEL_3 //!< OpenGL ES 3.1 features + 31 textures units + cubemap arrays
};
@@ -279,6 +280,15 @@ enum class UniformType : uint8_t {
STRUCT
};
/**
* Supported constant parameter types
*/
enum class ConstantType : uint8_t {
INT,
FLOAT,
BOOL
};
enum class Precision : uint8_t {
LOW,
MEDIUM,
@@ -286,6 +296,14 @@ enum class Precision : uint8_t {
DEFAULT
};
/**
* Shader compiler priority queue
*/
enum class CompilerPriorityQueue : uint8_t {
HIGH,
LOW
};
//! Texture sampler type
enum class SamplerType : uint8_t {
SAMPLER_2D, //!< 2D texture
@@ -1117,7 +1135,12 @@ enum class Workaround : uint16_t {
// the whole render pass.
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP,
// for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu
ADRENO_UNIFORM_ARRAY_CRASH
ADRENO_UNIFORM_ARRAY_CRASH,
// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
A8X_STATIC_TEXTURE_TARGET_ERROR,
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
};
} // namespace filament::backend

View File

@@ -39,7 +39,6 @@ struct HwRenderTarget;
struct HwSamplerGroup;
struct HwStream;
struct HwSwapChain;
struct HwSync;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBuffer;
@@ -126,7 +125,6 @@ using RenderTargetHandle = Handle<HwRenderTarget>;
using SamplerGroupHandle = Handle<HwSamplerGroup>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using SyncHandle = Handle<HwSync>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;

View File

@@ -279,8 +279,8 @@ public:
break;
}
size_t bpr = bpp * stride;
size_t bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
size_t const bpr = bpp * stride;
size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
return bprAligned * height;
}

View File

@@ -22,6 +22,7 @@
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
namespace filament::backend {
@@ -47,6 +48,8 @@ public:
size_t handleArenaSize = 0;
};
Platform() noexcept;
virtual ~Platform() noexcept;
/**
@@ -79,6 +82,85 @@ public:
* thread, or if the platform does not need to perform any special processing.
*/
virtual bool pumpEvents() noexcept;
/**
* InsertBlobFunc is an Invocable to an application-provided function that a
* backend implementation may use to insert a key/value pair into the
* cache.
*/
using InsertBlobFunc = utils::Invocable<
void(const void* key, size_t keySize, const void* value, size_t valueSize)>;
/*
* RetrieveBlobFunc is an Invocable to an application-provided function that a
* backend implementation may use to retrieve a cached value from the
* cache.
*/
using RetrieveBlobFunc = utils::Invocable<
size_t(const void* key, size_t keySize, void* value, size_t valueSize)>;
/**
* Sets the callback functions that the backend can use to interact with caching functionality
* provided by the application.
*
* Cache functions may only be specified once during the lifetime of a
* Platform. The <insert> and <retrieve> Invocables may be called at any time and
* from any thread from the time at which setBlobFunc is called until the time that Platform
* is destroyed. Concurrent calls to these functions from different threads is also allowed.
*
* @param insertBlob an Invocable that inserts a new value into the cache and associates
* it with the given key
* @param retrieveBlob an Invocable that retrieves from the cache the value associated with a
* given key
*/
void setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept;
/**
* @return true if setBlobFunc was called.
*/
bool hasBlobFunc() const noexcept;
/**
* To insert a new binary value into the cache and associate it with a given
* key, the backend implementation can call the application-provided callback
* function insertBlob.
*
* No guarantees are made as to whether a given key/value pair is present in
* the cache after the set call. If a different value has been associated
* with the given key in the past then it is undefined which value, if any, is
* associated with the key after the set call. Note that while there are no
* guarantees, the cache implementation should attempt to cache the most
* recently set value for a given key.
*
* @param key pointer to the beginning of the key data that is to be inserted
* @param keySize specifies the size in byte of the data pointed to by <key>
* @param value pointer to the beginning of the value data that is to be inserted
* @param valueSize specifies the size in byte of the data pointed to by <value>
*/
void insertBlob(const void* key, size_t keySize, const void* value, size_t valueSize);
/**
* To retrieve the binary value associated with a given key from the cache, a
* the backend implementation can call the application-provided callback
* function retrieveBlob.
*
* If the cache contains a value for the given key and its size in bytes is
* less than or equal to <valueSize> then the value is written to the memory
* pointed to by <value>. Otherwise nothing is written to the memory pointed
* to by <value>.
*
* @param key pointer to the beginning of the key
* @param keySize specifies the size in bytes of the binary key pointed to by <key>
* @param value pointer to a buffer to receive the cached binary data, if it exists
* @param valueSize specifies the size in bytes of the memory pointed to by <value>
* @return If the cache contains a value associated with the given key then the
* size of that binary value in bytes is returned. Otherwise 0 is returned.
*/
size_t retrieveBlob(const void* key, size_t keySize, void* value, size_t valueSize);
private:
InsertBlobFunc mInsertBlob;
RetrieveBlobFunc mRetrieveBlob;
};
} // namespace filament

View File

@@ -48,7 +48,15 @@ public:
ShaderStageFlags stageFlags = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS;
};
struct Uniform {
utils::CString name; // full qualified name of the uniform field
uint16_t offset; // offset in 'uint32_t' into the uniform buffer
uint8_t size; // >1 for arrays
UniformType type; // uniform type
};
using UniformBlockInfo = std::array<utils::CString, UNIFORM_BINDING_COUNT>;
using UniformInfo = utils::FixedCapacityVector<Uniform>;
using SamplerGroupInfo = std::array<SamplerGroupData, SAMPLER_BINDING_COUNT>;
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
@@ -63,6 +71,8 @@ public:
~Program() noexcept;
Program& priorityQueue(CompilerPriorityQueue priorityQueue) noexcept;
// sets the material name and variant for diagnostic purposes only
Program& diagnostics(utils::CString const& name,
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)>&& logger);
@@ -78,6 +88,14 @@ public:
Program& uniformBlockBindings(
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& uniformBlockBindings) noexcept;
// Note: This is only needed for GLES2.0, this is used to emulate UBO. This function tells
// the program everything it needs to know about the uniforms at a given binding
Program& uniforms(uint32_t index, UniformInfo const& uniforms) noexcept;
// Note: This is only needed for GLES2.0.
Program& attributes(
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> attributes) noexcept;
// sets the 'bindingPoint' sampler group descriptor for this program.
// 'samplers' can be destroyed after this call.
// This effectively associates a set of (BindingPoints, index) to a texture unit in the shader.
@@ -93,6 +111,7 @@ public:
Program& specializationConstants(
utils::FixedCapacityVector<SpecializationConstant> specConstants) noexcept;
Program& cacheId(uint64_t cacheId) noexcept;
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
@@ -103,6 +122,12 @@ public:
SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; }
SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; }
auto const& getBindingUniformInfo() const { return mBindingUniformInfo; }
auto& getBindingUniformInfo() { return mBindingUniformInfo; }
auto const& getAttributes() const { return mAttributes; }
auto& getAttributes() { return mAttributes; }
utils::CString const& getName() const noexcept { return mName; }
utils::CString& getName() noexcept { return mName; }
@@ -113,6 +138,10 @@ public:
return mSpecializationConstants;
}
uint64_t getCacheId() const noexcept { return mCacheId; }
CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; }
private:
friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder);
@@ -120,8 +149,12 @@ private:
SamplerGroupInfo mSamplerGroups = {};
ShaderSource mShadersSource;
utils::CString mName;
uint64_t mCacheId{};
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
utils::FixedCapacityVector<SpecializationConstant> mSpecializationConstants;
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
};
} // namespace filament::backend

View File

@@ -95,6 +95,19 @@ public:
*/
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
/**
* Returns the set of buffers that must be preserved up to the call to commit().
* The default value is TargetBufferFlags::NONE.
* The color buffer is always preserved, however ancillary buffers, such as the depth buffer
* are generally discarded. The preserve flags can be used to make sure those ancillary
* buffers are preserved until the call to commit.
*
* @param swapChain
* @return buffer that must be preserved
* @see commit()
*/
virtual TargetBufferFlags getPreservedFlags(SwapChain* swapChain) noexcept;
/**
* Called by the driver to establish the default FBO. The default implementation returns 0.
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
@@ -254,6 +267,27 @@ public:
* @return Transformed image.
*/
virtual AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept;
// --------------------------------------------------------------------------------------------
/**
* Returns true if additional OpenGL contexts can be created. Default: false.
* @return true if additional OpenGL contexts can be created.
* @see createContext
*/
virtual bool isExtraContextSupported() const noexcept;
/**
* Creates an OpenGL context with the same configuration than the main context and makes it
* current to the current thread. Must not be called from the main driver thread.
* createContext() is only supported if isExtraContextSupported() returns true.
* These additional contexts will be automatically terminated in terminate.
*
* @param shared whether the new context is shared with the main context.
* @see isExtraContextSupported()
* @see terminate()
*/
virtual void createContext(bool shared);
};
} // namespace filament

View File

@@ -50,6 +50,9 @@ protected:
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
@@ -57,6 +60,10 @@ protected:
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImage(ExternalTexture* texture) noexcept override;
void retainExternalImage(void* externalImage) noexcept override;
bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override;
private:
PlatformCocoaGLImpl* pImpl = nullptr;

View File

@@ -47,6 +47,9 @@ public:
uint32_t createDefaultRenderTarget() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;

View File

@@ -26,6 +26,9 @@
#include <backend/DriverEnums.h>
#include <utility>
#include <vector>
namespace filament::backend {
/**
@@ -35,8 +38,31 @@ class PlatformEGL : public OpenGLPlatform {
public:
PlatformEGL() noexcept;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
protected:
// --------------------------------------------------------------------------------------------
// Helper for EGL configs and attributes parameters
class Config {
public:
Config();
Config(std::initializer_list<std::pair<EGLint, EGLint>> list);
EGLint& operator[](EGLint name);
EGLint operator[](EGLint name) const;
void erase(EGLint name) noexcept;
EGLint const* data() const noexcept {
return reinterpret_cast<EGLint const*>(mConfig.data());
}
size_t size() const noexcept {
return mConfig.size();
}
private:
std::vector<std::pair<EGLint, EGLint>> mConfig = {{ EGL_NONE, EGL_NONE }};
};
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -79,6 +105,8 @@ protected:
* @param name a string giving some context on the error. Typically __func__.
*/
static void logEglError(const char* name) noexcept;
static void logEglError(const char* name, EGLint error) noexcept;
static const char* getEglErrorName(EGLint error) noexcept;
/**
* Calls glGetError() to clear the current error flags. logs a warning to log.w if
@@ -98,6 +126,8 @@ protected:
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
// supported extensions detected at runtime
struct {
@@ -105,13 +135,16 @@ protected:
bool OES_EGL_image_external_essl3 = false;
} gl;
struct {
bool KHR_no_config_context = false;
bool ANDROID_recordable = false;
bool KHR_create_context = false;
bool KHR_gl_colorspace = false;
bool KHR_no_config_context = false;
} egl;
} ext;
private:
void initializeGlExtensions() noexcept;
private:
EGLConfig findSwapChainConfig(uint64_t flags) const;
};

View File

@@ -23,9 +23,10 @@
#include "utils/unwindows.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <vector>
namespace filament::backend {
/**
@@ -46,6 +47,9 @@ protected:
void terminate() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
@@ -57,6 +61,8 @@ protected:
HWND mHWnd = NULL;
HDC mWhdc = NULL;
PIXELFORMATDESCRIPTOR mPfd = {};
std::vector<HGLRC> mAdditionalContexts;
std::vector<int> mAttribs;
};
} // namespace filament::backend

View File

@@ -19,31 +19,220 @@
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
#include <utils/FixedCapacityVector.h>
#include <utils/PrivateImplementation.h>
#include <tuple>
#include <unordered_set>
namespace filament::backend {
using SwapChain = Platform::SwapChain;
/**
* Private implementation details for the provided vulkan platform.
*/
struct VulkanPlatformPrivate;
/**
* A Platform interface that creates a Vulkan backend.
*/
class VulkanPlatform : public Platform {
class VulkanPlatform : public Platform, utils::PrivateImplementation<VulkanPlatformPrivate> {
public:
struct SurfaceBundle {
void* surface;
// On certain platforms, the extent of the surface cannot be queried from Vulkan. In those
// situations, we allow the frontend to pass in the extent to use in creating the swap
// chains. Platform implementation should set extent to 0 if they do not expect to set the
// swap chain extent.
uint32_t width;
uint32_t height;
/**
* A collection of handles to objects and metadata that comprises a Vulkan context. The client
* can instantiate this struct and pass to Engine::Builder::sharedContext if they wishes to
* share their vulkan context. This is specifically necessary if the client wishes to override
* the swapchain API.
*/
struct VulkanSharedContext {
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice logicalDevice = VK_NULL_HANDLE;
uint32_t graphicsQueueFamilyIndex = 0xFFFFFFFF;
// In the usual case, the client needs to allocate at least one more graphics queue
// for Filament, and this index is the param to pass into vkGetDeviceQueue. In the case
// where the gpu only has one graphics queue. Then the client needs to ensure that no
// concurrent access can occur.
uint32_t graphicsQueueIndex = 0xFFFFFFFF;
};
// Given a Vulkan instance and native window handle, creates the platform-specific surface.
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept = 0;
/**
* Shorthand for the pointer to the Platform SwapChain struct, we use it also as a handle (i.e.
* identifier for the swapchain).
*/
using SwapChainPtr = Platform::SwapChain*;
~VulkanPlatform() override;
/**
* Collection of images, formats, and extent (width/height) that defines the swapchain.
*/
struct SwapChainBundle {
utils::FixedCapacityVector<VkImage> colors;
VkImage depth = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
};
VulkanPlatform();
~VulkanPlatform() override;
Driver* createDriver(void* sharedContext,
Platform::DriverConfig const& driverConfig) noexcept override;
int getOSVersion() const noexcept override {
return 0;
}
// ----------------------------------------------------
// ---------- Platform Customization options ----------
/**
* The client preference can be stored within the struct. We allow for two specification of
* preference:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`.
* 2) Index of the device in the list as returned by vkEnumeratePhysicalDevices.
*/
struct GPUPreference {
std::string deviceName;
int8_t index = -1;
};
/**
* Client can provide a preference over the GPU to use in the vulkan instance
* @return `GPUPreference` struct that indicates the client's preference
*/
virtual GPUPreference getPreferredGPU() noexcept {
return {};
}
// -------- End platform customization options --------
// ----------------------------------------------------
/**
* Returns whether the platform supports sRGB swapchain. This is true by default, and the client
* needs to override this method to specify otherwise.
* @return Whether the platform supports sRGB swapchain.
*/
virtual bool isSRGBSwapChainSupported() const {
return true;
}
/**
* Get the images handles and format of the memory backing the swapchain. This should be called
* after createSwapChain() or after recreateIfResized().
* @param swapchain The handle returned by createSwapChain()
* @return An array of VkImages
*/
virtual SwapChainBundle getSwapChainBundle(SwapChainPtr handle);
/**
* Acquire the next image for rendering. The `index` will be written with an non-negative
* integer that the backend can use to index into the `SwapChainBundle.colors` array. The
* corresponding VkImage will be used as the output color attachment. The client should signal
* the `clientSignal` semaphore when the image is ready to be used by the backend.
* @param handle The handle returned by createSwapChain()
* @param clientSignal The semaphore that the client will signal to indicate that the backend
* may render into the image.
* @param index Pointer to memory that will be filled with the index that corresponding
* to an image in the `SwapChainBundle.colors` array.
* @return Result of acquire
*/
virtual VkResult acquire(SwapChainPtr handle, VkSemaphore clientSignal, uint32_t* index);
/**
* Present the image corresponding to `index` to the display. The client should wait on
* `finishedDrawing` before presenting.
* @param handle The handle returned by createSwapChain()
* @param index Index that corresponding to an image in the
* `SwapChainBundle.colors` array.
* @param finishedDrawing Backend passes in a semaphore that the client will signal to
* indicate that the client may render into the image.
* @return Result of present
*/
virtual VkResult present(SwapChainPtr handle, uint32_t index, VkSemaphore finishedDrawing);
/**
* Check if the surface size has changed.
* @param handle The handle returned by createSwapChain()
* @return Whether the swapchain has been resized
*/
virtual bool hasResized(SwapChainPtr handle);
/**
* Carry out a recreation of the swapchain.
* @param handle The handle returned by createSwapChain()
* @return Result of the recreation
*/
virtual VkResult recreate(SwapChainPtr handle);
/**
* Create a swapchain given a platform window, or if given a null `nativeWindow`, then we
* try to create a headless swapchain with the given `extent`.
* @param flags Optional parameters passed to the client as defined in
* Filament::SwapChain.h.
* @param extent Optional width and height that indicates the size of the headless swapchain.
* @return Result of the operation
*/
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});
/**
* Destroy the swapchain.
* @param handle The handle returned by createSwapChain()
*/
virtual void destroy(SwapChainPtr handle);
/**
* Clean up any resources owned by the Platform. For example, if the Vulkan instance handle was
* generated by the platform, we need to clean it up in this method.
*/
virtual void terminate();
/**
* @return The instance (VkInstance) for the Vulkan backend.
*/
VkInstance getInstance() const noexcept;
/**
* @return The logical device (VkDevice) that was selected as the backend device.
*/
VkDevice getDevice() const noexcept;
/**
* @return The physical device (i.e gpu) that was selected as the backend physical device.
*/
VkPhysicalDevice getPhysicalDevice() const noexcept;
/**
* @return The family index of the graphics queue selected for the Vulkan backend.
*/
uint32_t getGraphicsQueueFamilyIndex() const noexcept;
/**
* @return The index of the graphics queue (if there are multiple graphics queues)
* selected for the Vulkan backend.
*/
uint32_t getGraphicsQueueIndex() const noexcept;
/**
* @return The queue that was selected for the Vulkan backend.
*/
VkQueue getGraphicsQueue() const noexcept;
private:
// Platform dependent helper methods
using ExtensionSet = std::unordered_set<std::string_view>;
static ExtensionSet getRequiredInstanceExtensions();
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
friend struct VulkanPlatformPrivate;
};
} // namespace filament::backend
}// namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H

View File

@@ -34,6 +34,7 @@ using ParameterPrecision = MaterialBuilder::ParameterPrecision;
using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
using ConstantType = MaterialBuilder::ConstantType;
// Convenience methods to convert std::string to Enum and also iterate over Enum values.
class Enums {
@@ -77,6 +78,7 @@ private:
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
static std::unordered_map<std::string, ConstantType> mStringToConstantType;
};
template<typename T>

View File

@@ -39,6 +39,7 @@
#include <string>
#include <utility>
#include <vector>
#include <variant>
#include <stddef.h>
#include <stdint.h>
@@ -141,13 +142,6 @@ protected:
TargetLanguage targetLanguage;
};
std::vector<CodeGenParams> mCodeGenPermutations;
// For finding properties and running semantic analysis, we always use the same code gen
// permutation. This is the first permutation generated with default arguments passed to matc.
static constexpr const CodeGenParams mSemanticCodeGenParams = {
.shaderModel = ShaderModel::MOBILE,
.targetApi = TargetApi::OPENGL,
.targetLanguage = TargetLanguage::SPIRV
};
// Keeps track of how many times MaterialBuilder::init() has been called without a call to
// MaterialBuilder::shutdown(). Internally, glslang does something similar. We keep track for
@@ -237,11 +231,14 @@ public:
using TransparencyMode = filament::TransparencyMode;
using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion;
using AttributeType = filament::backend::UniformType;
using UniformType = filament::backend::UniformType;
using ConstantType = filament::backend::ConstantType;
using SamplerType = filament::backend::SamplerType;
using SubpassType = filament::backend::SubpassType;
using SamplerFormat = filament::backend::SamplerFormat;
using ParameterPrecision = filament::backend::Precision;
using Precision = filament::backend::Precision;
using CullingMode = filament::backend::CullingMode;
using FeatureLevel = filament::backend::FeatureLevel;
@@ -270,6 +267,9 @@ public:
};
using PreprocessorDefineList = std::vector<PreprocessorDefine>;
MaterialBuilder& noSamplerValidation(bool enabled) noexcept;
//! Set the name of this material.
MaterialBuilder& name(const char* name) noexcept;
@@ -290,6 +290,15 @@ public:
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
//! Add a constant parameter to this material.
template<typename T>
using is_supported_constant_parameter_t = typename std::enable_if<
std::is_same<int32_t, T>::value ||
std::is_same<float, T>::value ||
std::is_same<bool, T>::value>::type;
template<typename T, typename = is_supported_constant_parameter_t<T>>
MaterialBuilder& constant(const char *name, ConstantType type, T defaultValue = 0);
/**
* Add a sampler parameter to this material.
*
@@ -390,7 +399,10 @@ public:
MaterialBuilder& featureLevel(FeatureLevel featureLevel) noexcept;
//! Set the blending mode for this material.
/**
* Set the blending mode for this material. When set to MASKED, alpha to coverage is turned on.
* You can override this behavior using alphaToCoverage(false).
*/
MaterialBuilder& blending(BlendingMode blending) noexcept;
/**
@@ -436,6 +448,14 @@ public:
*/
MaterialBuilder& maskThreshold(float threshold) noexcept;
/**
* Enables or disables alpha-to-coverage. When enabled, the coverage of a fragment is based
* on its alpha value. This parameter is only useful when MSAA is in use. Alpha to coverage
* is enabled automatically when the blend mode is set to MASKED; this behavior can be
* overridden by calling alphaToCoverage(false).
*/
MaterialBuilder& alphaToCoverage(bool enable) noexcept;
//! The material output is multiplied by the shadowing factor (UNLIT model only).
MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept;
@@ -556,7 +576,7 @@ public:
MaterialBuilder& shaderDefine(const char* name, const char* value) noexcept;
//! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain.
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target,
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target, Precision precision,
OutputType type, const char* name, int location = -1) noexcept;
MaterialBuilder& enableFramebufferFetch() noexcept;
@@ -630,17 +650,28 @@ public:
struct Output {
Output() noexcept = default;
Output(const char* outputName, VariableQualifier qualifier, OutputTarget target,
OutputType type, int location) noexcept
: name(outputName), qualifier(qualifier), target(target), type(type),
location(location) { }
Precision precision, OutputType type, int location) noexcept
: name(outputName), qualifier(qualifier), target(target), precision(precision),
type(type), location(location) { }
utils::CString name;
VariableQualifier qualifier;
OutputTarget target;
Precision precision;
OutputType type;
int location;
};
struct Constant {
utils::CString name;
ConstantType type;
union {
int32_t i;
float f;
bool b;
} defaultValue;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
@@ -668,6 +699,7 @@ public:
using ParameterList = Parameter[MAX_PARAMETERS_COUNT];
using SubpassList = Parameter[MAX_SUBPASS_COUNT];
using BufferList = std::vector<std::unique_ptr<filament::BufferInterfaceBlock>>;
using ConstantList = std::vector<Constant>;
// returns the number of parameters declared in this material
uint8_t getParameterCount() const noexcept { return mParameterCount; }
@@ -683,22 +715,47 @@ public:
filament::UserVariantFilterMask getVariantFilter() const { return mVariantFilter; }
FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
/// @endcond
struct Attribute {
std::string_view name;
AttributeType type;
MaterialBuilder::VertexAttribute location;
std::string getAttributeName() const noexcept {
return "mesh_" + std::string{ name };
}
std::string getDefineName() const noexcept {
std::string uppercase{ name };
transform(uppercase.cbegin(), uppercase.cend(), uppercase.begin(), ::toupper);
return "HAS_ATTRIBUTE_" + uppercase;
}
};
using AttributeDatabase = std::array<Attribute, filament::backend::MAX_VERTEX_ATTRIBUTE_COUNT>;
static inline AttributeDatabase const& getAttributeDatabase() noexcept {
return sAttributeDatabase;
}
private:
static const AttributeDatabase sAttributeDatabase;
void prepareToBuild(MaterialInfo& info) noexcept;
// Return true if the shader is syntactically and semantically valid.
// This method finds all the properties defined in the fragment and
// vertex shaders of the material.
bool findAllProperties() noexcept;
bool findAllProperties(CodeGenParams const& semanticCodeGenParams) noexcept;
// Multiple calls to findProperties accumulate the property sets across fragment
// and vertex shaders in mProperties.
bool findProperties(filament::backend::ShaderStage type,
MaterialBuilder::PropertyList& p) noexcept;
MaterialBuilder::PropertyList& allProperties,
CodeGenParams const& semanticCodeGenParams) noexcept;
bool runSemanticAnalysis(MaterialInfo const& info) noexcept;
bool runSemanticAnalysis(MaterialInfo const& info,
CodeGenParams const& semanticCodeGenParams) noexcept;
bool checkLiteRequirements() noexcept;
@@ -751,6 +808,7 @@ private:
PropertyList mProperties;
ParameterList mParameters;
ConstantList mConstants;
SubpassList mSubpasses;
VariableList mVariables;
OutputList mOutputs;
@@ -791,6 +849,8 @@ private:
bool mInstanced = false;
bool mDepthWrite = true;
bool mDepthWriteSet = false;
bool mAlphaToCoverage = false;
bool mAlphaToCoverageSet = false;
bool mSpecularAntiAliasing = false;
bool mClearCoatIorChange = true;
@@ -814,6 +874,8 @@ private:
PreprocessorDefineList mDefines;
filament::UserVariantFilterMask mVariantFilter = {};
bool mNoSamplerValidation = false;
};
} // namespace filamat

View File

@@ -58,6 +58,10 @@ class Texture;
class UTILS_PUBLIC IBLPrefilterContext {
public:
enum class Kernel : uint8_t {
D_GGX, // Trowbridge-reitz distribution
};
/**
* Creates an IBLPrefilter context.
* @param engine filament engine to use
@@ -109,7 +113,7 @@ public:
* - Must be allocated with all mip levels.
* - Must be SAMPLEABLE
* @param outCubemap Output cubemap. If null the texture is automatically created
* with default parameters (size of 256 with 5 levels).
* with default parameters (size of 256 with 9 levels).
* - Must be a cubemap
* - Must have SAMPLEABLE and COLOR_ATTACHMENT usage bits
* @return returns outCubemap
@@ -123,6 +127,100 @@ public:
filament::Material* mEquirectMaterial = nullptr;
};
/**
* IrradianceFilter is a GPU based implementation of the diffuse probe pre-integration filter.
* An instance of IrradianceFilter is needed per filter configuration. A filter configuration
* contains the filter's kernel and sample count.
*/
class IrradianceFilter {
public:
using Kernel = Kernel;
/**
* Filter configuration.
*/
struct Config {
uint16_t sampleCount = 1024u; //!< filter sample count (max 2048)
Kernel kernel = Kernel::D_GGX; //!< filter kernel
};
/**
* Filtering options for the current environment.
*/
struct Options {
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
float lodOffset = 2.0f; //!< Good values are 2.0 or 3.0. Higher values help with heavily HDR inputs.
bool generateMipmap = true; //!< set to false if the input environment map already has mipmaps
};
/**
* Creates a IrradianceFilter processor.
* @param context IBLPrefilterContext to use
* @param config Configuration of the filter
*/
IrradianceFilter(IBLPrefilterContext& context, Config config);
/**
* Creates a filter with the default configuration.
* @param context IBLPrefilterContext to use
*/
explicit IrradianceFilter(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~IrradianceFilter() noexcept;
IrradianceFilter(IrradianceFilter const&) = delete;
IrradianceFilter& operator=(IrradianceFilter const&) = delete;
IrradianceFilter(IrradianceFilter&& rhs) noexcept;
IrradianceFilter& operator=(IrradianceFilter&& rhs) noexcept;
/**
* Generates an irradiance cubemap. Mipmaps are not generated even if present.
* @param options Options for this environment
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outIrradianceTexture Output irradiance texture or, if null, it is
* automatically created with some default parameters.
* outIrradianceTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages.
*
* @return returns outIrradianceTexture
*/
filament::Texture* operator()(Options options,
filament::Texture const* environmentCubemap,
filament::Texture* outIrradianceTexture = nullptr);
/**
* Generates a prefiltered cubemap.
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outIrradianceTexture Output irradiance texture or, if null, it is
* automatically created with some default parameters.
* outIrradianceTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages.
*
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(
filament::Texture const* environmentCubemap,
filament::Texture* outIrradianceTexture = nullptr);
private:
filament::Texture* createIrradianceTexture();
IBLPrefilterContext& mContext;
filament::Material* mKernelMaterial = nullptr;
filament::Texture* mKernelTexture = nullptr;
uint32_t mSampleCount = 0u;
};
/**
* SpecularFilter is a GPU based implementation of the specular probe pre-integration filter.
@@ -131,9 +229,7 @@ public:
*/
class SpecularFilter {
public:
enum class Kernel : uint8_t {
D_GGX, // Trowbridge-reitz distribution
};
using Kernel = Kernel;
/**
* Filter configuration.
@@ -151,7 +247,7 @@ public:
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
float lodOffset = 1.0f; //!< Good values are 1.0 or 2.0. Higher values help with heavily HDR inputs.
bool generateMipmap = true; //!< set to false if the environment map already has mipmaps
bool generateMipmap = true; //!< set to false if the input environment map already has mipmaps
};
/**
@@ -237,6 +333,7 @@ private:
utils::Entity mCameraEntity{};
filament::View* mView{};
filament::Material* mIntegrationMaterial{};
filament::Material* mIrradianceIntegrationMaterial{};
};
#endif //TNT_IBL_PREFILTER_IBLPREFILTER_H

View File

@@ -104,22 +104,24 @@ public:
}
/**
* Computes the bounding box of a box transformed by a rigid transform
* Transform a Box by a linear transform and a translation.
*
* @param m a 3x3 matrix, the linear transform
* @param t a float3, the translation
* @param box the box to transform
* @param m a 4x4 matrix that must be a rigid transform
* @return the bounding box of the transformed box.
* Result is undefined if \p m is not a rigid transform
* @return the bounding box of the transformed box
*/
friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept;
static Box transform(const math::mat3f& m, math::float3 const& t, const Box& box) noexcept {
return { m * box.center + t, abs(m) * box.halfExtent };
}
/**
* Computes the bounding box of a box transformed by a rigid transform
* @param box the box to transform
* @param m a 3x3 matrix that must be a rigid transform
* @return the bounding box of the transformed box.
* Result is undefined if \p m is not a rigid transform
* @deprecated Use transform() instead
* @see transform()
*/
friend Box rigidTransform(Box const& box, const math::mat3f& m) noexcept;
friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept {
return transform(m.upperLeft(), m[3].xyz, box);
}
};
/**
@@ -174,7 +176,18 @@ struct UTILS_PUBLIC Aabb {
/**
* Returns the 8 corner vertices of the AABB.
*/
Corners getCorners() const;
Corners getCorners() const {
return Aabb::Corners{ .vertices = {
{ min.x, min.y, min.z },
{ max.x, min.y, min.z },
{ min.x, max.y, min.z },
{ max.x, max.y, min.z },
{ min.x, min.y, max.z },
{ max.x, min.y, max.z },
{ min.x, max.y, max.z },
{ max.x, max.y, max.z },
}};
}
/**
* Returns whether the box contains a given point.
@@ -182,15 +195,44 @@ struct UTILS_PUBLIC Aabb {
* @param p the point to test
* @return the maximum signed distance to the box. Negative if p is in the box
*/
float contains(math::float3 p) const noexcept;
float contains(math::float3 p) const noexcept {
float d = min.x - p.x;
d = std::max(d, min.y - p.y);
d = std::max(d, min.z - p.z);
d = std::max(d, p.x - max.x);
d = std::max(d, p.y - max.y);
d = std::max(d, p.z - max.z);
return d;
}
/**
* Applies an affine transformation to the AABB.
*
* @param m the 4x4 transformation to apply
* @param m the 3x3 transformation to apply
* @param t the translation
* @return the transformed box
*/
Aabb transform(const math::mat4f& m) const noexcept;
static Aabb transform(const math::mat3f& m, math::float3 const& t, const Aabb& box) noexcept {
// Fast AABB transformation per Jim Arvo in Graphics Gems (1990).
Aabb result{ t, t };
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
const float a = m[col][row] * box.min[col];
const float b = m[col][row] * box.max[col];
result.min[row] += a < b ? a : b;
result.max[row] += a < b ? b : a;
}
}
return result;
}
/**
* @deprecated Use transform() instead
* @see transform()
*/
Aabb transform(const math::mat4f& m) const noexcept {
return transform(m.upperLeft(), m[3].xyz, *this);
}
};
} // namespace filament

View File

@@ -191,7 +191,7 @@ private:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* // Declares a "linear sRGB" color space.
* ColorSpace myColorSpace = Rec709-Linear-sRGB;
* ColorSpace myColorSpace = Rec709-Linear-D65;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class PartialColorSpace {

View File

@@ -17,6 +17,8 @@
#ifndef TNT_FILAMENT_ENGINE_H
#define TNT_FILAMENT_ENGINE_H
#include <filament/FilamentAPI.h>
#include <backend/Platform.h>
#include <utils/compiler.h>
@@ -49,6 +51,7 @@ class SwapChain;
class Texture;
class VertexBuffer;
class View;
class InstanceBuffer;
class LightManager;
class RenderableManager;
@@ -164,6 +167,7 @@ class TransformManager;
* @see Renderer
*/
class UTILS_PUBLIC Engine {
struct BuilderDetails;
public:
using Platform = backend::Platform;
using Backend = backend::Backend;
@@ -265,96 +269,124 @@ public:
uint32_t perFrameCommandsSizeMB = FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB;
};
#if UTILS_HAS_THREADING
using CreateCallback = void(void* user, void* token);
#endif
/**
* Creates an instance of Engine
*
* @param backend Which driver backend to use.
*
* @param platform A pointer to an object that implements Platform. If this is
* provided, then this object is used to create the hardware context
* and expose platform features to it.
*
* If not provided (or nullptr is used), an appropriate Platform
* is created automatically.
*
* All methods of this interface are called from filament's
* render thread, which is different from the main thread.
*
* The lifetime of \p platform must exceed the lifetime of
* the Engine object.
*
* @param sharedGLContext A platform-dependant OpenGL context used as a shared context
* when creating filament's internal context.
* Setting this parameter will force filament to use the OpenGL
* implementation (instead of Vulkan for instance).
*
* @param config A pointer to optional parameters to specify memory size
* configuration options. If nullptr, then defaults used.
*
* @return A pointer to the newly created Engine, or nullptr if the Engine couldn't be created.
*
* nullptr if the GPU driver couldn't be initialized, for instance if it doesn't
* support the right version of OpenGL or OpenGL ES.
*
* @exception utils::PostConditionPanic can be thrown if there isn't enough memory to
* allocate the command buffer. If exceptions are disabled, this condition if fatal and
* this function will abort.
*
* \remark
* This method is thread-safe.
* Engine::Builder is used to create a new filament Engine.
*/
static Engine* create(Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedGLContext = nullptr,
const Config* config = nullptr);
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
friend class FEngine;
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;
/**
* @param backend Which driver backend to use
* @return A reference to this Builder for chaining calls.
*/
Builder& backend(Backend backend) noexcept;
/**
* @param platform A pointer to an object that implements Platform. If this is
* provided, then this object is used to create the hardware context
* and expose platform features to it.
*
* If not provided (or nullptr is used), an appropriate Platform
* is created automatically.
*
* All methods of this interface are called from filament's
* render thread, which is different from the main thread.
*
* The lifetime of \p platform must exceed the lifetime of
* the Engine object.
*
* @return A reference to this Builder for chaining calls.
*/
Builder& platform(Platform* platform) noexcept;
/**
* @param config A pointer to optional parameters to specify memory size
* configuration options. If nullptr, then defaults used.
*
* @return A reference to this Builder for chaining calls.
*/
Builder& config(const Config* config) noexcept;
/**
* @param sharedContext A platform-dependant context used as a shared context
* when creating filament's internal context.
*
* @return A reference to this Builder for chaining calls.
*/
Builder& sharedContext(void* sharedContext) noexcept;
#if UTILS_HAS_THREADING
/**
* Creates the filament Engine asynchronously.
*
* @param callback Callback called once the engine is initialized and it is safe to
* call Engine::getEngine().
*/
void build(utils::Invocable<void(void* token)>&& callback) const;
#endif
/**
* Creates an instance of Engine.
*
* @return A pointer to the newly created Engine, or nullptr if the Engine couldn't be
* created.
* nullptr if the GPU driver couldn't be initialized, for instance if it doesn't
* support the right version of OpenGL or OpenGL ES.
*
* @exception utils::PostConditionPanic can be thrown if there isn't enough memory to
* allocate the command buffer. If exceptions are disabled, this condition if
* fatal and this function will abort.
*/
Engine* build() const;
};
/**
* Backward compatibility helper to create an Engine.
* @see Builder
*/
static inline Engine* create(Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedContext = nullptr,
const Config* config = nullptr) {
return Engine::Builder()
.backend(backend)
.platform(platform)
.sharedContext(sharedContext)
.config(config)
.build();
}
#if UTILS_HAS_THREADING
/**
* A callback used with Engine::createAsync() called once the engine is initialized and it is
* safe to call Engine::getEngine(token). This callback is invoked from an arbitrary worker
* thread. Engine::getEngine() CANNOT be called from that thread, instead it must be called
* from the same thread than Engine::createAsync() was called from.
*
* @param user User provided parameter given in createAsync().
*
* @param token An opaque token used to call Engine::getEngine().
* Backward compatibility helper to create an Engine asynchronously.
* @see Builder
*/
using CreateCallback = void(void* user, void* token);
/**
* Creates an instance of Engine asynchronously
*
* @param callback Callback called once the engine is initialized and it is safe to
* call Engine::getEngine.
*
* @param user A user provided pointer that is given back to callback unmodified.
*
* @param backend Which driver backend to use.
*
* @param platform A pointer to an object that implements Platform. If this is
* provided, then this object is used to create the hardware context
* and expose platform features to it.
*
* If not provided (or nullptr is used), an appropriate Platform
* is created automatically.
*
* All methods of this interface are called from filament's
* render thread, which is different from the main thread.
*
* The lifetime of \p platform must exceed the lifetime of
* the Engine object.
*
* @param sharedGLContext A platform-dependant OpenGL context used as a shared context
* when creating filament's internal context.
* Setting this parameter will force filament to use the OpenGL
* implementation (instead of Vulkan for instance).
*
* @param config A pointer to optional parameters to specify memory size
* configuration options
*/
static void createAsync(CreateCallback callback, void* user,
static inline void createAsync(CreateCallback callback, void* user,
Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedGLContext = nullptr,
const Config* config = nullptr);
Platform* platform = nullptr, void* sharedContext = nullptr,
const Config* config = nullptr) {
Engine::Builder()
.backend(backend)
.platform(platform)
.sharedContext(sharedContext)
.config(config)
.build([callback, user](void* token) {
callback(user, token);
});
}
/**
* Retrieve an Engine* from createAsync(). This must be called from the same thread than
@@ -371,6 +403,7 @@ public:
static Engine* getEngine(void* token);
#endif
/**
* Destroy the Engine instance and all associated resources.
*
@@ -464,6 +497,21 @@ public:
*/
FeatureLevel getActiveFeatureLevel() const noexcept;
/**
* Queries the maximum number of GPU instances that Filament creates when automatic instancing
* is enabled. This value is also the limit for the number of transforms that can be stored in
* an InstanceBuffer. This value may depend on the device and platform, but will remain constant
* during the lifetime of this Engine.
*
* This value does not apply when using the instances(size_t) method on
* RenderableManager::Builder.
*
* @return the number of max automatic instances
* @see setAutomaticInstancingEnabled
* @see RenderableManager::Builder::instances(size_t)
* @see RenderableManager::Builder::instances(size_t, InstanceBuffer*)
*/
size_t getMaxAutomaticInstances() const noexcept;
/**
* @return EntityManager used by filament
@@ -625,8 +673,28 @@ public:
bool destroy(const Texture* p); //!< Destroys a Texture object.
bool destroy(const RenderTarget* p); //!< Destroys a RenderTarget object.
bool destroy(const View* p); //!< Destroys a View object.
bool destroy(const InstanceBuffer* p); //!< Destroys an InstanceBuffer object.
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
bool isValid(const BufferObject* p); //!< Tells whether a BufferObject object is valid
bool isValid(const VertexBuffer* p); //!< Tells whether an VertexBuffer object is valid
bool isValid(const Fence* p); //!< Tells whether a Fence object is valid
bool isValid(const IndexBuffer* p); //!< Tells whether an IndexBuffer object is valid
bool isValid(const SkinningBuffer* p); //!< Tells whether a SkinningBuffer object is valid
bool isValid(const MorphTargetBuffer* p); //!< Tells whether a MorphTargetBuffer object is valid
bool isValid(const IndirectLight* p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Material* p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Renderer* p); //!< Tells whether a Renderer object is valid
bool isValid(const Scene* p); //!< Tells whether a Scene object is valid
bool isValid(const Skybox* p); //!< Tells whether a SkyBox object is valid
bool isValid(const ColorGrading* p); //!< Tells whether a ColorGrading object is valid
bool isValid(const SwapChain* p); //!< Tells whether a SwapChain object is valid
bool isValid(const Stream* p); //!< Tells whether a Stream object is valid
bool isValid(const Texture* p); //!< Tells whether a Texture object is valid
bool isValid(const RenderTarget* p); //!< Tells whether a RenderTarget object is valid
bool isValid(const View* p); //!< Tells whether a View object is valid
bool isValid(const InstanceBuffer* p); //!< Tells whether an InstanceBuffer object is valid
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that does guarantee that the

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2023 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_INSTANCEBUFFER_H
#define TNT_FILAMENT_INSTANCEBUFFER_H
#include <filament/FilamentAPI.h>
#include <filament/Engine.h>
#include <math/mathfwd.h>
namespace filament {
/**
* InstanceBuffer holds draw (GPU) instance transforms. These can be provided to a renderable to
* "offset" each draw instance.
*
* @see RenderableManager::Builder::instances(size_t, InstanceBuffer*)
*/
class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
/**
* @param instanceCount the number of instances this InstanceBuffer will support, must be
* >= 1 and <= \c Engine::getMaxAutomaticInstances()
* @see Engine::getMaxAutomaticInstances
*/
Builder(size_t instanceCount) noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Provide an initial local transform for each instance. Each local transform is relative to
* the transform of the associated renderable. This forms a parent-child relationship
* between the renderable and its instances, so adjusting the renderable's transform will
- * affect all instances.
*
* The array of math::mat4f must have length instanceCount, provided when constructing this
* Builder.
*
* @param localTransforms an array of math::mat4f with length instanceCount, must remain
* valid until after build() is called
*/
Builder& localTransforms(math::mat4f const* localTransforms) noexcept;
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
InstanceBuffer* build(Engine& engine);
private:
friend class FInstanceBuffer;
};
/**
* Returns the instance count specified when building this InstanceBuffer.
*/
size_t getInstanceCount() const noexcept;
/**
* Sets the local transform for each instance. Each local transform is relative to the transform
* of the associated renderable. This forms a parent-child relationship between the renderable
* and its instances, so adjusting the renderable's transform will affect all instances.
*
* @param localTransforms an array of math::mat4f with length count, need not outlive this call
* @param count the number of local transforms
* @param offset index of the first instance to set local transforms
*/
void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset = 0);
};
} // namespace filament
#endif //TNT_FILAMENT_INSTANCEBUFFER_H

View File

@@ -22,9 +22,11 @@
#include <filament/MaterialEnums.h>
#include <filament/MaterialInstance.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <math/mathfwd.h>
@@ -105,6 +107,34 @@ public:
*/
Builder& package(const void* payload, size_t size);
template<typename T>
using is_supported_constant_parameter_t = typename std::enable_if<
std::is_same<int32_t, T>::value ||
std::is_same<float, T>::value ||
std::is_same<bool, T>::value>::type;
/**
* Specialize a constant parameter specified in the material definition with a concrete
* value for this material. Once build() is called, this constant cannot be changed.
* Will throw an exception if the name does not match a constant specified in the
* material definition or if the type provided does not match.
*
* @tparam T The type of constant parameter, either int32_t, float, or bool.
* @param name The name of the constant parameter specified in the material definition, such
* as "myConstant".
* @param nameLength Length in `char` of the name parameter.
* @param value The value to use for the constant parameter, must match the type specified
* in the material definition.
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
Builder& constant(const char* name, size_t nameLength, T value);
/** inline helper to provide the constant name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
inline Builder& constant(const char* name, T value) {
return constant(name, strlen(name), value);
}
/**
* Creates the Material object and returns a pointer to it.
*
@@ -122,6 +152,60 @@ public:
friend class FMaterial;
};
using CompilerPriorityQueue = backend:: CompilerPriorityQueue;
/**
* Asynchronously ensures that a subset of this Material's variants are compiled. After issuing
* several Material::compile() calls in a row, it is recommended to call Engine::flush()
* such that the backend can start the compilation work as soon as possible.
* The provided callback is guaranteed to be called on the main thread after all specified
* variants of the material are compiled. This can take hundreds of milliseconds.
*
* If all the material's variants are already compiled, the callback will be scheduled as
* soon as possible, but this might take a few dozen millisecond, corresponding to how
* many previous frames are enqueued in the backend. This also varies by backend. Therefore,
* it is recommended to only call this method once per material shortly after creation.
*
* If the same variant is scheduled for compilation multiple times, the first scheduling
* takes precedence; later scheduling are ignored.
*
* caveat: A consequence is that if a variant is scheduled on the low priority queue and later
* scheduled again on the high priority queue, the later scheduling is ignored.
* Therefore, the second callback could be called before the variant is compiled.
* However, the first callback, if specified, will trigger as expected.
*
* The callback is guaranteed to be called. If the engine is destroyed while some material
* variants are still compiling or in the queue, these will be discarded and the corresponding
* callback will be called. In that case however the Material pointer passed to the callback
* is guaranteed to be invalid (either because it's been destroyed by the user already, or,
* because it's been cleaned-up by the Engine).
*
* @param priority Which priority queue to use, LOW or HIGH.
* @param variants Variants to include to the compile command.
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*/
void compile(CompilerPriorityQueue priority,
UserVariantFilterMask variants,
backend::CallbackHandler* handler = nullptr,
utils::Invocable<void(Material*)>&& callback = {}) noexcept;
inline void compile(CompilerPriorityQueue priority,
UserVariantFilterBit variants,
backend::CallbackHandler* handler = nullptr,
utils::Invocable<void(Material*)>&& callback = {}) noexcept {
compile(priority, UserVariantFilterMask(variants), handler,
std::forward<utils::Invocable<void(Material*)>>(callback));
}
inline void compile(CompilerPriorityQueue priority,
backend::CallbackHandler* handler = nullptr,
utils::Invocable<void(Material*)>&& callback = {}) noexcept {
compile(priority, UserVariantFilterBit::ALL, handler,
std::forward<utils::Invocable<void(Material*)>>(callback));
}
/**
* Creates a new instance of this material. Material instances should be freed using
* Engine::destroy(const MaterialInstance*).
@@ -171,6 +255,9 @@ public:
//! Indicates whether this material is double-sided.
bool isDoubleSided() const noexcept;
//! Indicates whether this material uses alpha to coverage.
bool isAlphaToCoverageEnabled() const noexcept;
//! Returns the alpha mask threshold used when the blending mode is set to masked.
float getMaskThreshold() const noexcept;

View File

@@ -47,10 +47,14 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"),
MaterialUniformBindings = charTo64bitNum("MAT_UNIF"),
MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"),
MaterialAttributeInfo = charTo64bitNum("MAT_ATTR"),
MaterialProperties = charTo64bitNum("MAT_PROP"),
MaterialConstants = charTo64bitNum("MAT_CONS"),
MaterialName = charTo64bitNum("MAT_NAME"),
MaterialVersion = charTo64bitNum("MAT_VERS"),
MaterialCacheId = charTo64bitNum("MAT_UUID"),
MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"),
MaterialShading = charTo64bitNum("MAT_SHAD"),
MaterialBlendingMode = charTo64bitNum("MAT_BLEN"),
@@ -67,15 +71,17 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialReflectionMode = charTo64bitNum("MAT_REFL"),
MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"),
MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"),
MaterialDoubleSidedSet = charTo64bitNum("MAT_DOSS"),
MaterialDoubleSided = charTo64bitNum("MAT_DOSI"),
MaterialColorWrite = charTo64bitNum("MAT_CWRIT"),
MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"),
MaterialDepthWrite = charTo64bitNum("MAT_DWRIT"),
MaterialDepthTest = charTo64bitNum("MAT_DTEST"),
MaterialInstanced = charTo64bitNum("MAT_INSTA"),
MaterialCullingMode = charTo64bitNum("MAT_CUMO"),
MaterialAlphaToCoverageSet = charTo64bitNum("MAT_A2CS"),
MaterialAlphaToCoverage = charTo64bitNum("MAT_A2CO"),
MaterialHasCustomDepthShader =charTo64bitNum("MAT_CSDP"),

View File

@@ -20,6 +20,7 @@
#define TNT_FILAMENT_MATERIAL_ENUM_H
#include <utils/bitset.h>
#include <utils/BitmaskEnum.h>
#include <stddef.h>
#include <stdint.h>
@@ -27,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 31;
static constexpr size_t MATERIAL_VERSION = 41;
/**
* Supported shading models
@@ -232,7 +233,9 @@ enum class Property : uint8_t {
// when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT
};
enum class UserVariantFilterBit : uint32_t {
using UserVariantFilterMask = uint32_t;
enum class UserVariantFilterBit : UserVariantFilterMask {
DIRECTIONAL_LIGHTING = 0x01,
DYNAMIC_LIGHTING = 0x02,
SHADOW_RECEIVER = 0x04,
@@ -240,10 +243,12 @@ enum class UserVariantFilterBit : uint32_t {
FOG = 0x10,
VSM = 0x20,
SSR = 0x40,
ALL = 0x7F,
};
using UserVariantFilterMask = uint32_t;
} // namespace filament
template<> struct utils::EnableBitMaskOperators<filament::UserVariantFilterBit>
: public std::true_type {};
#endif

View File

@@ -52,6 +52,7 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
public:
using CullingMode = filament::backend::CullingMode;
using TransparencyMode = filament::TransparencyMode;
using DepthFunc = filament::backend::SamplerCompareFunc;
using StencilCompareFunc = filament::backend::SamplerCompareFunc;
using StencilOperation = filament::backend::StencilOperation;
using StencilFace = filament::backend::StencilFace;
@@ -367,6 +368,16 @@ public:
*/
void setDepthCulling(bool enable) noexcept;
/**
* Overrides the default depth function state that was set on the material.
*/
void setDepthFunc(DepthFunc depthFunc) noexcept;
/**
* Returns the depth function state.
*/
DepthFunc getDepthFunc() const noexcept;
/**
* Returns whether depth culling is enabled.
*/

View File

@@ -21,6 +21,8 @@
#include <stdint.h>
#include <math.h>
namespace filament {
class Texture;
@@ -151,19 +153,120 @@ struct BloomOptions {
};
/**
* Options to control fog in the scene
* Options to control large-scale fog in the scene
*/
struct FogOptions {
float distance = 0.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 )
float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1
float height = 0.0f; //!< fog's floor in world units
float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude
LinearColor color = {0.5f, 0.5f, 0.5f};//!< fog's color (linear), see fogColorFromIbl
float density = 0.1f; //!< fog's density at altitude given by 'height'
float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts
float inScatteringSize = -1.0f; //!< size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction.
bool enabled = false; //!< enable or disable fog
/**
* Distance in world units [m] from the camera to where the fog starts ( >= 0.0 )
*/
float distance = 0.0f;
/**
* Distance in world units [m] after which the fog calculation is disabled.
* This can be used to exclude the skybox, which is desirable if it already contains clouds or
* fog. The default value is +infinity which applies the fog to everything.
*
* Note: The SkyBox is typically at a distance of 1e19 in world space (depending on the near
* plane distance and projection used though).
*/
float cutOffDistance = INFINITY;
/**
* fog's maximum opacity between 0 and 1
*/
float maximumOpacity = 1.0f;
/**
* Fog's floor in world units [m]. This sets the "sea level".
*/
float height = 0.0f;
/**
* How fast the fog dissipates with altitude. heightFalloff has a unit of [1/m].
* It can be expressed as 1/H, where H is the altitude change in world units [m] that causes a
* factor 2.78 (e) change in fog density.
*
* A falloff of 0 means the fog density is constant everywhere and may result is slightly
* faster computations.
*/
float heightFalloff = 1.0f;
/**
* Fog's color is used for ambient light in-scattering, a good value is
* to use the average of the ambient light, possibly tinted towards blue
* for outdoors environments. Color component's values should be between 0 and 1, values
* above one are allowed but could create a non energy-conservative fog (this is dependant
* on the IBL's intensity as well).
*
* We assume that our fog has no absorption and therefore all the light it scatters out
* becomes ambient light in-scattering and has lost all directionality, i.e.: scattering is
* isotropic. This somewhat simulates Rayleigh scattering.
*
* This value is used as a tint instead, when fogColorFromIbl is enabled.
*
* @see fogColorFromIbl
*/
LinearColor color = { 1.0f, 1.0f, 1.0f };
/**
* Extinction factor in [1/m] at altitude 'height'. The extinction factor controls how much
* light is absorbed and out-scattered per unit of distance. Each unit of extinction reduces
* the incoming light to 37% of its original value.
*
* Note: The extinction factor is related to the fog density, it's usually some constant K times
* the density at sea level (more specifically at fog height). The constant K depends on
* the composition of the fog/atmosphere.
*
* For historical reason this parameter is called `density`.
*/
float density = 0.1f;
/**
* Distance in world units [m] from the camera where the Sun in-scattering starts.
*/
float inScatteringStart = 0.0f;
/**
* Very inaccurately simulates the Sun's in-scattering. That is, the light from the sun that
* is scattered (by the fog) towards the camera.
* Size of the Sun in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
* Smaller values result is a larger scattering size.
*/
float inScatteringSize = -1.0f;
/**
* The fog color will be sampled from the IBL in the view direction and tinted by `color`.
* Depending on the scene this can produce very convincing results.
*
* This simulates a more anisotropic phase-function.
*
* `fogColorFromIbl` is ignored when skyTexture is specified.
*
* @see skyColor
*/
bool fogColorFromIbl = false;
/**
* skyTexture must be a mipmapped cubemap. When provided, the fog color will be sampled from
* this texture, higher resolution mip levels will be used for objects at the far clip plane,
* and lower resolution mip levels for objects closer to the camera. The skyTexture should
* typically be heavily blurred; a typical way to produce this texture is to blur the base
* level with a strong gaussian filter or even an irradiance filter and then generate mip
* levels as usual. How blurred the base level is somewhat of an artistic decision.
*
* This simulates a more anisotropic phase-function.
*
* `fogColorFromIbl` is ignored when skyTexture is specified.
*
* @see Texture
* @see fogColorFromIbl
*/
Texture* skyColor = nullptr; //!< %codegen_skip_json% %codegen_skip_javascript%
/**
* Enable or disable large-scale fog
*/
bool enabled = false;
};
/**

View File

@@ -91,8 +91,6 @@ public:
/**
* Sets a texture to a given attachment point.
*
* 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.

View File

@@ -45,6 +45,7 @@ class Renderer;
class SkinningBuffer;
class VertexBuffer;
class Texture;
class InstanceBuffer;
class FEngine;
class FRenderPrimitive;
@@ -119,6 +120,12 @@ public:
public:
enum Result { Error = -1, Success = 0 };
/**
* Default render channel
* @see Builder::channel()
*/
static constexpr uint8_t DEFAULT_CHANNEL = 2u;
/**
* Creates a builder for renderable components.
*
@@ -231,10 +238,13 @@ public:
/**
* Set the channel this renderable is associated to. There can be 4 channels.
* All renderables in a given channel are rendered together, regardless of anything else.
* They are sorted as usual withing a channel.
* They are sorted as usual within a channel.
* Channels work similarly to priorities, except that they enforce the strongest ordering.
*
* @param channel clamped to the range [0..3], defaults to 0.
* Channels 0 and 1 may not have render primitives using a material with `refractionType`
* set to `screenspace`.
*
* @param channel clamped to the range [0..3], defaults to 2.
*
* @return Builder reference for chaining calls.
*
@@ -293,6 +303,14 @@ public:
*/
Builder& enableSkinningBuffers(bool enabled = true) noexcept;
/**
* Controls if this renderable is affected by the large-scale fog.
* @param enabled If true, enables large-scale fog on this object. Disables it otherwise.
* True by default.
* @return A reference to this Builder for chaining calls.
*/
Builder& fog(bool enabled = true) noexcept;
/**
* Enables GPU vertex skinning for up to 255 bones, 0 by default.
*
@@ -399,20 +417,47 @@ public:
*/
Builder& globalBlendOrderEnabled(size_t primitiveIndex, bool enabled) noexcept;
/**
* Specifies the number of draw instance of this renderable. The default is 1 instance and
* the maximum number of instances allowed is 65535. 0 is invalid.
* Specifies the number of draw instances of this renderable. The default is 1 instance and
* the maximum number of instances allowed is 32767. 0 is invalid.
*
* All instances are culled using the same bounding box, so care must be taken to make
* sure all instances render inside the specified bounding box.
*
* The material must set its `instanced` parameter to `true` in order to use
* getInstanceIndex() in the vertex or fragment shader to get the instance index and
* possibly adjust the position or transform.
*
* @param instanceCount the number of instances silently clamped between 1 and 65535.
* @param instanceCount the number of instances silently clamped between 1 and 32767.
*/
Builder& instances(size_t instanceCount) noexcept;
/**
* Specifies the number of draw instances of this renderable and an \c InstanceBuffer
* containing their local transforms. The default is 1 instance and the maximum number of
* instances allowed when supplying transforms is given by
* \c Engine::getMaxAutomaticInstances (64 on most platforms). 0 is invalid. The
* \c InstanceBuffer must not be destroyed before this renderable.
*
* All instances are culled using the same bounding box, so care must be taken to make
* sure all instances render inside the specified bounding box.
*
* The material must set its `instanced` parameter to `true` in order to use
* \c getInstanceIndex() in the vertex or fragment shader to get the instance index.
*
* Only the \c VERTEX_DOMAIN_OBJECT vertex domain is supported.
*
* The local transforms of each instance can be updated with
* \c InstanceBuffer::setLocalTransforms.
*
* \see InstanceBuffer
* \see instances(size_t, * math::mat4f const*)
* @param instanceCount the number of instances, silently clamped between 1 and
* the result of Engine::getMaxAutomaticInstances().
* @param instanceBuffer an InstanceBuffer containing at least instanceCount transforms
*/
Builder& instances(size_t instanceCount, InstanceBuffer* instanceBuffer) noexcept;
/**
* Adds the Renderable component to an entity.
*
@@ -499,6 +544,19 @@ public:
*/
void setCulling(Instance instance, bool enable) noexcept;
/**
* Changes whether or not the large-scale fog is applied to this renderable
* @see Builder::fog()
*/
void setFogEnabled(Instance instance, bool enable) noexcept;
/**
* Returns whether large-scale fog is enabled for this renderable.
* @return True if fog is enabled for this renderable.
* @see Builder::fog()
*/
bool getFogEnabled(Instance instance) const noexcept;
/**
* Enables or disables a light channel.
* Light channel 0 is enabled by default.

View File

@@ -707,6 +707,9 @@ public:
* The viewport, projection and model matrices can be obtained from Camera. Because
* pick() has some latency, it might be more accurate to obtain these values at the
* time the View::pick() call is made.
*
* Note: if the Engine is running at FEATURE_LEVEL_0, the precision or `depth` and
* `fragCoords.z` is only 8-bits.
*/
math::float3 fragCoords; //! screen space coordinates in GL convention
};
@@ -803,6 +806,37 @@ public:
PickingQuery& pick(uint32_t x, uint32_t y, backend::CallbackHandler* handler,
PickingQueryResultCallback callback) noexcept;
/**
* Set the value of material global variables. There are up-to four such variable each of
* type float4. These variables can be read in a user Material with
* `getMaterialGlobal{0|1|2|3}()`. All variable start with a default value of { 0, 0, 0, 1 }
*
* @param index index of the variable to set between 0 and 3.
* @param value new value for the variable.
* @see getMaterialGlobal
*/
void setMaterialGlobal(uint32_t index, math::float4 const& value);
/**
* Get the value of the material global variables.
* All variable start with a default value of { 0, 0, 0, 1 }
*
* @param index index of the variable to set between 0 and 3.
* @return current value of the variable.
* @see setMaterialGlobal
*/
math::float4 getMaterialGlobal(uint32_t index) const;
/**
* Get an Entity representing the large scale fog object.
* This entity is always inherited by the View's Scene.
*
* It is for example possible to create a TransformManager component with this
* Entity and apply a transformation globally on the fog.
*
* @return an Entity representing the large scale fog object.
*/
utils::Entity getFogEntity() const noexcept;
/**
* List of available ambient occlusion techniques

View File

@@ -20,7 +20,6 @@
#include <math/quat.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
namespace filament {
namespace geometry {
@@ -28,8 +27,7 @@ namespace geometry {
struct TangentSpaceMeshInput;
struct TangentSpaceMeshOutput;
/* WARNING: WORK-IN-PROGRESS, PLEASE DO NOT USE */
/**
/**
* This class builds Filament-style TANGENTS buffers given an input mesh.
*
* This class enables the client to chose between several algorithms. The client can retrieve the
@@ -140,8 +138,8 @@ public:
*/
Builder& operator=(Builder&& that) noexcept;
Builder(const Builder&) = delete;
Builder& operator=(const Builder&) = delete;
Builder(Builder const&) = delete;
Builder& operator=(Builder const&) = delete;
/**
* Client must provide this parameter
@@ -155,7 +153,7 @@ public:
* @param stride The stride for iterating through `normals`
* @return Builder
*/
Builder& normals(const filament::math::float3* normals, size_t stride = 0) noexcept;
Builder& normals(filament::math::float3 const* normals, size_t stride = 0) noexcept;
/**
* @param tangents The input tangents. The `w` component is for use with
@@ -163,25 +161,25 @@ public:
* @param stride The stride for iterating through `tangents`
* @return Builder
*/
Builder& tangents(const filament::math::float4* tangents, size_t stride = 0) noexcept;
Builder& tangents(filament::math::float4 const* tangents, size_t stride = 0) noexcept;
/**
* @param uvs The input uvs
* @param stride The stride for iterating through `uvs`
* @return Builder
*/
Builder& uvs(const filament::math::float2* uvs, size_t stride = 0) noexcept;
Builder& uvs(filament::math::float2 const* uvs, size_t stride = 0) noexcept;
/**
* @param positions The input positions
* @param stride The stride for iterating through `positions`
* @return Builder
*/
Builder& positions(const filament::math::float3* positions, size_t stride = 0) noexcept;
Builder& positions(filament::math::float3 const* positions, size_t stride = 0) noexcept;
Builder& triangleCount(size_t triangleCount) noexcept;
Builder& triangles(const filament::math::uint3* triangles) noexcept;
Builder& triangles(const filament::math::ushort3* triangles) noexcept;
Builder& triangles(filament::math::uint3 const* triangles) noexcept;
Builder& triangles(filament::math::ushort3 const* triangles) noexcept;
Builder& algorithm(Algorithm algorithm) noexcept;
@@ -216,8 +214,8 @@ public:
*/
TangentSpaceMesh& operator=(TangentSpaceMesh&& that) noexcept;
TangentSpaceMesh(const TangentSpaceMesh&) = delete;
TangentSpaceMesh& operator=(const TangentSpaceMesh&) = delete;
TangentSpaceMesh(TangentSpaceMesh const&) = delete;
TangentSpaceMesh& operator=(TangentSpaceMesh const&) = delete;
/**
* Number of output vertices

View File

@@ -142,7 +142,7 @@ class Ktx2Reader {
protected:
Async() noexcept = default;
~Async() = default;
virtual ~Async();
public:
Async(Async const&) = delete;

View File

@@ -8,5 +8,5 @@ IMAGE_PACKAGE:
IMAGE_IMAGE_OFFSET:
.int 0
IMAGE_IMAGE_SIZE:
.int 44497
.int 17687

View File

@@ -8,5 +8,5 @@ _IMAGE_PACKAGE:
_IMAGE_IMAGE_OFFSET:
.int 0
_IMAGE_IMAGE_SIZE:
.int 44497
.int 17687

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -430,8 +430,21 @@ constexpr TQuaternion<T> TMat33<T>::packTangentFrame(const TMat33<T>& m, size_t
return q;
}
} // namespace details
/**
* Pre-scale a matrix m by the inverse of the largest scale factor to avoid large post-transform
* magnitudes in the shader. This is useful for normal transformations, to avoid large
* post-transform magnitudes in the shader, especially in the fragment shader, where we use
* medium precision.
*/
template<typename T>
constexpr details::TMat33<T> prescaleForNormals(const details::TMat33<T>& m) noexcept {
return m * details::TMat33<T>(
1.0 / std::sqrt(max(float3{length2(m[0]), length2(m[1]), length2(m[2])})));
}
// ----------------------------------------------------------------------------------------
typedef details::TMat33<double> mat3;

View File

@@ -0,0 +1,145 @@
/** \file mikktspace/mikktspace.h
* \ingroup mikktspace
*/
/**
* Copyright (C) 2011 by Morten S. Mikkelsen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef __MIKKTSPACE_H__
#define __MIKKTSPACE_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Author: Morten S. Mikkelsen
* Version: 1.0
*
* The files mikktspace.h and mikktspace.c are designed to be
* stand-alone files and it is important that they are kept this way.
* Not having dependencies on structures/classes/libraries specific
* to the program, in which they are used, allows them to be copied
* and used as is into any tool, program or plugin.
* The code is designed to consistently generate the same
* tangent spaces, for a given mesh, in any tool in which it is used.
* This is done by performing an internal welding step and subsequently an order-independent evaluation
* of tangent space for meshes consisting of triangles and quads.
* This means faces can be received in any order and the same is true for
* the order of vertices of each face. The generated result will not be affected
* by such reordering. Additionally, whether degenerate (vertices or texture coordinates)
* primitives are present or not will not affect the generated results either.
* Once tangent space calculation is done the vertices of degenerate primitives will simply
* inherit tangent space from neighboring non degenerate primitives.
* The analysis behind this implementation can be found in my master's thesis
* which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
* Note that though the tangent spaces at the vertices are generated in an order-independent way,
* by this implementation, the interpolated tangent space is still affected by which diagonal is
* chosen to split each quad. A sensible solution is to have your tools pipeline always
* split quads by the shortest diagonal. This choice is order-independent and works with mirroring.
* If these have the same length then compare the diagonals defined by the texture coordinates.
* XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin
* and also quad triangulator plugin.
*/
typedef int tbool;
typedef struct SMikkTSpaceContext SMikkTSpaceContext;
typedef struct {
// Returns the number of faces (triangles/quads) on the mesh to be processed.
int (*m_getNumFaces)(const SMikkTSpaceContext * pContext);
// Returns the number of vertices on face number iFace
// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace);
// returns the position/normal/texcoord of the referenced face of vertex number iVert.
// iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert);
void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert);
void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert);
// either (or both) of the two setTSpace callbacks can be set.
// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
// This function is used to return the tangent and fSign to the application.
// fvTangent is a unit length vector.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
// This function is used to return tangent space results to the application.
// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
// true magnitudes which can be used for relief mapping effects.
// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
// However, both are perpendicular to the vertex normal.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
} SMikkTSpaceInterface;
struct SMikkTSpaceContext
{
SMikkTSpaceInterface * m_pInterface; // initialized with callback functions
void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call)
};
// these are both thread safe!
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold);
// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
// normal map sampler must use the exact inverse of the pixel shader transformation.
// The most efficient transformation we can possibly do in the pixel shader is
// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
// pixel shader (fast transform out)
// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
// where vNt is the tangent space normal. The normal map sampler must likewise use the
// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
// sampler does (exact inverse of pixel shader):
// float3 row0 = cross(vB, vN);
// float3 row1 = cross(vN, vT);
// float3 row2 = cross(vT, vB);
// float fSign = dot(vT, row0)<0 ? -1 : 1;
// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
// where vNout is the sampled normal in some chosen 3D space.
//
// Should you choose to reconstruct the bitangent in the pixel shader instead
// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
// However, this must be used both by the sampler and your tools/rendering pipeline.
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -141,5 +141,4 @@ inline constexpr bool any(Enum lhs) noexcept {
return !none(lhs);
}
#endif // TNT_UTILS_BITMASKENUM_H

View File

@@ -253,6 +253,12 @@ public:
*/
static Path getTemporaryDirectory();
/**
* @return a path representing a directory where settings files can be stored,
* it is recommended to append an app specific folder name to that path
*/
static Path getUserSettingsDirectory();
/**
* Creates a directory denoted by the given path.
* This is not recursive and doesn't create intermediate directories.

View File

@@ -60,16 +60,19 @@ private:
protected:
static constexpr size_t ENTITY_INDEX = sizeof ... (Elements);
public:
using SoA = StructureOfArrays<Elements ..., Entity>;
using Structure = typename SoA::Structure;
using Instance = EntityInstanceBase::Type;
SingleInstanceComponentManager() noexcept {
// We always start with a dummy entry because index=0 is reserved. The component
// at index = 0, is guaranteed to be default-initialized.
// Sub-classes can use this to their advantage.
mData.push_back();
mData.push_back(Structure{});
}
SingleInstanceComponentManager(SingleInstanceComponentManager&&) noexcept {/* = default */}
@@ -269,7 +272,7 @@ SingleInstanceComponentManager<Elements ...>::addComponent(Entity e) {
if (!e.isNull()) {
if (!hasComponent(e)) {
// this is like a push_back(e);
mData.push_back().template back<ENTITY_INDEX>() = e;
mData.push_back(Structure{}).template back<ENTITY_INDEX>() = e;
// index 0 is used when the component doesn't exist
ci = Instance(mData.size() - 1);
mInstanceMap[e] = ci;

View File

@@ -41,11 +41,13 @@ class StructureOfArraysBase {
static constexpr const size_t kArrayCount = sizeof...(Elements);
public:
using SoA = StructureOfArraysBase<Allocator, Elements ...>;
using SoA = StructureOfArraysBase<Allocator, Elements...>;
using Structure = std::tuple<Elements...>;
// Type of the Nth array
template<size_t N>
using TypeAt = typename std::tuple_element_t<N, std::tuple<Elements...>>;
using TypeAt = typename std::tuple_element_t<N, Structure>;
// Number of arrays
static constexpr size_t getArrayCount() noexcept { return kArrayCount; }
@@ -57,7 +59,7 @@ public:
// --------------------------------------------------------------------------------------------
class Structure;
class IteratorValue;
template<typename T> class Iterator;
using iterator = Iterator<StructureOfArraysBase*>;
using const_iterator = Iterator<StructureOfArraysBase const*>;
@@ -69,45 +71,45 @@ public:
* In other words, it's the return type of iterator::operator*(), and since it
* cannot be a C++ reference (&), it's an object that acts like it.
*/
class StructureRef {
friend class Structure;
class IteratorValueRef {
friend class IteratorValue;
friend iterator;
friend const_iterator;
StructureOfArraysBase* const UTILS_RESTRICT soa;
size_t const index;
StructureRef(StructureOfArraysBase* soa, size_t index) : soa(soa), index(index) { }
IteratorValueRef(StructureOfArraysBase* soa, size_t index) : soa(soa), index(index) { }
// assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference)
template<size_t ... Is>
StructureRef& assign(Structure const& rhs, std::index_sequence<Is...>);
IteratorValueRef& assign(IteratorValue const& rhs, std::index_sequence<Is...>);
// assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference)
template<size_t ... Is>
StructureRef& assign(Structure&& rhs, std::index_sequence<Is...>) noexcept;
IteratorValueRef& assign(IteratorValue&& rhs, std::index_sequence<Is...>) noexcept;
// objects pointed to by reference can be swapped, so provide the special swap() function.
friend void swap(StructureRef lhs, StructureRef rhs) {
friend void swap(IteratorValueRef lhs, IteratorValueRef rhs) {
lhs.soa->swap(lhs.index, rhs.index);
}
public:
// references can be created by copy-assignment only
StructureRef(StructureRef const& rhs) noexcept : soa(rhs.soa), index(rhs.index) { }
IteratorValueRef(IteratorValueRef const& rhs) noexcept : soa(rhs.soa), index(rhs.index) { }
// copy the content of a reference to the content of this one
StructureRef& operator=(StructureRef const& rhs);
IteratorValueRef& operator=(IteratorValueRef const& rhs);
// move the content of a reference to the content of this one
StructureRef& operator=(StructureRef&& rhs) noexcept;
IteratorValueRef& operator=(IteratorValueRef&& rhs) noexcept;
// copy a value_type to the content of this reference
StructureRef& operator=(Structure const& rhs) {
IteratorValueRef& operator=(IteratorValue const& rhs) {
return assign(rhs, std::make_index_sequence<kArrayCount>());
}
// move a value_type to the content of this reference
StructureRef& operator=(Structure&& rhs) noexcept {
IteratorValueRef& operator=(IteratorValue&& rhs) noexcept {
return assign(rhs, std::make_index_sequence<kArrayCount>());
}
@@ -122,36 +124,36 @@ public:
* Internally we're using a tuple<> to store the data.
* This object is not trivial to construct, as it copies an entry of the SoA.
*/
class Structure {
friend class StructureRef;
class IteratorValue {
friend class IteratorValueRef;
friend iterator;
friend const_iterator;
using Type = std::tuple<typename std::decay<Elements>::type...>;
Type elements;
template<size_t ... Is>
static Type init(StructureRef const& rhs, std::index_sequence<Is...>) {
static Type init(IteratorValueRef const& rhs, std::index_sequence<Is...>) {
return Type{ rhs.soa->template elementAt<Is>(rhs.index)... };
}
template<size_t ... Is>
static Type init(StructureRef&& rhs, std::index_sequence<Is...>) noexcept {
static Type init(IteratorValueRef&& rhs, std::index_sequence<Is...>) noexcept {
return Type{ std::move(rhs.soa->template elementAt<Is>(rhs.index))... };
}
public:
Structure(Structure const& rhs) = default;
Structure(Structure&& rhs) noexcept = default;
Structure& operator=(Structure const& rhs) = default;
Structure& operator=(Structure&& rhs) noexcept = default;
IteratorValue(IteratorValue const& rhs) = default;
IteratorValue(IteratorValue&& rhs) noexcept = default;
IteratorValue& operator=(IteratorValue const& rhs) = default;
IteratorValue& operator=(IteratorValue&& rhs) noexcept = default;
// initialize and assign from a StructureRef
Structure(StructureRef const& rhs)
IteratorValue(IteratorValueRef const& rhs)
: elements(init(rhs, std::make_index_sequence<kArrayCount>())) {}
Structure(StructureRef&& rhs) noexcept
IteratorValue(IteratorValueRef&& rhs) noexcept
: elements(init(rhs, std::make_index_sequence<kArrayCount>())) {}
Structure& operator=(StructureRef const& rhs) { return operator=(Structure(rhs)); }
Structure& operator=(StructureRef&& rhs) noexcept { return operator=(Structure(rhs)); }
IteratorValue& operator=(IteratorValueRef const& rhs) { return operator=(IteratorValue(rhs)); }
IteratorValue& operator=(IteratorValueRef&& rhs) noexcept { return operator=(IteratorValue(rhs)); }
// access the elements of this value_Type (i.e. the "fields" of the structure)
template<size_t I> TypeAt<I> const& get() const { return std::get<I>(elements); }
@@ -174,9 +176,9 @@ public:
Iterator(CVQualifiedSOAPointer soa, size_t index) : soa(soa), index(index) {}
public:
using value_type = Structure;
using reference = StructureRef;
using pointer = StructureRef*; // FIXME: this should be a StructurePtr type
using value_type = IteratorValue;
using reference = IteratorValueRef;
using pointer = IteratorValueRef*; // FIXME: this should be a StructurePtr type
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
@@ -335,6 +337,11 @@ public:
return *this;
}
StructureOfArraysBase& push_back(Structure&& args) noexcept {
ensureCapacity(mSize + 1);
return push_back_unsafe(std::forward<Structure>(args));
}
StructureOfArraysBase& push_back(Elements const& ... args) noexcept {
ensureCapacity(mSize + 1);
return push_back_unsafe(args...);
@@ -349,23 +356,29 @@ public:
struct PushBackUnsafeClosure {
size_t last;
std::tuple<Elements...> args;
inline explicit PushBackUnsafeClosure(size_t last, Elements&& ... args)
: last(last), args(std::forward<Elements>(args)...) {}
inline explicit PushBackUnsafeClosure(size_t last, Elements const& ... args)
: last(last), args(args...) {}
inline explicit PushBackUnsafeClosure(size_t last, Structure&& args)
: last(last), args(std::forward<Structure>(args)) {}
template<size_t I>
inline void operator()(TypeAt<I>* p) {
new(p + last) TypeAt<I>{ std::get<I>(args) };
}
};
StructureOfArraysBase& push_back_unsafe(Structure&& args) noexcept {
for_each_index(mArrays,
PushBackUnsafeClosure{ mSize++, std::forward<Structure>(args) });
return *this;
}
StructureOfArraysBase& push_back_unsafe(Elements const& ... args) noexcept {
for_each_index(mArrays, PushBackUnsafeClosure{ mSize++, args... });
for_each_index(mArrays,
PushBackUnsafeClosure{ mSize++, { args... } });
return *this;
}
StructureOfArraysBase& push_back_unsafe(Elements&& ... args) noexcept {
for_each_index(mArrays, PushBackUnsafeClosure{ mSize++, std::forward<Elements>(args)... });
for_each_index(mArrays,
PushBackUnsafeClosure{ mSize++, { std::forward<Elements>(args)... }});
return *this;
}
@@ -562,8 +575,10 @@ private:
forEach([from, to](auto p) {
using T = typename std::decay<decltype(*p)>::type;
// note: scalar types like int/float get initialized to zero
for (size_t i = from; i < to; i++) {
new(p + i) T();
if constexpr (!std::is_trivially_default_constructible_v<T>) {
for (size_t i = from; i < to; i++) {
new(p + i) T();
}
}
});
}
@@ -571,8 +586,10 @@ private:
void destroy_each(size_t from, size_t to) noexcept {
forEach([from, to](auto p) {
using T = typename std::decay<decltype(*p)>::type;
for (size_t i = from; i < to; i++) {
p[i].~T();
if constexpr (!std::is_trivially_destructible_v<T>) {
for (size_t i = from; i < to; i++) {
p[i].~T();
}
}
});
}
@@ -592,15 +609,17 @@ private:
reinterpret_cast<T*>(uintptr_t(b) + offsets[index]);
// for trivial cases, just call memcpy()
if (std::is_trivially_copyable<T>::value &&
std::is_trivially_destructible<T>::value) {
if constexpr (std::is_trivially_copyable_v<T> &&
std::is_trivially_destructible_v<T>) {
memcpy(arrayPointer, p, size * sizeof(T));
} else {
for (size_t i = 0; i < size; i++) {
// we move an element by using the in-place move-constructor
new(arrayPointer + i) T(std::move(p[i]));
// and delete them by calling the destructor directly
p[i].~T();
if constexpr (!std::is_trivially_destructible_v<T>) {
// and delete them by calling the destructor directly
p[i].~T();
}
}
}
index++;
@@ -626,27 +645,27 @@ private:
template<typename Allocator, typename... Elements>
inline
typename StructureOfArraysBase<Allocator, Elements...>::StructureRef&
StructureOfArraysBase<Allocator, Elements...>::StructureRef::operator=(
StructureOfArraysBase::StructureRef const& rhs) {
return operator=(Structure(rhs));
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
StructureOfArraysBase::IteratorValueRef const& rhs) {
return operator=(IteratorValue(rhs));
}
template<typename Allocator, typename... Elements>
inline
typename StructureOfArraysBase<Allocator, Elements...>::StructureRef&
StructureOfArraysBase<Allocator, Elements...>::StructureRef::operator=(
StructureOfArraysBase::StructureRef&& rhs) noexcept {
return operator=(Structure(rhs));
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
StructureOfArraysBase::IteratorValueRef&& rhs) noexcept {
return operator=(IteratorValue(rhs));
}
template<typename Allocator, typename... Elements>
template<size_t... Is>
inline
typename StructureOfArraysBase<Allocator, Elements...>::StructureRef&
StructureOfArraysBase<Allocator, Elements...>::StructureRef::assign(
StructureOfArraysBase::Structure const& rhs, std::index_sequence<Is...>) {
// implements StructureRef& StructureRef::operator=(Structure const& rhs)
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
StructureOfArraysBase::IteratorValue const& rhs, std::index_sequence<Is...>) {
// implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue const& rhs)
auto UTILS_UNUSED l = { (soa->elementAt<Is>(index) = std::get<Is>(rhs.elements), 0)... };
return *this;
}
@@ -654,10 +673,10 @@ StructureOfArraysBase<Allocator, Elements...>::StructureRef::assign(
template<typename Allocator, typename... Elements>
template<size_t... Is>
inline
typename StructureOfArraysBase<Allocator, Elements...>::StructureRef&
StructureOfArraysBase<Allocator, Elements...>::StructureRef::assign(
StructureOfArraysBase::Structure&& rhs, std::index_sequence<Is...>) noexcept {
// implements StructureRef& StructureRef::operator=(Structure&& rhs) noexcept
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
StructureOfArraysBase::IteratorValue&& rhs, std::index_sequence<Is...>) noexcept {
// implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue&& rhs) noexcept
auto UTILS_UNUSED l = {
(soa->elementAt<Is>(index) = std::move(std::get<Is>(rhs.elements)), 0)... };
return *this;

View File

@@ -54,6 +54,12 @@
# define UTILS_NORETURN
#endif
#if __has_attribute(fallthrough)
# define UTILS_FALLTHROUGH [[fallthrough]]
#else
# define UTILS_FALLTHROUGH
#endif
#if __has_attribute(visibility)
# ifndef TNT_DEV
# define UTILS_PRIVATE __attribute__((visibility("hidden")))

View File

@@ -107,7 +107,7 @@ private:
friend ostream& flush(ostream& s) noexcept;
enum type {
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE,
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, FLOAT, DOUBLE,
LONG_DOUBLE
};

View File

@@ -82,7 +82,7 @@ using LightManager = filament::LightManager;
void applySettings(Engine* engine, const ViewSettings& settings, View* dest);
void applySettings(Engine* engine, const MaterialSettings& settings, MaterialInstance* dest);
void applySettings(Engine* engine, const LightSettings& settings, IndirectLight* ibl, utils::Entity sunlight,
utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene, View* view);
const utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene, View* view);
void applySettings(Engine* engine, const ViewerOptions& settings, Camera* camera, Skybox* skybox,
Renderer* renderer);
@@ -160,6 +160,10 @@ struct DynamicLightingSettings {
float zLightFar = 100;
};
struct FogSettings {
Texture* fogColorTexture = nullptr;
};
// This defines fields in the same order as the setter methods in filament::View.
struct ViewSettings {
// standalone View settings
@@ -185,6 +189,7 @@ struct ViewSettings {
// Custom View Options
ColorGradingSettings colorGrading;
DynamicLightingSettings dynamicLighting;
FogSettings fogSettings;
};
template <typename T>
@@ -204,6 +209,9 @@ struct LightSettings {
LightManager::ShadowOptions shadowOptions;
SoftShadowOptions softShadowOptions;
float sunlightIntensity = 100000.0f;
float sunlightHaloSize = 10.0f;
float sunlightHaloFalloff = 80.0f;
float sunlightAngularRadius = 1.9f;
math::float3 sunlightDirection = {0.6, -1.0, -0.8};
math::float3 sunlightColor = filament::Color::toLinear<filament::ACCURATE>({ 0.98, 0.92, 0.89});
float iblIntensity = 30000.0f;
@@ -214,6 +222,8 @@ struct ViewerOptions {
float cameraAperture = 16.0f;
float cameraSpeed = 125.0f;
float cameraISO = 100.0f;
float cameraNear = 0.1f;
float cameraFar = 100.0f;
float groundShadowStrength = 0.75f;
bool groundPlaneEnabled = false;
bool skyboxEnabled = true;

View File

@@ -234,8 +234,6 @@ public:
private:
using SceneMask = gltfio::NodeManager::SceneMask;
void updateIndirectLight();
bool isRemoteMode() const { return mAsset == nullptr; }
void sceneSelectionUI();

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:727ae0b0182f8396c773d03595a75999c4319d910efd671351d76c31acf9727f
size 5040760
oid sha256:e1e044f3b3e95fee3edc6debe1a92bd012c9d65e5391e2731a93c8157bbc56d6
size 5419176

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f372cfaffd87f10a19c711b83ab9812e3837a6c7ebfc85580c27debe170ee547
size 1840568
oid sha256:d824fb83a754483b0f0e9ec5f38586a957db8701908368456b3a7e8d0a687553
size 1891912

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57929e8746f231f84c32cfa99fb3b4858a9b8e75f447fe191dc47759b9eec8fe
size 169376
oid sha256:dfde12c8171576c3899fb162e65bf4b1ba259f58456dac7813f3c2c12289072d
size 168408

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f452831b344a41be49175695b5afe6ac2b1f8d5864194d501a044928a4acad0d
size 1190936
oid sha256:d0ce847e34c51384069e6d813cbcd1aa3913382cb5d34654231b7de4b941882b
size 1179768

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aa5555078f560713f905990f4f09514cfba5b7528a9dc576a6f5d1790577aa56
size 8222192
oid sha256:17f1e396afdaf7bc4766f2b39ec4c31239e5caefb909a6bf53e5c4e3d888edcd
size 8512688

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b5224a5a0db2f0eb1457eecbb4439ccd8468cf362c9bb83e997bb64426bb8e9c
size 237120
oid sha256:a6de84bd277fbc9582e0df6840c05368405661eec81e3e2a6dc8af40a0349234
size 241048

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b49556c745e6bb4ffeecf7f7dd335f8437843e11d5cc959b87c2e7317500aa6d
size 193104
oid sha256:6b18b76e0b97a2f2fa5ead6ba3a0d2ea2051341ca1a6368058b2025ac342be81
size 198000

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bcd1671a33ffee2804c62a7a5b19229524645a43825a47a943ecd179a61820c
size 110068064
oid sha256:da27790d62d20ce89cfed64ab400a25a55b6b6fd2273f2ad2fcad842006daa83
size 115257128

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8d9e6fe650240c455ff85fbafd3a1d38600bf7ddd6d1006f0813c27388651a22
size 1909248
oid sha256:c924851f26120c2ece81ffe46a22b7b9e7d599fd57d6a6092d2b0067f3202a46
size 2163592

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:65af7056a290d8cee4a70d310dc05489479c574e5d6811746e99593c81a682f0
size 181840
oid sha256:f914249a9ebb0b1591607656a9657b7a964676052a663427a34590fa3b7c564b
size 228376

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4672339739201dd750c6072692d000ceea80e2547fbcf868364fd72b474d284
size 11750664
oid sha256:bc538575c4e30d0f41fd6708f6d1d3c7c3e9e157a719e7eb94914f88df82d10b
size 12595392

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5ff97e7f4605648fc6529428211bbfda761372dc27b8a625d5ddabde6da25860
size 138664
oid sha256:ea61594db369d10fa6d6ce6ca4f4ad38417b6ca6f48e718b11011005508dcbc5
size 146360

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:24bdc4d65f7dbba8349bdbd2a79e0cd8d2eae8e899f60dd81473d9724043f79f
size 279808
oid sha256:de37c101e8489e462b8d6e98297b8ef3aca41c95226c5c2f89998a1a1e1ded97
size 397936

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d8d2de200dcf24aef51f0de4c67ec2b9ec52e11d790b95b3cedf1ed33dd96cdb
size 4159392
oid sha256:369c95d496cdc92522cdddb18e1f4a3cf58f85871036ca0a608d715de01048dc
size 4163864

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:da81f59acfcaab798d26c0e2e77fffb4c43b00a1c98dfb6ba9d8993ebfdf46d9
size 974968
oid sha256:173810d56015e9b63df61204006bcfd06513d76024c34480916b43aeb1831cd4
size 956936

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf69b4695234b586cf8703b7e0cdc80d369deac7da76a449b376bf9532a8e720
size 1175464
oid sha256:ce9d5f52667351a837e4301ed9711517181aede24d7247b32dde56fb769a8b16
size 1140504

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b6466eae2193826b9b8523c8718f00e278d7ff5408ff13c2d196fce05fb1982
size 388912
oid sha256:85298bf987d5ba877142b63ba8c6e637bd227faeb9195b3f3e59fe9f461067ba
size 370704

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e8f99dbae75e2fe0db8f7693fce76995815dc553c58018f44bb565c399e51454
size 188624
oid sha256:893aa4f6c44d391094931dbcc9a26eb4033a5c859abf94af3b9cf020a6a4dcfe
size 190616

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e7db53ca7b330403e43ec84be9fe610faa8d923e7b327d5f1d876f76cf3a833
size 572152
oid sha256:8d8d6f8293fc06cb8058adea3e9139d842e300e1889e18062c3c25d39089aaf8
size 571232

3
ios/lib/libmikktspace.a Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e6c1434d302a6992db21c833ef68b63451c6a5121622e56b8952de896ebac638
size 125840

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:780b282645aa9f9733a29de049b5b2625cce3d6e634494d847d2f9a5917c21dd
size 251560
oid sha256:4b5919a771c56c70d919db33b723e4285c8e5b2f49e3010e97a4a1bd071349db
size 254376

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff5a2e3c6282fe0c4af8237500ee46aba884fb2568cb1c1369e44aa619c17435
size 234480
oid sha256:418000b4a8d3de1ff617c5d2b1bc023b651f02eed2898d998dfd0cfc9443b02d
size 198432

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c91e3145b720f6d4d2d1beaa6c9730db627bcaa53b5495183afefaec3d0c6ec
size 664160
oid sha256:7d899823872dc4ed38b36c455ac4cd2dec8bf07c6f7b59e9eccf0752b73de2d6
size 546064

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b7da234c9d5cb2f9e230b25886727348f20d987d4ac95a98dee6bcc623952a24
size 2280400
oid sha256:722e3ccd232aca659cf0810c4ac7c37289e5a45009da3eec49d72f43998b47a1
size 2369928

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6a2c66e6baa854d7a28f8c1a2840eac8234cd314592a3a8fb311aece12be170
size 136480
oid sha256:0ee58ba6a64e3e42a3749f53666b4a43688c5fa4da0f107f7006d60503d34488
size 130864

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:86b54c73185d521cfda519b3aede98a0a856a634887cf6a137cc7e69189afa3d
size 992880
oid sha256:9ad868ab35518fa24331520d92567942d660b0958432020b2de9ac33e6575240
size 1011088

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0af2880fbb48f85a0f9ec97b6196750c48c10f97a3d3d3e992d6b6e2674a56fc
size 1778328
oid sha256:1d2901b004a5d437efd94e07c15da2e2d905b7880fb7b8f51279a59cfdfa37b3
size 1744952

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b5c1772d372e8dfbeb2b17b3d4350c12271f8d1ab48884f2d76535c9cf4747fd
size 8984
oid sha256:44d8bdd21fffff9b0491720a86132f5c043bf277599ce3bc9795d6a4303e7921
size 8992

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9f7f504a3313b17e32984219a0352d4525180195b45a9e99decd485f4c38caf0
size 5171008
oid sha256:849c1898060222ff8e14a0db79f85f7e55553f356a484d63d577c16919fb7743
size 3733856

File diff suppressed because it is too large Load Diff

View File

@@ -107,7 +107,7 @@ static constexpr float4 sFullScreenTriangleVertices[3] = {
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
FilamentViewer::FilamentViewer(void* context, ResourceLoaderWrapper* resourceLoaderWrapper)
FilamentViewer::FilamentViewer(const void* context, const ResourceLoaderWrapper* const resourceLoaderWrapper)
: _resourceLoaderWrapper(resourceLoaderWrapper) {
#if TARGET_OS_IPHONE
@@ -137,13 +137,14 @@ FilamentViewer::FilamentViewer(void* context, ResourceLoaderWrapper* resourceLoa
_view = _engine->createView();
decltype(_view->getBloomOptions()) opts;
opts.enabled = false;//true;
// opts.strength = 0.6f;
opts.enabled = true;
opts.strength = 0.6f;
_view->setBloomOptions(opts);
_view->setScene(_scene);
_view->setCamera(_mainCamera);
// ToneMapper *tm = new ACESToneMapper();
ToneMapper *tm = new LinearToneMapper();
colorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine);
delete tm;
@@ -168,8 +169,7 @@ FilamentViewer::FilamentViewer(void* context, ResourceLoaderWrapper* resourceLoa
// options.minScale = filament::math::float2{ minScale };
// options.maxScale = filament::math::float2{ maxScale };
// options.sharpness = sharpness;
options.quality = View::QualityLevel::ULTRA;
// options.quality = View::QualityLevel::ULTRA;
_view->setDynamicResolutionOptions(options);
View::MultiSampleAntiAliasingOptions multiSampleAntiAliasingOptions;
@@ -206,7 +206,7 @@ FilamentViewer::FilamentViewer(void* context, ResourceLoaderWrapper* resourceLoa
.package(IMAGE_PACKAGE, IMAGE_IMAGE_SIZE)
.build(*_engine);
_imageMaterial->setDefaultParameter("showImage",0);
_imageMaterial->setDefaultParameter("backgroundColor", RgbType::sRGB, float3(0.f));
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f));
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
_imageScale = mat4f { 1.0f , 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
@@ -251,7 +251,6 @@ void FilamentViewer::setFrameInterval(float frameInterval) {
}
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) {
Log("Adding light of type %d with colour %f intensity %f at (%f, %f, %f) with direction (%f, %f, %f) with shadows %d", t, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
auto light = EntityManager::get().create();
LightManager::Builder(t)
.color(Color::cct(colour))
@@ -262,14 +261,22 @@ int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float inten
.build(*_engine, light);
_scene->addEntity(light);
_lights.push_back(light);
return Entity::smuggle(light);
auto entityId = Entity::smuggle(light);
Log("Added light under entity ID %d of type %d with colour %f intensity %f at (%f, %f, %f) with direction (%f, %f, %f) with shadows %d", entityId, t, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
return entityId;
}
void FilamentViewer::removeLight(int32_t id) {
Log("Removing light with entity ID %d", id);
auto e = utils::Entity::import(id);
_scene->removeEntities(&e, 1);
EntityManager::get().destroy(1, &e);
void FilamentViewer::removeLight(EntityId entityId) {
Log("Removing light with entity ID %d", entityId);
auto entity = utils::Entity::import(entityId);
if(entity.isNull()) {
Log("Error: light entity not found under ID %d", entityId);
} else {
remove(_lights.begin(), _lights.end(), entity);
_scene->remove(entity);
EntityManager::get().destroy(1, &entity);
}
}
void FilamentViewer::clearLights() {
@@ -388,7 +395,6 @@ void FilamentViewer::setBackgroundColor(const float r, const float g, const floa
_imageMaterial->setDefaultParameter("showImage", 0);
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a));
const Viewport& vp = _view->getViewport();
Log("Image width %d height %d vp width %d height %d", _imageWidth, _imageHeight, vp.width, vp.height);
_imageMaterial->setDefaultParameter("transform", _imageScale);
}
@@ -522,9 +528,9 @@ FilamentViewer::~FilamentViewer() {
Renderer *FilamentViewer::getRenderer() { return _renderer; }
void FilamentViewer::createSwapChain(void *surface, uint32_t width, uint32_t height) {
void FilamentViewer::createSwapChain(const void *surface, uint32_t width, uint32_t height) {
#if TARGET_OS_IPHONE
_swapChain = _engine->createSwapChain(surface, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
_swapChain = _engine->createSwapChain((void*)surface, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
#else
if(surface) {
_swapChain = _engine->createSwapChain(surface);
@@ -824,10 +830,10 @@ void FilamentViewer::updateViewportAndCameraProjection(
const double aspect = (double)width / height;
Camera& cam =_view->getCamera();
cam.setLensProjection(_cameraFocalLength, aspect, kNearPlane,
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
kFarPlane);
// cam.setScaling({1.0 / aspect, 1.0});
cam.setScaling({1.0 / aspect, 1.0});
Log("Set viewport to width: %d height: %d aspect %f scaleFactor : %f", width, height, aspect,
contentScaleFactor);

View File

@@ -12,310 +12,158 @@ using namespace polyvox;
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
// static ThreadPool* _tp;
extern "C" {
#include "PolyvoxFilamentApi.h"
FLUTTER_PLUGIN_EXPORT void* create_filament_viewer(void* context, ResourceLoaderWrapper* loader) {
// if(!_tp) {
// _tp = new ThreadPool();
// }
// //std::packaged_task<void*()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer(const void* context, const ResourceLoaderWrapper* const loader) {
return (void*) new FilamentViewer(context, loader);
// });
// auto fut = _tp->add_task(lambda);
// fut.wait();
// //return fut.get();
}
FLUTTER_PLUGIN_EXPORT ResourceLoaderWrapper* make_resource_loader(LoadResourceFromOwner loadFn, FreeResourceFromOwner freeFn, void* const owner) {
return new ResourceLoaderWrapper(loadFn, freeFn, owner);
// ResourceLoaderWrapper* lod(loadFn, freeFn, owner);
// return &lod;
return new ResourceLoaderWrapper(loadFn, freeFn, owner);
}
FLUTTER_PLUGIN_EXPORT void create_render_target(void* viewer, uint32_t textureId, uint32_t width, uint32_t height) {
// //std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void create_render_target(const void* const viewer, uint32_t textureId, uint32_t width, uint32_t height) {
((FilamentViewer*)viewer)->createRenderTarget(textureId, width, height);
// });
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void delete_filament_viewer(void* viewer) {
FLUTTER_PLUGIN_EXPORT void delete_filament_viewer(const void* const viewer) {
delete((FilamentViewer*)viewer);
}
FLUTTER_PLUGIN_EXPORT void set_background_color(void* viewer, const float r, const float g, const float b, const float a) {
// //std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_background_color(const void* const viewer, const float r, const float g, const float b, const float a) {
((FilamentViewer*)viewer)->setBackgroundColor(r, g, b, a);
// });
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void clear_background_image(void* viewer) {
// //std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void clear_background_image(const void* const viewer) {
((FilamentViewer*)viewer)->clearBackgroundImage();
// });
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_background_image(void* viewer, const char* path) {
// //std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_background_image(const void* const viewer, const char* path) {
((FilamentViewer*)viewer)->setBackgroundImage(path);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_background_image_position(void* viewer, float x, float y, bool clamp) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_background_image_position(const void* const viewer, float x, float y, bool clamp) {
((FilamentViewer*)viewer)->setBackgroundImagePosition(x, y, clamp);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void load_skybox(void* viewer, const char* skyboxPath) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void load_skybox(const void* const viewer, const char* skyboxPath) {
((FilamentViewer*)viewer)->loadSkybox(skyboxPath);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void load_ibl(void* viewer, const char* iblPath, float intensity) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void load_ibl(const void* const viewer, const char* iblPath, float intensity) {
((FilamentViewer*)viewer)->loadIbl(iblPath, intensity);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void remove_skybox(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void* const viewer) {
((FilamentViewer*)viewer)->removeSkybox();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void remove_ibl(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void* const viewer) {
((FilamentViewer*)viewer)->removeIbl();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT EntityId add_light(void* viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) {
//std::packaged_task<EntityId()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT EntityId add_light(const void* const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) {
return ((FilamentViewer*)viewer)->addLight((LightManager::Type)type, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT void remove_light(void* viewer, int32_t entityId) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void remove_light(const void* const viewer, int32_t entityId) {
((FilamentViewer*)viewer)->removeLight(entityId);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void clear_lights(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void clear_lights(const void* const viewer) {
((FilamentViewer*)viewer)->clearLights();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb(void* assetManager, const char* assetPath, bool unlit) {
//std::packaged_task<EntityId()> lambda([=]() mutable {
return ((AssetManager*)assetManager)->loadGlb(assetPath, unlit);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void* assetManager, const char* assetPath, const char* relativePath) {
//std::packaged_task<EntityId()> lambda([=]() mutable {
return ((AssetManager*)assetManager)->loadGltf(assetPath, relativePath);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT bool set_camera(void* viewer, EntityId asset, const char* nodeName) {
//std::packaged_task<bool()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT bool set_camera(const void* const viewer, EntityId asset, const char* nodeName) {
return ((FilamentViewer*)viewer)->setCamera(asset, nodeName);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(void* viewer, float aperture, float shutterSpeed, float sensitivity) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void* const viewer, float distance) {
((FilamentViewer*)viewer)->setCameraFocusDistance(distance);
}
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void* const viewer, float aperture, float shutterSpeed, float sensitivity) {
((FilamentViewer*)viewer)->setCameraExposure(aperture, shutterSpeed, sensitivity);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_camera_position(void* viewer, float x, float y, float z) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_camera_position(const void* const viewer, float x, float y, float z) {
((FilamentViewer*)viewer)->setCameraPosition(x, y, z);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(void* viewer, float rads, float x, float y, float z) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void* const viewer, float rads, float x, float y, float z) {
((FilamentViewer*)viewer)->setCameraRotation(rads, x, y, z);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(void* viewer, const float* const matrix) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void* const viewer, const float* const matrix) {
((FilamentViewer*)viewer)->setCameraModelMatrix(matrix);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(void* viewer, float focalLength) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, float focalLength) {
((FilamentViewer*)viewer)->setCameraFocalLength(focalLength);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void render(
void* viewer,
const void* const viewer,
uint64_t frameTimeInNanos
) {
//std::packaged_task<void()> lambda([=]() mutable {
((FilamentViewer*)viewer)->render(frameTimeInNanos);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_frame_interval(
void* viewer,
const void* const viewer,
float frameInterval
) {
//std::packaged_task<void()> lambda([=]() mutable {
((FilamentViewer*)viewer)->setFrameInterval(frameInterval);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void* const viewer) {
((FilamentViewer*)viewer)->destroySwapChain();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void create_swap_chain(void* viewer, void* surface=nullptr, uint32_t width=0, uint32_t height=0) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const surface=nullptr, uint32_t width=0, uint32_t height=0) {
((FilamentViewer*)viewer)->createSwapChain(surface, width, height);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void* get_renderer(void* viewer) {
//std::packaged_task<void*()> lambda([=]() mutable {
return ((FilamentViewer*)viewer)->getRenderer();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(const void* const viewer, int width, int height, float scaleFactor) {
return ((FilamentViewer*)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void scroll_update(void* viewer, float x, float y, float delta) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void scroll_update(const void* const viewer, float x, float y, float delta) {
((FilamentViewer*)viewer)->scrollUpdate(x, y, delta);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void scroll_begin(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void scroll_begin(const void* const viewer) {
((FilamentViewer*)viewer)->scrollBegin();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void scroll_end(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void scroll_end(const void* const viewer) {
((FilamentViewer*)viewer)->scrollEnd();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void grab_begin(void* viewer, float x, float y, bool pan) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void grab_begin(const void* const viewer, float x, float y, bool pan) {
((FilamentViewer*)viewer)->grabBegin(x, y, pan);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void grab_update(void* viewer, float x, float y) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void grab_update(const void* const viewer, float x, float y) {
((FilamentViewer*)viewer)->grabUpdate(x, y);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void grab_end(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void grab_end(const void* const viewer) {
((FilamentViewer*)viewer)->grabEnd();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void* get_asset_manager(void* viewer) {
//std::packaged_task<void*()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void* get_asset_manager(const void* const viewer) {
return (void*)((FilamentViewer*)viewer)->getAssetManager();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT void apply_weights(
@@ -324,11 +172,7 @@ extern "C" {
const char* const entityName,
float* const weights,
int count) {
// //std::packaged_task<void()> lambda([=]() mutable {
// ((AssetManager*)assetManager)->setMorphTargetWeights(asset, entityName, weights, count);
// });
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights(
@@ -339,16 +183,12 @@ extern "C" {
const int numWeights
) {
//std::packaged_task<void()> lambda([=]() mutable {
return ((AssetManager*)assetManager)->setMorphTargetWeights(
asset,
entityName,
weights,
numWeights
);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT bool set_morph_animation(
@@ -356,22 +196,20 @@ extern "C" {
EntityId asset,
const char* const entityName,
const float* const morphData,
int numMorphWeights,
const int* const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs) {
//std::packaged_task<void()> lambda([=]() mutable {
return ((AssetManager*)assetManager)->setMorphAnimationBuffer(
asset,
entityName,
morphData,
numMorphWeights,
morphIndices,
numMorphTargets,
numFrames,
frameLengthInMs
);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_bone_animation(
@@ -384,7 +222,6 @@ extern "C" {
const char** const meshNames,
int numMeshTargets,
float frameLengthInMs) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->setBoneAnimationBuffer(
asset,
frameData,
@@ -395,10 +232,7 @@ extern "C" {
numMeshTargets,
frameLengthInMs
);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
}
@@ -435,13 +269,10 @@ extern "C" {
EntityId asset,
int index,
bool loop,
bool reverse) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->playAnimation(asset, index, loop, reverse);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
bool reverse,
bool replaceActive,
float crossfade) {
((AssetManager*)assetManager)->playAnimation(asset, index, loop, reverse, replaceActive, crossfade);
}
FLUTTER_PLUGIN_EXPORT void set_animation_frame(
@@ -449,23 +280,19 @@ extern "C" {
EntityId asset,
int animationIndex,
int animationFrame) {
// //std::packaged_task<void()> lambda([=]() mutable {
// ((AssetManager*)assetManager)->setAnimationFrame(asset, animationIndex, animationFrame);
// });
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT float get_animation_duration(void* assetManager, EntityId asset, int animationIndex) {
return ((AssetManager*)assetManager)->getAnimationDuration(asset, animationIndex);
}
FLUTTER_PLUGIN_EXPORT int get_animation_count(
void* assetManager,
EntityId asset) {
//std::packaged_task<int()> lambda([=]() mutable {
auto names = ((AssetManager*)assetManager)->getAnimationNames(asset);
return names->size();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT void get_animation_name(
@@ -474,49 +301,32 @@ extern "C" {
char* const outPtr,
int index
) {
//std::packaged_task<void()> lambda([=]() mutable {
auto names = ((AssetManager*)assetManager)->getAnimationNames(asset);
string name = names->at(index);
strcpy(outPtr, name.c_str());
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count(void* assetManager, EntityId asset, const char* meshName) {
//std::packaged_task<int()> lambda([=]() mutable {
unique_ptr<vector<string>> names = ((AssetManager*)assetManager)->getMorphTargetNames(asset, meshName);
return names->size();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
//return fut.get();
}
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void* assetManager, EntityId asset, const char* meshName, char* const outPtr, int index ) {
//std::packaged_task<void()> lambda([=]() mutable {
unique_ptr<vector<string>> names = ((AssetManager*)assetManager)->getMorphTargetNames(asset, meshName);
string name = names->at(index);
strcpy(outPtr, name.c_str());
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void remove_asset(void* viewer, EntityId asset) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void remove_asset(const void* const viewer, EntityId asset) {
((FilamentViewer*)viewer)->removeAsset(asset);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void clear_assets(void* viewer) {
//std::packaged_task<void()> lambda([=]() mutable {
FLUTTER_PLUGIN_EXPORT void clear_assets(const void* const viewer) {
((FilamentViewer*)viewer)->clearAssets();
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void load_texture(void* assetManager, EntityId asset, const char* assetPath, int renderableIndex) {
@@ -527,60 +337,36 @@ extern "C" {
// ((AssetManager*)assetManager)->setTexture();
}
bool set_material_color(void* assetManager, EntityId asset, const char* meshName, int materialIndex, const float r, const float g, const float b, const float a) {
return ((AssetManager*)assetManager)->setMaterialColor(asset, meshName, materialIndex, r, g, b, a);
}
FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void* assetManager, EntityId asset) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->transformToUnitCube(asset);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_position(void* assetManager, EntityId asset, float x, float y, float z) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->setPosition(asset, x, y, z);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->setRotation(asset, rads, x, y, z);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->setScale(asset, scale);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void stop_animation(void* assetManager, EntityId asset, int index) {
//std::packaged_task<void()> lambda([=]() mutable {
((AssetManager*)assetManager)->stopAnimation(asset, index);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT int hide_mesh(void* assetManager, EntityId asset, const char* meshName) {
//std::packaged_task<void()> lambda([=]() mutable {
return ((AssetManager*)assetManager)->hide(asset, meshName);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT int reveal_mesh(void* assetManager, EntityId asset, const char* meshName) {
//std::packaged_task<void()> lambda([=]() mutable {
return ((AssetManager*)assetManager)->reveal(asset, meshName);
//});
// auto fut = _tp->add_task(lambda);
// fut.wait();
}
FLUTTER_PLUGIN_EXPORT void ios_dummy() {

View File

@@ -1,4 +1,3 @@
import 'package:polyvox_filament/animations/bone_animation_data.dart';
import 'package:polyvox_filament/animations/morph_animation_data.dart';
import 'package:polyvox_filament/filament_controller.dart';
@@ -6,7 +5,6 @@ import 'package:flutter/foundation.dart';
import 'package:vector_math/vector_math.dart';
class AnimationBuilder {
final FilamentController controller;
// BoneAnimationData? BoneAnimationData;
double _frameLengthInMs = 0;
double _duration = 0;
@@ -18,26 +16,25 @@ class AnimationBuilder {
// List<BoneAnimationData>? _BoneAnimationDatas = null;
FilamentEntity asset;
String meshName;
late List<String> morphNames;
late List<String> availableMorphs;
late List<int> _morphTargets;
AnimationBuilder(
{required this.controller,
required this.asset,
{required this.availableMorphs,
required this.meshName,
required int framerate}) {
_frameLengthInMs = 1000 / framerate;
morphNames = controller.getMorphTargetNames(asset, meshName);
}
void set() {
if (morphNames.isEmpty == 0 || _duration == 0 || _frameLengthInMs == 0)
MorphAnimationData build() {
if (availableMorphs.isEmpty == 0 || _duration == 0 || _frameLengthInMs == 0)
throw Exception();
int numFrames = _duration * 1000 ~/ _frameLengthInMs;
final morphData = Float32List((numFrames * morphNames.length).toInt());
final morphData =
List<double>.filled((numFrames * _morphTargets.length).toInt(), 0.0);
var frameStart = (_interpMorphStart! * 1000) ~/ _frameLengthInMs;
var frameEnd = (_interpMorphEnd! * 1000) ~/ _frameLengthInMs;
@@ -47,17 +44,16 @@ class AnimationBuilder {
var val = ((1 - linear) * _interpMorphStartValue!) +
(linear * _interpMorphEndValue!);
for (int j = 0; j < morphNames.length; j++) {
morphData[(i * morphNames.length) + j] = val;
for (int j = 0; j < _morphTargets.length; j++) {
morphData[(i * _morphTargets.length) + j] = val;
}
}
var morphAnimation =
MorphAnimationData(meshName, morphData, morphNames, _frameLengthInMs);
controller.setMorphAnimationData(asset, morphAnimation);
// return Tuple2<MorphAnimationData, List<BoneAnimationData>>(
// morphAnimation, _BoneAnimationDatas!);
return MorphAnimationData(
meshName,
morphData,
_morphTargets.map((i) => availableMorphs[i]).toList(),
_morphTargets,
_frameLengthInMs);
}
AnimationBuilder setDuration(double secs) {
@@ -65,6 +61,11 @@ class AnimationBuilder {
return this;
}
AnimationBuilder setMorphTargets(List<String> names) {
_morphTargets = names.map((name) => availableMorphs.indexOf(name)).toList();
return this;
}
AnimationBuilder interpolateMorphWeights(
double start, double end, double startValue, double endValue) {
this._interpMorphStart = start;

View File

@@ -32,7 +32,11 @@ class DynamicAnimation {
}
var morphAnimationData = MorphAnimationData(
meshName ?? "NULL", llf.item2, morphNames, frameLengthInMs);
meshName ?? "NULL",
llf.item2,
morphNames,
List<int>.generate(morphNames.length, (index) => index),
frameLengthInMs);
final boneAnimations = <BoneAnimationData>[];

View File

@@ -9,24 +9,25 @@ import 'dart:typed_data';
class MorphAnimationData {
final String meshName;
final List<String> morphNames;
final List<int> morphIndices;
final Float32List data;
final List<double> data;
MorphAnimationData(
this.meshName, this.data, this.morphNames, this.frameLengthInMs) {
MorphAnimationData(this.meshName, this.data, this.morphNames,
this.morphIndices, this.frameLengthInMs) {
assert(data.length == morphNames.length * numFrames);
}
int get numMorphWeights => morphNames.length;
int get numMorphTargets => morphNames.length;
int get numFrames => data.length ~/ numMorphWeights;
int get numFrames => data.length ~/ numMorphTargets;
final double frameLengthInMs;
Iterable<double> getData(String morphName) sync* {
int index = morphNames.indexOf(morphName);
for (int i = 0; i < numFrames; i++) {
yield data[(i * numMorphWeights) + index];
yield data[(i * numMorphTargets) + index];
}
}
}

View File

@@ -1,20 +1,15 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:ui' as ui;
import 'package:ffi/ffi.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:polyvox_filament/animations/bone_animation_data.dart';
import 'package:polyvox_filament/animations/morph_animation_data.dart';
import 'package:polyvox_filament/generated_bindings.dart';
typedef AssetManager = Pointer<Void>;
typedef FilamentViewer = Pointer<Void>;
typedef AssetManager = int;
typedef FilamentEntity = int;
const FilamentEntity FILAMENT_ASSET_ERROR = 0;
@@ -34,23 +29,13 @@ class FilamentController {
final _initialized = Completer();
Future get initialized => _initialized.future;
late NativeLibrary _nativeLibrary;
late FilamentViewer _viewer;
late AssetManager _assetManager;
bool _rendering = false;
final TickerProvider _tickerProvider;
Ticker? _ticker;
///
/// This now uses an FFI implementation.
/// Platform channels are only used to setup the context/texture (since this is platform-specific) and the render ticker.
/// All other methods directly invoke the FFI functions defined in PolyvoxFilamentApi.cpp,
/// which itself uses a threadpool so that calls are run on a separate thread.
/// This controller uses platform channels to bridge Dart with the C/C++ code for the Filament API.
/// Setting up the context/texture (since this is platform-specific) and the render ticker are platform-specific; all other methods are passed through by the platform channel to the methods specified in PolyvoxFilamentApi.h.
///
FilamentController(this._tickerProvider) {
FilamentController() {
_channel.setMethodCallHandler((call) async {
throw Exception("Unknown method channel invocation ${call.method}");
});
@@ -58,10 +43,6 @@ class FilamentController {
_textureIdController.onListen = () {
_textureIdController.add(_textureId);
};
_nativeLibrary = NativeLibrary(Platform.isAndroid || Platform.isLinux
? DynamicLibrary.open("libpolyvox_filament_plugin.so")
: DynamicLibrary.process());
}
Future initialize() async {
@@ -70,126 +51,94 @@ class FilamentController {
}
Future setRendering(bool render) async {
_rendering = render;
_channel.invokeMethod("setRendering", render);
}
void render() {
_nativeLibrary.render(_viewer, 0);
_channel.invokeMethod("render");
}
int _frameLengthInMicroseconds = 1000000 ~/ 60;
Future setFrameRate(int framerate) async {
_frameLengthInMicroseconds = 1000000 ~/ framerate;
_nativeLibrary.set_frame_interval(_viewer, 1 / framerate);
_channel.invokeMethod("setFrameInterval", 1.0 / framerate);
}
void setPixelRatio(double ratio) {
_pixelRatio = ratio;
}
int _last = 0;
Future createViewer(int width, int height) async {
size = ui.Size(width * _pixelRatio, height * _pixelRatio);
_textureId =
await _channel.invokeMethod("createTexture", [size.width, size.height]);
_textureIdController.add(_textureId);
var glContext =
Pointer<Void>.fromAddress(await _channel.invokeMethod("getContext"));
final resourceLoader = Pointer<ResourceLoaderWrapper>.fromAddress(
await _channel.invokeMethod("getResourceLoader"));
await _channel
.invokeMethod("createFilamentViewer", [size.width, size.height]);
_viewer = _nativeLibrary.create_filament_viewer(glContext, resourceLoader);
if (Platform.isLinux) {
// don't pass a surface to the SwapChain as we are effectively creating a headless SwapChain that will render into a RenderTarget associated with a texture
_nativeLibrary.create_swap_chain(
_viewer, nullptr, size.width.toInt(), size.height.toInt());
// if (Platform.isLinux) {
// // don't pass a surface to the SwapChain as we are effectively creating a headless SwapChain that will render into a RenderTarget associated with a texture
// _nativeLibrary.create_swap_chain(
// nullptr, size.width.toInt(), size.height.toInt());
var glTextureId = await _channel.invokeMethod("getGlTextureId");
// var glTextureId = await _channel.invokeMethod("getGlTextureId");
_nativeLibrary.create_render_target(
_viewer, glTextureId, size.width.toInt(), size.height.toInt());
} else {
var surface =
Pointer<Void>.fromAddress(await _channel.invokeMethod("getSurface"));
_nativeLibrary.create_swap_chain(
_viewer, surface, size.width.toInt(), size.height.toInt());
}
// await _channel.invokeMethod("create_render_target(
// glTextureId, size.width.toInt(), size.height.toInt());
// } else {
_nativeLibrary.update_viewport_and_camera_projection(
_viewer, size.width.toInt(), size.height.toInt(), 1.0);
// }
await _channel.invokeMethod("updateViewportAndCameraProjection",
[size.width.toInt(), size.height.toInt(), 1.0]);
_initialized.complete(true);
_assetManager = _nativeLibrary.get_asset_manager(_viewer);
_ticker = _tickerProvider.createTicker((Duration elapsed) async {
if (elapsed.inMicroseconds - _last > _frameLengthInMicroseconds) {
render();
_last = elapsed.inMicroseconds;
}
});
_ticker!.start();
_assetManager = await _channel.invokeMethod("getAssetManager");
_textureIdController.add(_textureId);
}
Future resize(int width, int height,
{double contentScaleFactor = 1.0}) async {
// await setRendering(false);
// _textureIdController.add(null);
// _nativeLibrary.destroy_swap_chain(_viewer);
// size = ui.Size(width * _pixelRatio, height * _pixelRatio);
// _textureId = await _channel.invokeMethod("resize",
// [width * _pixelRatio, height * _pixelRatio, contentScaleFactor]);
// _textureIdController.add(_textureId);
// _nativeLibrary.create_swap_chain(_viewer, nullptr, width, height);
// _nativeLibrary.create_render_target(
// _viewer, await _channel.invokeMethod("getGlTextureId"), width, height);
// _nativeLibrary.update_viewport_and_camera_projection(
// _viewer, width, height, contentScaleFactor);
// await setRendering(true);
_textureId = await _channel.invokeMethod("resize",
[width * _pixelRatio, height * _pixelRatio, contentScaleFactor]);
_textureIdController.add(_textureId);
}
void clearBackgroundImage() async {
_nativeLibrary.clear_background_image(_viewer);
await _channel.invokeMethod("clearBackgroundImage");
}
void setBackgroundImage(String path) async {
_nativeLibrary.set_background_image(
_viewer, path.toNativeUtf8().cast<Char>());
await _channel.invokeMethod("setBackgroundImage", path);
}
void setBackgroundColor(Color color) async {
_nativeLibrary.set_background_color(
_viewer,
color.red.toDouble() / 255.0,
color.green.toDouble() / 255.0,
color.blue.toDouble() / 255.0,
color.alpha.toDouble() / 255.0);
await _channel.invokeMethod("setBackgroundColor", [
color.red.toDouble() / 255.0,
color.green.toDouble() / 255.0,
color.blue.toDouble() / 255.0,
color.alpha.toDouble() / 255.0
]);
}
void setBackgroundImagePosition(double x, double y,
{bool clamp = false}) async {
_nativeLibrary.set_background_image_position(_viewer, x, y, clamp ? 1 : 0);
await _channel
.invokeMethod("setBackgroundImagePosition", [x, y, clamp ? 1 : 0]);
}
void loadSkybox(String skyboxPath) async {
_nativeLibrary.load_skybox(_viewer, skyboxPath.toNativeUtf8().cast<Char>());
await _channel.invokeMethod("loadSkybox", skyboxPath);
}
void loadIbl(String lightingPath, {double intensity = 30000}) async {
_nativeLibrary.load_ibl(
_viewer, lightingPath.toNativeUtf8().cast<Char>(), intensity);
await _channel.invokeMethod("loadIbl", [lightingPath, intensity]);
}
void removeSkybox() async {
_nativeLibrary.remove_skybox(_viewer);
await _channel.invokeMethod("removeSkybox");
}
void removeIbl() async {
_nativeLibrary.remove_ibl(_viewer);
await _channel.invokeMethod("removeIbl");
}
// copied from LightManager.h
@@ -200,8 +149,7 @@ class FilamentController {
// FOCUSED_SPOT, //!< Physically correct spot light.
// SPOT, //!< Spot light with coupling of outer cone and illumination disabled.
// };
FilamentEntity addLight(
Future<FilamentEntity> addLight(
int type,
double colour,
double intensity,
@@ -211,94 +159,98 @@ class FilamentController {
double dirX,
double dirY,
double dirZ,
bool castShadows) {
return _nativeLibrary.add_light(_viewer, type, colour, intensity, posX,
posY, posZ, dirX, dirY, dirZ, castShadows ? 1 : 0);
bool castShadows) async {
var entity = await _channel.invokeMethod("addLight", [
type,
colour,
intensity,
posX,
posY,
posZ,
dirX,
dirY,
dirZ,
castShadows ? 1 : 0
]);
return entity as FilamentEntity;
}
void removeLight(FilamentEntity light) async {
_nativeLibrary.remove_light(_viewer, light);
await _channel.invokeMethod("removeLight", light);
}
void clearLights() async {
_nativeLibrary.clear_lights(_viewer);
await _channel.invokeMethod("clearLights");
}
FilamentEntity loadGlb(String path, {bool unlit = false}) {
var asset = _nativeLibrary.load_glb(
_assetManager, path.toNativeUtf8().cast<Char>(), unlit ? 1 : 0);
Future<FilamentEntity> loadGlb(String path, {bool unlit = false}) async {
var asset = await _channel
.invokeMethod("loadGlb", [_assetManager, path, unlit ? 1 : 0]);
if (asset == FILAMENT_ASSET_ERROR) {
throw Exception("An error occurred loading the asset at $path");
}
return asset;
}
FilamentEntity loadGltf(String path, String relativeResourcePath) {
return _nativeLibrary.load_gltf(
_assetManager,
path.toNativeUtf8().cast<Char>(),
relativeResourcePath.toNativeUtf8().cast<Char>());
Future<FilamentEntity> loadGltf(
String path, String relativeResourcePath) async {
var entity = await _channel
.invokeMethod("loadGltf", [_assetManager, path, relativeResourcePath]);
return entity as FilamentEntity;
}
void panStart(double x, double y) async {
_nativeLibrary.grab_begin(_viewer, x * _pixelRatio, y * _pixelRatio, 1);
await _channel
.invokeMethod("grabBegin", [x * _pixelRatio, y * _pixelRatio, 1]);
}
void panUpdate(double x, double y) async {
_nativeLibrary.grab_update(_viewer, x * _pixelRatio, y * _pixelRatio);
await _channel
.invokeMethod("grabUpdate", [x * _pixelRatio, y * _pixelRatio]);
}
void panEnd() async {
_nativeLibrary.grab_end(_viewer);
await _channel.invokeMethod("grabEnd");
}
void rotateStart(double x, double y) async {
_nativeLibrary.grab_begin(_viewer, x * _pixelRatio, y * _pixelRatio, 0);
await _channel
.invokeMethod("grabBegin", [x * _pixelRatio, y * _pixelRatio, 0]);
}
void rotateUpdate(double x, double y) async {
_nativeLibrary.grab_update(_viewer, x * _pixelRatio, y * _pixelRatio);
await _channel
.invokeMethod("grabUpdate", [x * _pixelRatio, y * _pixelRatio]);
}
void rotateEnd() async {
_nativeLibrary.grab_end(_viewer);
await _channel.invokeMethod("grabEnd");
}
void setMorphTargetWeights(
FilamentEntity asset, String meshName, List<double> weights) {
var weightPtr = calloc<Float>(weights.length);
for (int i = 0; i < weights.length; i++) {
weightPtr[i] = weights[i];
}
_nativeLibrary.set_morph_target_weights(_assetManager, asset,
meshName.toNativeUtf8().cast<Char>(), weightPtr, weights.length);
calloc.free(weightPtr);
FilamentEntity asset, String meshName, List<double> weights) async {
await _channel.invokeMethod("setMorphTargetWeights",
[_assetManager, asset, meshName, weights, weights.length]);
}
List<String> getMorphTargetNames(FilamentEntity asset, String meshName) {
var meshNamePtr = meshName.toNativeUtf8().cast<Char>();
var count = _nativeLibrary.get_morph_target_name_count(
_assetManager, asset, meshNamePtr);
var names = <String>[];
for (int i = 0; i < count; i++) {
var outPtr = calloc<Char>(255);
_nativeLibrary.get_morph_target_name(
_assetManager, asset, meshNamePtr, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
return names;
Future<List<String>> getMorphTargetNames(
FilamentEntity asset, String meshName) async {
var names = await _channel
.invokeMethod("getMorphTargetNames", [_assetManager, asset, meshName]);
return names.cast<String>();
}
List<String> getAnimationNames(FilamentEntity asset) {
var count = _nativeLibrary.get_animation_count(_assetManager, asset);
var names = <String>[];
for (int i = 0; i < count; i++) {
var outPtr = calloc<Char>(255);
_nativeLibrary.get_animation_name(_assetManager, asset, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
return names;
Future<List<String>> getAnimationNames(FilamentEntity asset) async {
var names = await _channel
.invokeMethod("getAnimationNames", [_assetManager, asset]);
return names.cast<String>();
}
Future<double> getAnimationDuration(
FilamentEntity asset, int animationIndex) async {
var duration = await _channel.invokeMethod(
"getAnimationDuration", [_assetManager, asset, animationIndex]);
return duration as double;
}
///
@@ -308,19 +260,16 @@ class FilamentController {
///
void setMorphAnimationData(
FilamentEntity asset, MorphAnimationData animation) async {
var data = calloc<Float>(animation.data.length);
for (int i = 0; i < animation.data.length; i++) {
data.elementAt(i).value = animation.data[i];
}
_nativeLibrary.set_morph_animation(
_assetManager,
asset,
animation.meshName.toNativeUtf8().cast<Char>(),
data,
animation.numMorphWeights,
animation.numFrames,
animation.frameLengthInMs);
calloc.free(data);
await _channel.invokeMethod("setMorphAnimation", [
_assetManager,
asset,
animation.meshName,
animation.data,
animation.animatedMorphIndices,
animation.numMorphTargets,
animation.numFrames,
animation.frameLengthInMs
]);
}
///
@@ -349,84 +298,93 @@ class FilamentController {
offset += 1;
}
_nativeLibrary.set_bone_animation(
_assetManager,
asset,
data,
numFrames,
1,
boneNames,
meshNames,
animation.meshNames.length,
animation.frameLengthInMs);
await _channel.invokeMethod("setBoneAnimation", [
_assetManager,
asset,
data,
numFrames,
1,
boneNames,
meshNames,
animation.meshNames.length,
animation.frameLengthInMs
]);
calloc.free(data);
}
void removeAsset(FilamentEntity asset) async {
_nativeLibrary.remove_asset(_viewer, asset);
await _channel.invokeMethod("removeAsset", asset);
}
void clearAssets() async {
_nativeLibrary.clear_assets(_viewer);
await _channel.invokeMethod("clearAssets");
}
void zoomBegin() async {
_nativeLibrary.scroll_begin(_viewer);
await _channel.invokeMethod("scrollBegin");
}
void zoomUpdate(double z) async {
_nativeLibrary.scroll_update(_viewer, 0.0, 0.0, z);
await _channel.invokeMethod("scrollUpdate", [0.0, 0.0, z]);
}
void zoomEnd() async {
_nativeLibrary.scroll_end(_viewer);
await _channel.invokeMethod("scrollEnd");
}
void playAnimation(FilamentEntity asset, int index,
{bool loop = false, bool reverse = false}) async {
_nativeLibrary.play_animation(
_assetManager, asset, index, loop ? 1 : 0, reverse ? 1 : 0);
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) async {
await _channel.invokeMethod("playAnimation", [
_assetManager,
asset,
index,
loop ? 1 : 0,
reverse ? 1 : 0,
replaceActive,
crossfade
]);
}
void setAnimationFrame(
FilamentEntity asset, int index, int animationFrame) async {
_nativeLibrary.set_animation_frame(
_assetManager, asset, index, animationFrame);
await _channel.invokeMethod(
"setAnimationFrame", [_assetManager, asset, index, animationFrame]);
}
void stopAnimation(FilamentEntity asset, int animationIndex) async {
_nativeLibrary.stop_animation(_assetManager, asset, animationIndex);
await _channel
.invokeMethod("stopAnimation", [_assetManager, asset, animationIndex]);
}
void setCamera(FilamentEntity asset, String? name) async {
if (_nativeLibrary.set_camera(
_viewer, asset, name?.toNativeUtf8()?.cast<Char>() ?? nullptr) !=
1) {
if (await _channel.invokeMethod("setCamera", [asset, name]) != true) {
throw Exception("Failed to set camera");
}
;
}
void setCameraFocalLength(double focalLength) async {
_nativeLibrary.set_camera_focal_length(_viewer, focalLength);
await _channel.invokeMethod("setCameraFocalLength", focalLength);
}
void setCameraFocusDistance(double focusDistance) async {
_nativeLibrary.set_camera_focus_distance(_viewer, focusDistance);
await _channel.invokeMethod("setCameraFocusDistance", focusDistance);
}
void setCameraPosition(double x, double y, double z) async {
_nativeLibrary.set_camera_position(_viewer, x, y, z);
await _channel.invokeMethod("setCameraPosition", [x, y, z]);
}
void setCameraExposure(
double aperture, double shutterSpeed, double sensitivity) async {
_nativeLibrary.set_camera_exposure(
_viewer, aperture, shutterSpeed, sensitivity);
await _channel.invokeMethod(
"setCameraExposure", [aperture, shutterSpeed, sensitivity]);
}
void setCameraRotation(double rads, double x, double y, double z) async {
_nativeLibrary.set_camera_rotation(_viewer, rads, x, y, z);
await _channel.invokeMethod("setCameraRotation", [rads, x, y, z]);
}
void setCameraModelMatrix(List<double> matrix) async {
@@ -435,42 +393,59 @@ class FilamentController {
for (int i = 0; i < 16; i++) {
ptr.elementAt(i).value = matrix[i];
}
_nativeLibrary.set_camera_model_matrix(_viewer, ptr);
await _channel.invokeMethod("setCameraModelMatrix", [ptr]);
}
void setTexture(FilamentEntity asset, String assetPath,
{int renderableIndex = 0}) async {
_nativeLibrary.set_texture(_assetManager, asset);
await _channel.invokeMethod("setTexture", [_assetManager, asset]);
}
void setMaterialColor(FilamentEntity asset, String meshName,
int materialIndex, Color color) async {
await _channel.invokeMethod("setMaterialColor", [
_assetManager,
asset,
meshName,
materialIndex,
[
color.red.toDouble() / 255.0,
color.green.toDouble() / 255.0,
color.blue.toDouble() / 255.0,
color.alpha.toDouble() / 255.0
]
]);
}
void transformToUnitCube(FilamentEntity asset) async {
_nativeLibrary.transform_to_unit_cube(_assetManager, asset);
await _channel.invokeMethod("transformToUnitCube", [_assetManager, asset]);
}
void setPosition(FilamentEntity asset, double x, double y, double z) async {
_nativeLibrary.set_position(_assetManager, asset, x, y, z);
await _channel.invokeMethod("setPosition", [_assetManager, asset, x, y, z]);
}
void setScale(FilamentEntity asset, double scale) async {
_nativeLibrary.set_scale(_assetManager, asset, scale);
await _channel.invokeMethod("setScale", [_assetManager, asset, scale]);
}
void setRotation(
FilamentEntity asset, double rads, double x, double y, double z) async {
_nativeLibrary.set_rotation(_assetManager, asset, rads, x, y, z);
await _channel
.invokeMethod("setRotation", [_assetManager, asset, rads, x, y, z]);
}
void hide(FilamentEntity asset, String meshName) {
if (_nativeLibrary.hide_mesh(
_assetManager, asset, meshName.toNativeUtf8().cast<Char>()) !=
void hide(FilamentEntity asset, String meshName) async {
if (await _channel
.invokeMethod("hideMesh", [_assetManager, asset, meshName]) !=
1) {
throw Exception("Failed to hide mesh $meshName");
}
}
void reveal(FilamentEntity asset, String meshName) {
if (_nativeLibrary.reveal_mesh(
_assetManager, asset, meshName.toNativeUtf8().cast<Char>()) !=
void reveal(FilamentEntity asset, String meshName) async {
if (await _channel
.invokeMethod("revealMesh", [_assetManager, asset, meshName]) !=
1) {
throw Exception("Failed to reveal mesh $meshName");
}

View File

@@ -1,6 +1,7 @@
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'dart:async';
@@ -55,73 +56,98 @@ class FilamentWidget extends StatefulWidget {
}
class _FilamentWidgetState extends State<FilamentWidget> {
StreamSubscription? _listener;
StreamSubscription? _initializationListener;
StreamSubscription? _textureIdListener;
int? _textureId;
bool _resizing = false;
bool _hasViewer = false;
@override
void initState() {
_listener = widget.controller.onInitializationRequested.listen((_) {
_initializationListener =
widget.controller.onInitializationRequested.listen((_) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
var size = ((context.findRenderObject()) as RenderBox).size;
print(
"Requesting creation of Filament back-end texture/viewer for viewport size $size");
await widget.controller
.createViewer(size.width.toInt(), size.height.toInt());
print("Filament texture/viewer created.");
_listener!.cancel();
_listener = null;
setState(() {
_hasViewer = true;
});
_initializationListener!.cancel();
_initializationListener = null;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
var size = ((context.findRenderObject()) as RenderBox).size;
widget.controller.resize(size.width.toInt(), size.height.toInt());
print("RESIZED IN POST FRAME CALLBACK TO $size");
});
setState(() {});
});
});
_textureIdListener = widget.controller.textureId.listen((int? textureId) {
setState(() {
_textureId = textureId;
});
setState(() {});
});
super.initState();
}
@override
void dispose() {
_listener?.cancel();
_initializationListener?.cancel();
_textureIdListener?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: widget.controller.textureId,
builder: (ctx, AsyncSnapshot<int?> textureId) {
if (textureId.data == null) {
return Container();
}
return LayoutBuilder(builder: ((context, constraints) {
print("constraints $constraints");
if (_textureId == null) {
return Container(color: Colors.transparent);
}
var texture = Texture(
key: ObjectKey("texture_${textureId.data}"),
textureId: textureId.data!,
filterQuality: FilterQuality.high,
);
return LayoutBuilder(
builder: ((context, constraints) => SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth,
child: ResizeObserver(
onResized: (Size oldSize, Size newSize) async {
// setState(() {
// _resizing = true;
// });
var texture = Texture(
key: ObjectKey("texture_$_textureId"),
textureId: _textureId!,
filterQuality: FilterQuality.high,
);
return SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth,
child: ResizeObserver(
onResized: (Size oldSize, Size newSize) async {
if (!_hasViewer) {
return;
}
print("RESIZE OBSERVER $newSize");
WidgetsBinding.instance.addPostFrameCallback((_) async {
setState(() {
_resizing = true;
});
await widget.controller.resize(
newSize.width.toInt(), newSize.height.toInt());
setState(() {
_resizing = false;
});
},
child: Platform.isLinux
? _resizing
? Container()
: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationX(
pi), // TODO - this rotation is due to OpenGL texture coordinate working in a different space from Flutter, can we move this to the C++ side somewhere?
child: texture)
: texture))));
});
await widget.controller
.resize(newSize.width.toInt(), newSize.height.toInt());
WidgetsBinding.instance.addPostFrameCallback((_) async {
setState(() {
_resizing = false;
});
});
});
},
child: Platform.isLinux
? _resizing
? Container()
: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationX(
pi), // TODO - this rotation is due to OpenGL texture coordinate working in a different space from Flutter, can we move this to the C++ side somewhere?
child: texture)
: texture));
}));
}
}

View File

@@ -450,21 +450,6 @@ class NativeLibrary {
late final _set_frame_interval = _set_frame_intervalPtr
.asFunction<void Function(ffi.Pointer<ffi.Void>, double)>();
ffi.Pointer<ffi.Void> get_renderer(
ffi.Pointer<ffi.Void> viewer,
) {
return _get_renderer(
viewer,
);
}
late final _get_rendererPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Void> Function(
ffi.Pointer<ffi.Void>)>>('get_renderer');
late final _get_renderer = _get_rendererPtr
.asFunction<ffi.Pointer<ffi.Void> Function(ffi.Pointer<ffi.Void>)>();
void update_viewport_and_camera_projection(
ffi.Pointer<ffi.Void> viewer,
int width,
@@ -652,7 +637,8 @@ class NativeLibrary {
int asset,
ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> morphData,
int numMorphWeights,
ffi.Pointer<ffi.Int> morphIndices,
int numMorphTargets,
int numFrames,
double frameLengthInMs,
) {
@@ -661,7 +647,8 @@ class NativeLibrary {
asset,
entityName,
morphData,
numMorphWeights,
morphIndices,
numMorphTargets,
numFrames,
frameLengthInMs,
);
@@ -674,12 +661,13 @@ class NativeLibrary {
EntityId,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Float>,
ffi.Pointer<ffi.Int>,
ffi.Int,
ffi.Int,
ffi.Float)>>('set_morph_animation');
late final _set_morph_animation = _set_morph_animationPtr.asFunction<
int Function(ffi.Pointer<ffi.Void>, int, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Float>, int, int, double)>();
ffi.Pointer<ffi.Float>, ffi.Pointer<ffi.Int>, int, int, double)>();
void set_bone_animation(
ffi.Pointer<ffi.Void> assetManager,
@@ -735,6 +723,8 @@ class NativeLibrary {
int index,
int loop,
int reverse,
int replaceActive,
double crossfade,
) {
return _play_animation(
assetManager,
@@ -742,15 +732,17 @@ class NativeLibrary {
index,
loop,
reverse,
replaceActive,
crossfade,
);
}
late final _play_animationPtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int, ffi.Int,
ffi.Int)>>('play_animation');
late final _play_animation = _play_animationPtr
.asFunction<void Function(ffi.Pointer<ffi.Void>, int, int, int, int)>();
ffi.Int, ffi.Int, ffi.Float)>>('play_animation');
late final _play_animation = _play_animationPtr.asFunction<
void Function(ffi.Pointer<ffi.Void>, int, int, int, int, int, double)>();
void set_animation_frame(
ffi.Pointer<ffi.Void> assetManager,
@@ -830,6 +822,25 @@ class NativeLibrary {
late final _get_animation_name = _get_animation_namePtr.asFunction<
void Function(ffi.Pointer<ffi.Void>, int, ffi.Pointer<ffi.Char>, int)>();
double get_animation_duration(
ffi.Pointer<ffi.Void> assetManager,
int asset,
int index,
) {
return _get_animation_duration(
assetManager,
asset,
index,
);
}
late final _get_animation_durationPtr = _lookup<
ffi.NativeFunction<
ffi.Float Function(ffi.Pointer<ffi.Void>, EntityId,
ffi.Int)>>('get_animation_duration');
late final _get_animation_duration = _get_animation_durationPtr
.asFunction<double Function(ffi.Pointer<ffi.Void>, int, int)>();
void get_morph_target_name(
ffi.Pointer<ffi.Void> assetManager,
int asset,
@@ -1484,13 +1495,3 @@ const int WINT_MAX = 2147483647;
const int SIG_ATOMIC_MIN = -2147483648;
const int SIG_ATOMIC_MAX = 2147483647;
const int __DARWIN_WCHAR_MAX = 2147483647;
const int __DARWIN_WCHAR_MIN = -2147483648;
const int __DARWIN_WEOF = -1;
const int _FORTIFY_SOURCE = 2;
const int NULL = 0;

View File

@@ -4,7 +4,7 @@ version: 0.0.1
homepage:
environment:
sdk: ">=2.17.1 <3.0.0"
sdk: ">=3.0.0 <3.11.0"
flutter: ">=1.20.0"
dependencies: