replace view with texture, move plugin to Swift implementation

This commit is contained in:
Nick Fisher
2022-08-19 12:39:55 +08:00
parent 21fef19359
commit c6e429bcba
20 changed files with 658 additions and 491 deletions

View File

@@ -1,20 +0,0 @@
#ifndef FilamentMethodCallHandler_h
#define FilamentMethodCallHandler_h
#endif /* FilamentNativeViewFactory_h */
#import <Flutter/Flutter.h>
#include "FilamentViewer.hpp"
using namespace polyvox;
static const id VIEW_TYPE = @"app.polyvox.filament/filament_view";
@interface FilamentMethodCallHandler : FlutterMethodChannel
- (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:( FlutterResult _Nonnull)result;
- (polyvox::FilamentViewer*) _viewer;
- (polyvox::ResourceBuffer)loadResource:(const char* const)path;
- (void)freeResource:(polyvox::ResourceBuffer)rb;
- (void)ready;
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar viewId:(int64_t)viewId viewer:(FilamentViewer*)viewer;
@end

View File

@@ -1,170 +0,0 @@
#import "FilamentMethodCallHandler.h"
#import "FilamentNativeViewFactory.h"
static int _resourceId = 0;
using namespace polyvox;
using namespace std;
@implementation FilamentMethodCallHandler {
FlutterMethodChannel* _channel;
FilamentViewer* _viewer;
NSObject<FlutterPluginRegistrar>* _registrar;
}
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar
viewId:(int64_t)viewId
viewer:(FilamentViewer*)viewer
{
_registrar = registrar;
_viewer = viewer;
NSString* channelName = [NSString stringWithFormat:@"%@_%d",VIEW_TYPE,viewId];
_channel = [FlutterMethodChannel
methodChannelWithName:channelName
binaryMessenger:[registrar messenger]];
[_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
[self handleMethodCall:call result:result];
}];
[self ready];
return self;
}
- (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:(FlutterResult _Nonnull )result {
if(!_viewer) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"View unavailable"
details:nil]);
return;
}
if([@"loadSkybox" isEqualToString:call.method]) {
_viewer->loadSkybox([call.arguments UTF8String]);
result(@"OK");
} else if([@"removeSkybox" isEqualToString:call.method]) {
_viewer->removeSkybox();
result(@"OK");
} else if([@"loadIbl" isEqualToString:call.method]) {
_viewer->loadIbl([call.arguments UTF8String]);
result(@"OK");
} else if([@"removeIbl" isEqualToString:call.method]) {
_viewer->removeIbl();
result(@"OK");
} else if([@"loadGlb" isEqualToString:call.method]) {
_viewer->loadGlb([call.arguments UTF8String]);
result(@"OK");
} else if([@"loadGltf" isEqualToString:call.method]) {
_viewer->loadGltf([call.arguments[0] UTF8String], [call.arguments[1] UTF8String]);
result(@"OK");
} else if([@"setCamera" isEqualToString:call.method]) {
_viewer->setCamera([call.arguments UTF8String]);
result(@"OK");
} else if([@"panStart" isEqualToString:call.method]) {
_viewer->manipulator->grabBegin([call.arguments[0] intValue], [call.arguments[1] intValue], true);
result(@"OK");
} else if([@"panUpdate" isEqualToString:call.method]) {
_viewer->manipulator->grabUpdate([call.arguments[0] intValue], [call.arguments[1] intValue]);
result(@"OK");
} else if([@"panEnd" isEqualToString:call.method]) {
_viewer->manipulator->grabEnd();
result(@"OK");
} else if([@"rotateStart" isEqualToString:call.method]) {
_viewer->manipulator->grabBegin([call.arguments[0] intValue], [call.arguments[1] intValue], false);
result(@"OK");
} else if([@"rotateUpdate" isEqualToString:call.method]) {
_viewer->manipulator->grabUpdate([call.arguments[0] intValue], [call.arguments[1] intValue]);
result(@"OK");
} else if([@"rotateEnd" isEqualToString:call.method]) {
_viewer->manipulator->grabEnd();
result(@"OK");
} else if([@"animateWeights" isEqualToString:call.method]) {
NSArray* frameData = call.arguments[0];
NSNumber* numWeights = call.arguments[1];
NSNumber* numFrames = call.arguments[2];
NSNumber* frameLenInMs = call.arguments[3];
float* framesArr = (float*)malloc([frameData count] *sizeof(float));
for(int i =0 ; i < [frameData count]; i++) {
*(framesArr+i) = [[frameData objectAtIndex:i] floatValue];
}
_viewer->animateWeights((float*)framesArr, [numWeights intValue], [numFrames intValue], [frameLenInMs floatValue]);
result(@"OK");
} else if([@"playAnimation" isEqualToString:call.method]) {
int animationIndex = [call.arguments[0] intValue];
bool loop = call.arguments[1];
_viewer->playAnimation(animationIndex, loop);
result(@"OK");
} else if ([@"stopAnimation" isEqualToString:call.method]) {
_viewer->stopAnimation();
result(@"OK");
} else if([@"getTargetNames" isEqualToString:call.method]) {
NSMutableArray* names = [self getTargetNames:call.arguments];
result(names);
} else if([@"getAnimationNames" isEqualToString:call.method]) {
NSMutableArray* names = [self getAnimationNames];
result(names);
} else if([@"applyWeights" isEqualToString:call.method]) {
NSArray* nWeights = call.arguments;
int count = [nWeights count];
float weights[count];
for(int i=0; i < count; i++) {
weights[i] = [nWeights[i] floatValue];
}
_viewer->applyWeights(weights, count);
result(@"OK");
} else if([@"zoom" isEqualToString:call.method]) {
_viewer->manipulator->scroll(0.0f, 0.0f, [call.arguments floatValue]);
result(@"OK");
} else {
result(FlutterMethodNotImplemented);
}
}
- (ResourceBuffer)loadResource:(const char* const)path {
NSString* p = [NSString stringWithFormat:@"%s", path];
NSString* key = [_registrar lookupKeyForAsset:p];
NSString* nsPath = [[NSBundle mainBundle] pathForResource:key
ofType:nil];
if (![[NSFileManager defaultManager] fileExistsAtPath:nsPath]) {
NSLog(@"Error: no file exists at %@", p);
exit(-1);
}
NSData* buffer = [NSData dataWithContentsOfFile:nsPath];
void* cpy = malloc([buffer length]);
memcpy(cpy, [buffer bytes], [buffer length]); // can we avoid this copy somehow?
_resourceId++;
ResourceBuffer rbuf(cpy, [buffer length], _resourceId);
return rbuf;
}
- (void)freeResource:(ResourceBuffer)rb {
free((void*)rb.data);
}
- (void)ready {
[_channel invokeMethod:@"ready" arguments:nil];
}
- (NSMutableArray*) getAnimationNames {
unique_ptr<vector<string>> list = _viewer->getAnimationNames();
NSMutableArray* asArray = [NSMutableArray arrayWithCapacity:list->size()];
for(int i = 0; i < list->size(); i++) {
asArray[i] = [NSString stringWithFormat:@"%s", list->at(i).c_str()];
}
return asArray;
}
- (NSMutableArray*) getTargetNames:(NSString*) meshName {
unique_ptr<vector<string>> list = _viewer->getTargetNames([meshName UTF8String]);
NSMutableArray* asArray = [NSMutableArray arrayWithCapacity:list->size()];
for(int i = 0; i < list->size(); i++) {
asArray[i] = [NSString stringWithFormat:@"%s", list->at(i).c_str()];
}
return asArray;
}
@end

