From 3d7627cf43ccaba28c2a47c98112836ac061f521 Mon Sep 17 00:00:00 2001 From: Mark Rickert Date: Wed, 12 Jun 2024 17:28:14 -0600 Subject: [PATCH] Clean up some examples and add a generic template file for making new examples --- example/app/_generic-template.tsx | 139 ++++++++++++++++++++ example/app/animated-robot.tsx | 3 +- example/app/cube-mesh-physical-material.tsx | 11 +- example/app/flower-field.tsx | 3 +- example/components/StatsPanel.tsx | 2 +- package.json | 1 + yarn.lock | 7 + 7 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 example/app/_generic-template.tsx diff --git a/example/app/_generic-template.tsx b/example/app/_generic-template.tsx new file mode 100644 index 0000000..090c6e6 --- /dev/null +++ b/example/app/_generic-template.tsx @@ -0,0 +1,139 @@ +/** + * This is a generic template for a 3D scene using Expo and Three.js. + * To create a new example, copy this file and modify the `onContextCreate` function. + * + * It comes with a loading view, stats panel, and orbit controls. + */ + +// Fast refresh doesn't work very well with GLViews. +// Always reload the entire component when the file changes: +// https://reactnative.dev/docs/fast-refresh#fast-refresh-and-hooks +// @refresh reset + +import React, { useEffect, useRef, useState } from 'react'; +import { View } from 'react-native'; + +import { ExpoWebGLRenderingContext, GLView } from 'expo-gl'; +import { Renderer, THREE } from 'expo-three'; +import OrbitControlsView from 'expo-three-orbit-controls'; +import { LoadingView } from '../components/LoadingView'; +import { useSceneStats } from '../components/StatsPanel'; + +export default function ThreeScene() { + const [isLoading, setIsLoading] = useState(true); + const cameraRef = useRef(); + + const { calculateSceneStats, StatsPanel, mark } = useSceneStats(); + + const timeoutRef = useRef(); + useEffect(() => { + // Clear the animation loop when the component unmounts + return () => clearTimeout(timeoutRef.current); + }, []); + + const sceneRef = useRef(); + const clockRef = useRef(); + + const onContextCreate = async (gl: ExpoWebGLRenderingContext) => { + setIsLoading(true); + clockRef.current = new THREE.Clock(); + + // removes the warning EXGL: gl.pixelStorei() doesn't support this parameter yet! + const pixelStorei = gl.pixelStorei.bind(gl); + gl.pixelStorei = function (...args) { + const [parameter] = args; + switch (parameter) { + case gl.UNPACK_FLIP_Y_WEBGL: + return pixelStorei(...args); + } + }; + + const renderer = new Renderer({ gl }); + let cam = new THREE.PerspectiveCamera( + 75, + gl.drawingBufferWidth / gl.drawingBufferHeight, + 0.25, + 100 + ); + cam.position.set(7, 3, 10); + cam.lookAt(0, 2, 0); + cameraRef.current = cam; + + sceneRef.current = new THREE.Scene(); + sceneRef.current.background = new THREE.Color(0xe0e0e0); + sceneRef.current.fog = new THREE.Fog(0xe0e0e0, 20, 100); + + // lights + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); + hemiLight.position.set(0, 20, 0); + sceneRef.current.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(0, 20, 10); + sceneRef.current.add(dirLight); + + // ground + // https://threejs.org/docs/#api/en/geometries/PlaneGeometry + // Delete this if you don't need a ground plane + const groundMesh = new THREE.Mesh( + new THREE.PlaneGeometry(2000, 2000), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }) + ); + groundMesh.rotation.x = -Math.PI / 2; + sceneRef.current.add(groundMesh); + + // grid + // https://threejs.org/docs/#api/en/helpers/GridHelper + // Delete this if you don't need a grid + const grid = new THREE.GridHelper(200, 40, 0x000000, 0x000000); + grid.material.opacity = 0.2; + grid.material.transparent = true; + sceneRef.current.add(grid); + + // Add the rest of your objects here: + // + + // Start the animation loop + function animate() { + timeoutRef.current = requestAnimationFrame(animate); + + // FPS counter + mark(); + + if (cameraRef.current && sceneRef.current) { + renderer.render(sceneRef.current, cameraRef.current); + } + gl.endFrameEXP(); + } + animate(); + + setIsLoading(false); + + // Calculate the objects, vertices, and triangles in the scene + calculateSceneStats(sceneRef.current); + }; + + return ( + + + + + {isLoading ? ( + + ) : ( + + )} + + ); +} diff --git a/example/app/animated-robot.tsx b/example/app/animated-robot.tsx index f96cf98..41abceb 100644 --- a/example/app/animated-robot.tsx +++ b/example/app/animated-robot.tsx @@ -8,8 +8,7 @@ import { View } from 'react-native'; import { Picker } from '@react-native-picker/picker'; import { ExpoWebGLRenderingContext, GLView } from 'expo-gl'; -import { loadAsync, Renderer } from 'expo-three'; -import * as THREE from 'three'; +import { loadAsync, Renderer, THREE } from 'expo-three'; import OrbitControlsView from 'expo-three-orbit-controls'; import { LoadingView } from '../components/LoadingView'; diff --git a/example/app/cube-mesh-physical-material.tsx b/example/app/cube-mesh-physical-material.tsx index 7bf5e52..6b09a1d 100644 --- a/example/app/cube-mesh-physical-material.tsx +++ b/example/app/cube-mesh-physical-material.tsx @@ -3,22 +3,15 @@ // https://reactnative.dev/docs/fast-refresh#fast-refresh-and-hooks // @refresh reset -import { Asset } from 'expo-asset'; import { ExpoWebGLRenderingContext, GLView } from 'expo-gl'; -import { loadAsync, Renderer, TextureLoader } from 'expo-three'; +import { Renderer } from 'expo-three'; import * as React from 'react'; -import { ActivityIndicator, Text, View } from 'react-native'; +import { View } from 'react-native'; import * as THREE from 'three'; import { - AmbientLight, BoxGeometry, - Fog, - GridHelper, - Mesh, PerspectiveCamera, - PointLight, Scene, - SpotLight, MeshPhysicalMaterial, } from 'three'; import { LoadingView } from '../components/LoadingView'; diff --git a/example/app/flower-field.tsx b/example/app/flower-field.tsx index 04fe2ae..c35a247 100644 --- a/example/app/flower-field.tsx +++ b/example/app/flower-field.tsx @@ -7,8 +7,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { View } from 'react-native'; import { ExpoWebGLRenderingContext, GLView } from 'expo-gl'; -import { loadAsync, Renderer } from 'expo-three'; -import * as THREE from 'three'; +import { loadAsync, Renderer, THREE } from 'expo-three'; import OrbitControlsView from 'expo-three-orbit-controls'; import { LoadingView } from '../components/LoadingView'; import { useSceneStats } from '../components/StatsPanel'; diff --git a/example/components/StatsPanel.tsx b/example/components/StatsPanel.tsx index dc1add2..a05b98e 100644 --- a/example/components/StatsPanel.tsx +++ b/example/components/StatsPanel.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useRef, useState } from 'react'; import { Text, View, ViewProps } from 'react-native'; -import * as THREE from 'three'; +import { THREE } from 'expo-three'; /** * Custom hook that provides scene statistics and FPS calculation for a THREE.js scene. diff --git a/package.json b/package.json index 6b81f29..2adb90b 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "@types/three": "^0.165.0", "expo-asset": "~9.0.2", "expo-file-system": "~16.0.8", + "expo-gl": "^13.6.0", "expo-module-scripts": "^3.4.1", "expo-modules-core": "^1.11.10", "jest": "^29.2.1", diff --git a/yarn.lock b/yarn.lock index 3614721..115aeba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3910,6 +3910,13 @@ expo-file-system@~16.0.0, expo-file-system@~16.0.8: resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-16.0.8.tgz#13c79a8e06e42a8e76e9297df6920597a011d989" integrity sha512-yDbVT0TUKd7ewQjaY5THum2VRFx2n/biskGhkUmLh3ai21xjIVtaeIzHXyv9ir537eVgt4ReqDNWi7jcXjdUcA== +expo-gl@^13.6.0: + version "13.6.0" + resolved "https://registry.yarnpkg.com/expo-gl/-/expo-gl-13.6.0.tgz#b93c7dd2df8f2afe3823b043c37f898adda8ee92" + integrity sha512-hhRC2ZTDpc2YoElojutJTOYQsjSxX68lgL+2TSgWRrrvUXFeua1Ohz5DL/0iNCeoabs1earf/4qhxIdozkrhMA== + dependencies: + invariant "^2.2.4" + expo-module-scripts@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/expo-module-scripts/-/expo-module-scripts-3.4.1.tgz#e3dd944d81b6daf9f6f0bde2329b7cf15336a397"