Skip to content
Merged
Show file tree
Hide file tree
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
309 changes: 239 additions & 70 deletions examples/jsm/loaders/SPZLoader.js

Large diffs are not rendered by default.

File renamed without changes.
6 changes: 6 additions & 0 deletions examples/models/spz/tomatoes.license.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Title: Scan was created on my DIY automated rig.
Author: Grail (https://superspl.at/user?id=grail)
Source: https://superspl.at/scene/2826d2c0
License: CC Attribution (Creative Commons Attribution)
License URL: http://creativecommons.org/licenses/by/4.0/
Requirements: Author must be credited. Commercial use is allowed.
Binary file added examples/models/spz/tomatoes.v4.spz
Binary file not shown.
Binary file modified examples/screenshots/webgpu_materials_arrays.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion examples/webgpu_gaussian_splatting.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
},
lion: {
name: 'Lion (SPZ)',
url: './models/spz/lion.spz',
url: './models/spz/lion.v3.spz',
loader: SPZLoader,
rotation: new THREE.Euler( Math.PI, 0, 0 ),
cameraPosition: new THREE.Vector3( 0, 0.2, 1 ),
Expand All @@ -82,6 +82,19 @@
license: 'CC Attribution',
licenseUrl: 'http://creativecommons.org/licenses/by/4.0/'
}
},
tomatoes: {
name: 'Tomatoes (SPZ v4)',
url: './models/spz/tomatoes.v4.spz',
loader: SPZLoader,
cameraPosition: new THREE.Vector3( 0, 0.35, 1 ),
credit: {
author: 'Grail',
authorUrl: 'https://superspl.at/user/grail',
source: 'https://superspl.at/scene/2826d2c0',
license: 'CC Attribution',
licenseUrl: 'http://creativecommons.org/licenses/by/4.0/'
}
}
};
const sourceNames = Object.values( sources ).map( ( source ) => source.name );
Expand Down
221 changes: 142 additions & 79 deletions examples/webgpu_materials_arrays.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</div>

<small>
Materials Arrays and Geometry Groups.
Materials Arrays and Geometry Groups. Inspired by <a href="https://mathcraft.wonderhowto.com/how-to/welcome-math-craft-world-bonus-make-your-own-paper-polyhedra-0130467/" target="_blank" rel="noopener">paper polyhedra</a>.
</small>
</div>

Expand All @@ -41,136 +41,191 @@
import * as THREE from 'three/webgpu';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

import { Inspector } from 'three/addons/inspector/Inspector.js';

let renderer, scene, camera, controls;
let planeMesh, boxMesh, boxMeshWireframe, planeMeshWireframe;
let materials;

const api = {
webgpu: true
};


init( ! api.webgpu );
const _a = new THREE.Vector3(), _b = new THREE.Vector3();