View File

@@ -1,25 +0,0 @@
#ifndef FilamentNativeViewFactory_h
#define FilamentNativeViewFactory_h
#endif /* FilamentNativeViewFactory_h */
#import <Flutter/Flutter.h>
#import "FilamentView.h"
#include "FilamentViewer.hpp"
@interface FilamentNativeViewFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;
@end
@interface FilamentNativeView : NSObject <FlutterPlatformView>
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
registrar:(NSObject<FlutterPluginRegistrar>*)registrar;
- (UIView*)view;
@end

View File

@@ -1,67 +0,0 @@
#import "FilamentNativeViewFactory.h"
#import "FilamentMethodCallHandler.h"
using namespace polyvox;
static const FilamentMethodCallHandler* _shandler;
static ResourceBuffer loadResource(const char* name) {
return [_shandler loadResource:name];
}
static void* freeResource(ResourceBuffer rb) {
[_shandler freeResource:rb ];
return nullptr;
}
@implementation FilamentNativeViewFactory {
NSObject<FlutterPluginRegistrar>* _registrar;
}
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
self = [super init];
if (self) {
_registrar = registrar;
}
return self;
}
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args {
return [[FilamentNativeView alloc] initWithFrame:frame
viewIdentifier:viewId
arguments:args
registrar:_registrar];
}
@end
@implementation FilamentNativeView {
FilamentView* _view;
FilamentViewer* _viewer;
FilamentMethodCallHandler* _handler;
void* _layer;
}
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
registrar:(NSObject<FlutterPluginRegistrar>*)registrar {
if (self = [super init]) {
_view = [[FilamentView alloc] init];
_layer = (__bridge_retained void*)[_view layer];
_viewer = new FilamentViewer(_layer, loadResource, freeResource);
[_view setViewer:_viewer];
_handler = [[FilamentMethodCallHandler alloc] initWithRegistrar:registrar viewId:viewId viewer:_viewer ];
_shandler = _handler;
}
return self;
}
- (UIView*)view {
return _view;
}
@end

