Skip to content

Commit

Permalink
Fix/js api - Introduce new consumer JS API (#22)
Browse files Browse the repository at this point in the history
Co-authored-by: Marc Rousavy <[email protected]>
  • Loading branch information
hannojg and mrousavy authored Mar 8, 2024
1 parent ca56b03 commit 7d6adbc
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 150 deletions.
4 changes: 2 additions & 2 deletions package/.prettierrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ module.exports = {
bracketSpacing: true,
bracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
trailingComma: 'es5',
semi: false,
tabWidth: 2,
useTabs: false,
printWidth: 140
printWidth: 140,
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private void setupView() {
textureView.setClipToOutline(true);
textureView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
textureView.setSurfaceTextureListener(this);
textureView.setOpaque(false);
addView(textureView);
}

Expand Down
3 changes: 0 additions & 3 deletions package/cpp/core/EngineWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,6 @@ void EngineWrapper::surfaceSizeChanged(int width, int height) {
if (_view) {
_view->setViewport(0, 0, width, height);
}

// TODO: when the surface resizes we need to update the camera projection, but that one is owned by JS now.
// updateCameraProjection();
}

void EngineWrapper::destroySurface() {
Expand Down
26 changes: 18 additions & 8 deletions package/example/ios/FilamentExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
89C6BE57DB24E9ADA2F236DE /* Pods-FilamentExample-FilamentExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FilamentExample-FilamentExampleTests.release.xcconfig"; path = "Target Support Files/Pods-FilamentExample-FilamentExampleTests/Pods-FilamentExample-FilamentExampleTests.release.xcconfig"; sourceTree = "<group>"; };
BE173E0B3B23427D84C92C91 /* pijamas.glb */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = pijamas.glb; path = ../assets/pijamas.glb; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
7A69823713B34897AF31CAF0 /* default_env_ibl.ktx */ = {isa = PBXFileReference; name = "default_env_ibl.ktx"; path = "../assets/default_env_ibl.ktx"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
3E2439B417DA497E83EB1F05 /* pengu.glb */ = {isa = PBXFileReference; name = "pengu.glb"; path = "../assets/pengu.glb"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
BE173E0B3B23427D84C92C91 /* pijamas.glb */ = {isa = PBXFileReference; name = "pijamas.glb"; path = "../assets/pijamas.glb"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
034C2E5BF6EE449290528E8D /* pirate.glb */ = {isa = PBXFileReference; name = "pirate.glb"; path = "../assets/pirate.glb"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -169,6 +173,18 @@
path = Pods;
sourceTree = "<group>";
};
995DFB4F30584326AD631A9F /* Resources */ = {
isa = "PBXGroup";
children = (
7A69823713B34897AF31CAF0 /* default_env_ibl.ktx */,
3E2439B417DA497E83EB1F05 /* pengu.glb */,
BE173E0B3B23427D84C92C91 /* pijamas.glb */,
034C2E5BF6EE449290528E8D /* pirate.glb */,
);
name = Resources;
sourceTree = "<group>";
path = "";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -607,10 +623,7 @@
"-DFOLLY_USE_LIBCPP=1",
"-DFOLLY_CFG_NO_COROUTINES=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
Expand Down Expand Up @@ -678,10 +691,7 @@
"-DFOLLY_USE_LIBCPP=1",
"-DFOLLY_CFG_NO_COROUTINES=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
Expand Down
67 changes: 64 additions & 3 deletions package/example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,71 @@
import * as React from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'

import { StyleSheet } from 'react-native'
import { FilamentView } from 'react-native-filament'
import { Platform, StyleSheet } from 'react-native'
import { FilamentProxy, FilamentView, Float3, RenderCallback } from 'react-native-filament'

const engine = FilamentProxy.createEngine()

const penguModelPath = Platform.select({
android: 'custom/pengu.glb',
ios: 'pengu.glb',
})!

const indirectLightPath = Platform.select({
android: 'custom/default_env_ibl.ktx',
ios: 'default_env_ibl.ktx',
})!

// Camera config:
const cameraPosition: Float3 = [0, 0, 8]
const cameraTarget: Float3 = [0, 0, 0]
const cameraUp: Float3 = [0, 1, 0]
const focalLengthInMillimeters = 28
const near = 0.1
const far = 1000

export default function App() {
return <FilamentView style={styles.filamentView} />
const [_pengu, penguAnimator] = useMemo(() => {
const modelBuffer = FilamentProxy.getAssetByteBuffer(penguModelPath)
const asset = engine.loadAsset(modelBuffer)
const animator = asset.getAnimator()
asset.releaseSourceData()

return [asset, animator]
}, [])

const prevAspectRatio = useRef(0)
const renderCallback: RenderCallback = useCallback(
(_timestamp, _startTime, passedSeconds) => {
const view = engine.getView()
const aspectRatio = view.aspectRatio
if (prevAspectRatio.current !== aspectRatio) {
prevAspectRatio.current = aspectRatio
// Setup camera lens:
const camera = engine.getCamera()
camera.setLensProjection(focalLengthInMillimeters, aspectRatio, near, far)
}

penguAnimator.applyAnimation(0, passedSeconds)
penguAnimator.updateBoneMatrices()

engine.getCamera().lookAt(cameraPosition, cameraTarget, cameraUp)
},
[penguAnimator]
)

// Setup the 3D scene:
useEffect(() => {
// Create a default light:
const indirectLightBuffer = FilamentProxy.getAssetByteBuffer(indirectLightPath)
engine.setIndirectLight(indirectLightBuffer)

// Create a directional light for supporting shadows
const light = engine.createLightEntity('directional', 6500, 10000, 0, -1, 0, true)
engine.getScene().addEntity(light)
}, [])

return <FilamentView style={styles.filamentView} engine={engine} renderCallback={renderCallback} />
}

const styles = StyleSheet.create({
Expand Down
5 changes: 3 additions & 2 deletions package/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-filament",
"version": "0.1.0",
"version": "0.2.0",
"description": "A real-time physically based 3D rendering engine for React Native",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down Expand Up @@ -97,7 +97,8 @@
"release-it": {
"git": {
"commitMessage": "chore: release ${version}",
"tagName": "v${version}"
"tagName": "v${version}",
"requireCleanWorkingDir": false
},
"npm": {
"publish": true
Expand Down
146 changes: 16 additions & 130 deletions package/src/FilamentView.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,22 @@
import React from 'react'
import { Button, findNodeHandle, NativeMethods, Platform, ScrollView, View } from 'react-native'
import { findNodeHandle, NativeMethods } from 'react-native'
import { FilamentProxy } from './native/FilamentProxy'
import { FilamentNativeView, NativeProps } from './native/FilamentNativeView'
import type { Float3 } from './types/float3'
import { Animator } from './types/Animator'
import { Engine, RenderCallback } from './types'

type FilamentViewProps = NativeProps
type FilamentViewProps = NativeProps & {
engine: Engine
renderCallback: RenderCallback
}

type RefType = React.Component<NativeProps> & Readonly<NativeMethods>

const penguModelPath = Platform.select({
android: 'custom/pengu.glb',
ios: 'pengu.glb',
})!

const indirectLightPath = Platform.select({
android: 'custom/default_env_ibl.ktx',
ios: 'default_env_ibl.ktx',
})!

const pirateHatPath = Platform.select({
android: 'custom/pirate.glb',
ios: 'pirate.glb',
})!

const pijamaPath = Platform.select({
android: 'custom/pijamas.glb',
ios: 'pijamas.glb',
})!

// Temporarily
type _State = {
animationNames: string[]
currentAnimation: number
}

export class FilamentView extends React.PureComponent<FilamentViewProps, _State> {
export class FilamentView extends React.PureComponent<FilamentViewProps> {
private readonly ref: React.RefObject<RefType>
private readonly engine = FilamentProxy.createEngine()
private animator: Animator
private hatAnimator: Animator
private pijamaAnimator: Animator

constructor(props: FilamentViewProps) {
super(props)
this.ref = React.createRef<RefType>()

const pengu = this.loadPengu()
this.animator = pengu.getAnimator()
this.state = {
animationNames: Array.from({ length: this.animator.getAnimationCount() }, (_, i) => this.animator.getAnimationName(i)),
currentAnimation: 0,
}

const pirateHatBuffer = FilamentProxy.getAssetByteBuffer(pirateHatPath)
const pirateHatAsset = this.engine.loadAsset(pirateHatBuffer)
this.hatAnimator = pirateHatAsset.createAnimatorWithAnimationsFrom(pengu)
const pijamaBuffer = FilamentProxy.getAssetByteBuffer(pijamaPath)
const pijamaAsset = this.engine.loadAsset(pijamaBuffer)
this.pijamaAnimator = pijamaAsset.createAnimatorWithAnimationsFrom(pengu)

// Cleanup memory after loading the asset
pengu.releaseSourceData()
pirateHatAsset.releaseSourceData()
pijamaAsset.releaseSourceData()

this.setup3dScene()
}

// TODO: Does this also work for Fabric?
Expand All @@ -79,49 +30,9 @@ export class FilamentView extends React.PureComponent<FilamentViewProps, _State>
}

componentDidMount() {
// TODO: lets get rid of this timeout
setTimeout(() => {
this.setupSurface()
}, 100)
}

loadPengu = () => {
// Load a model into the scene:
const modelBuffer = FilamentProxy.getAssetByteBuffer(penguModelPath)
const penguAsset = this.engine.loadAsset(modelBuffer)

return penguAsset
}

setup3dScene = () => {
// Create a default light:
const indirectLightBuffer = FilamentProxy.getAssetByteBuffer(indirectLightPath)
this.engine.setIndirectLight(indirectLightBuffer)

// Create a directional light for supporting shadows
const light = this.engine.createLightEntity('directional', 6500, 10000, 0, -1, 0, true)
this.engine.getScene().addEntity(light)
}

renderCallback = (_timestamp: number, _startTime: number, passedSeconds: number) => {
const cameraPosition: Float3 = [0, 0, 8]
const cameraTarget: Float3 = [0, 0, 0]
const cameraUp: Float3 = [0, 1, 0]

if (this.animator) {
this.animator.applyAnimation(this.state.currentAnimation, passedSeconds)
this.animator.updateBoneMatrices()
}
if (this.hatAnimator) {
this.hatAnimator.applyAnimation(this.state.currentAnimation, passedSeconds)
this.hatAnimator.updateBoneMatrices()
}
if (this.pijamaAnimator) {
this.pijamaAnimator.applyAnimation(this.state.currentAnimation, passedSeconds)
this.pijamaAnimator.updateBoneMatrices()
}

this.engine.getCamera().lookAt(cameraPosition, cameraTarget, cameraUp)
// TODO(Marc): I had to add this setTimeout(, 0), otherwise there would be no render output on iOS.
// I assume its because the surface isn't ready yet or something?
setTimeout(this.setupSurface, 0)
}

setupSurface = () => {
Expand All @@ -130,41 +41,16 @@ export class FilamentView extends React.PureComponent<FilamentViewProps, _State>
const surfaceProvider = fView.getSurfaceProvider()

// Link the surface with the engine:
this.engine.setSurfaceProvider(surfaceProvider)

// Configure camera lens (Important, do so after linking the surface)
const view = this.engine.getView()
const aspectRatio = view.aspectRatio
const focalLengthInMillimeters = 28
const near = 0.1
const far = 1000
this.engine.getCamera().setLensProjection(focalLengthInMillimeters, aspectRatio, near, far)
console.log('aspectRatio', aspectRatio)
// Alternatively setProjection can be used
this.props.engine.setSurfaceProvider(surfaceProvider)

// Callback for rendering every frame
this.engine.setRenderCallback(this.renderCallback)
// Set the render callback:
this.props.engine.setRenderCallback(this.props.renderCallback)
}

/** @internal */
public render(): React.ReactNode {
return (
<View style={this.props.style}>
<FilamentNativeView ref={this.ref} {...this.props} />

<ScrollView style={{ position: 'absolute', bottom: 0, maxHeight: 200, width: '100%' }}>
{this.state.animationNames.map((name, i) => (
<Button
key={i}
onPress={() => {
// this.prevAnimationIndex = this.state.currentAnimation
this.setState({ currentAnimation: i })
}}
title={name}
/>
))}
</ScrollView>
</View>
)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { engine, renderCallback, ...nativeProps } = this.props
return <FilamentNativeView ref={this.ref} {...nativeProps} />
}
}
6 changes: 4 additions & 2 deletions package/src/types/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { Entity } from './Entity'
import { FilamentAsset } from './FilamentAsset'
import { Float3 } from './float3'

export type RenderCallback = (timestamp: number, startTime: number, passedSeconds: number) => void

export interface Engine {
setSurfaceProvider(surfaceProvider: SurfaceProvider): void
setRenderCallback(callback: (timestamp: number, startTime: number, passedSeconds: number) => void): void
setRenderCallback(callback: RenderCallback): void

/**
* Given a @see FilamentBuffer (e.g. from a .glb file), load the asset into the engine.
Expand Down Expand Up @@ -50,7 +52,7 @@ export interface Engine {
directionX: number,
directionY: number,
directionZ: number,
castShadows: boolean
castShadows: boolean,
): Entity

/**
Expand Down
1 change: 1 addition & 0 deletions package/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './Renderer'
export * from './Scene'
export * from './View'
export * from './SwapChain'
export * from './float3'

0 comments on commit 7d6adbc

Please sign in to comment.