blob: 3a2898198948ce0fe2b96b695561de6aaea3f9c1 [file] [log] [blame] [edit]
/*
* Copyright (C) 2025 Mozilla. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
const NUM_PARTICLES = 8000;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const innerHeight = 1080;
const innerWidth = 1920;
camera.position.z = 500;
const canvas = {
addEventListener() {},
style: {},
getContext(kind) {
return {
getExtension(extension) {
if (extension == 'EXT_blend_minmax') {
return {MIN_EXT: 32775, MAX_EXT: 32776}
}
if (extension == 'OES_vertex_array_object') {
return {
createVertexArrayOES() { return 1 },
bindVertexArrayOES() {},
deleteVertexArrayOES() {},
}
}
},
createTexture() {},
bindTexture() {},
texImage2D() {},
texImage3D() {},
texParameteri() {},
uniform1i() {},
uniform1f() {},
uniform2f() {},
uniform3f() {},
uniform4f() {},
clearColor() {},
clear() {},
clearDepth() {},
clearStencil() {},
enable() {},
disable() {},
depthFunc() {},
depthMask() {},
frontFace() {},
cullFace() {},
getContextAttributes() {},
createBuffer() {},
createFramebuffer() {},
bindBuffer() {},
bufferData() {},
createProgram() {},
attachShader() {},
linkProgram() {},
useProgram() {},
getAttribLocation() {},
getUniformLocation() {},
createShader() {},
shaderSource() {},
compileShader() {},
getShaderParameter() {},
getProgramInfoLog() { return "" },
getShaderInfoLog() { return "" },
getProgramParameter() {},
deleteShader() {},
colorMask() {},
stencilMask() {},
createVertexArray() {},
bindVertexArray() {},
drawElements() {},
lineWidth() {},
drawArrays() {},
viewport() {},
getParameter(param) {
if (param == 34930) { return 16 }
if (param == 35660) { return 16 }
if (param == 3379) { return 8192 }
if (param == 36347) { return 1024 }
if (param == 36348) { return 32 }
if (param == 36349) { return 1024 }
if (param == 35661) { return 80 }
if (param == 7938) { return "WebGL 2.0" }
if (param == 3088) { return [0,0,1024,480] }
if (param == 2978) { return [0,0,1024,480] }
},
MAX_TEXTURE_IMAGE_UNITS: 34930,
MAX_VERTEX_TEXTURE_IMAGE_UNITS: 35660,
MAX_TEXTURE_SIZE: 3379,
MAX_VERTEX_UNIFORM_VECTORS: 36347,
MAX_VARYING_VECTORS: 36348,
MAX_FRAGMENT_UNIFORM_VECTORS: 36349,
MAX_COMBINED_TEXTURE_IMAGE_UNITS: 35661,
VERSION: 7938,
SCISSOR_BOX: 3088,
VIEWPORT: 2978
}
}
}
const renderer = new THREE.WebGLRenderer({
antialias: false,
canvas,
powerPreference: 'low-power',
precision: 'lowp'
});
renderer.setSize(innerWidth, innerHeight);
const createGeometryParticle = (size) => {
const visibleHeight = 2 * Math.tan(75 * Math.PI/360) * 500;
const radius = (size / innerHeight) * visibleHeight / 2;
// Main circle
const geometry = new THREE.CircleGeometry(radius, 32);
const material = new THREE.MeshBasicMaterial({
color: 0xffffff,
depthTest: false
});
const circle = new THREE.Mesh(geometry, material);
const posArray = geometry.attributes.position.array;
const outlineVertices = [];
for (let i = 3; i < posArray.length; i += 3) {
outlineVertices.push(
new THREE.Vector3(
posArray[i],
posArray[i + 1],
posArray[i + 2]
)
);
}
const outlineGeometry = new THREE.BufferGeometry().setFromPoints(outlineVertices);
const outline = new THREE.LineLoop(
outlineGeometry,
new THREE.LineBasicMaterial({ color: 0x000000, depthTest: false })
);
const group = new THREE.Group();
group.add(circle);
group.add(outline);
return group;
};
// Initialize particles
var initialized = false;
const particles = [];
function initialize() {
for(let i = 0; i < NUM_PARTICLES; i++) {
const size = 10 + Math.random() * 80;
const particle = createGeometryParticle(size);
// Random initial position
const visibleWidth = 2 * Math.tan(75 * Math.PI/360) * 500 * camera.aspect;
particle.position.set(
THREE.MathUtils.randFloatSpread(visibleWidth),
THREE.MathUtils.randFloatSpread(visibleWidth/camera.aspect),
0
);
// Velocity storage
particle.velocity = new THREE.Vector2(
(Math.random() - 0.5) * 8,
(Math.random() - 0.5) * 8
);
scene.add(particle);
particles.push(particle);
}
initialized = true;
}
// Metrics and animation
const visibleWidth = 2 * Math.tan(75 * Math.PI/360) * 500 * camera.aspect;
const visibleHeight = visibleWidth / camera.aspect;
function animate() {
particles.forEach(particle => {
particle.position.x += particle.velocity.x;
particle.position.y += particle.velocity.y;
// Boundary checks
if(Math.abs(particle.position.x) > visibleWidth/2)
particle.velocity.x *= -1;
if(Math.abs(particle.position.y) > visibleHeight/2)
particle.velocity.y *= -1;
});
renderer.render(scene, camera);
}
class Benchmark {
runIteration() {
if (!initialized) {
initialize();
}
animate();
}
}