View File

@@ -1,32 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
#include "FilamentViewer.hpp"
NS_ASSUME_NONNULL_BEGIN
/**
* FILModelView is simply a UIView with an OpenGL layer.
*
*
*/
@interface FilamentView : UIView
- (void)setViewer:(polyvox::FilamentViewer*)viewer;
- (void)startDisplayLink;
- (void)stopDisplayLink;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// These defines are set in the "Preprocessor Macros" build setting for each scheme.
#include "FilamentView.h"
#include "FilamentViewer.hpp"
#import <Foundation/Foundation.h>
using namespace std;
using namespace polyvox;
@interface FilamentView ()
- (void)initCommon;
- (void)setViewer:(FilamentViewer*)viewer;
@end
@implementation FilamentView {
FilamentViewer* _viewer;
CADisplayLink* _displayLink;
}
- (void)setViewer:(FilamentViewer*)viewer {
_viewer = viewer;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initCommon];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder*)coder {
if (self = [super initWithCoder:coder]) {
[self initCommon];
}
return self;
}
- (void)initCommon {
[self initializeGLLayer];
}
- (void)initializeGLLayer {
CAEAGLLayer* glLayer = (CAEAGLLayer*)self.layer;
glLayer.opaque = YES;
}
+ (Class)layerClass {
return [CAEAGLLayer class];
}
- (void)layoutSubviews {
[super layoutSubviews];
NSLog(@"layout subview");
if(_viewer) {
_viewer->updateViewportAndCameraProjection(self.bounds.size.width, self.bounds.size.height, self.contentScaleFactor);
}
[self startDisplayLink];
}
- (void)setContentScaleFactor:(CGFloat)contentScaleFactor {
[super setContentScaleFactor:contentScaleFactor];
if(_viewer) {
_viewer->updateViewportAndCameraProjection(self.bounds.size.width, self.bounds.size.height, self.contentScaleFactor);
}
}
- (void)drawRect:(CGRect)rect {
NSLog(@"Drawing rect");
[super drawRect:rect];
}
- (void)startDisplayLink {
NSLog(@"Starting display link");
[self stopDisplayLink];
// Call our render method 60 times a second.
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
_displayLink.preferredFramesPerSecond = 60;
[_displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink {
[_displayLink invalidate];
_displayLink = nil;
}
- (void)render {
_viewer->render();
}
@end

View File

@@ -0,0 +1,147 @@
#ifndef _POLYVOX_FILAMENT_API_H
#define _POLYVOX_FILAMENT_API_H
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
using namespace polyvox;
extern "C" {
// ResourceBuffer create_resource_buffer(const void* data, const uint32_t size, const uint32_t id) {
// return ResourceBuffer {data, size, id };
// }
void* filament_viewer_new(void* texture, ResourceBuffer (*loadResource)(const char*), void (*freeResource)(uint32_t)) {
return nullptr;
}
void set_background_image(void* viewer, const char* path) {
((FilamentViewer*)viewer)->setBackgroundImage(path);
}
void load_skybox(void* viewer, const char* skyboxPath) {
((FilamentViewer*)viewer)->loadSkybox(skyboxPath);
}
void load_ibl(void* viewer, const char* iblPath) {
((FilamentViewer*)viewer)->loadIbl(iblPath);
}
void remove_skybox(void* viewer) {
((FilamentViewer*)viewer)->removeSkybox();
}
void remove_ibl(void* viewer) {
((FilamentViewer*)viewer)->removeIbl();
}
void* load_glb(void* viewer, const char* assetPath) {
return ((FilamentViewer*)viewer)->loadGlb(assetPath);
}
void* load_gltf(void* viewer, const char* assetPath, const char* relativePath) {
return ((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath);
}
bool set_camera(void* viewer, void* asset, const char* nodeName) {
return ((FilamentViewer*)viewer)->setCamera((SceneAsset*)asset, nodeName);
}
void render(
void* viewer
) {
((FilamentViewer*)viewer)->render();
}
void destroy_swap_chain(void* viewer) {
((FilamentViewer*)viewer)->destroySwapChain();
}
void* get_renderer(void* viewer) {
return ((FilamentViewer*)viewer)->getRenderer();
}
void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor) {
return ((FilamentViewer*)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor);
}
void scroll(void* viewer, float x, float y , float z) {
return ((FilamentViewer*)viewer)->manipulator->scroll(x, y, z);
}
void grab_begin(void* viewer, int x, int y, bool pan) {
((FilamentViewer*)viewer)->manipulator->grabBegin(x, y, pan);
}
void grab_update(void* viewer, int x, int y) {
((FilamentViewer*)viewer)->manipulator->grabUpdate(x, y);
}
void grab_end(void* viewer) {
((FilamentViewer*)viewer)->manipulator->grabEnd();
}
void apply_weights(void* asset, float* const weights, int count) {
((SceneAsset*)asset)->applyWeights(weights, count);
}
void animate_weights(void* asset, float* data, int numWeights, int numFrames, float frameRate) {
((SceneAsset*)asset)->animateWeights((float*)data, numWeights, numFrames, frameRate);
}
void play_animation(void* asset, int index, bool loop) {
((SceneAsset*)asset)->playAnimation(index, loop);
}
int get_animation_count(void* asset) {
auto names = ((SceneAsset*)asset)->getAnimationNames();
return names->size();
}
void get_animation_name(void* asset, char* const outPtr, int index) {
auto names = ((SceneAsset*)asset)->getAnimationNames();
string name = names->at(index);
strcpy(outPtr, name.c_str());
}
int get_target_name_count(void* asset, const char* meshName) {
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getTargetNames(meshName);
return names->size();
}
void get_target_name(void* asset, const char* meshName, char* const outPtr, int index ) {
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getTargetNames(meshName);
string name = names->at(index);
strcpy(outPtr, name.c_str());
}
void remove_asset(void* viewer, void* asset) {
((FilamentViewer*)viewer)->removeAsset((SceneAsset*)asset);
}
void clear_assets(void* viewer) {
((FilamentViewer*)viewer)->clearAssets();
}
void set_texture(void* asset, const char* assetPath, int renderableIndex) {
((SceneAsset*)asset)->setTexture(assetPath, renderableIndex);
}
void transform_to_unit_cube(void* asset) {
((SceneAsset*)asset)->transformToUnitCube();
}
// void set_position(void* asset, float x, float y, float z) {
// ((SceneAsset*)asset)->setPosition(x, y, z);
// }
// void set_rotation(void* asset, float rads, float x, float y, float z) {
// ((SceneAsset*)asset)->setRotation(rads, x, y, z);
// }
}
#endif

View File

@@ -0,0 +1,56 @@
#ifndef _POLYVOX_FILAMENT_API_H
#define _POLYVOX_FILAMENT_API_H
#include "ResourceBuffer.hpp"
typedef struct ResourceBuffer ResourceBuffer;
//ResourceBuffer create_resource_buffer(const void* data, const uint32_t size, const uint32_t id);
void* filament_viewer_new(void* texture, void* loadResource, void* freeResource);
void set_background_image(void* viewer, const char* path);
void load_skybox(void* viewer, const char* skyboxPath);
void load_ibl(void* viewer, const char* iblPath);
void remove_skybox(void* viewer);
void remove_ibl(void* viewer);
void* load_glb(void* viewer, const char* assetPath);
void* load_gltf(void* viewer, const char* assetPath, const char* relativePath);
bool set_camera(void* viewer, void* asset, const char* nodeName);
void render(void* viewer);
void destroy_swap_chain(void* viewer);
void* get_renderer(void* viewer);
void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor);
void scroll(void* viewer, float x, float y , float z);
void grab_begin(void* viewer, int x, int y, bool pan);
void grab_update(void* viewer, int x, int y);
void grab_end(void* viewer);
void apply_weights(void* asset, float* const weights, int count);
void animate_weights(void* asset, float* data, int numWeights, int numFrames, float frameRate);
void play_animation(void* asset, int index, bool loop);
int get_animation_count(void* asset);
void get_animation_name(void* asset, char* const outPtr, int index);
void get_target_name(void* asset, const char* meshName, char* const outPtr, int index );
int get_target_name_count(void* asset, const char* meshName);
void remove_asset(void* viewer, void* asset);
void clear_assets(void* viewer);
void set_texture(void* asset, const char* assetPath, int renderableIndex);
void transform_to_unit_cube(void* asset);
void set_position(void* asset, float x, float y, float z);
void set_rotation(void* asset, float rads, float x, float y, float z);
#endif

View File

@@ -0,0 +1,32 @@
#include "PolyvoxFilamentApi.h"
#include "FilamentViewer.hpp"
#include "ResourceBuffer.hpp"
#include "SceneResources.hpp"
#include <functional>
using namespace polyvox;
using namespace std;
extern "C" {
using RawLoadType = ResourceBuffer(const char*, void* resource);
using RawFreeType = void(uint32_t, void*);
void* filament_viewer_new_ios(void* pb, void* loadResource, void* freeResource, void* resources) {
FreeResource _freeResource = [=](uint32_t rid) {
reinterpret_cast<RawFreeType*>(freeResource)(rid, resources);
};
function<ResourceBuffer(const char*)> _loadResource = [=](const char* uri){
auto cast = reinterpret_cast<RawLoadType*>(loadResource);
return cast(uri, resources);
};
auto viewer = new FilamentViewer(pb,_loadResource, _freeResource);
return (void*)viewer;
}
void create_swap_chain(void* viewer, void* texture) {
((FilamentViewer*)viewer)->createSwapChain(texture);
}
}

View File

@@ -0,0 +1,3 @@
void* filament_viewer_new_ios(void* texture, void* loadResource, void* freeResource, void* resources);
void create_swap_chain(void* viewer, void* texture);

View File

@@ -0,0 +1,63 @@
#import "PolyvoxFilamentPlugin.h"
#if __has_include(<polyvox_filament/polyvox_filament-Swift.h>)
#import <polyvox_filament/polyvox_filament-Swift.h>
#else
// Support project import fallback if the generated compatibility header
// is not copied when this plugin is created as a library.
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
#import "polyvox_filament-Swift.h"
#endif
@implementation PolyvoxFilamentPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftPolyvoxFilamentPlugin registerWithRegistrar:registrar];
}
@end
// - (ResourceBuffer)loadResource:(const char* const)path {
// NSString* p = [NSString stringWithFormat:@"%s", path];
// NSString* key = [_registrar lookupKeyForAsset:p];
// NSString* nsPath = [[NSBundle mainBundle] pathForResource:key
// ofType:nil];
// if (![[NSFileManager defaultManager] fileExistsAtPath:nsPath]) {
// NSLog(@"Error: no file exists at %@", p);
// exit(-1);
// }
// NSData* buffer = [NSData dataWithContentsOfFile:nsPath];
// void* cpy = malloc([buffer length]);
// memcpy(cpy, [buffer bytes], [buffer length]); // can we avoid this copy somehow?
// _resourceId++;
// ResourceBuffer rbuf(cpy, [buffer length], _resourceId);
// return rbuf;
// }
// - (void)freeResource:(ResourceBuffer)rb {
// free((void*)rb.data);
// }
// - (void)ready {
// [_channel invokeMethod:@"ready" arguments:nil];
// }
// - (NSMutableArray*) getAnimationNames {
// unique_ptr<vector<string>> list = _viewer->getAnimationNames();
// NSMutableArray* asArray = [NSMutableArray arrayWithCapacity:list->size()];
// for(int i = 0; i < list->size(); i++) {
// asArray[i] = [NSString stringWithFormat:@"%s", list->at(i).c_str()];
// }
// return asArray;
// }
// - (NSMutableArray*) getTargetNames:(NSString*) meshName {
// unique_ptr<vector<string>> list = _viewer->getTargetNames([meshName UTF8String]);
// NSMutableArray* asArray = [NSMutableArray arrayWithCapacity:list->size()];
// for(int i = 0; i < list->size(); i++) {
// asArray[i] = [NSString stringWithFormat:@"%s", list->at(i).c_str()];
// }
// return asArray;
// }
// @end
// @end

