feat: add flag for keepData for gltf instancing, add highlightScene, add stencilHighlight method
This commit is contained in:
33
materials/unlit.mat
Normal file
33
materials/unlit.mat
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
material {
|
||||||
|
name : unlit,
|
||||||
|
parameters : [
|
||||||
|
{
|
||||||
|
type : float3,
|
||||||
|
name : color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type : float,
|
||||||
|
name : scale
|
||||||
|
}
|
||||||
|
],
|
||||||
|
depthWrite : true,
|
||||||
|
depthCulling : false,
|
||||||
|
shadingModel : unlit,
|
||||||
|
blending: opaque,
|
||||||
|
variantFilter : [ skinning, shadowReceiver, vsm ],
|
||||||
|
culling: none,
|
||||||
|
instanced: false,
|
||||||
|
vertexDomain: object
|
||||||
|
}
|
||||||
|
vertex {
|
||||||
|
void materialVertex(inout MaterialVertexInputs material) {
|
||||||
|
material.worldPosition = materialParams.scale * getWorldFromModelMatrix() * getPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment {
|
||||||
|
void material(inout MaterialInputs material) {
|
||||||
|
prepareMaterial(material);
|
||||||
|
material.baseColor = float4(materialParams.color, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,6 +61,7 @@ void main(List<String> args) async {
|
|||||||
"${config.packageRoot.toFilePath()}/native/include/material/gizmo.c",
|
"${config.packageRoot.toFilePath()}/native/include/material/gizmo.c",
|
||||||
"${config.packageRoot.toFilePath()}/native/include/material/image.c",
|
"${config.packageRoot.toFilePath()}/native/include/material/image.c",
|
||||||
"${config.packageRoot.toFilePath()}/native/include/material/grid.c",
|
"${config.packageRoot.toFilePath()}/native/include/material/grid.c",
|
||||||
|
"${config.packageRoot.toFilePath()}/native/include/material/unlit.c",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var libs = [
|
var libs = [
|
||||||
|
|||||||
@@ -206,28 +206,33 @@ external void set_light_direction(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, ffi.Int)>()
|
EntityId Function(
|
||||||
|
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, ffi.Int, ffi.Bool)>()
|
||||||
external int load_glb(
|
external int load_glb(
|
||||||
ffi.Pointer<ffi.Void> sceneManager,
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
ffi.Pointer<ffi.Char> assetPath,
|
ffi.Pointer<ffi.Char> assetPath,
|
||||||
int numInstances,
|
int numInstances,
|
||||||
);
|
bool keepData,
|
||||||
|
|
||||||
@ffi.Native<
|
|
||||||
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>, ffi.Size)>()
|
|
||||||
external int load_glb_from_buffer(
|
|
||||||
ffi.Pointer<ffi.Void> sceneManager,
|
|
||||||
ffi.Pointer<ffi.Void> data,
|
|
||||||
int length,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
EntityId Function(
|
EntityId Function(
|
||||||
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)>()
|
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>, ffi.Size, ffi.Bool)>()
|
||||||
|
external int load_glb_from_buffer(
|
||||||
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
|
ffi.Pointer<ffi.Void> data,
|
||||||
|
int length,
|
||||||
|
bool keepData,
|
||||||
|
);
|
||||||
|
|
||||||
|
@ffi.Native<
|
||||||
|
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
|
||||||
|
ffi.Pointer<ffi.Char>, ffi.Bool)>()
|
||||||
external int load_gltf(
|
external int load_gltf(
|
||||||
ffi.Pointer<ffi.Void> sceneManager,
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
ffi.Pointer<ffi.Char> assetPath,
|
ffi.Pointer<ffi.Char> assetPath,
|
||||||
ffi.Pointer<ffi.Char> relativePath,
|
ffi.Pointer<ffi.Char> relativePath,
|
||||||
|
bool keepData,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>, EntityId)>()
|
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>, EntityId)>()
|
||||||
@@ -1111,6 +1116,12 @@ external void set_gizmo_visibility(
|
|||||||
bool visible,
|
bool visible,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>()
|
||||||
|
external void set_stencil_highlight(
|
||||||
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
|
int entity,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(
|
ffi.Void Function(
|
||||||
ffi.Pointer<ffi.Void>,
|
ffi.Pointer<ffi.Void>,
|
||||||
@@ -1347,12 +1358,17 @@ external void clear_lights_ffi(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, ffi.Int,
|
ffi.Void Function(
|
||||||
|
ffi.Pointer<ffi.Void>,
|
||||||
|
ffi.Pointer<ffi.Char>,
|
||||||
|
ffi.Int,
|
||||||
|
ffi.Bool,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>()
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>()
|
||||||
external void load_glb_ffi(
|
external void load_glb_ffi(
|
||||||
ffi.Pointer<ffi.Void> sceneManager,
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
ffi.Pointer<ffi.Char> assetPath,
|
ffi.Pointer<ffi.Char> assetPath,
|
||||||
int numInstances,
|
int numInstances,
|
||||||
|
bool keepData,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1362,12 +1378,14 @@ external void load_glb_ffi(
|
|||||||
ffi.Pointer<ffi.Void>,
|
ffi.Pointer<ffi.Void>,
|
||||||
ffi.Size,
|
ffi.Size,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
|
ffi.Bool,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>()
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>()
|
||||||
external void load_glb_from_buffer_ffi(
|
external void load_glb_from_buffer_ffi(
|
||||||
ffi.Pointer<ffi.Void> sceneManager,
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
ffi.Pointer<ffi.Void> data,
|
ffi.Pointer<ffi.Void> data,
|
||||||
int length,
|
int length,
|
||||||
int numInstances,
|
int numInstances,
|
||||||
|
bool keepData,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1376,11 +1394,13 @@ external void load_glb_from_buffer_ffi(
|
|||||||
ffi.Pointer<ffi.Void>,
|
ffi.Pointer<ffi.Void>,
|
||||||
ffi.Pointer<ffi.Char>,
|
ffi.Pointer<ffi.Char>,
|
||||||
ffi.Pointer<ffi.Char>,
|
ffi.Pointer<ffi.Char>,
|
||||||
|
ffi.Bool,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>()
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>()
|
||||||
external void load_gltf_ffi(
|
external void load_gltf_ffi(
|
||||||
ffi.Pointer<ffi.Void> sceneManager,
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
ffi.Pointer<ffi.Char> assetPath,
|
ffi.Pointer<ffi.Char> assetPath,
|
||||||
ffi.Pointer<ffi.Char> relativePath,
|
ffi.Pointer<ffi.Char> relativePath,
|
||||||
|
bool keepData,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -199,11 +199,14 @@ abstract class ThermionViewer {
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// Load the .glb asset at the given path and insert into the scene.
|
/// Load the .glb asset at the given path and insert into the scene.
|
||||||
|
/// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances].
|
||||||
|
/// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData].
|
||||||
|
/// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception.
|
||||||
///
|
///
|
||||||
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1});
|
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1, bool keepData = false});
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Create a new instance of [entity].
|
/// Create a new instance of [entity].
|
||||||
///
|
///
|
||||||
Future<ThermionEntity> createInstance(ThermionEntity entity);
|
Future<ThermionEntity> createInstance(ThermionEntity entity);
|
||||||
|
|
||||||
@@ -221,9 +224,11 @@ abstract class ThermionViewer {
|
|||||||
/// Load the .gltf asset at the given path and insert into the scene.
|
/// Load the .gltf asset at the given path and insert into the scene.
|
||||||
/// [relativeResourcePath] is the folder path where the glTF resources are stored;
|
/// [relativeResourcePath] is the folder path where the glTF resources are stored;
|
||||||
/// this is usually the parent directory of the .gltf file itself.
|
/// this is usually the parent directory of the .gltf file itself.
|
||||||
|
///
|
||||||
|
/// See [loadGlb] for an explanation of [keepData].
|
||||||
///
|
///
|
||||||
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
|
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
|
||||||
{bool force = false});
|
{bool keepData = false});
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
||||||
@@ -577,7 +582,8 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
/// Set the world space position for [lightEntity] to the given coordinates.
|
/// Set the world space position for [lightEntity] to the given coordinates.
|
||||||
///
|
///
|
||||||
Future setLightPosition(ThermionEntity lightEntity, double x, double y, double z);
|
Future setLightPosition(
|
||||||
|
ThermionEntity lightEntity, double x, double y, double z);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Sets the world space direction for [lightEntity] to the given vector.
|
/// Sets the world space direction for [lightEntity] to the given vector.
|
||||||
@@ -810,4 +816,9 @@ abstract class ThermionViewer {
|
|||||||
/// Show/hide the translation gizmo.
|
/// Show/hide the translation gizmo.
|
||||||
///
|
///
|
||||||
Future setGizmoVisibility(bool visible);
|
Future setGizmoVisibility(bool visible);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Outlines [entity].
|
||||||
|
///
|
||||||
|
Future setStencilHighlight(ThermionEntity entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,8 +403,7 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future<ThermionEntity> createInstance(ThermionEntity entity) async {
|
Future<ThermionEntity> createInstance(ThermionEntity entity) async {
|
||||||
var created = await withIntCallback(
|
var created = create_instance(_sceneManager!, entity);
|
||||||
(callback) => create_instance(_sceneManager!, entity));
|
|
||||||
if (created == _FILAMENT_ASSET_ERROR) {
|
if (created == _FILAMENT_ASSET_ERROR) {
|
||||||
throw Exception("Failed to create instance");
|
throw Exception("Failed to create instance");
|
||||||
}
|
}
|
||||||
@@ -440,13 +439,13 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future<ThermionEntity> loadGlb(String path,
|
Future<ThermionEntity> loadGlb(String path,
|
||||||
{bool unlit = false, int numInstances = 1}) async {
|
{bool unlit = false, int numInstances = 1, bool keepData = false}) async {
|
||||||
if (unlit) {
|
if (unlit) {
|
||||||
throw Exception("Not yet implemented");
|
throw Exception("Not yet implemented");
|
||||||
}
|
}
|
||||||
final pathPtr = path.toNativeUtf8(allocator: allocator).cast<Char>();
|
final pathPtr = path.toNativeUtf8(allocator: allocator).cast<Char>();
|
||||||
var entity = await withIntCallback((callback) =>
|
var entity = await withIntCallback((callback) =>
|
||||||
load_glb_ffi(_sceneManager!, pathPtr, numInstances, callback));
|
load_glb_ffi(_sceneManager!, pathPtr, numInstances, keepData, callback));
|
||||||
allocator.free(pathPtr);
|
allocator.free(pathPtr);
|
||||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||||
throw Exception("An error occurred loading the asset at $path");
|
throw Exception("An error occurred loading the asset at $path");
|
||||||
@@ -461,12 +460,12 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
|
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
|
||||||
{bool force = false}) async {
|
{bool keepData = false}) async {
|
||||||
final pathPtr = path.toNativeUtf8(allocator: allocator).cast<Char>();
|
final pathPtr = path.toNativeUtf8(allocator: allocator).cast<Char>();
|
||||||
final relativeResourcePathPtr =
|
final relativeResourcePathPtr =
|
||||||
relativeResourcePath.toNativeUtf8(allocator: allocator).cast<Char>();
|
relativeResourcePath.toNativeUtf8(allocator: allocator).cast<Char>();
|
||||||
var entity = await withIntCallback((callback) => load_gltf_ffi(
|
var entity = await withIntCallback((callback) => load_gltf_ffi(
|
||||||
_sceneManager!, pathPtr, relativeResourcePathPtr, callback));
|
_sceneManager!, pathPtr, relativeResourcePathPtr, keepData, callback));
|
||||||
allocator.free(pathPtr);
|
allocator.free(pathPtr);
|
||||||
allocator.free(relativeResourcePathPtr);
|
allocator.free(relativeResourcePathPtr);
|
||||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||||
@@ -1300,9 +1299,11 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) async {
|
Future setLightDirection(
|
||||||
|
ThermionEntity lightEntity, Vector3 direction) async {
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
set_light_direction(_viewer!, lightEntity, direction.x, direction.y, direction.z);
|
set_light_direction(
|
||||||
|
_viewer!, lightEntity, direction.x, direction.y, direction.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -1828,4 +1829,8 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
Future setGizmoVisibility(bool visible) async {
|
Future setGizmoVisibility(bool visible) async {
|
||||||
set_gizmo_visibility(_sceneManager!, visible);
|
set_gizmo_visibility(_sceneManager!, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future setStencilHighlight(ThermionEntity entity) async {
|
||||||
|
set_stencil_highlight(_sceneManager!, entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ namespace thermion_filament
|
|||||||
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
|
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
|
||||||
void* _context = nullptr;
|
void* _context = nullptr;
|
||||||
Scene *_scene = nullptr;
|
Scene *_scene = nullptr;
|
||||||
|
Scene *_highlightScene = nullptr;
|
||||||
View *_view = nullptr;
|
View *_view = nullptr;
|
||||||
|
|
||||||
Engine *_engine = nullptr;
|
Engine *_engine = nullptr;
|
||||||
|
|||||||
@@ -46,10 +46,11 @@ namespace thermion_filament
|
|||||||
const ResourceLoaderWrapperImpl *const loader,
|
const ResourceLoaderWrapperImpl *const loader,
|
||||||
Engine *engine,
|
Engine *engine,
|
||||||
Scene *scene,
|
Scene *scene,
|
||||||
|
Scene *highlightScene,
|
||||||
const char *uberArchivePath);
|
const char *uberArchivePath);
|
||||||
~SceneManager();
|
~SceneManager();
|
||||||
|
|
||||||
EntityId loadGltf(const char *uri, const char *relativeResourcePath);
|
EntityId loadGltf(const char *uri, const char *relativeResourcePath, bool keepData = false);
|
||||||
|
|
||||||
////
|
////
|
||||||
/// @brief Load the GLB from the specified path, optionally creating multiple instances.
|
/// @brief Load the GLB from the specified path, optionally creating multiple instances.
|
||||||
@@ -57,8 +58,8 @@ namespace thermion_filament
|
|||||||
/// @param numInstances the number of instances to create.
|
/// @param numInstances the number of instances to create.
|
||||||
/// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset.
|
/// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset.
|
||||||
///
|
///
|
||||||
EntityId loadGlb(const char *uri, int numInstances);
|
EntityId loadGlb(const char *uri, int numInstances, bool keepData);
|
||||||
EntityId loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances = 1);
|
EntityId loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances = 1, bool keepData = false);
|
||||||
EntityId createInstance(EntityId entityId);
|
EntityId createInstance(EntityId entityId);
|
||||||
|
|
||||||
void remove(EntityId entity);
|
void remove(EntityId entity);
|
||||||
@@ -157,6 +158,8 @@ namespace thermion_filament
|
|||||||
bool addAnimationComponent(EntityId entity);
|
bool addAnimationComponent(EntityId entity);
|
||||||
void removeAnimationComponent(EntityId entity);
|
void removeAnimationComponent(EntityId entity);
|
||||||
|
|
||||||
|
void setStencilHighlight(EntityId entity);
|
||||||
|
|
||||||
/// @brief returns the number of instances of the FilamentAsset represented by the given entity.
|
/// @brief returns the number of instances of the FilamentAsset represented by the given entity.
|
||||||
/// @param entityId
|
/// @param entityId
|
||||||
/// @return the number of instances
|
/// @return the number of instances
|
||||||
@@ -194,9 +197,11 @@ namespace thermion_filament
|
|||||||
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
|
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
|
||||||
Engine *_engine = nullptr;
|
Engine *_engine = nullptr;
|
||||||
Scene *_scene = nullptr;
|
Scene *_scene = nullptr;
|
||||||
|
Scene *_highlightScene = nullptr;
|
||||||
View* _view = nullptr;
|
View* _view = nullptr;
|
||||||
|
|
||||||
gltfio::MaterialProvider *_ubershaderProvider = nullptr;
|
gltfio::MaterialProvider *_ubershaderProvider = nullptr;
|
||||||
|
gltfio::MaterialProvider *_unlitMaterialProvider = nullptr;
|
||||||
gltfio::ResourceLoader *_gltfResourceLoader = nullptr;
|
gltfio::ResourceLoader *_gltfResourceLoader = nullptr;
|
||||||
gltfio::TextureProvider *_stbDecoder = nullptr;
|
gltfio::TextureProvider *_stbDecoder = nullptr;
|
||||||
gltfio::TextureProvider *_ktxDecoder = nullptr;
|
gltfio::TextureProvider *_ktxDecoder = nullptr;
|
||||||
|
|||||||
@@ -95,9 +95,9 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE void clear_lights(const void *const viewer);
|
EMSCRIPTEN_KEEPALIVE void clear_lights(const void *const viewer);
|
||||||
EMSCRIPTEN_KEEPALIVE void set_light_position(const void *const viewer, EntityId light, float x, float y, float z);
|
EMSCRIPTEN_KEEPALIVE void set_light_position(const void *const viewer, EntityId light, float x, float y, float z);
|
||||||
EMSCRIPTEN_KEEPALIVE void set_light_direction(const void *const viewer, EntityId light, float x, float y, float z);
|
EMSCRIPTEN_KEEPALIVE void set_light_direction(const void *const viewer, EntityId light, float x, float y, float z);
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances);
|
EMSCRIPTEN_KEEPALIVE EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances, bool keepData);
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(void *sceneManager, const void *const data, size_t length);
|
EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(void *sceneManager, const void *const data, size_t length, bool keepData);
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath);
|
EMSCRIPTEN_KEEPALIVE EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath, bool keepData);
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId create_instance(void *sceneManager, EntityId id);
|
EMSCRIPTEN_KEEPALIVE EntityId create_instance(void *sceneManager, EntityId id);
|
||||||
EMSCRIPTEN_KEEPALIVE int get_instance_count(void *sceneManager, EntityId entityId);
|
EMSCRIPTEN_KEEPALIVE int get_instance_count(void *sceneManager, EntityId entityId);
|
||||||
EMSCRIPTEN_KEEPALIVE void get_instances(void *sceneManager, EntityId entityId, EntityId *out);
|
EMSCRIPTEN_KEEPALIVE void get_instances(void *sceneManager, EntityId entityId, EntityId *out);
|
||||||
@@ -259,6 +259,7 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE void set_layer_enabled(void *const sceneManager, int layer, bool enabled);
|
EMSCRIPTEN_KEEPALIVE void set_layer_enabled(void *const sceneManager, int layer, bool enabled);
|
||||||
EMSCRIPTEN_KEEPALIVE void pick_gizmo(void *const sceneManager, int x, int y, void (*callback)(EntityId entityId, int x, int y));
|
EMSCRIPTEN_KEEPALIVE void pick_gizmo(void *const sceneManager, int x, int y, void (*callback)(EntityId entityId, int x, int y));
|
||||||
EMSCRIPTEN_KEEPALIVE void set_gizmo_visibility(void *const sceneManager, bool visible);
|
EMSCRIPTEN_KEEPALIVE void set_gizmo_visibility(void *const sceneManager, bool visible);
|
||||||
|
EMSCRIPTEN_KEEPALIVE void set_stencil_highlight(void *const sceneManager, EntityId entity);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ extern "C"
|
|||||||
void (*callback)(EntityId));
|
void (*callback)(EntityId));
|
||||||
EMSCRIPTEN_KEEPALIVE void remove_light_ffi(void *const viewer, EntityId entityId);
|
EMSCRIPTEN_KEEPALIVE void remove_light_ffi(void *const viewer, EntityId entityId);
|
||||||
EMSCRIPTEN_KEEPALIVE void clear_lights_ffi(void *const viewer);
|
EMSCRIPTEN_KEEPALIVE void clear_lights_ffi(void *const viewer);
|
||||||
EMSCRIPTEN_KEEPALIVE void load_glb_ffi(void *const sceneManager, const char *assetPath, int numInstances, void (*callback)(EntityId));
|
EMSCRIPTEN_KEEPALIVE void load_glb_ffi(void *const sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(EntityId));
|
||||||
EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_ffi(void *const sceneManager, const void *const data, size_t length, int numInstances, void (*callback)(EntityId));
|
EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_ffi(void *const sceneManager, const void *const data, size_t length, int numInstances, bool keepData, void (*callback)(EntityId));
|
||||||
EMSCRIPTEN_KEEPALIVE void load_gltf_ffi(void *const sceneManager, const char *assetPath, const char *relativePath, void (*callback)(EntityId));
|
EMSCRIPTEN_KEEPALIVE void load_gltf_ffi(void *const sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(EntityId));
|
||||||
EMSCRIPTEN_KEEPALIVE void create_instance_ffi(void *const sceneManager, EntityId entityId, void (*callback)(EntityId));
|
EMSCRIPTEN_KEEPALIVE void create_instance_ffi(void *const sceneManager, EntityId entityId, void (*callback)(EntityId));
|
||||||
EMSCRIPTEN_KEEPALIVE void remove_entity_ffi(void *const viewer, EntityId asset, void (*callback)());
|
EMSCRIPTEN_KEEPALIVE void remove_entity_ffi(void *const viewer, EntityId asset, void (*callback)());
|
||||||
EMSCRIPTEN_KEEPALIVE void clear_entities_ffi(void *const viewer, void (*callback)());
|
EMSCRIPTEN_KEEPALIVE void clear_entities_ffi(void *const viewer, void (*callback)());
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#ifndef UNLIT_MATERIAL_PROVIDER_HPP
|
||||||
|
#define UNLIT_MATERIAL_PROVIDER_HPP
|
||||||
|
|
||||||
|
#include <filament/gltfio/MaterialProvider.h>
|
||||||
|
#include <filament/Material.h>
|
||||||
|
#include <filament/MaterialInstance.h>
|
||||||
|
#include <filament/Texture.h>
|
||||||
|
#include <filament/TextureSampler.h>
|
||||||
|
#include <math/mat3.h>
|
||||||
|
#include <math/vec3.h>
|
||||||
|
#include <math/vec4.h>
|
||||||
|
|
||||||
|
namespace thermion_filament {
|
||||||
|
|
||||||
|
class UnlitMaterialProvider : public filament::gltfio::MaterialProvider {
|
||||||
|
private:
|
||||||
|
filament::Material* mUnlitMaterial;
|
||||||
|
const filament::Material* mMaterials[1];
|
||||||
|
filament::Engine* mEngine;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UnlitMaterialProvider(filament::Engine* engine, const void* const data, const size_t size) : mEngine(engine) {
|
||||||
|
mUnlitMaterial = filament::Material::Builder()
|
||||||
|
.package(data, size)
|
||||||
|
.build(*engine);
|
||||||
|
mMaterials[0] = mUnlitMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
~UnlitMaterialProvider() {
|
||||||
|
mEngine->destroy(mUnlitMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
filament::MaterialInstance* createMaterialInstance(filament::gltfio::MaterialKey* config,
|
||||||
|
filament::gltfio::UvMap* uvmap,
|
||||||
|
const char* label = "unlit",
|
||||||
|
const char* extras = nullptr) override {
|
||||||
|
auto instance = mUnlitMaterial->createInstance();
|
||||||
|
|
||||||
|
// Set default parameters
|
||||||
|
instance->setParameter("color", filament::math::float3{1.0f, 1.0f, 1.0f});
|
||||||
|
instance->setParameter("scale", 1.0f);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
filament::Material* getMaterial(filament::gltfio::MaterialKey* config,
|
||||||
|
filament::gltfio::UvMap* uvmap,
|
||||||
|
const char* label = "unlit") override {
|
||||||
|
return mUnlitMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filament::Material* const* getMaterials() const noexcept override {
|
||||||
|
return mMaterials;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getMaterialsCount() const noexcept override {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyMaterials() override {
|
||||||
|
// Materials are destroyed in the destructor
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needsDummyData(filament::VertexAttribute attrib) const noexcept override {
|
||||||
|
// For unlit material, we don't need dummy data for any attribute
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace thermion_filament
|
||||||
|
|
||||||
|
#endif // UNLIT_MATERIAL_PROVIDER_HPP
|
||||||
12
thermion_dart/native/include/material/unlit.S
Normal file
12
thermion_dart/native/include/material/unlit.S
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.global UNLIT_UNLIT_OFFSET;
|
||||||
|
.global UNLIT_UNLIT_SIZE;
|
||||||
|
|
||||||
|
.global UNLIT_PACKAGE
|
||||||
|
.section .rodata
|
||||||
|
UNLIT_PACKAGE:
|
||||||
|
.incbin "unlit.bin"
|
||||||
|
UNLIT_UNLIT_OFFSET:
|
||||||
|
.int 0
|
||||||
|
UNLIT_UNLIT_SIZE:
|
||||||
|
.int 26426
|
||||||
|
|
||||||
12
thermion_dart/native/include/material/unlit.apple.S
Normal file
12
thermion_dart/native/include/material/unlit.apple.S
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.global _UNLIT_UNLIT_OFFSET;
|
||||||
|
.global _UNLIT_UNLIT_SIZE;
|
||||||
|
|
||||||
|
.global _UNLIT_PACKAGE
|
||||||
|
.section __TEXT,__const
|
||||||
|
_UNLIT_PACKAGE:
|
||||||
|
.incbin "unlit.bin"
|
||||||
|
_UNLIT_UNLIT_OFFSET:
|
||||||
|
.int 0
|
||||||
|
_UNLIT_UNLIT_SIZE:
|
||||||
|
.int 26426
|
||||||
|
|
||||||
BIN
thermion_dart/native/include/material/unlit.bin
Normal file
BIN
thermion_dart/native/include/material/unlit.bin
Normal file
Binary file not shown.
1332
thermion_dart/native/include/material/unlit.c
Normal file
1332
thermion_dart/native/include/material/unlit.c
Normal file
File diff suppressed because it is too large
Load Diff
13
thermion_dart/native/include/material/unlit.h
Normal file
13
thermion_dart/native/include/material/unlit.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef UNLIT_H_
|
||||||
|
#define UNLIT_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern const uint8_t UNLIT_PACKAGE[];
|
||||||
|
extern int UNLIT_UNLIT_OFFSET;
|
||||||
|
extern int UNLIT_UNLIT_SIZE;
|
||||||
|
}
|
||||||
|
#define UNLIT_UNLIT_DATA (UNLIT_PACKAGE + UNLIT_UNLIT_OFFSET)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include <filament/Camera.h>
|
#include <filament/Camera.h>
|
||||||
|
#include <filament/SwapChain.h>
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/platforms/OpenGLPlatform.h>
|
#include <backend/platforms/OpenGLPlatform.h>
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
@@ -158,6 +158,7 @@ namespace thermion_filament
|
|||||||
setFrameInterval(_frameInterval);
|
setFrameInterval(_frameInterval);
|
||||||
|
|
||||||
_scene = _engine->createScene();
|
_scene = _engine->createScene();
|
||||||
|
_highlightScene = _engine->createScene();
|
||||||
|
|
||||||
utils::Entity camera = EntityManager::get().create();
|
utils::Entity camera = EntityManager::get().create();
|
||||||
|
|
||||||
@@ -195,8 +196,6 @@ namespace thermion_filament
|
|||||||
const float shutterSpeed = _mainCamera->getShutterSpeed();
|
const float shutterSpeed = _mainCamera->getShutterSpeed();
|
||||||
const float sens = _mainCamera->getSensitivity();
|
const float sens = _mainCamera->getSensitivity();
|
||||||
|
|
||||||
Log("Camera aperture %f shutter %f sensitivity %f", aperture, shutterSpeed, sens);
|
|
||||||
|
|
||||||
EntityManager &em = EntityManager::get();
|
EntityManager &em = EntityManager::get();
|
||||||
|
|
||||||
_sceneManager = new SceneManager(
|
_sceneManager = new SceneManager(
|
||||||
@@ -204,6 +203,7 @@ namespace thermion_filament
|
|||||||
_resourceLoaderWrapper,
|
_resourceLoaderWrapper,
|
||||||
_engine,
|
_engine,
|
||||||
_scene,
|
_scene,
|
||||||
|
_highlightScene,
|
||||||
uberArchivePath);
|
uberArchivePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +780,7 @@ namespace thermion_filament
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log("Created headless swapchain.");
|
Log("Created headless swapchain.");
|
||||||
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -800,7 +800,7 @@ namespace thermion_filament
|
|||||||
.width(width)
|
.width(width)
|
||||||
.height(height)
|
.height(height)
|
||||||
.levels(1)
|
.levels(1)
|
||||||
.usage(filament::Texture::Usage::DEPTH_ATTACHMENT)
|
.usage(filament::Texture::Usage::DEPTH_ATTACHMENT | filament::Texture::Usage::SAMPLEABLE)
|
||||||
.format(filament::Texture::InternalFormat::DEPTH32F)
|
.format(filament::Texture::InternalFormat::DEPTH32F)
|
||||||
.build(*_engine);
|
.build(*_engine);
|
||||||
_rt = filament::RenderTarget::Builder()
|
_rt = filament::RenderTarget::Builder()
|
||||||
@@ -1180,24 +1180,12 @@ namespace thermion_filament
|
|||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_view)
|
|
||||||
{
|
|
||||||
Log("No view");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (!_swapChain)
|
|
||||||
{
|
|
||||||
Log("No swapchain");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto now = std::chrono::high_resolution_clock::now();
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
auto secsSinceLastFpsCheck = float(std::chrono::duration_cast<std::chrono::seconds>(now - _fpsCounterStartTime).count());
|
auto secsSinceLastFpsCheck = float(std::chrono::duration_cast<std::chrono::seconds>(now - _fpsCounterStartTime).count());
|
||||||
|
|
||||||
if (secsSinceLastFpsCheck >= 1)
|
if (secsSinceLastFpsCheck >= 1)
|
||||||
{
|
{
|
||||||
auto fps = _frameCount / secsSinceLastFpsCheck;
|
auto fps = _frameCount / secsSinceLastFpsCheck;
|
||||||
Log("%ffps (%d skipped)", fps, _skippedFrames);
|
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
_skippedFrames = 0;
|
_skippedFrames = 0;
|
||||||
_fpsCounterStartTime = now;
|
_fpsCounterStartTime = now;
|
||||||
@@ -1231,6 +1219,12 @@ namespace thermion_filament
|
|||||||
|
|
||||||
_renderer->render(_view);
|
_renderer->render(_view);
|
||||||
|
|
||||||
|
_view->setScene(_highlightScene);
|
||||||
|
|
||||||
|
_renderer->render(_view);
|
||||||
|
|
||||||
|
_view->setScene(_scene);
|
||||||
|
|
||||||
_frameCount++;
|
_frameCount++;
|
||||||
|
|
||||||
if (_recording)
|
if (_recording)
|
||||||
@@ -1295,6 +1289,12 @@ namespace thermion_filament
|
|||||||
|
|
||||||
_renderer->render(_view);
|
_renderer->render(_view);
|
||||||
|
|
||||||
|
_view->setScene(_highlightScene);
|
||||||
|
|
||||||
|
_renderer->render(_view);
|
||||||
|
|
||||||
|
_view->setScene(_scene);
|
||||||
|
|
||||||
if (_rt)
|
if (_rt)
|
||||||
{
|
{
|
||||||
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
#include <imageio/ImageDecoder.h>
|
#include <imageio/ImageDecoder.h>
|
||||||
|
|
||||||
#include "material/FileMaterialProvider.hpp"
|
#include "material/FileMaterialProvider.hpp"
|
||||||
|
#include "material/UnlitMaterialProvider.hpp"
|
||||||
|
#include "material/unlit.h"
|
||||||
|
|
||||||
#include "StreamBufferAdapter.hpp"
|
#include "StreamBufferAdapter.hpp"
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
#include "SceneManager.hpp"
|
#include "SceneManager.hpp"
|
||||||
@@ -49,11 +52,13 @@ namespace thermion_filament
|
|||||||
const ResourceLoaderWrapperImpl *const resourceLoaderWrapper,
|
const ResourceLoaderWrapperImpl *const resourceLoaderWrapper,
|
||||||
Engine *engine,
|
Engine *engine,
|
||||||
Scene *scene,
|
Scene *scene,
|
||||||
|
Scene *highlightScene,
|
||||||
const char *uberArchivePath)
|
const char *uberArchivePath)
|
||||||
: _view(view),
|
: _view(view),
|
||||||
_resourceLoaderWrapper(resourceLoaderWrapper),
|
_resourceLoaderWrapper(resourceLoaderWrapper),
|
||||||
_engine(engine),
|
_engine(engine),
|
||||||
_scene(scene)
|
_scene(scene),
|
||||||
|
_highlightScene(highlightScene)
|
||||||
{
|
{
|
||||||
|
|
||||||
_stbDecoder = createStbProvider(_engine);
|
_stbDecoder = createStbProvider(_engine);
|
||||||
@@ -77,6 +82,8 @@ namespace thermion_filament
|
|||||||
_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
|
_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_unlitMaterialProvider = new UnlitMaterialProvider(_engine, UNLIT_PACKAGE, UNLIT_UNLIT_SIZE);
|
||||||
|
|
||||||
utils::EntityManager &em = utils::EntityManager::get();
|
utils::EntityManager &em = utils::EntityManager::get();
|
||||||
|
|
||||||
_ncm = new NameComponentManager(em);
|
_ncm = new NameComponentManager(em);
|
||||||
@@ -152,7 +159,8 @@ namespace thermion_filament
|
|||||||
}
|
}
|
||||||
|
|
||||||
EntityId SceneManager::loadGltf(const char *uri,
|
EntityId SceneManager::loadGltf(const char *uri,
|
||||||
const char *relativeResourcePath)
|
const char *relativeResourcePath,
|
||||||
|
bool keepData)
|
||||||
{
|
{
|
||||||
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
|
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
|
||||||
|
|
||||||
@@ -172,7 +180,6 @@ namespace thermion_filament
|
|||||||
for (size_t i = 0; i < resourceUriCount; i++)
|
for (size_t i = 0; i < resourceUriCount; i++)
|
||||||
{
|
{
|
||||||
std::string uri = std::string(relativeResourcePath) + std::string("/") + std::string(resourceUris[i]);
|
std::string uri = std::string(relativeResourcePath) + std::string("/") + std::string(resourceUris[i]);
|
||||||
Log("Loading resource URI from relative path %s", resourceUris[i], uri.c_str());
|
|
||||||
ResourceBuffer buf = _resourceLoaderWrapper->load(uri.c_str());
|
ResourceBuffer buf = _resourceLoaderWrapper->load(uri.c_str());
|
||||||
|
|
||||||
resourceBuffers.push_back(buf);
|
resourceBuffers.push_back(buf);
|
||||||
@@ -215,8 +222,10 @@ namespace thermion_filament
|
|||||||
FilamentInstance *inst = asset->getInstance();
|
FilamentInstance *inst = asset->getInstance();
|
||||||
inst->getAnimator()->updateBoneMatrices();
|
inst->getAnimator()->updateBoneMatrices();
|
||||||
inst->recomputeBoundingBoxes();
|
inst->recomputeBoundingBoxes();
|
||||||
|
|
||||||
asset->releaseSourceData();
|
if(!keepData) {
|
||||||
|
asset->releaseSourceData();
|
||||||
|
}
|
||||||
|
|
||||||
EntityId eid = Entity::smuggle(asset->getRoot());
|
EntityId eid = Entity::smuggle(asset->getRoot());
|
||||||
|
|
||||||
@@ -233,11 +242,9 @@ namespace thermion_filament
|
|||||||
return eid;
|
return eid;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityId SceneManager::loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances)
|
EntityId SceneManager::loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances, bool keepData)
|
||||||
{
|
{
|
||||||
|
|
||||||
Log("Loading GLB from buffer of length %d", length);
|
|
||||||
|
|
||||||
FilamentAsset *asset = nullptr;
|
FilamentAsset *asset = nullptr;
|
||||||
if (numInstances > 1)
|
if (numInstances > 1)
|
||||||
{
|
{
|
||||||
@@ -290,7 +297,9 @@ namespace thermion_filament
|
|||||||
_instances.emplace(instanceEntityId, inst);
|
_instances.emplace(instanceEntityId, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
asset->releaseSourceData();
|
if(!keepData) {
|
||||||
|
asset->releaseSourceData();
|
||||||
|
}
|
||||||
|
|
||||||
EntityId eid = Entity::smuggle(asset->getRoot());
|
EntityId eid = Entity::smuggle(asset->getRoot());
|
||||||
_assets.emplace(eid, asset);
|
_assets.emplace(eid, asset);
|
||||||
@@ -352,18 +361,26 @@ namespace thermion_filament
|
|||||||
if (pos == _assets.end())
|
if (pos == _assets.end())
|
||||||
{
|
{
|
||||||
Log("Couldn't find asset under specified entity id.");
|
Log("Couldn't find asset under specified entity id.");
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto asset = pos->second;
|
const auto asset = pos->second;
|
||||||
auto instance = _assetLoader->createInstance(asset);
|
auto instance = _assetLoader->createInstance(asset);
|
||||||
|
|
||||||
return Entity::smuggle(instance->getRoot());
|
if(!instance) {
|
||||||
|
Log("Failed to create instance");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto root = instance->getRoot();
|
||||||
|
_scene->addEntities(instance->getEntities(), instance->getEntityCount());
|
||||||
|
|
||||||
|
return Entity::smuggle(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityId SceneManager::loadGlb(const char *uri, int numInstances)
|
EntityId SceneManager::loadGlb(const char *uri, int numInstances, bool keepData)
|
||||||
{
|
{
|
||||||
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
|
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
|
||||||
auto entity = loadGlbFromBuffer((const uint8_t *)rbuf.data, rbuf.size, numInstances);
|
auto entity = loadGlbFromBuffer((const uint8_t *)rbuf.data, rbuf.size, numInstances, keepData);
|
||||||
_resourceLoaderWrapper->free(rbuf);
|
_resourceLoaderWrapper->free(rbuf);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
@@ -2272,8 +2289,11 @@ void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float v
|
|||||||
|
|
||||||
void SceneManager::setLayerEnabled(int layer, bool enabled)
|
void SceneManager::setLayerEnabled(int layer, bool enabled)
|
||||||
{
|
{
|
||||||
Log("Setting layer %d to %d", layer, enabled);
|
|
||||||
_view->setLayerEnabled(layer, enabled);
|
_view->setLayerEnabled(layer, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace thermion_filament
|
} // namespace thermion_filament
|
||||||
|
|||||||
@@ -166,14 +166,14 @@ extern "C"
|
|||||||
((FilamentViewer *)viewer)->clearLights();
|
((FilamentViewer *)viewer)->clearLights();
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances)
|
EMSCRIPTEN_KEEPALIVE EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances, bool keepData)
|
||||||
{
|
{
|
||||||
return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances);
|
return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances, keepData);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(void *sceneManager, const void *const data, size_t length)
|
EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(void *sceneManager, const void *const data, size_t length, bool keepData)
|
||||||
{
|
{
|
||||||
return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t *)data, length);
|
return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t *)data, length, keepData);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId create_instance(void *sceneManager, EntityId entityId)
|
EMSCRIPTEN_KEEPALIVE EntityId create_instance(void *sceneManager, EntityId entityId)
|
||||||
@@ -191,9 +191,9 @@ extern "C"
|
|||||||
return ((SceneManager *)sceneManager)->getInstances(entityId, out);
|
return ((SceneManager *)sceneManager)->getInstances(entityId, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath)
|
EMSCRIPTEN_KEEPALIVE EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath, bool keepData)
|
||||||
{
|
{
|
||||||
return ((SceneManager *)sceneManager)->loadGltf(assetPath, relativePath);
|
return ((SceneManager *)sceneManager)->loadGltf(assetPath, relativePath, keepData);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void set_main_camera(const void *const viewer)
|
EMSCRIPTEN_KEEPALIVE void set_main_camera(const void *const viewer)
|
||||||
@@ -892,7 +892,10 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE void set_gizmo_visibility(void *const sceneManager, bool visible) {
|
EMSCRIPTEN_KEEPALIVE void set_gizmo_visibility(void *const sceneManager, bool visible) {
|
||||||
((SceneManager*)sceneManager)->gizmo->setVisibility(visible);
|
((SceneManager*)sceneManager)->gizmo->setVisibility(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE void set_stencil_highlight(void *const sceneManager, EntityId entityId) {
|
||||||
|
((SceneManager*)sceneManager)->setStencilHighlight(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -392,11 +392,12 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE void load_gltf_ffi(void *const sceneManager,
|
EMSCRIPTEN_KEEPALIVE void load_gltf_ffi(void *const sceneManager,
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *relativeResourcePath,
|
const char *relativeResourcePath,
|
||||||
|
bool keepData,
|
||||||
void (*callback)(EntityId))
|
void (*callback)(EntityId))
|
||||||
{
|
{
|
||||||
std::packaged_task<EntityId()> lambda([=]() mutable
|
std::packaged_task<EntityId()> lambda([=]() mutable
|
||||||
{
|
{
|
||||||
auto entity = load_gltf(sceneManager, path, relativeResourcePath);
|
auto entity = load_gltf(sceneManager, path, relativeResourcePath, keepData);
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
MAIN_THREAD_EM_ASM({
|
MAIN_THREAD_EM_ASM({
|
||||||
moduleArg.dartFilamentResolveCallback($0, $1);
|
moduleArg.dartFilamentResolveCallback($0, $1);
|
||||||
@@ -409,12 +410,15 @@ extern "C"
|
|||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void load_glb_ffi(void *const sceneManager,
|
EMSCRIPTEN_KEEPALIVE void load_glb_ffi(void *const sceneManager,
|
||||||
const char *path, int numInstances, void (*callback)(EntityId))
|
const char *path,
|
||||||
|
int numInstances,
|
||||||
|
bool keepData,
|
||||||
|
void (*callback)(EntityId))
|
||||||
{
|
{
|
||||||
std::packaged_task<EntityId()> lambda(
|
std::packaged_task<EntityId()> lambda(
|
||||||
[=]() mutable
|
[=]() mutable
|
||||||
{
|
{
|
||||||
auto entity = load_glb(sceneManager, path, numInstances);
|
auto entity = load_glb(sceneManager, path, numInstances, keepData);
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
MAIN_THREAD_EM_ASM({
|
MAIN_THREAD_EM_ASM({
|
||||||
moduleArg.dartFilamentResolveCallback($0, $1);
|
moduleArg.dartFilamentResolveCallback($0, $1);
|
||||||
@@ -428,12 +432,16 @@ extern "C"
|
|||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_ffi(void *const sceneManager,
|
EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_ffi(void *const sceneManager,
|
||||||
const void *const data, size_t length, int numInstances, void (*callback)(EntityId))
|
const void *const data,
|
||||||
|
size_t length,
|
||||||
|
int numInstances,
|
||||||
|
bool keepData,
|
||||||
|
void (*callback)(EntityId))
|
||||||
{
|
{
|
||||||
std::packaged_task<EntityId()> lambda(
|
std::packaged_task<EntityId()> lambda(
|
||||||
[=]() mutable
|
[=]() mutable
|
||||||
{
|
{
|
||||||
auto entity = load_glb_from_buffer(sceneManager, data, length);
|
auto entity = load_glb_from_buffer(sceneManager, data, length, keepData);
|
||||||
callback(entity);
|
callback(entity);
|
||||||
return entity;
|
return entity;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/swift/swift_bindings.g.dart';
|
import 'package:thermion_dart/thermion_dart/swift/swift_bindings.g.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart';
|
import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/utils/dart_resources.dart';
|
import 'package:thermion_dart/thermion_dart/utils/dart_resources.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart';
|
import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
/// Test files are run in a variety of ways, find this package root in all.
|
/// Test files are run in a variety of ways, find this package root in all.
|
||||||
///
|
///
|
||||||
@@ -46,13 +52,69 @@ extension on Uri {
|
|||||||
}
|
}
|
||||||
|
|
||||||
late String testDir;
|
late String testDir;
|
||||||
|
|
||||||
|
Future<void> pixelBufferToBmp(
|
||||||
|
Uint8List pixelBuffer, int width, int height, String outputPath) async {
|
||||||
|
// BMP file header (14 bytes)
|
||||||
|
final fileHeader = ByteData(14);
|
||||||
|
fileHeader.setUint16(0, 0x4D42, Endian.little); // 'BM'
|
||||||
|
final fileSize = 54 + width * height * 3; // 54 bytes header + RGB data
|
||||||
|
fileHeader.setUint32(2, fileSize, Endian.little);
|
||||||
|
fileHeader.setUint32(10, 54, Endian.little); // Offset to pixel data
|
||||||
|
|
||||||
|
// BMP info header (40 bytes)
|
||||||
|
final infoHeader = ByteData(40);
|
||||||
|
infoHeader.setUint32(0, 40, Endian.little); // Info header size
|
||||||
|
infoHeader.setInt32(4, width, Endian.little);
|
||||||
|
infoHeader.setInt32(8, -height, Endian.little); // Negative for top-down
|
||||||
|
infoHeader.setUint16(12, 1, Endian.little); // Number of color planes
|
||||||
|
infoHeader.setUint16(14, 24, Endian.little); // Bits per pixel (RGB)
|
||||||
|
infoHeader.setUint32(16, 0, Endian.little); // No compression
|
||||||
|
infoHeader.setUint32(20, width * height * 3, Endian.little); // Image size
|
||||||
|
infoHeader.setInt32(24, 2835, Endian.little); // X pixels per meter
|
||||||
|
infoHeader.setInt32(28, 2835, Endian.little); // Y pixels per meter
|
||||||
|
|
||||||
|
// Calculate row size and padding
|
||||||
|
final rowSize = (width * 3 + 3) & ~3;
|
||||||
|
final padding = rowSize - (width * 3);
|
||||||
|
|
||||||
|
// Pixel data (BMP stores in BGR format)
|
||||||
|
final bmpData = Uint8List(rowSize * height);
|
||||||
|
for (var y = 0; y < height; y++) {
|
||||||
|
for (var x = 0; x < width; x++) {
|
||||||
|
final srcIndex = (y * width + x) * 4; // RGBA format
|
||||||
|
final dstIndex = y * rowSize + x * 3; // BGR format
|
||||||
|
bmpData[dstIndex] = pixelBuffer[srcIndex + 2]; // Blue
|
||||||
|
bmpData[dstIndex + 1] = pixelBuffer[srcIndex + 1]; // Green
|
||||||
|
bmpData[dstIndex + 2] = pixelBuffer[srcIndex]; // Red
|
||||||
|
// Alpha channel is discarded
|
||||||
|
}
|
||||||
|
// Add padding to the end of each row
|
||||||
|
for (var p = 0; p < padding; p++) {
|
||||||
|
bmpData[y * rowSize + width * 3 + p] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write BMP file
|
||||||
|
final file = File(outputPath);
|
||||||
|
final sink = file.openWrite();
|
||||||
|
sink.add(fileHeader.buffer.asUint8List());
|
||||||
|
sink.add(infoHeader.buffer.asUint8List());
|
||||||
|
sink.add(bmpData);
|
||||||
|
await sink.close();
|
||||||
|
|
||||||
|
print('BMP image saved to: $outputPath');
|
||||||
|
}
|
||||||
|
|
||||||
|
final viewportDimensions = (width: 500, height: 500);
|
||||||
void main() async {
|
void main() async {
|
||||||
final packageUri = findPackageRoot('thermion_dart');
|
final packageUri = findPackageRoot('thermion_dart');
|
||||||
testDir = Directory("${packageUri.toFilePath()}/test").path;
|
testDir = Directory("${packageUri.toFilePath()}/test").path;
|
||||||
final lib = ThermionDartTexture1(DynamicLibrary.open(
|
final lib = ThermionDartTexture1(DynamicLibrary.open(
|
||||||
'${packageUri.toFilePath()}/native/lib/macos/swift/libdartfilamenttexture.dylib'));
|
'${packageUri.toFilePath()}/native/lib/macos/swift/libthermion_swift.dylib'));
|
||||||
final object = ThermionDartTexture.new1(lib);
|
final object = ThermionDartTexture.new1(lib);
|
||||||
object.initWithWidth_height_(500, 500);
|
object.initWithWidth_height_(
|
||||||
|
viewportDimensions.width, viewportDimensions.height);
|
||||||
|
|
||||||
final resourceLoader = calloc<ResourceLoaderWrapper>(1);
|
final resourceLoader = calloc<ResourceLoaderWrapper>(1);
|
||||||
var loadToOut = NativeCallable<
|
var loadToOut = NativeCallable<
|
||||||
@@ -67,32 +129,82 @@ void main() async {
|
|||||||
var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast<Void>());
|
var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast<Void>());
|
||||||
|
|
||||||
await viewer.initialized;
|
await viewer.initialized;
|
||||||
await viewer.createSwapChain(500, 500);
|
await viewer.createSwapChain(viewportDimensions.width.toDouble(),
|
||||||
await viewer.createRenderTarget(500, 500, object.metalTextureAddress);
|
viewportDimensions.height.toDouble());
|
||||||
await viewer.updateViewportAndCameraProjection(500, 500);
|
await viewer.createRenderTarget(viewportDimensions.width.toDouble(),
|
||||||
|
viewportDimensions.height.toDouble(), object.metalTextureAddress);
|
||||||
|
await viewer.updateViewportAndCameraProjection(
|
||||||
|
viewportDimensions.width.toDouble(),
|
||||||
|
viewportDimensions.height.toDouble());
|
||||||
|
|
||||||
|
var outDir = Directory("$testDir/output");
|
||||||
|
|
||||||
|
// outDir.deleteSync(recursive: true);
|
||||||
|
outDir.createSync();
|
||||||
|
|
||||||
|
Future _capture(String outputFilename) async {
|
||||||
|
var outPath = p.join(outDir.path, "$outputFilename.bmp");
|
||||||
|
var pixelBuffer = await viewer.capture();
|
||||||
|
await pixelBufferToBmp(pixelBuffer, viewportDimensions.width,
|
||||||
|
viewportDimensions.height, outPath);
|
||||||
|
}
|
||||||
|
|
||||||
group('background', () {
|
group('background', () {
|
||||||
test('set background color', () async {
|
test('set background color to solid green', () async {
|
||||||
var outDir = Directory("$testDir/bgcolor");
|
await viewer.setRendering(true);
|
||||||
outDir.createSync();
|
|
||||||
await viewer.setRecordingOutputDirectory(outDir.path);
|
|
||||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||||
await viewer.setRecording(true);
|
await _capture("bgcolor");
|
||||||
await viewer.render();
|
await viewer.setRendering(false);
|
||||||
await viewer.render();
|
|
||||||
await viewer.render();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('load skybox', () async {
|
test('load skybox', () async {
|
||||||
var outDir = Directory("$testDir/skybox");
|
var outDir = Directory("$testDir/skybox");
|
||||||
outDir.createSync();
|
outDir.createSync();
|
||||||
await viewer.setRecordingOutputDirectory(outDir.path);
|
await viewer.setRendering(true);
|
||||||
await viewer.setRecording(true);
|
|
||||||
await viewer.loadSkybox(
|
await viewer.loadSkybox(
|
||||||
"file:///$testDir/../../examples/assets/default_env/default_env_skybox.ktx");
|
"file:///$testDir/../../examples/assets/default_env/default_env_skybox.ktx");
|
||||||
await viewer.render();
|
await Future.delayed(Duration(seconds: 1));
|
||||||
await viewer.render();
|
await _capture("skybox");
|
||||||
await viewer.setRecording(false);
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group("gltf", () {
|
||||||
|
test('load glb', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb");
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await _capture("load_glb");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create instance from glb when keepData is true', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb", keepData: true);
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
var instance = await viewer.createInstance(model);
|
||||||
|
await viewer.setPosition(instance, 0.5, 0.5, -0.5);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await _capture("glb_create_instance");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create instance from glb fails when keepData is false', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb", keepData: false);
|
||||||
|
bool thrown = false;
|
||||||
|
try {
|
||||||
|
await viewer.createInstance(model);
|
||||||
|
} catch (err) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,5 +236,218 @@ void main() async {
|
|||||||
fadeInInSecs: 0.5, fadeOutInSecs: 0.5);
|
fadeInInSecs: 0.5, fadeOutInSecs: 0.5);
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('create geometry', () async {
|
||||||
|
// Define the vertices of the cube
|
||||||
|
List<double> vertices = [
|
||||||
|
// Front face
|
||||||
|
-1, -1, 1,
|
||||||
|
1, -1, 1,
|
||||||
|
1, 1, 1,
|
||||||
|
-1, 1, 1,
|
||||||
|
|
||||||
|
// Back face
|
||||||
|
-1, -1, -1,
|
||||||
|
-1, 1, -1,
|
||||||
|
1, 1, -1,
|
||||||
|
1, -1, -1,
|
||||||
|
|
||||||
|
// Top face
|
||||||
|
-1, 1, -1,
|
||||||
|
-1, 1, 1,
|
||||||
|
1, 1, 1,
|
||||||
|
1, 1, -1,
|
||||||
|
|
||||||
|
// Bottom face
|
||||||
|
-1, -1, -1,
|
||||||
|
1, -1, -1,
|
||||||
|
1, -1, 1,
|
||||||
|
-1, -1, 1,
|
||||||
|
|
||||||
|
// Right face
|
||||||
|
1, -1, -1,
|
||||||
|
1, 1, -1,
|
||||||
|
1, 1, 1,
|
||||||
|
1, -1, 1,
|
||||||
|
|
||||||
|
// Left face
|
||||||
|
-1, -1, -1,
|
||||||
|
-1, -1, 1,
|
||||||
|
-1, 1, 1,
|
||||||
|
-1, 1, -1,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Define the indices for the cube
|
||||||
|
List<int> indices = [
|
||||||
|
0, 1, 2, 0, 2, 3, // Front face
|
||||||
|
4, 5, 6, 4, 6, 7, // Back face
|
||||||
|
8, 9, 10, 8, 10, 11, // Top face
|
||||||
|
12, 13, 14, 12, 14, 15, // Bottom face
|
||||||
|
16, 17, 18, 16, 18, 19, // Right face
|
||||||
|
20, 21, 22, 20, 22, 23 // Left face
|
||||||
|
];
|
||||||
|
await viewer.createIbl(1.0, 1.0, 1.0, 1000);
|
||||||
|
await viewer.setCameraPosition(0, 0.5, 6);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
|
||||||
|
// Create the cube geometry
|
||||||
|
await viewer.createGeometry(vertices, indices,
|
||||||
|
primitiveType: PrimitiveType.TRIANGLES);
|
||||||
|
|
||||||
|
await _capture("geometry_cube");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create sphere', () async {
|
||||||
|
// Define the parameters for the sphere
|
||||||
|
int latitudeBands = 30;
|
||||||
|
int longitudeBands = 30;
|
||||||
|
double radius = 1.0;
|
||||||
|
|
||||||
|
List<double> vertices = [];
|
||||||
|
List<int> indices = [];
|
||||||
|
|
||||||
|
// Generate vertices
|
||||||
|
for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) {
|
||||||
|
double theta = latNumber * pi / latitudeBands;
|
||||||
|
double sinTheta = sin(theta);
|
||||||
|
double cosTheta = cos(theta);
|
||||||
|
|
||||||
|
for (int longNumber = 0; longNumber <= longitudeBands; longNumber++) {
|
||||||
|
double phi = longNumber * 2 * pi / longitudeBands;
|
||||||
|
double sinPhi = sin(phi);
|
||||||
|
double cosPhi = cos(phi);
|
||||||
|
|
||||||
|
double x = cosPhi * sinTheta;
|
||||||
|
double y = cosTheta;
|
||||||
|
double z = sinPhi * sinTheta;
|
||||||
|
|
||||||
|
vertices.addAll([radius * x, radius * y, radius * z]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate indices
|
||||||
|
for (int latNumber = 0; latNumber < latitudeBands; latNumber++) {
|
||||||
|
for (int longNumber = 0; longNumber < longitudeBands; longNumber++) {
|
||||||
|
int first = (latNumber * (longitudeBands + 1)) + longNumber;
|
||||||
|
int second = first + longitudeBands + 1;
|
||||||
|
|
||||||
|
indices.addAll(
|
||||||
|
[first, second, first + 1, second, second + 1, first + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await viewer.createIbl(1.0, 1.0, 1.0, 1000);
|
||||||
|
await viewer.setCameraPosition(0, 0.5, 10);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
|
||||||
|
// Create the sphere geometry
|
||||||
|
// final sphere = await viewer.createGeometry(vertices, indices,
|
||||||
|
// primitiveType: PrimitiveType.TRIANGLES);
|
||||||
|
|
||||||
|
// await viewer.gizmo!.attach(sphere);
|
||||||
|
// await viewer.setPosition(sphere, -1.0, 0.0, -10.0);
|
||||||
|
// await viewer.setRotationQuat(
|
||||||
|
// sphere, Quaternion.axisAngle(Vector3(1, 0, 0), pi / 8));
|
||||||
|
await _capture("geometry_sphere");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('enable grid overlay', () async {
|
||||||
|
await viewer.setBackgroundColor(0, 0, 0, 1);
|
||||||
|
await viewer.setCameraPosition(0, 0.5, 0);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.1));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await viewer.setLayerEnabled(2, true);
|
||||||
|
await _capture("grid");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('point light', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb");
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
var light = await viewer.addLight(
|
||||||
|
LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0,
|
||||||
|
falloffRadius: 10.0);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await _capture("point_light");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('set point light position', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb");
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
var light = await viewer.addLight(
|
||||||
|
LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0,
|
||||||
|
falloffRadius: 10.0);
|
||||||
|
await viewer.setLightPosition(light, 0.5, 2, 0);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await _capture("move_point_light");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('directional light', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb");
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
var light = await viewer.addLight(
|
||||||
|
LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0);
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await _capture("directional_light");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('set directional light direction', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb");
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
var light = await viewer.addLight(
|
||||||
|
LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0);
|
||||||
|
await viewer.setLightDirection(light, Vector3(-1, -1, -1));
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await _capture("set_directional_light_direction");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('set stencil highlight', () async {
|
||||||
|
var model = await viewer.loadGlb("$testDir/cube.glb");
|
||||||
|
await viewer.transformToUnitCube(model);
|
||||||
|
await viewer.setPostProcessing(true);
|
||||||
|
|
||||||
|
var light = await viewer.addLight(
|
||||||
|
LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0);
|
||||||
|
await viewer.setLightDirection(light, Vector3(-1, -1, -1));
|
||||||
|
|
||||||
|
await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
await viewer.setCameraPosition(0, 1, 5);
|
||||||
|
await viewer
|
||||||
|
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||||
|
await viewer.setStencilHighlight(model);
|
||||||
|
await viewer.setRendering(true);
|
||||||
|
await Future.delayed(Duration(milliseconds: 500));
|
||||||
|
await _capture("stencil_highlight");
|
||||||
|
await viewer.setRendering(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user