summit/frontend/node_modules/@react-three/fiber/native/dist/react-three-fiber-native.es...

500 lines
16 KiB
JavaScript

import { c as createPointerEvents, e as extend, u as useMutableCallback, b as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode, f as createEvents } from '../../dist/events-776716bd.esm.js';
export { t as ReactThreeFiber, z as _roots, x as act, p as addAfterEffect, o as addEffect, q as addTail, n as advance, k as applyProps, y as buildGraph, g as context, f as createEvents, c as createPointerEvents, h as createPortal, b as createRoot, l as dispose, e as extend, s as flushGlobalEffects, v as flushSync, w as getRootState, m as invalidate, j as reconciler, r as render, d as unmountComponentAtNode, F as useFrame, G as useGraph, A as useInstanceHandle, H as useLoader, C as useStore, D as useThree } from '../../dist/events-776716bd.esm.js';
import * as React from 'react';
import * as THREE from 'three';
import { PanResponder, PixelRatio, View, StyleSheet, Platform, Image, NativeModules } from 'react-native';
import { GLView } from 'expo-gl';
import { FiberProvider, useContextBridge } from 'its-fine';
import { jsx } from 'react/jsx-runtime';
import { Asset } from 'expo-asset';
import * as fs from 'expo-file-system';
import { fromByteArray } from 'base64-js';
import { Buffer } from 'buffer';
import 'react-reconciler/constants';
import 'zustand';
import 'suspend-react';
import 'react-reconciler';
import 'scheduler';
/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
const CanvasImpl = /*#__PURE__*/React.forwardRef(({
children,
style,
gl,
events = createPointerEvents,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
...props
}, forwardedRef) => {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React.useMemo(() => extend(THREE), []);
const Bridge = useContextBridge();
const [{
width,
height,
top,
left
}, setSize] = React.useState({
width: 0,
height: 0,
top: 0,
left: 0
});
const [canvas, setCanvas] = React.useState(null);
const [bind, setBind] = React.useState();
React.useImperativeHandle(forwardedRef, () => viewRef.current);
const handlePointerMissed = useMutableCallback(onPointerMissed);
const [block, setBlock] = React.useState(false);
const [error, setError] = React.useState(undefined);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const viewRef = React.useRef(null);
const root = React.useRef(null);
const [antialias, setAntialias] = React.useState(true);
const onLayout = React.useCallback(e => {
const {
width,
height,
x,
y
} = e.nativeEvent.layout;
setSize({
width,
height,
top: y,
left: x
});
}, []);
// Called on context create or swap
// https://github.com/pmndrs/react-three-fiber/pull/2297
const onContextCreate = React.useCallback(context => {
const listeners = new Map();
const canvas = {
style: {},
width: context.drawingBufferWidth,
height: context.drawingBufferHeight,
clientWidth: context.drawingBufferWidth,
clientHeight: context.drawingBufferHeight,
getContext: (_, {
antialias = false
}) => {
setAntialias(antialias);
return context;
},
addEventListener(type, listener) {
let callbacks = listeners.get(type);
if (!callbacks) {
callbacks = [];
listeners.set(type, callbacks);
}
callbacks.push(listener);
},
removeEventListener(type, listener) {
const callbacks = listeners.get(type);
if (callbacks) {
const index = callbacks.indexOf(listener);
if (index !== -1) callbacks.splice(index, 1);
}
},
dispatchEvent(event) {
Object.assign(event, {
target: this
});
const callbacks = listeners.get(event.type);
if (callbacks) {
for (const callback of callbacks) {
callback(event);
}
}
},
setPointerCapture() {
// TODO
},
releasePointerCapture() {
// TODO
}
};
// TODO: this is wrong but necessary to trick controls
// @ts-ignore
canvas.ownerDocument = canvas;
canvas.getRootNode = () => canvas;
root.current = createRoot(canvas);
setCanvas(canvas);
function handleTouch(gestureEvent, type) {
gestureEvent.persist();
canvas.dispatchEvent(Object.assign(gestureEvent.nativeEvent, {
type,
offsetX: gestureEvent.nativeEvent.locationX,
offsetY: gestureEvent.nativeEvent.locationY,
pointerType: 'touch',
pointerId: gestureEvent.nativeEvent.identifier
}));
return true;
}
const responder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'pointercapture'),
onPanResponderStart: e => handleTouch(e, 'pointerdown'),
onPanResponderMove: e => handleTouch(e, 'pointermove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'pointerup');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'click');
},
onPanResponderRelease: e => handleTouch(e, 'pointerleave'),
onPanResponderTerminate: e => handleTouch(e, 'lostpointercapture'),
onPanResponderReject: e => handleTouch(e, 'lostpointercapture')
});
setBind(responder.panHandlers);
}, []);
if (root.current && width > 0 && height > 0) {
root.current.configure({
gl,
events,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
// expo-gl can only render at native dpr/resolution
// https://github.com/expo/expo-three/issues/39
dpr: PixelRatio.get(),
size: {
width,
height,
top,
left
},
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
// Overwrite onCreated to apply RN bindings
onCreated: state => {
// Bind render to RN bridge
const context = state.gl.getContext();
const renderFrame = state.gl.render.bind(state.gl);
state.gl.render = (scene, camera) => {
renderFrame(scene, camera);
context.endFrameEXP();
};
return onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsx(Bridge, {
children: /*#__PURE__*/jsx(ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsx(React.Suspense, {
fallback: /*#__PURE__*/jsx(Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
React.useEffect(() => {
if (canvas) {
return () => unmountComponentAtNode(canvas);
}
}, [canvas]);
return /*#__PURE__*/jsx(View, {
...props,
ref: viewRef,
onLayout: onLayout,
style: {
flex: 1,
...style
},
...bind,
children: width > 0 && /*#__PURE__*/jsx(GLView, {
msaaSamples: antialias ? 4 : 0,
onContextCreate: onContextCreate,
style: StyleSheet.absoluteFill
})
});
});
/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
const Canvas = /*#__PURE__*/React.forwardRef(function CanvasWrapper(props, ref) {
return /*#__PURE__*/jsx(FiberProvider, {
children: /*#__PURE__*/jsx(CanvasImpl, {
...props,
ref: ref
})
});
});
/** Default R3F event manager for react-native */
function createTouchEvents(store) {
const {
handlePointer
} = createEvents(store);
const handleTouch = (event, name) => {
event.persist()
// Apply offset
;
event.nativeEvent.offsetX = event.nativeEvent.locationX;
event.nativeEvent.offsetY = event.nativeEvent.locationY;
// Emulate DOM event
const callback = handlePointer(name);
callback(event.nativeEvent);
return true;
};
const responder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'onPointerUp');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
},
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
});
return {
priority: 1,
enabled: true,
compute(event, state, previous) {
// https://github.com/pmndrs/react-three-fiber/pull/782
// Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
},
connected: undefined,
handlers: responder.panHandlers,
update: () => {
var _internal$lastEvent;
const {
events,
internal
} = store.getState();
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
handlePointer('onPointerMove')(internal.lastEvent.current);
}
},
connect: () => {
const {
set,
events
} = store.getState();
events.disconnect == null ? void 0 : events.disconnect();
set(state => ({
events: {
...state.events,
connected: true
}
}));
},
disconnect: () => {
const {
set
} = store.getState();
set(state => ({
events: {
...state.events,
connected: false
}
}));
}
};
}
// http://stackoverflow.com/questions/105034
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0,
v = c == 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
}
async function getAsset(input) {
if (typeof input === 'string') {
var _NativeModules$BlobMo;
// Don't process storage
if (input.startsWith('file:')) return input;
// Unpack Blobs from react-native BlobManager
// https://github.com/facebook/react-native/issues/22681#issuecomment-523258955
if (input.startsWith('blob:') || input.startsWith((_NativeModules$BlobMo = NativeModules.BlobModule) == null ? void 0 : _NativeModules$BlobMo.BLOB_URI_SCHEME)) {
const blob = await new Promise((res, rej) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', input);
xhr.responseType = 'blob';
xhr.onload = () => res(xhr.response);
xhr.onerror = rej;
xhr.send();
});
const data = await new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = () => res(reader.result);
reader.onerror = rej;
reader.readAsText(blob);
});
input = `data:${blob.type};base64,${data}`;
}
// Create safe URI for JSI serialization
if (input.startsWith('data:')) {
const [header, data] = input.split(';base64,');
const [, type] = header.split('/');
const uri = fs.cacheDirectory + uuidv4() + `.${type}`;
await fs.writeAsStringAsync(uri, data, {
encoding: fs.EncodingType.Base64
});
return uri;
}
}
// Download bundler module or external URL
const asset = await Asset.fromModule(input).downloadAsync();
let uri = asset.localUri || asset.uri;
// Unpack assets in Android Release Mode
if (!uri.includes(':')) {
const file = `${fs.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
await fs.copyAsync({
from: uri,
to: file
});
uri = file;
}
return uri;
}
function polyfills() {
// Patch Blob for ArrayBuffer and URL if unsupported
// https://github.com/facebook/react-native/pull/39276
// https://github.com/pmndrs/react-three-fiber/issues/3058
if (Platform.OS !== 'web') {
try {
const blob = new Blob([new ArrayBuffer(4)]);
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
} catch (_) {
const BlobManager = require('react-native/Libraries/Blob/BlobManager.js');
const createObjectURL = URL.createObjectURL;
URL.createObjectURL = function (blob) {
if (blob.data._base64) {
return `data:${blob.type};base64,${blob.data._base64}`;
}
return createObjectURL(blob);
};
const createFromParts = BlobManager.createFromParts;
BlobManager.createFromParts = function (parts, options) {
parts = parts.map(part => {
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
part = fromByteArray(new Uint8Array(part));
}
return part;
});
const blob = createFromParts(parts, options);
// Always enable slow but safe path for iOS (previously for Android unauth)
// https://github.com/pmndrs/react-three-fiber/issues/3075
// if (!NativeModules.BlobModule?.BLOB_URI_SCHEME) {
blob.data._base64 = '';
for (const part of parts) {
var _data$_base, _data;
blob.data._base64 += (_data$_base = (_data = part.data) == null ? void 0 : _data._base64) != null ? _data$_base : part;
}
// }
return blob;
};
}
}
// Don't pre-process urls, let expo-asset generate an absolute URL
const extractUrlBase = THREE.LoaderUtils.extractUrlBase.bind(THREE.LoaderUtils);
THREE.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
// There's no Image in native, so create a data texture instead
THREE.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
const texture = new THREE.Texture();
getAsset(url).then(async uri => {
// https://github.com/expo/expo-three/pull/266
const {
width,
height
} = await new Promise((res, rej) => Image.getSize(uri, (width, height) => res({
width,
height
}), rej));
texture.image = {
// Special case for EXGLImageUtils::loadImage
data: {
localUri: uri
},
width,
height
};
texture.flipY = true; // Since expo-gl@12.4.0
texture.needsUpdate = true;
// Force non-DOM upload for EXGL texImage2D
// @ts-expect-error
texture.isDataTexture = true;
onLoad == null ? void 0 : onLoad(texture);
}).catch(onError);
return texture;
};
// Fetches assets via FS
THREE.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
this.manager.itemStart(url);
getAsset(url).then(async uri => {
const base64 = await fs.readAsStringAsync(uri, {
encoding: fs.EncodingType.Base64
});
const data = Buffer.from(base64, 'base64');
onLoad == null ? void 0 : onLoad(data.buffer);
}).catch(error => {
onError == null ? void 0 : onError(error);
this.manager.itemError(url);
}).finally(() => {
this.manager.itemEnd(url);
});
};
}
if (Platform.OS !== 'web') polyfills();
export { Canvas, createTouchEvents as events };