function init( forceWebGL = false ) {
init();

if ( renderer ) {

renderer.dispose();
controls.dispose();
document.body.removeChild( renderer.domElement );

}
function init() {

// renderer
renderer = new THREE.WebGPURenderer( {
forceWebGL,
antialias: true,
} );

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.enabled = true;
renderer.setAnimationLoop( animate );
renderer.inspector = new Inspector();
document.body.appendChild( renderer.domElement );

// scene

scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
scene.background = new THREE.Color( 0x222222 );

// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 0, 0, 10 );

camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( - 6, 7, 14 );

// controls

controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0.5, 0 );
controls.minDistance = 5;
controls.maxDistance = 50;
controls.enableDamping = true;
controls.update();

// materials
materials = [
new THREE.MeshBasicMaterial( { color: 0xff1493, side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { color: 0x0000ff, side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.DoubleSide } ),
];
// environment

// plane geometry
const planeGeometry = new THREE.PlaneGeometry( 1, 1, 4, 4 );
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x0e696c );
scene.add( hemiLight );

planeGeometry.clearGroups();
const numFacesPerRow = 4; // Number of faces in a row (since each face is made of 2 triangles)
// lights

planeGeometry.addGroup( 0, 6 * numFacesPerRow, 0 );
planeGeometry.addGroup( 6 * numFacesPerRow, 6 * numFacesPerRow, 1 );
planeGeometry.addGroup( 12 * numFacesPerRow, 6 * numFacesPerRow, 2 );
const dirLight = new THREE.DirectionalLight( 0xffffff, 6 );
dirLight.position.set( 5, 10, 6 );
dirLight.castShadow = true;
dirLight.shadow.camera.left = - 10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = - 10;
dirLight.shadow.camera.far = 20;
dirLight.shadow.mapSize.set( 2048, 2048 );
dirLight.shadow.radius = 10;
scene.add( dirLight );

// box geometry
const boxGeometry = new THREE.BoxGeometry( .75, .75, .75 );
// scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) );

boxGeometry.clearGroups();
boxGeometry.addGroup( 0, 6, 0 ); // front face
boxGeometry.addGroup( 6, 6, 0 ); // back face
boxGeometry.addGroup( 12, 6, 2 ); // top face
boxGeometry.addGroup( 18, 6, 2 ); // bottom face
boxGeometry.addGroup( 24, 6, 1 ); // left face
boxGeometry.addGroup( 30, 6, 1 ); // right face
// materials, one per paper color, shared across all solids

scene.background = forceWebGL ? new THREE.Color( 0x000000 ) : new THREE.Color( 0x222222 );
const palette = [ 0xe4002b, 0xff7f11, 0xffd100, 0x00a651, 0x0072ce, 0x8a2be2 ];

// meshes
planeMesh = new THREE.Mesh( planeGeometry, materials );
materials = palette.map( color => new THREE.MeshStandardMaterial( { color, roughness: 0.8, side: THREE.DoubleSide } ) );

const materialsWireframe = [];
const geometries = [
makeHoleyGeometry( new THREE.TetrahedronGeometry( 1.2 ), 3 ), // 4 faces, 1 triangle each
makeHoleyGeometry( new THREE.BoxGeometry( 1.5, 1.5, 1.5 ), 6 ), // 6 faces, 2 triangles each (indexed)
makeHoleyGeometry( new THREE.OctahedronGeometry( 1.2 ), 3 ), // 8 faces, 1 triangle each
makeHoleyGeometry( new THREE.DodecahedronGeometry( 1.1 ), 9 ), // 12 faces, 3 triangles each
makeHoleyGeometry( new THREE.IcosahedronGeometry( 1.1 ), 3 ) // 20 faces, 1 triangle each
];

for ( let index = 0; index < materials.length; index ++ ) {
// table

const material = new THREE.MeshBasicMaterial( { color: materials[ index ].color, side: THREE.DoubleSide, wireframe: true } );
materialsWireframe.push( material );
const table = new THREE.Mesh(
new THREE.BoxGeometry( 17, 0.5, 17 ),
new THREE.MeshStandardMaterial( { color: 0x0e696c, roughness: 0.5 } )
);
table.position.y = - 0.25;
table.receiveShadow = true;
scene.add( table );

}
// grid

planeMeshWireframe = new THREE.Mesh( planeGeometry, materialsWireframe );
boxMeshWireframe = new THREE.Mesh( boxGeometry, materialsWireframe );
const order = [
0, 1, 2, 3,
3, 4, 0, 1,
1, 2, 3, 4,
4, 0, 1, 2
];

boxMesh = new THREE.Mesh( boxGeometry, materials );
for ( let i = 0; i < 16; i ++ ) {

planeMesh.position.set( - 1.5, - 1, 0 );
boxMesh.position.set( 1.5, - 0.75, 0 );
boxMesh.rotation.set( - Math.PI / 8, Math.PI / 4, Math.PI / 4 );
const row = Math.floor( i / 4 );
const col = i % 4;

planeMeshWireframe.position.set( - 1.5, 1, 0 );
boxMeshWireframe.position.set( 1.5, 1.25, 0 );
boxMeshWireframe.rotation.set( - Math.PI / 8, Math.PI / 4, Math.PI / 4 );
const mesh = new THREE.Mesh( geometries[ order[ i ] ], materials );
mesh.castShadow = true;

scene.add( planeMesh, planeMeshWireframe );
scene.add( boxMesh, boxMeshWireframe );
mesh.scale.setScalar( 0.5 + row * 0.25 );
mesh.position.set( ( col - 1.5 ) * 3.5, 0, ( 1.5 - row ) * 3.5 );

mesh.position.y = - new THREE.Box3().setFromObject( mesh, true ).min.y - 0.01;

}
scene.add( mesh );

function animate() {
}

boxMesh.rotation.y += 0.005;
boxMesh.rotation.x += 0.005;
boxMeshWireframe.rotation.y += 0.005;
boxMeshWireframe.rotation.x += 0.005;
renderer.render( scene, camera );
// listeners

window.addEventListener( 'resize', onWindowResize );

}

// rebuilds a polyhedron with a hole cut into each polygon face, like an open paper model

function makeHoleyGeometry( geometry, verticesPerFace, holeScale = 0.6 ) {

if ( geometry.index !== null ) geometry = geometry.toNonIndexed();

const position = geometry.getAttribute( 'position' );

const positions = [];
const holey = new THREE.BufferGeometry();

for ( let face = 0; face < position.count / verticesPerFace; face ++ ) {

// collect the unique corner vertices of the face

// gui
const corners = [];
const keys = new Set();

const gui = renderer.inspector.createParameters( 'Parameters' );
for ( let i = 0; i < verticesPerFace; i ++ ) {

gui.add( api, 'webgpu' ).onChange( () => {
const v = new THREE.Vector3().fromBufferAttribute( position, face * verticesPerFace + i );
const key = v.toArray().join( ',' );

init( ! api.webgpu );
if ( keys.has( key ) === false ) {

} );
keys.add( key );
corners.push( v );

// listeners
}

window.addEventListener( 'resize', onWindowResize );
}

// sort the corners counterclockwise around the face centroid

const centroid = corners.reduce( ( c, v ) => c.add( v ), new THREE.Vector3() ).divideScalar( corners.length );

const normal = new THREE.Vector3().crossVectors( _a.subVectors( corners[ 1 ], corners[ 0 ] ), _b.subVectors( corners[ 2 ], corners[ 0 ] ) ).normalize();
const tangent = new THREE.Vector3().subVectors( corners[ 0 ], centroid ).normalize();
const bitangent = new THREE.Vector3().crossVectors( normal, tangent );

const angleOf = ( v ) => Math.atan2( _b.subVectors( v, centroid ).dot( bitangent ), _b.dot( tangent ) );
corners.sort( ( p, q ) => angleOf( p ) - angleOf( q ) );

// bridge each outer edge with its scaled-down inner edge

const inner = corners.map( v => new THREE.Vector3().lerpVectors( centroid, v, holeScale ) );

holey.addGroup( positions.length / 3, corners.length * 6, face % materials.length );

for ( let i = 0; i < corners.length; i ++ ) {

const j = ( i + 1 ) % corners.length;

positions.push(
...corners[ i ], ...corners[ j ], ...inner[ j ],
...corners[ i ], ...inner[ j ], ...inner[ i ]
);

}

}

holey.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
holey.computeVertexNormals();

return holey;

}

function onWindowResize() {

Expand All @@ -184,6 +239,14 @@

}

function animate() {

controls.update();

renderer.render( scene, camera );

}

</script>

</body>
Expand Down
Loading