Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment(native): implement readAsArrayBuffer for compat #2991

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 32 additions & 78 deletions packages/fiber/src/native/polyfills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as THREE from 'three'
import { Image } from 'react-native'
import { Asset } from 'expo-asset'
import * as fs from 'expo-file-system'
import { fromByteArray } from 'base64-js'
import { fromByteArray, toByteArray } from 'base64-js'

export function polyfills() {
// Patch Blob for ArrayBuffer if unsupported
// Implement Blob from ArrayBuffer if not implemented
try {
new Blob([new ArrayBuffer(4) as any])
} catch (_) {
Expand All @@ -25,6 +25,21 @@ export function polyfills() {
}
}

// Implement FileReader.readAsArrayBuffer if not implemented
try {
new FileReader().readAsArrayBuffer(new Blob([new ArrayBuffer(4) as any]))
} catch (_) {
FileReader.prototype.readAsArrayBuffer = function (blob) {
const onloadend = this.onloadend
this.onloadend = () => {
this._result = toByteArray(this._result.split(',')[1]).buffer
onloadend?.()
}

return this.readAsDataURL(blob)
}
}

function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0,
Expand Down Expand Up @@ -87,7 +102,7 @@ export function polyfills() {

// Don't pre-process urls, let expo-asset generate an absolute URL
const extractUrlBase = THREE.LoaderUtils.extractUrlBase.bind(THREE.LoaderUtils)
THREE.LoaderUtils.extractUrlBase = (url: string) => (typeof url === 'string' ? extractUrlBase(url) : './')
THREE.LoaderUtils.extractUrlBase = (url: unknown) => (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) {
Expand Down Expand Up @@ -127,84 +142,23 @@ export function polyfills() {
return texture
}

// Fetches assets via XMLHttpRequest
THREE.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
// Special handling for bundler assets
const originalFileLoad = THREE.FileLoader.prototype.load
THREE.FileLoader.prototype.load = async function load(url, onLoad, onProgress, onError) {
const path = this.path
if (this.path) url = this.path + url

const request = new XMLHttpRequest()

getAsset(url)
.then(async (asset) => {
let uri = asset.localUri || asset.uri

// Make FS paths web-safe
if (uri.startsWith('file://')) {
const data = await fs.readAsStringAsync(uri, { encoding: fs.EncodingType.Base64 })
uri = `data:application/octet-stream;base64,${data}`
}

request.open('GET', uri, true)

request.addEventListener(
'load',
(event) => {
if (request.status === 200) {
onLoad?.(request.response)

this.manager.itemEnd(url)
} else {
onError?.(event as unknown as ErrorEvent)

this.manager.itemError(url)
this.manager.itemEnd(url)
}
},
false,
)

request.addEventListener(
'progress',
(event) => {
onProgress?.(event)
},
false,
)

request.addEventListener(
'error',
(event) => {
onError?.(event as unknown as ErrorEvent)

this.manager.itemError(url)
this.manager.itemEnd(url)
},
false,
)

request.addEventListener(
'abort',
(event) => {
onError?.(event as unknown as ErrorEvent)

this.manager.itemError(url)
this.manager.itemEnd(url)
},
false,
)

if (this.responseType) request.responseType = this.responseType
if (this.withCredentials) request.withCredentials = this.withCredentials

for (const header in this.requestHeader) {
request.setRequestHeader(header, this.requestHeader[header])
}
const asset = await getAsset(url)
url = asset.localUri || asset.uri

request.send(null)

this.manager.itemStart(url)
})
.catch(onError)
// Make FS paths web-safe
if (url.startsWith('file://')) {
const data = await fs.readAsStringAsync(url, { encoding: fs.EncodingType.Base64 })
url = `data:application/octet-stream;base64,${data}`
}

return request
this.path = ''
originalFileLoad.apply(this, [url, onLoad, onProgress, onError])
this.path = path
}
}
Loading