View File

@@ -1,12 +0,0 @@
#import "PolyvoxFilamentPlugin.h"
#import "FilamentNativeViewFactory.h"
FilamentNativeViewFactory* factory;
@implementation PolyvoxFilamentPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
factory =
[[FilamentNativeViewFactory alloc] initWithRegistrar:registrar];
[registrar registerViewFactory:factory withId:@"app.polyvox.filament/filament_view"];
}
@end

View File

@@ -0,0 +1 @@
#import "PolyvoxFilamentApi.h"

View File

@@ -0,0 +1,283 @@
import Flutter
import UIKit
import OpenGLES.ES3
import GLKit
public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture {
var registrar : FlutterPluginRegistrar
var textureId: Int64?
var registry: FlutterTextureRegistry
var width: Double = 0
var height: Double = 0
var context: EAGLContext?;
var targetPixelBuffer: CVPixelBuffer?;
var textureCache: CVOpenGLESTextureCache?;
var texture: CVOpenGLESTexture? = nil;
var frameBuffer: GLuint = 0;
var pixelBufferAttrs = [
kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_32BGRA),
kCVPixelBufferOpenGLCompatibilityKey: kCFBooleanTrue,
kCVPixelBufferOpenGLESCompatibilityKey: kCFBooleanTrue,
kCVPixelBufferIOSurfacePropertiesKey: [:]
] as CFDictionary
var resources:NSMutableDictionary = [:]
var viewer:UnsafeMutableRawPointer? = nil
var displayLink:CADisplayLink? = nil
static var messenger : FlutterBinaryMessenger? = nil;
var loadResourcePtr: UnsafeMutableRawPointer? = nil
var freeResourcePtr: UnsafeMutableRawPointer? = nil
var resourcesPtr : UnsafeMutableRawPointer? = nil
var loadResource : @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer) -> ResourceBuffer = { uri, resourcesPtr in
print("Loading resource buffer")
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(resourcesPtr).takeUnretainedValue()
let uriString = String(cString:uri.assumingMemoryBound(to: UInt8.self))
let key = instance.registrar.lookupKey(forAsset:uriString)
let path = Bundle.main.path(forResource: key, ofType:nil)
do {
let foo: String = path!
let data = try Data(contentsOf: URL(fileURLWithPath:foo))
let resId = instance.resources.count
let nsData = data as NSData
instance.resources[resId] = nsData
let rawPtr = nsData.bytes
return ResourceBuffer(data:rawPtr, size:UInt32(nsData.count), id:UInt32(resId))
} catch {
print("Error opening file: \(error)")
return ResourceBuffer()
}
return ResourceBuffer()
}
var freeResource : @convention(c) (UInt32,UnsafeMutableRawPointer) -> () = { rid, resourcesPtr in
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(resourcesPtr).takeUnretainedValue()
instance.resources.removeObject(forKey:rid)
}
func createDisplayLink() {
displayLink = CADisplayLink(target: self,
selector: #selector(doRender))
displayLink!.add(to: .current, forMode: RunLoop.Mode.default)
}
@objc func doRender() {
if(viewer != nil) {
render(viewer)
self.registry.textureFrameAvailable(self.textureId!)
}
}
public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
if(targetPixelBuffer == nil) {
print("empty")
return nil;
}
return Unmanaged.passRetained(targetPixelBuffer!);
}
public static func register(with registrar: FlutterPluginRegistrar) {
let _messenger = registrar.messenger();
messenger = _messenger;
let channel = FlutterMethodChannel(name: "app.polyvox.filament/event", binaryMessenger: _messenger)
let instance = SwiftPolyvoxFilamentPlugin(textureRegistry: registrar.textures(), registrar:registrar)
registrar.addMethodCallDelegate(instance, channel: channel)
}
init(textureRegistry: FlutterTextureRegistry, registrar:FlutterPluginRegistrar) {
self.registry = textureRegistry;
self.registrar = registrar
}
private func createPixelBuffer(width:Int, height:Int) {
if(targetPixelBuffer != nil) {
destroy_swap_chain(self.viewer)
}
if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height),
kCVPixelFormatType_32BGRA, pixelBufferAttrs, &targetPixelBuffer) != kCVReturnSuccess) {
print("Error allocating pixel buffer")
}
if(self.viewer != nil) {
create_swap_chain(self.viewer, unsafeBitCast(targetPixelBuffer!, to: UnsafeMutableRawPointer.self))
update_viewport_and_camera_projection(self.viewer!, Int32(width), Int32(height), 1.0);
}
print("Pixel buffer created")
}
private func initialize(width:Int32, height:Int32) {
createPixelBuffer(width:Int(width), height:Int(height))
self.textureId = self.registry.register(self)
loadResourcePtr = unsafeBitCast(loadResource, to: UnsafeMutableRawPointer.self)
freeResourcePtr = unsafeBitCast(freeResource, to: UnsafeMutableRawPointer.self)
viewer = filament_viewer_new_ios(
unsafeBitCast(targetPixelBuffer!, to: UnsafeMutableRawPointer.self),
loadResourcePtr!,
freeResourcePtr!,
Unmanaged.passUnretained(self).toOpaque()
)
update_viewport_and_camera_projection(self.viewer!, Int32(width), Int32(height), 1.0);
createDisplayLink()
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let methodName = call.method;
switch methodName {
case "initialize":
let args = call.arguments as! Array<Int32>
initialize(width:args[0], height:args[1])
result(self.textureId);
case "setBackgroundImage":
let uri = call.arguments as! String
set_background_image(self.viewer!, uri)
render(self.viewer!)
self.registry.textureFrameAvailable(self.textureId!)
result("OK")
case "resize":
let args = call.arguments as! Array<Double>
let width = Int(args[0])
let height = Int(args[1])
createPixelBuffer(width: width, height:height)
result("OK")
case "loadSkybox":
load_skybox(self.viewer!, call.arguments as! String)
result("OK");
case "removeSkybox":
remove_skybox(self.viewer!)
result("OK");
case "loadGlb":
let assetPtr = load_glb(self.viewer, call.arguments as! String)
result(unsafeBitCast(assetPtr, to:Int64.self));
case "loadGltf":
let args = call.arguments as! Array<Any?>
result(load_gltf(self.viewer, args[0] as! String, args[1] as! String));
case "removeAsset":
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: call.arguments as! Int)
remove_asset(viewer!, assetPtr)
result("OK")
case "clearAssets":
clear_assets(viewer!)
result("OK")
case "loadIbl":
load_ibl(self.viewer, call.arguments as! String)
result("OK");
case "removeIbl":
remove_ibl(self.viewer)
result("OK");
case "setCamera":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
set_camera(self.viewer, assetPtr, args[1] as! String)
result("OK");
case "playAnimation":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let animationIndex = args[1] as! Int32;
let loop = args[2] as! Bool;
play_animation(assetPtr, animationIndex, loop)
result("OK");
case "stopAnimation":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let animationIndex = args[1] as! Int32
// stop_animation(assetPtr, animationIndex) // TODO
result("OK");
case "getTargetNames":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let meshName = args[1] as! String
let numNames = get_target_name_count(assetPtr, meshName)
var names = [String]()
for i in 0...numNames - 1{
let outPtr = UnsafeMutablePointer<CChar>.allocate(capacity:256)
get_target_name(assetPtr, meshName, outPtr, i)
names.append(String(cString:outPtr))
}
result(names);
case "getAnimationNames":
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: call.arguments as! Int)
let numNames = get_animation_count(assetPtr)
var names = [String]()
for i in 0...numNames - 1{
let outPtr = UnsafeMutablePointer<CChar>.allocate(capacity:256)
get_animation_name(assetPtr, outPtr, i)
names.append(String(cString:outPtr))
}
result(names);
case "applyWeights":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let weights = args[1] as! Array<Double>
weights.map { Float($0) }.withUnsafeBufferPointer {
apply_weights(assetPtr, UnsafeMutablePointer<Float>.init(mutating:$0.baseAddress), Int32(weights.count))
}
result("OK")
case "zoom":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let factor = args[1] as! Double
scroll(assetPtr, 0,0, Float(factor))
result("OK")
case "animateWeights":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let frameData = args[1] as! Array<Double>
let numWeights = args[2] as! Int
let numFrames = args[3] as! Int
let frameLenInMs = args[4] as! Double
frameData.map { Float($0)}.withUnsafeBufferPointer {
animate_weights(assetPtr, UnsafeMutablePointer<Float>.init(mutating:$0.baseAddress), Int32(numWeights), Int32(numFrames), Float(frameLenInMs))
}
result("OK")
default:
result(FlutterMethodNotImplemented)
}
}
}
// } else if([@"panStart" isEqualToString:call.method]) {
// _viewer->manipulator->grabBegin([call.arguments[0] intValue], [call.arguments[1] intValue], true);
// result(@"OK");
// } else if([@"panUpdate" isEqualToString:call.method]) {
// _viewer->manipulator->grabUpdate([call.arguments[0] intValue], [call.arguments[1] intValue]);
// result(@"OK");
// } else if([@"panEnd" isEqualToString:call.method]) {
// _viewer->manipulator->grabEnd();
// result(@"OK");
// } else if([@"rotateStart" isEqualToString:call.method]) {
// _viewer->manipulator->grabBegin([call.arguments[0] intValue], [call.arguments[1] intValue], false);
// result(@"OK");
// } else if([@"rotateUpdate" isEqualToString:call.method]) {
// _viewer->manipulator->grabUpdate([call.arguments[0] intValue], [call.arguments[1] intValue]);
// result(@"OK");
// } else if([@"rotateEnd" isEqualToString:call.method]) {
// _viewer->manipulator->grabEnd();
// result(@"OK");
// } else

