Skip to content

Commit

Permalink
Clean up some examples and add a generic template file for making new…
Browse files Browse the repository at this point in the history
… examples
  • Loading branch information
markrickert committed Jun 12, 2024
1 parent 723d802 commit 3d7627c
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 14 deletions.
139 changes: 139 additions & 0 deletions example/app/_generic-template.tsx
Original file line number Diff line number Diff line change
@@ -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<THREE.Camera>();

const { calculateSceneStats, StatsPanel, mark } = useSceneStats();

const timeoutRef = useRef<number>();
useEffect(() => {
// Clear the animation loop when the component unmounts
return () => clearTimeout(timeoutRef.current);
}, []);

const sceneRef = useRef<THREE.Scene>();
const clockRef = useRef<THREE.Clock>();

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 (
<View style={{ flex: 1 }}>
<OrbitControlsView style={{ flex: 1 }} camera={cameraRef.current}>
<GLView style={{ flex: 1 }} onContextCreate={onContextCreate} />
</OrbitControlsView>
{isLoading ? (
<LoadingView />
) : (
<StatsPanel
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
padding: 10,
backgroundColor: 'yellow',
flexDirection: 'row',
justifyContent: 'space-between',
}}
/>
)}
</View>
);
}
3 changes: 1 addition & 2 deletions example/app/animated-robot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
11 changes: 2 additions & 9 deletions example/app/cube-mesh-physical-material.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
3 changes: 1 addition & 2 deletions example/app/flower-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion example/components/StatsPanel.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 3d7627c

Please sign in to comment.