From 8d52690c97b403bf7eb1850a2875c7d09f6568a4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 3 Mar 2025 15:52:03 +0800 Subject: [PATCH] fix texture/material methods --- .../src/viewer/src/ffi/src/ffi_material.dart | 8 + .../viewer/src/ffi/src/thermion_dart.g.dart | 227 ++++++++++++++++++ .../src/ffi/src/thermion_viewer_ffi.dart | 32 +++ .../src/viewer/src/shared_types/material.dart | 24 +- .../src/viewer/src/shared_types/texture.dart | 196 ++++++++++----- .../src/viewer/src/thermion_viewer_base.dart | 11 + thermion_dart/native/include/c_api/TTexture.h | 64 +++++ thermion_dart/native/src/c_api/TTexture.cpp | 183 +++++++++++--- thermion_dart/test/material_tests.dart | 146 ++++++----- thermion_dart/test/texture_tests.dart | 14 +- 10 files changed, 742 insertions(+), 163 deletions(-) diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_material.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_material.dart index a7eb653e..d163e0ec 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_material.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_material.dart @@ -1,4 +1,5 @@ import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart'; import 'package:thermion_dart/thermion_dart.dart'; class FFIMaterial extends Material { @@ -149,4 +150,11 @@ class FFIMaterialInstance extends MaterialInstance { MaterialInstance_setTransparencyMode( pointer, TTransparencyMode.values[mode.index]); } + + @override + Future setParameterTexture(String name, covariant FFITexture texture, + covariant FFITextureSampler sampler) async { + MaterialInstance_setParameterTexture( + pointer, name.toNativeUtf8().cast(), texture.pointer, sampler.pointer); + } } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart index dbde8b74..0320e2d7 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart @@ -949,6 +949,166 @@ external int Image_getChannels( ffi.Pointer tLinearImage, ); +@ffi.Native Function()>(isLeaf: true) +external ffi.Pointer TextureSampler_create(); + +@ffi.Native< + ffi.Pointer Function(ffi.UnsignedInt, ffi.UnsignedInt, + ffi.UnsignedInt, ffi.UnsignedInt, ffi.UnsignedInt)>( + symbol: "TextureSampler_createWithFiltering", isLeaf: true) +external ffi.Pointer _TextureSampler_createWithFiltering( + int minFilter, + int magFilter, + int wrapS, + int wrapT, + int wrapR, +); + +ffi.Pointer TextureSampler_createWithFiltering( + TSamplerMinFilter minFilter, + TSamplerMagFilter magFilter, + TSamplerWrapMode wrapS, + TSamplerWrapMode wrapT, + TSamplerWrapMode wrapR, +) => + _TextureSampler_createWithFiltering( + minFilter.value, + magFilter.value, + wrapS.value, + wrapT.value, + wrapR.value, + ); + +@ffi.Native< + ffi.Pointer Function( + ffi.UnsignedInt, ffi.UnsignedInt)>( + symbol: "TextureSampler_createWithComparison", isLeaf: true) +external ffi.Pointer _TextureSampler_createWithComparison( + int compareMode, + int compareFunc, +); + +ffi.Pointer TextureSampler_createWithComparison( + TSamplerCompareMode compareMode, + TSamplerCompareFunc compareFunc, +) => + _TextureSampler_createWithComparison( + compareMode.value, + compareFunc.value, + ); + +@ffi.Native, ffi.UnsignedInt)>( + symbol: "TextureSampler_setMinFilter", isLeaf: true) +external void _TextureSampler_setMinFilter( + ffi.Pointer sampler, + int filter, +); + +void TextureSampler_setMinFilter( + ffi.Pointer sampler, + TSamplerMinFilter filter, +) => + _TextureSampler_setMinFilter( + sampler, + filter.value, + ); + +@ffi.Native, ffi.UnsignedInt)>( + symbol: "TextureSampler_setMagFilter", isLeaf: true) +external void _TextureSampler_setMagFilter( + ffi.Pointer sampler, + int filter, +); + +void TextureSampler_setMagFilter( + ffi.Pointer sampler, + TSamplerMagFilter filter, +) => + _TextureSampler_setMagFilter( + sampler, + filter.value, + ); + +@ffi.Native, ffi.UnsignedInt)>( + symbol: "TextureSampler_setWrapModeS", isLeaf: true) +external void _TextureSampler_setWrapModeS( + ffi.Pointer sampler, + int mode, +); + +void TextureSampler_setWrapModeS( + ffi.Pointer sampler, + TSamplerWrapMode mode, +) => + _TextureSampler_setWrapModeS( + sampler, + mode.value, + ); + +@ffi.Native, ffi.UnsignedInt)>( + symbol: "TextureSampler_setWrapModeT", isLeaf: true) +external void _TextureSampler_setWrapModeT( + ffi.Pointer sampler, + int mode, +); + +void TextureSampler_setWrapModeT( + ffi.Pointer sampler, + TSamplerWrapMode mode, +) => + _TextureSampler_setWrapModeT( + sampler, + mode.value, + ); + +@ffi.Native, ffi.UnsignedInt)>( + symbol: "TextureSampler_setWrapModeR", isLeaf: true) +external void _TextureSampler_setWrapModeR( + ffi.Pointer sampler, + int mode, +); + +void TextureSampler_setWrapModeR( + ffi.Pointer sampler, + TSamplerWrapMode mode, +) => + _TextureSampler_setWrapModeR( + sampler, + mode.value, + ); + +@ffi.Native, ffi.Double)>( + isLeaf: true) +external void TextureSampler_setAnisotropy( + ffi.Pointer sampler, + double anisotropy, +); + +@ffi.Native< + ffi.Void Function(ffi.Pointer, ffi.UnsignedInt, + ffi.UnsignedInt)>(symbol: "TextureSampler_setCompareMode", isLeaf: true) +external void _TextureSampler_setCompareMode( + ffi.Pointer sampler, + int mode, + int func, +); + +void TextureSampler_setCompareMode( + ffi.Pointer sampler, + TSamplerCompareMode mode, + TSamplerCompareFunc func, +) => + _TextureSampler_setCompareMode( + sampler, + mode.value, + func.value, + ); + +@ffi.Native)>(isLeaf: true) +external void TextureSampler_destroy( + ffi.Pointer sampler, +); + @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Uint32, ffi.Uint32, GizmoPickCallback)>(isLeaf: true) @@ -3596,6 +3756,73 @@ enum TPixelDataType { }; } +enum TSamplerWrapMode { + WRAP_CLAMP_TO_EDGE(0), + WRAP_REPEAT(1), + WRAP_MIRRORED_REPEAT(2); + + final int value; + const TSamplerWrapMode(this.value); + + static TSamplerWrapMode fromValue(int value) => switch (value) { + 0 => WRAP_CLAMP_TO_EDGE, + 1 => WRAP_REPEAT, + 2 => WRAP_MIRRORED_REPEAT, + _ => throw ArgumentError("Unknown value for TSamplerWrapMode: $value"), + }; +} + +enum TSamplerMinFilter { + FILTER_NEAREST(0), + FILTER_LINEAR(1), + FILTER_NEAREST_MIPMAP_NEAREST(2), + FILTER_LINEAR_MIPMAP_NEAREST(3), + FILTER_NEAREST_MIPMAP_LINEAR(4), + FILTER_LINEAR_MIPMAP_LINEAR(5); + + final int value; + const TSamplerMinFilter(this.value); + + static TSamplerMinFilter fromValue(int value) => switch (value) { + 0 => FILTER_NEAREST, + 1 => FILTER_LINEAR, + 2 => FILTER_NEAREST_MIPMAP_NEAREST, + 3 => FILTER_LINEAR_MIPMAP_NEAREST, + 4 => FILTER_NEAREST_MIPMAP_LINEAR, + 5 => FILTER_LINEAR_MIPMAP_LINEAR, + _ => throw ArgumentError("Unknown value for TSamplerMinFilter: $value"), + }; +} + +enum TSamplerMagFilter { + MAG_FILTER_NEAREST(0), + MAG_FILTER_LINEAR(1); + + final int value; + const TSamplerMagFilter(this.value); + + static TSamplerMagFilter fromValue(int value) => switch (value) { + 0 => MAG_FILTER_NEAREST, + 1 => MAG_FILTER_LINEAR, + _ => throw ArgumentError("Unknown value for TSamplerMagFilter: $value"), + }; +} + +enum TSamplerCompareMode { + COMPARE_MODE_NONE(0), + COMPARE_MODE_COMPARE_TO_TEXTURE(1); + + final int value; + const TSamplerCompareMode(this.value); + + static TSamplerCompareMode fromValue(int value) => switch (value) { + 0 => COMPARE_MODE_NONE, + 1 => COMPARE_MODE_COMPARE_TO_TEXTURE, + _ => + throw ArgumentError("Unknown value for TSamplerCompareMode: $value"), + }; +} + enum TGizmoAxis { X(0), Y(1), diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart index 5305e077..aa72fa91 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart @@ -1865,6 +1865,38 @@ class ThermionViewerFFI extends ThermionViewer { ); } + Future createTextureSampler( + {TextureMinFilter minFilter = TextureMinFilter.LINEAR, + TextureMagFilter magFilter = TextureMagFilter.LINEAR, + TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, + double anisotropy = 0.0, + TextureCompareMode compareMode = TextureCompareMode.NONE, + TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) async { + final samplerPtr = TextureSampler_create(); + TextureSampler_setMinFilter( + samplerPtr, TSamplerMinFilter.values[minFilter.index]); + TextureSampler_setMagFilter( + samplerPtr, TSamplerMagFilter.values[magFilter.index]); + TextureSampler_setWrapModeS( + samplerPtr, TSamplerWrapMode.values[wrapS.index]); + TextureSampler_setWrapModeT( + samplerPtr, TSamplerWrapMode.values[wrapT.index]); + TextureSampler_setWrapModeR( + samplerPtr, TSamplerWrapMode.values[wrapR.index]); + if (anisotropy > 0) { + TextureSampler_setAnisotropy(samplerPtr, anisotropy); + } + if (compareMode != TextureCompareMode.NONE) { + TextureSampler_setCompareMode( + samplerPtr, + TSamplerCompareMode.values[compareMode.index], + TSamplerCompareFunc.values[compareFunc.index]); + } + return FFITextureSampler(samplerPtr); + } + Future decodeImage(Uint8List data) async { final name = "image"; var ptr = Image_decode( diff --git a/thermion_dart/lib/src/viewer/src/shared_types/material.dart b/thermion_dart/lib/src/viewer/src/shared_types/material.dart index 3fd207b3..0f85554c 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/material.dart +++ b/thermion_dart/lib/src/viewer/src/shared_types/material.dart @@ -1,3 +1,5 @@ +import 'package:thermion_dart/thermion_dart.dart'; + enum SamplerCompareFunction { /// !< Less or equal LE, @@ -70,27 +72,23 @@ enum StencilFace { FRONT_AND_BACK } -enum AlphaMode { - OPAQUE, - MASK, - BLEND -} +enum AlphaMode { OPAQUE, MASK, BLEND } -enum TransparencyMode { - //! the transparent object is drawn honoring the raster state - DEFAULT, - /** +enum TransparencyMode { + //! the transparent object is drawn honoring the raster state + DEFAULT, + /** * the transparent object is first drawn in the depth buffer, * then in the color buffer, honoring the culling mode, but ignoring the depth test function */ - TWO_PASSES_ONE_SIDE, + TWO_PASSES_ONE_SIDE, - /** + /** * the transparent object is drawn twice in the color buffer, * first with back faces only, then with front faces; the culling * mode is ignored. Can be combined with two-sided lighting */ - TWO_PASSES_TWO_SIDES + TWO_PASSES_TWO_SIDES } abstract class Material { @@ -108,6 +106,8 @@ abstract class MaterialInstance { Future setParameterFloat2(String name, double x, double y); Future setParameterFloat(String name, double x); Future setParameterInt(String name, int value); + Future setParameterTexture( + String name, covariant Texture texture, covariant TextureSampler sampler); /// Sets the stencil operation to be performed when the stencil test fails Future setStencilOpStencilFail(StencilOperation op, diff --git a/thermion_dart/lib/src/viewer/src/shared_types/texture.dart b/thermion_dart/lib/src/viewer/src/shared_types/texture.dart index 3d8f9f88..dc7808ed 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/texture.dart +++ b/thermion_dart/lib/src/viewer/src/shared_types/texture.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart'; import 'package:thermion_dart/thermion_dart.dart'; /// Defines the type of sampler to use with a texture @@ -173,40 +174,82 @@ enum TextureUsage { SAMPLEABLE } -/// Defines texture filter types for magnification and minification -enum TextureFilter { +/// Defines texture wrapping modes for texture coordinates +enum TextureWrapMode { + /// Clamps texture coordinates to edge, extending edge pixels + CLAMP_TO_EDGE, + + /// Repeats the texture (tiles) + REPEAT, + + /// Mirrors the texture at each repeat + MIRRORED_REPEAT +} + +/// Defines texture minification filter types +enum TextureMinFilter { /// Nearest neighbor sampling (pixelated look) NEAREST, /// Linear interpolation between texels LINEAR, - /// Nearest neighbor filtering but uses mipmaps for minification + /// Nearest neighbor filtering with nearest mipmap NEAREST_MIPMAP_NEAREST, - /// Linear filtering and uses nearest mipmap level + /// Linear filtering with nearest mipmap LINEAR_MIPMAP_NEAREST, - /// Nearest filtering but linearly interpolates between mipmap levels + /// Nearest filtering with linear mipmap interpolation NEAREST_MIPMAP_LINEAR, - /// Linear filtering and linear interpolation between mipmap levels (best quality) + /// Linear filtering with linear mipmap interpolation (best quality) LINEAR_MIPMAP_LINEAR } -/// Defines texture wrapping modes for when texture coordinates exceed [0,1] -enum TextureWrapMode { - /// Repeats the texture (tiles) - REPEAT, +/// Defines texture magnification filter types +enum TextureMagFilter { + /// Nearest neighbor sampling (pixelated look) + NEAREST, - /// Mirrors the texture at each repeat - MIRRORED_REPEAT, + /// Linear interpolation between texels + LINEAR +} - /// Clamps texture coordinates to edge, extending edge pixels - CLAMP_TO_EDGE, +/// Defines texture comparison modes +enum TextureCompareMode { + /// No comparison is performed + NONE, - /// Clamps to border color (usually transparent or black) - CLAMP_TO_BORDER + /// Compare texture values to reference value + COMPARE_TO_TEXTURE +} + +/// Defines texture comparison functions +enum TextureCompareFunc { + /// Less than or equal + LESS_EQUAL, + + /// Greater than or equal + GREATER_EQUAL, + + /// Less than + LESS, + + /// Greater than + GREATER, + + /// Equal + EQUAL, + + /// Not equal + NOT_EQUAL, + + /// Always passes + ALWAYS, + + /// Never passes + NEVER } /// Defines swizzle operations for texture components @@ -235,20 +278,6 @@ enum TextureSwizzle { /// Defines the texture sampler configuration abstract class TextureSampler { - /// Creates a new texture sampler with specified filtering and wrapping modes - Future create(TextureFilter minFilter, - TextureFilter magFilter, TextureWrapMode wrapS, TextureWrapMode wrapT, - [TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE]); - - /// Creates a texture sampler with comparison mode for shadow mapping - Future createComparisonSampler( - TextureFilter minFilter, - TextureFilter magFilter, - TextureWrapMode wrapS, - TextureWrapMode wrapT, - SamplerCompareFunction compareMode, - [TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE]); - /// Disposes the sampler resources Future dispose(); } @@ -273,7 +302,8 @@ abstract class Texture { /// Returns the internal format of this texture Future getFormat(); - Future setLinearImage(covariant LinearImage image, PixelDataFormat format, PixelDataType type); + Future setLinearImage( + covariant LinearImage image, PixelDataFormat format, PixelDataType type); /// Sets the image data for a 2D texture or a texture level Future setImage( @@ -307,34 +337,82 @@ abstract class Texture { } enum PixelDataFormat { - R, /// One Red channel, float - R_INTEGER, /// One Red channel, integer - RG, /// Two Red and Green channels, float - RG_INTEGER, /// Two Red and Green channels, integer - RGB, /// Three Red, Green and Blue channels, float - RGB_INTEGER, /// Three Red, Green and Blue channels, integer - RGBA, /// Four Red, Green, Blue and Alpha channels, float - RGBA_INTEGER, /// Four Red, Green, Blue and Alpha channels, integer - UNUSED, /// Used to be rgbm - DEPTH_COMPONENT, /// Depth, 16-bit or 24-bits usually - DEPTH_STENCIL, /// Two Depth (24-bits) + Stencil (8-bits) channels - ALPHA /// One Alpha channel, float + R, + + /// One Red channel, float + R_INTEGER, + + /// One Red channel, integer + RG, + + /// Two Red and Green channels, float + RG_INTEGER, + + /// Two Red and Green channels, integer + RGB, + + /// Three Red, Green and Blue channels, float + RGB_INTEGER, + + /// Three Red, Green and Blue channels, integer + RGBA, + + /// Four Red, Green, Blue and Alpha channels, float + RGBA_INTEGER, + + /// Four Red, Green, Blue and Alpha channels, integer + UNUSED, + + /// Used to be rgbm + DEPTH_COMPONENT, + + /// Depth, 16-bit or 24-bits usually + DEPTH_STENCIL, + + /// Two Depth (24-bits) + Stencil (8-bits) channels + ALPHA + + /// One Alpha channel, float } /// Pixel Data Type enum PixelDataType { - UBYTE, /// Unsigned byte - BYTE, /// Signed byte - USHORT, /// Unsigned short (16-bit) - SHORT, /// Signed short (16-bit) - UINT, /// Unsigned int (32-bit) - INT, /// Signed int (32-bit) - HALF, /// Half-float (16-bit float) - FLOAT, /// Float (32-bits float) - COMPRESSED, /// Compressed pixels, see CompressedPixelDataType - UINT_10F_11F_11F_REV, /// Three low precision floating-point numbers - USHORT_565, /// Unsigned int (16-bit), encodes 3 RGB channels - UINT_2_10_10_10_REV, /// Unsigned normalized 10 bits RGB, 2 bits alpha + UBYTE, + + /// Unsigned byte + BYTE, + + /// Signed byte + USHORT, + + /// Unsigned short (16-bit) + SHORT, + + /// Signed short (16-bit) + UINT, + + /// Unsigned int (32-bit) + INT, + + /// Signed int (32-bit) + HALF, + + /// Half-float (16-bit float) + FLOAT, + + /// Float (32-bits float) + COMPRESSED, + + /// Compressed pixels, see CompressedPixelDataType + UINT_10F_11F_11F_REV, + + /// Three low precision floating-point numbers + USHORT_565, + + /// Unsigned int (16-bit), encodes 3 RGB channels + UINT_2_10_10_10_REV, + + /// Unsigned normalized 10 bits RGB, 2 bits alpha } @deprecated @@ -346,3 +424,11 @@ abstract class LinearImage { Future getHeight(); Future getChannels(); } + +class FFITextureSampler extends TextureSampler { + final Pointer pointer; + + FFITextureSampler(this.pointer); + @override + Future dispose() async {} +} diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart index b02ff454..12693c9b 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart @@ -778,6 +778,17 @@ abstract class ThermionViewer { Future createTexture(int width, int height, {TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, TextureFormat textureFormat = TextureFormat.RGBA16F}); + +Future createTextureSampler({ + TextureMinFilter minFilter = TextureMinFilter.LINEAR, + TextureMagFilter magFilter = TextureMagFilter.LINEAR, + TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, + double anisotropy = 0.0, + TextureCompareMode compareMode = TextureCompareMode.NONE, + TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL +}); /// /// Decodes the specified image data. diff --git a/thermion_dart/native/include/c_api/TTexture.h b/thermion_dart/native/include/c_api/TTexture.h index 98d12a70..dea7bc82 100644 --- a/thermion_dart/native/include/c_api/TTexture.h +++ b/thermion_dart/native/include/c_api/TTexture.h @@ -197,6 +197,70 @@ EMSCRIPTEN_KEEPALIVE void Image_destroy(TLinearImage* tLinearImage); EMSCRIPTEN_KEEPALIVE uint32_t Image_getWidth(TLinearImage* tLinearImage); EMSCRIPTEN_KEEPALIVE uint32_t Image_getHeight(TLinearImage* tLinearImage); EMSCRIPTEN_KEEPALIVE uint32_t Image_getChannels(TLinearImage* tLinearImage); + +// Texture Sampler related enums +enum TSamplerWrapMode { + WRAP_CLAMP_TO_EDGE, // Clamp to edge wrapping mode + WRAP_REPEAT, // Repeat wrapping mode + WRAP_MIRRORED_REPEAT // Mirrored repeat wrapping mode +}; + +enum TSamplerMinFilter { + FILTER_NEAREST, // Nearest filtering + FILTER_LINEAR, // Linear filtering + FILTER_NEAREST_MIPMAP_NEAREST, // Nearest mipmap nearest filtering + FILTER_LINEAR_MIPMAP_NEAREST, // Linear mipmap nearest filtering + FILTER_NEAREST_MIPMAP_LINEAR, // Nearest mipmap linear filtering + FILTER_LINEAR_MIPMAP_LINEAR // Linear mipmap linear filtering +}; + +enum TSamplerMagFilter { + MAG_FILTER_NEAREST, // Nearest filtering + MAG_FILTER_LINEAR // Linear filtering +}; + +enum TSamplerCompareMode { + COMPARE_MODE_NONE, // No comparison + COMPARE_MODE_COMPARE_TO_TEXTURE // Compare to texture +}; + +typedef TSamplerCompareFunc TTextureSamplerCompareFunc ; + + +EMSCRIPTEN_KEEPALIVE TTextureSampler* TextureSampler_create(); +EMSCRIPTEN_KEEPALIVE TTextureSampler* TextureSampler_createWithFiltering( + TSamplerMinFilter minFilter, + TSamplerMagFilter magFilter, + TSamplerWrapMode wrapS, + TSamplerWrapMode wrapT, + TSamplerWrapMode wrapR); +EMSCRIPTEN_KEEPALIVE TTextureSampler* TextureSampler_createWithComparison( + TSamplerCompareMode compareMode, + TSamplerCompareFunc compareFunc); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setMinFilter( + TTextureSampler* sampler, + TSamplerMinFilter filter); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setMagFilter( + TTextureSampler* sampler, + TSamplerMagFilter filter); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeS( + TTextureSampler* sampler, + TSamplerWrapMode mode); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeT( + TTextureSampler* sampler, + TSamplerWrapMode mode); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeR( + TTextureSampler* sampler, + TSamplerWrapMode mode); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setAnisotropy( + TTextureSampler* sampler, + double anisotropy); +EMSCRIPTEN_KEEPALIVE void TextureSampler_setCompareMode( + TTextureSampler* sampler, + TSamplerCompareMode mode, + TTextureSamplerCompareFunc func); + +EMSCRIPTEN_KEEPALIVE void TextureSampler_destroy(TTextureSampler* sampler); #ifdef __cplusplus diff --git a/thermion_dart/native/src/c_api/TTexture.cpp b/thermion_dart/native/src/c_api/TTexture.cpp index a5f86ae7..098217ef 100644 --- a/thermion_dart/native/src/c_api/TTexture.cpp +++ b/thermion_dart/native/src/c_api/TTexture.cpp @@ -19,10 +19,11 @@ namespace thermion extern "C" { using namespace filament::backend; - + #endif - EMSCRIPTEN_KEEPALIVE TLinearImage* Image_decode(uint8_t* data, size_t length, const char* name = "image") { + EMSCRIPTEN_KEEPALIVE TLinearImage *Image_decode(uint8_t *data, size_t length, const char *name = "image") + { std::istringstream stream(std::string(reinterpret_cast(data), length)); auto *linearImage = new image::LinearImage(::image::ImageDecoder::decode(stream, name, ::image::ImageDecoder::ColorSpace::SRGB)); @@ -32,57 +33,60 @@ namespace thermion Log("Failed to decode image."); return nullptr; } - return reinterpret_cast(linearImage); - + return reinterpret_cast(linearImage); } - EMSCRIPTEN_KEEPALIVE uint32_t Image_getWidth(TLinearImage* tLinearImage) { - auto *linearImage = reinterpret_cast<::image::LinearImage*>(tLinearImage); + EMSCRIPTEN_KEEPALIVE uint32_t Image_getWidth(TLinearImage *tLinearImage) + { + auto *linearImage = reinterpret_cast<::image::LinearImage *>(tLinearImage); return linearImage->getWidth(); } - EMSCRIPTEN_KEEPALIVE uint32_t Image_getHeight(TLinearImage* tLinearImage) { - auto *linearImage = reinterpret_cast<::image::LinearImage*>(tLinearImage); + EMSCRIPTEN_KEEPALIVE uint32_t Image_getHeight(TLinearImage *tLinearImage) + { + auto *linearImage = reinterpret_cast<::image::LinearImage *>(tLinearImage); return linearImage->getHeight(); } - EMSCRIPTEN_KEEPALIVE uint32_t Image_getChannels(TLinearImage* tLinearImage) { - auto *linearImage = reinterpret_cast<::image::LinearImage*>(tLinearImage); + EMSCRIPTEN_KEEPALIVE uint32_t Image_getChannels(TLinearImage *tLinearImage) + { + auto *linearImage = reinterpret_cast<::image::LinearImage *>(tLinearImage); return linearImage->getChannels(); } - EMSCRIPTEN_KEEPALIVE void Image_destroy(TLinearImage* tLinearImage) { - auto *linearImage = reinterpret_cast<::image::LinearImage*>(tLinearImage); + EMSCRIPTEN_KEEPALIVE void Image_destroy(TLinearImage *tLinearImage) + { + auto *linearImage = reinterpret_cast<::image::LinearImage *>(tLinearImage); delete linearImage; } EMSCRIPTEN_KEEPALIVE bool Texture_loadImage(TEngine *tEngine, TTexture *tTexture, TLinearImage *tImage, TPixelDataFormat tBufferFormat, TPixelDataType tPixelDataType) { - auto engine = reinterpret_cast(tEngine); - auto image = reinterpret_cast<::image::LinearImage*>(tImage); - auto texture = reinterpret_cast(tTexture); + auto engine = reinterpret_cast(tEngine); + auto image = reinterpret_cast<::image::LinearImage *>(tImage); + auto texture = reinterpret_cast(tTexture); auto bufferFormat = static_cast(static_cast(tBufferFormat)); auto pixelDataType = static_cast(static_cast(tPixelDataType)); - + uint32_t w = image->getWidth(); uint32_t h = image->getHeight(); uint32_t channels = image->getChannels(); size_t size; - switch(bufferFormat) { - case PixelBufferDescriptor::PixelDataFormat::RGB: - case PixelBufferDescriptor::PixelDataFormat::RGBA: - size = w * h * channels * sizeof(float); - break; - case PixelBufferDescriptor::PixelDataFormat::RGB_INTEGER: - case PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER: - size = w * h * channels * sizeof(uint8_t); - break; - default: - Log("Unsupported buffer format type : %d", bufferFormat); - return false; + switch (bufferFormat) + { + case PixelBufferDescriptor::PixelDataFormat::RGB: + case PixelBufferDescriptor::PixelDataFormat::RGBA: + size = w * h * channels * sizeof(float); + break; + case PixelBufferDescriptor::PixelDataFormat::RGB_INTEGER: + case PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER: + size = w * h * channels * sizeof(uint8_t); + break; + default: + Log("Unsupported buffer format type : %d", bufferFormat); + return false; } - Log("Dimensions %d x %d, channels %d, size %d, buffer format %d and pixel data type %d", w, h,channels, size, bufferFormat, pixelDataType); - + Log("Dimensions %d x %d, channels %d, size %d, buffer format %d and pixel data type %d", w, h, channels, size, bufferFormat, pixelDataType); filament::Texture::PixelBufferDescriptor buffer( image->getPixelRef(), @@ -92,7 +96,126 @@ namespace thermion texture->setImage(*engine, 0, std::move(buffer)); return true; + } + EMSCRIPTEN_KEEPALIVE TTextureSampler *TextureSampler_create() + { + auto *sampler = new filament::TextureSampler(); + return reinterpret_cast(sampler); + } + + EMSCRIPTEN_KEEPALIVE TTextureSampler *TextureSampler_createWithFiltering( + TSamplerMinFilter minFilter, + TSamplerMagFilter magFilter, + TSamplerWrapMode wrapS, + TSamplerWrapMode wrapT, + TSamplerWrapMode wrapR) + { + + filament::TextureSampler::MinFilter min = static_cast(minFilter); + filament::TextureSampler::MagFilter mag = static_cast(magFilter); + filament::TextureSampler::WrapMode s = static_cast(wrapS); + filament::TextureSampler::WrapMode t = static_cast(wrapT); + filament::TextureSampler::WrapMode r = static_cast(wrapR); + + auto *sampler = new filament::TextureSampler(min, mag, s, t, r); + return reinterpret_cast(sampler); + } + + EMSCRIPTEN_KEEPALIVE TTextureSampler *TextureSampler_createWithComparison( + TSamplerCompareMode compareMode, + TSamplerCompareFunc compareFunc) + { + + filament::TextureSampler::CompareMode mode = static_cast(compareMode); + filament::TextureSampler::CompareFunc func = static_cast(compareFunc); + + auto *sampler = new filament::TextureSampler(mode, func); + return reinterpret_cast(sampler); + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_setMinFilter( + TTextureSampler *sampler, + TSamplerMinFilter filter) + { + + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + textureSampler->setMinFilter(static_cast(filter)); + } + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_setMagFilter( + TTextureSampler *sampler, + TSamplerMagFilter filter) + { + + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + textureSampler->setMagFilter(static_cast(filter)); + } + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeS( + TTextureSampler *sampler, + TSamplerWrapMode mode) + { + + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + textureSampler->setWrapModeS(static_cast(mode)); + } + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeT( + TTextureSampler *sampler, + TSamplerWrapMode mode) + { + + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + textureSampler->setWrapModeT(static_cast(mode)); + } + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeR( + TTextureSampler *sampler, + TSamplerWrapMode mode) + { + + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + textureSampler->setWrapModeR(static_cast(mode)); + } + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_setCompareMode( + TTextureSampler *sampler, + TSamplerCompareMode mode, + TSamplerCompareFunc func) + { + + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + textureSampler->setCompareMode( + static_cast(mode), + static_cast(func)); + } + } + + EMSCRIPTEN_KEEPALIVE void TextureSampler_destroy(TTextureSampler *sampler) + { + if (sampler) + { + auto *textureSampler = reinterpret_cast(sampler); + delete textureSampler; + } } #ifdef __cplusplus diff --git a/thermion_dart/test/material_tests.dart b/thermion_dart/test/material_tests.dart index 87fad056..00563f9b 100644 --- a/thermion_dart/test/material_tests.dart +++ b/thermion_dart/test/material_tests.dart @@ -42,7 +42,7 @@ Future< void main() async { final testHelper = TestHelper("material"); - group("material tests", () { + group("unlit material tests", () { test('unlit material with color only', () async { await testHelper.withViewer((viewer) async { await viewer.setPostProcessing(true); @@ -77,7 +77,6 @@ void main() async { await testHelper.capture(viewer, "unlit_material_base_color_alpha"); await viewer.destroyMaterialInstance(materialInstance); }, bg: kRed); - }); test('unlit fixed size material', () async { @@ -103,8 +102,10 @@ void main() async { await viewer.dispose(); }); + }); - test('ubershader material with color only', () async { + group("ubershader material tests", () { + test('ubershader material with color only', () async { await testHelper.withViewer((viewer) async { var materialInstance = await viewer.createUbershaderMaterialInstance(); await viewer @@ -121,29 +122,38 @@ void main() async { }, bg: kRed, postProcessing: true); }); - test('apply texture to custom ubershader material instance', () async { - var viewer = await testHelper.createViewer(); - await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1); - await viewer.setCameraPosition(0, 2, 6); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); - await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + test('set ubershader texture', () async { + await testHelper.withViewer((viewer) async { + await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1); + await viewer.setCameraPosition(0, 2, 6); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); - var materialInstance = await viewer.createUbershaderMaterialInstance(); - final cube = await viewer.createGeometry( - GeometryHelper.cube(uvs: true, normals: true), - materialInstances: [materialInstance]); - var textureData = - File("${testHelper.testDir}/assets/cube_texture_512x512.png") - .readAsBytesSync(); - var texture = await viewer.createTexture(textureData); - await viewer.applyTexture(texture as ThermionFFITexture, cube.entity); - await testHelper.capture( - viewer, "geometry_cube_with_custom_material_ubershader_texture"); - await viewer.destroyAsset(cube); - await viewer.destroyMaterialInstance(materialInstance); - await viewer.destroyTexture(texture); - await viewer.dispose(); + var materialInstance = await viewer.createUbershaderMaterialInstance(); + final cube = await viewer.createGeometry( + GeometryHelper.cube(uvs: true, normals: true), + materialInstances: [materialInstance]); + var data = File("${testHelper.testDir}/assets/cube_texture_512x512.png") + .readAsBytesSync(); + final image = await viewer.decodeImage(data); + final texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight(), + textureFormat: TextureFormat.RGBA32F); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + final sampler = await viewer.createTextureSampler(); + await materialInstance.setParameterFloat4( + "baseColorFactor", 1.0, 1.0, 1.0, 0.0); + await materialInstance.setParameterInt("baseColorIndex", 0); + await materialInstance.setParameterTexture( + "baseColorMap", texture, sampler); + + await testHelper.capture( + viewer, "geometry_cube_with_custom_material_ubershader_texture"); + await viewer.destroyAsset(cube); + await viewer.destroyMaterialInstance(materialInstance); + await viewer.destroyTexture(texture); + }); }); test('create cube with custom material instance (unlit)', () async { @@ -162,51 +172,42 @@ void main() async { var textureData = File("${testHelper.testDir}/assets/cube_texture_512x512.png") .readAsBytesSync(); - var texture = await viewer.createTexture(textureData); - await viewer.applyTexture(texture, cube.entity); - await testHelper.capture( - viewer, "geometry_cube_with_custom_material_unlit_texture_only"); - await viewer.destroyAsset(cube); + throw UnimplementedError(); + // var texture = await viewer.createTexture(textureData); + // await viewer.applyTexture(texture, cube.entity); + // await testHelper.capture( + // viewer, "geometry_cube_with_custom_material_unlit_texture_only"); + // await viewer.destroyAsset(cube); - cube = await viewer.createGeometry(GeometryHelper.cube(), - materialInstances: [materialInstance]); - // reusing same material instance, so set baseColorIndex to -1 to disable the texture - await materialInstance.setParameterInt("baseColorIndex", -1); - await materialInstance.setParameterFloat4( - "baseColorFactor", 0.0, 1.0, 0.0, 1.0); - await testHelper.capture( - viewer, "geometry_cube_with_custom_material_unlit_color_only"); - await viewer.destroyAsset(cube); + // cube = await viewer.createGeometry(GeometryHelper.cube(), + // materialInstances: [materialInstance]); + // // reusing same material instance, so set baseColorIndex to -1 to disable the texture + // await materialInstance.setParameterInt("baseColorIndex", -1); + // await materialInstance.setParameterFloat4( + // "baseColorFactor", 0.0, 1.0, 0.0, 1.0); + // await testHelper.capture( + // viewer, "geometry_cube_with_custom_material_unlit_color_only"); + // await viewer.destroyAsset(cube); - cube = await viewer.createGeometry(GeometryHelper.cube(), - materialInstances: [materialInstance]); - // now set baseColorIndex to 0 to enable the texture and the base color - await materialInstance.setParameterInt("baseColorIndex", 0); - await materialInstance.setParameterFloat4( - "baseColorFactor", 0.0, 1.0, 0.0, 0.5); - await viewer.applyTexture(texture, cube.entity); + // cube = await viewer.createGeometry(GeometryHelper.cube(), + // materialInstances: [materialInstance]); + // // now set baseColorIndex to 0 to enable the texture and the base color + // await materialInstance.setParameterInt("baseColorIndex", 0); + // await materialInstance.setParameterFloat4( + // "baseColorFactor", 0.0, 1.0, 0.0, 0.5); + // await viewer.applyTexture(texture, cube.entity); - await testHelper.capture( - viewer, "geometry_cube_with_custom_material_unlit_color_and_texture"); + // await testHelper.capture( + // viewer, "geometry_cube_with_custom_material_unlit_color_and_texture"); - await viewer.destroyAsset(cube); + // await viewer.destroyAsset(cube); - await viewer.destroyTexture(texture); - await viewer.destroyMaterialInstance(materialInstance); - await viewer.dispose(); + // await viewer.destroyTexture(texture); + // await viewer.destroyMaterialInstance(materialInstance); + // await viewer.dispose(); }); - - test('create sphere (no normals)', () async { - var viewer = await testHelper.createViewer(); - await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); - await viewer.setCameraPosition(0, 0, 6); - await viewer - .createGeometry(GeometryHelper.sphere(normals: false, uvs: false)); - await testHelper.capture(viewer, "geometry_sphere_no_normals"); - - await viewer.dispose(); - }); - + }); + group('depth & stencil', () { test('set depth func to always', () async { await testHelper.withViewer((viewer) async { final ( @@ -338,6 +339,23 @@ void main() async { }, postProcessing: true); }); }); + + group('projection', () { + test('apply projection material', () async { + await testHelper.withViewer((viewer) async { + var materialData = File( + "/Users/nickfisher/Documents/thermion/materials/capture_uv.filamat") + .readAsBytesSync(); + var material = await viewer.createMaterial(materialData); + var instance = await material.createInstance(); + + final cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [instance]); + await cube.addToScene(); + await testHelper.capture(viewer, "projection"); + }, cameraPosition: Vector3(0, 0, 100)); + }); + }); } // group("MaterialInstance", () { diff --git a/thermion_dart/test/texture_tests.dart b/thermion_dart/test/texture_tests.dart index 3e111a57..57e58a54 100644 --- a/thermion_dart/test/texture_tests.dart +++ b/thermion_dart/test/texture_tests.dart @@ -18,8 +18,18 @@ void main() async { expect(await image.getHeight(), 512); final texture = await viewer.createTexture( - await image.getWidth(), await image.getHeight(), textureFormat: TextureFormat.RGBA32F); - await texture.setLinearImage(image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + await image.getWidth(), await image.getHeight(), + textureFormat: TextureFormat.RGBA32F); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + }, bg: kRed); + }); + }); + + group("sampler", () { + test('create sampler', () async { + await testHelper.withViewer((viewer) async { + final sampler = viewer.createTextureSampler(); }, bg: kRed); }); });