View File

@@ -13,7 +13,7 @@ A new flutter plugin project.
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*', 'src/*.*', 'src/morph/*.*', 'src/morph/UbershaderLoader.cpp' #, "include/**/*"
s.source_files = 'Classes/**/*', 'src/ResourceBuffer.hpp', 'src/FilamentViewer.cpp', 'src/SceneAsset.cpp', 'src/SceneAssetLoader.cpp', 'src/StreamBufferAdapter.cpp', 'src/image/imagematerial.c', 'src/image/imagematerials_ios.c'
#s.header_dir = "include"
#s.header_mappings_dir = "include"
s.dependency 'Flutter'
@@ -23,26 +23,27 @@ A new flutter plugin project.
s.library = "c++"
s.user_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "$(inherited)"',
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image "$(inherited)"',
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -limageio -lpng -lpng16 -ltinyexr -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd',
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
}
# Flutter.framework does not contain a i386 slice.
# s.user_target_xcconfig = {
# 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
# 'ALWAYS_SEARCH_USER_PATHS' => 'YES',
# 'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src", "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/morph" "$(inherited)"',
# 'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "$(inherited)"',
# "CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
# #"CLANG_CXX_LIBRARY" => "libc++"
# "OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_resources_lite -lgltfio_core -lfilament-iblprefilter -limage -lcamutils -lgltfio_resources -lfilaflat -ldracodec -libl',
# 'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
# }
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "$(inherited)"',
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src", "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/morph" "$(inherited)"',
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image "$(inherited)"',
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_resources_lite -lgltfio_core -lfilament-iblprefilter -limage -lcamutils -lgltfio_resources -lfilaflat -ldracodec -libl',
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -limageio -lpng -lpng16 -ltinyexr -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd',
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
}

