quadsphere v1.2.2
QuadSphere
@Created: 29 Oct. 2018 (Unity version)
@Updated: 2023 (Threejs & @react-three/fiber)
@Author: Jason Holt Smith (bicarbon8@gmail.com)
Description
A QuadSphere (a sphere made up of a mesh cube whose vertices are adjusted into a spherical shape) with QuadTree-based level of detail support.

Installation
npm install quadsphere
QuadSphereMesh
a @react-three/fiber compatible THREE.Mesh that can be used directly from the Canvas to render a QuadSphere
Properties
all @react-three/fiber MeshProps are supported in addition to the following:
radiusone half the number of units width of the renderedQuadSphere(without factoring in any displacement material offsets)segmentsthe number of divisions plus edges in each direction of eachQuadof theQuadSphere(i.e. a value of3means one division and two edges when all sides activated). values must be odd numbers starting from 3 or greatermaxlevelthe maximum depth minus 1, starting from 0 meaning no subdivision allowed, that eachQuadin theQuadSpherecan be. a value of 2 means that the top-levelQuadcan be subdivided as can it's child quads, but no further subdivision is allowedtextureMappinga value of eitherunified(default) orsplitwhich indicates the type of UVs to generate for theQuadSphere. ifunifiedthen an unwrapped cube texture should be used example, but ifsplitthen six separate materials should be supplied each mapping to a face of theQuadSpherein the following order:positive-x,negative-x,positive-y,negative-y,positive-z,negative-zonCreateMesha function that will be called every time a newTHREE.Meshis created (each subdivision or unification results in the need to generate a new mesh otherwise the view will not update)onCreateSpherea function that will be called every time a newQuadSphereGeometryis created (changing any of the abovepropsvalues results in a newQuadSphereGeometrybeing created and the prior being disposed of)
Usage
import { Canvas, useLoader } from '@react-three/fiber';
import { QuadSphereMesh } from 'quadsphere';
function App() {
const texture = useLoader(THREE.TextureLoader, `./assets/texture.png`);
const bump = useLoader(THREE.TextureLoader, `./assets/bump.jpg`);
return (
<Canvas>
<QuadSphereMesh
position={[1.2, 0, 0]} {/* x, y, z position */}
radius={1} {/* radius of sphere */}
segments={5} {/* number of edges & divisions in each Quad */}
>
<meshStandardMaterial
map={texture}
displacementMap={bump}
displacementScale={0.2} />
</QuadSphereMesh>
</Canvas>
);
}Updating the Sphere based on distance to camera
DEMO
import { Canvas, useLoader, useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { QuadSphereMesh, V3, QuadSphereGeometry } from 'quadsphere';
import * as THREE from 'three';
function distanceCheck(sphereMeshRef: THREE.Mesh, worldLocation: V3) {
const distanceValues = new Array<number>(5, 4, 3, 2, 1, 0);
const geom = sphereMeshRef.geometry as QuadSphereGeometry;
// convert camera location to location relative to QuadSphere
const localLocation = new THREE.Vector3(worldLocation.x, worldLocation.y, worldLocation.z)
.sub(sphereMeshRef.position)
.applyQuaternion(sphereMeshRef.quaternion.invert());
let updated = false;
for (let i=0; i<distanceValues.length; i++) {
const dist = distanceValues[i];
const levelQuads = geom.sphere.registry.getQuadsAtLevel(i);
if (levelQuads.length > 0) {
const inRange = levelQuads.filter(q => geom.sphere.utils.isWithinDistance(q, dist, localLocation));
if (inRange.length > 0) {
const closest = geom.sphere.utils.getClosestQuad(localLocation, false, ...inRange);
closest.subdivide();
levelQuads.filter(q => q.id !== closest.id).forEach(q => q.unify());
} else {
levelQuads.forEach(q => q.unify());
}
updated = true;
}
}
if (updated) {
geom.updateAttributes();
}
}
function App() {
const texture = useLoader(THREE.TextureLoader, `./assets/texture.png`);
const bump = useLoader(THREE.TextureLoader, `./assets/bump.jpg`);
const ref = useRef<THREE.Mesh>(null);
useFrame((state: RootState, delta: number) => {
// check distance from camera to QuadSphere and subdivide when within range
distanceCheck(ref.current, state.camera.position);
});
return (
<Canvas>
<QuadSphereMesh
ref={ref} {/* provides access to QuadSphereGeometry and QuadSphere */}
position={[1.2, 0, 0]} {/* x, y, z position */}
radius={1} {/* radius of sphere */}
segments={5} {/* number of edges + divisions in each Quad */}
>
<meshStandardMaterial
map={texture}
displacementMap={bump}
displacementScale={0.2}
/>
</QuadSphereMesh>
</Canvas>
);
}Using a different material for each face of the QuadSphere
by default the QuadSphere uses a unified texture mapping that assumes all faces are contained in a single unwrapped cube texture that
like the following example, but you can also assign each face individually by specifying a textureMapping of
split
import { Canvas, useLoader } from '@react-three/fiber';
import { QuadSphereMesh } from 'quadsphere';
function App() {
return (
<Canvas>
<QuadSphereMesh
position={[1.2, 0, 0]} {/* x, y, z position */}
radius={1} {/* radius of sphere */}
segments={5} {/* number of edges & divisions in each Quad */}
textureMapping="split" {/* indicates that texture groups should be used */}
>
<meshBasicMaterial attach="material-0" color="red" /* px */ />
<meshBasicMaterial attach="material-1" color="blue" /* nx */ />
<meshBasicMaterial attach="material-2" color="green" /* py */ />
<meshBasicMaterial attach="material-3" color="purple" /* ny */ />
<meshBasicMaterial attach="material-4" color="white" /* pz */ />
<meshBasicMaterial attach="material-5" color="black" /* nz */ />
</QuadSphereMesh>
</Canvas>
);
}QuadMesh
a @react-three/fiber compatible THREE.Mesh that can be used directly from the Canvas to render one face of the QuadSphere
Properties
all QuadSphereMesh properties are supported in addition to the following:
applyCurveiftruethen the renderedQuadwill be curved based on the distance of thecentreproperty from thecurveOrigin.centreaV3made up of anx,y, andznumber as an offset from thecurveOrigin. Note that theTHREE.Meshpositionproperty is used for position and thecentreis only used if you need to useapplyCurveso you can offset from the origin to ensure the curve is applied to round the mesh instead of creating a flattened circlecurveOriginthe location to move out from byradiusamount when applying the curveuvStartaV2containing auandvnumber representing the starting offset for uvs in thisQuadMesh(defaults to{u: 0, v: 0})uvEndaV2containing auandvnumber representing the ending offset for uvs in thisQuadMesh(defaults to{u: 1, v: 1})
QuadSphereGeometry
a THREE.js compatible BufferGeometry that can be passed to a THREE.Mesh. all properties available to a QuadSphereMesh and that aren't part of the @react-three/fiber MeshProps is available as a contructor argument
Properties
subdividetakes in aV3specifying a local-space translatedV3and will find the closestQuador childQuadand call thesubdividefunction on itunifytakes in aV3specifying a local-space translatedV3and will find the closestQuador childQuadand call theunifyfunction on it's parentupdateAttributesautomatically called after callingsubdivideorunify, this enables you to force an update of the vertices, normals, uvs and indices of theBufferGeometry
QuadGeometry
a THREE.js compatible BufferGeometry that can be passed to a THREE.Mesh. all properties available to a QuadMesh and that aren't part of the @react-three/fiber MeshProps is available as a contructor argument
Properties
same as QuadSphereGeometry
QuadSphere
an implementation of a CubeSphere that uses Quad-Tree subdivision algorithm to increase the mesh complexity near a specific area with minimal increase to the overall mesh complexity as you move away from the specified location. Having this object separated from the specific rendering technology, namely THREE.js allows it to be used with other renderers if so desired
Quad
a single face used in the QuadSphere. this can be used to further reduce the complexity of a scene by removing other, unseen faces of the sphere when up close or can be used in a flattened mode as a Terrain implementation that supports the same level of detail adjustments as the QuadSphere