View File

@@ -0,0 +1,44 @@
#ifndef RESOURCE_BUFFER_H
#define RESOURCE_BUFFER_H
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
//
// Pairs a memory buffer with an ID that can be used to unload the backing asset if needed.
// Use this when you want to load an asset from a resource that requires more than just `free` on the underlying buffer.
// e.g.
// ```
// uint64_t id = get_next_resource_id();
// AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER);
// off_t length = AAsset_getLength(asset);
// const void * buffer = AAsset_getBuffer(asset);
// uint8_t *buf = new uint8_t[length ];
// memcpy(buf,buffer, length);
// ResourceBuffer rb(buf, length, id);
// ...
// ...
// (elsewhere)
// AAsset* asset = get_asset_from_id(rb.id);
// AAsset_close(asset);
// free_asset_id(rb.id);
//
struct ResourceBuffer {
#if defined(__cplusplus)
ResourceBuffer(const void* data, const uint32_t size, const uint32_t id) : data(data), size(size), id(id) {};
ResourceBuffer& operator=(ResourceBuffer other) {
data = other.data;
size = other.size;
id = other.id;
return *this;
}
#endif
const void* data;
uint32_t size;
uint32_t id;
};
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -139,7 +139,7 @@ void SceneAsset::setTexture(const char* resourcePath, int renderableIndex) {
delete inputStream;
_freeResource(imageResource);
_freeResource(imageResource.id);
uint32_t channels = image->getChannels();
uint32_t w = image->getWidth();

View File

@@ -52,7 +52,7 @@ SceneAsset *SceneAssetLoader::fromGltf(const char *uri,
ResourceLoader::BufferDescriptor b(buf.data, buf.size);
_resourceLoader->addResourceData(resourceUris[i], std::move(b));
_freeResource(buf);
_freeResource(buf.id);
}
_resourceLoader->loadResources(asset);
@@ -115,7 +115,7 @@ SceneAsset *SceneAssetLoader::fromGlb(const char *uri) {
asset->releaseSourceData();
Log("Source data released.");
_freeResource(rbuf);
_freeResource(rbuf.id);
Log("Successfully loaded GLB.");
return new SceneAsset(asset, _engine, _ncm, _loadResource, _freeResource);
@@ -128,4 +128,4 @@ void SceneAssetLoader::remove(SceneAsset *asset) {
_assetLoader->destroyAsset(asset->_asset);
delete asset;
}
} // namespace polyvox
} // namespace polyvox

View File

@@ -8,6 +8,7 @@
#include "SceneResources.hpp"
#include "SceneAsset.hpp"
#include "ResourceBuffer.hpp"
namespace polyvox {
using namespace filament;

View File

@@ -1,52 +1,21 @@
#pragma once
#include <functional>
#include <memory>
#include "ResourceBuffer.hpp"
namespace polyvox {
//
// Pairs a memory buffer with an ID that can be used to unload the backing asset if needed.
// Use this when you want to load an asset from a resource that requires more than just `free` on the underlying buffer.
// e.g.
// ```
// uint64_t id = get_next_resource_id();
// AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER);
// off_t length = AAsset_getLength(asset);
// const void * buffer = AAsset_getBuffer(asset);
// uint8_t *buf = new uint8_t[length ];
// memcpy(buf,buffer, length);
// ResourceBuffer rb(buf, length, id);
// ...
// ...
// (elsewhere)
// AAsset* asset = get_asset_from_id(rb.id);
// AAsset_close(asset);
// free_asset_id(rb.id);
//
struct ResourceBuffer {
ResourceBuffer(const void* data, const uint32_t size, const uint32_t id) : data(data), size(size), id(id) {};
ResourceBuffer& operator=(ResourceBuffer other)
{
data = other.data;
size = other.size;
id = other.id;
return *this;
}
const void* data;
uint32_t size;
uint32_t id;
};
using namespace std;
//
// Typedef for any function that loads a resource into a ResourceBuffer from an asset URI.
// Typedef for a function that loads a resource into a ResourceBuffer from an asset URI.
//
using LoadResource = std::function<ResourceBuffer(const char* uri)>;
using LoadResource = function<ResourceBuffer(const char* uri)>;
//
// Typedef for any function that frees a ResourceBuffer.
// Typedef for a function that frees an ID associated with a ResourceBuffer.
//
using FreeResource = std::function<void (ResourceBuffer)>;
using FreeResource = function<void (uint32_t)>;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
@@ -110,4 +79,5 @@ namespace polyvox {
float* frameData;
int numWeights;
};
}
}