mirror of
https://gitlab.com/arnekeller/tunnel-racer.git
synced 2024-11-21 14:34:58 +00:00
Initial commit
This commit is contained in:
commit
afa224ab36
22
game.html
Normal file
22
game.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>three.js webgl - tunnel racer</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<link type="text/css" rel="stylesheet" href="main.css">
|
||||
<script src="/gyronorm.complete.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="info">
|
||||
Score: <div id="score">0</div><br/>
|
||||
<button id="set_head">Calibrate neutral position</button>
|
||||
<p id="log"></p>
|
||||
</div>
|
||||
<button id="start">Start</button>
|
||||
|
||||
<script type="module" src="/game.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
490
game.js
Normal file
490
game.js
Normal file
@ -0,0 +1,490 @@
|
||||
const vertexShaderGlow = `
|
||||
uniform vec3 viewVector;
|
||||
uniform float c;
|
||||
uniform float p;
|
||||
varying float intensity;
|
||||
varying vec3 vColor;
|
||||
|
||||
vec3 hsv2rgb(vec3 c) {
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 vNormal = normalize( normalMatrix * normal );
|
||||
vec3 vNormel = normalize( normalMatrix * viewVector );
|
||||
intensity = pow( c - dot(vNormal, vNormel), p );
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
vColor = hsv2rgb(vec3(fract(gl_Position[2] / 270.0), 0.7, 0.7));
|
||||
//vColor = vec3((sin(position[2] / 360.0) + 1.0) * 0.5, (sin(position[2] / 1000.0) + 1.0) * 0.5, (sin(position[2] / 70.0) + 1.0) * 0.5);
|
||||
//vColor = vec3(0.5, 0, 0.5);
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShaderGlow = `
|
||||
varying vec3 vColor;
|
||||
varying float intensity;
|
||||
void main()
|
||||
{
|
||||
vec3 glow = vColor * intensity;
|
||||
gl_FragColor = vec4( glow, 1.0 );
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader = `
|
||||
#include <common>
|
||||
|
||||
uniform vec3 iResolution;
|
||||
uniform float iTime;
|
||||
uniform sampler2D iChannel0;
|
||||
|
||||
// By Daedelus: https://www.shadertoy.com/user/Daedelus
|
||||
// license: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
#define TIMESCALE 0.25
|
||||
#define TILES 8
|
||||
#define COLOR 0.7, 1.6, 2.8
|
||||
|
||||
varying vec2 vUv;
|
||||
varying float z;
|
||||
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||
{
|
||||
vec2 uv = fragCoord.xy / iResolution.xy;
|
||||
uv.x *= iResolution.x / iResolution.y;
|
||||
|
||||
vec4 noise = texture2D(iChannel0, floor(uv * float(TILES)) / float(TILES));
|
||||
float p = 1.0 - mod(noise.r + noise.g + noise.b + (iTime + z) * float(TIMESCALE), 1.0);
|
||||
p = min(max(p * 3.0 - 1.8, 0.1), 2.0);
|
||||
|
||||
vec2 r = mod(uv * float(TILES), 1.0);
|
||||
r = vec2(pow(r.x - 0.5, 2.0), pow(r.y - 0.5, 2.0));
|
||||
p *= 1.0 - pow(min(1.0, 12.0 * dot(r, r)), 2.0);
|
||||
|
||||
fragColor = vec4(COLOR, 1.0) * p;
|
||||
}
|
||||
|
||||
void main() {
|
||||
mainImage(gl_FragColor, vUv * iResolution.xy);
|
||||
}
|
||||
`;
|
||||
const vertexShader = `
|
||||
varying vec2 vUv;
|
||||
varying float z;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
z = -position[1];
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}
|
||||
`;
|
||||
|
||||
const diameterOfTunnel = 100;
|
||||
|
||||
var gn;
|
||||
var headSet = false;
|
||||
var betaReference = 0.0;
|
||||
var leftRightMove = 0.0;
|
||||
var upDownMove = 0.0;
|
||||
|
||||
var speed = 5.0;
|
||||
let score = 0;
|
||||
let removed = 0;
|
||||
let running = false;
|
||||
let frameCount = 0;
|
||||
|
||||
function logger(text) {
|
||||
|
||||
}
|
||||
|
||||
function init_gn() {
|
||||
var args = {
|
||||
logger: logger
|
||||
};
|
||||
|
||||
gn = new GyroNorm();
|
||||
|
||||
gn.init(args).then(function() {
|
||||
var isAvailable = gn.isAvailable();
|
||||
if(!isAvailable.deviceOrientationAvailable) {
|
||||
console.log({message:'Device orientation is not available.'});
|
||||
}
|
||||
|
||||
if(!isAvailable.accelerationAvailable) {
|
||||
console.log({message:'Device acceleration is not available.'});
|
||||
}
|
||||
|
||||
if(!isAvailable.accelerationIncludingGravityAvailable) {
|
||||
console.log({message:'Device acceleration incl. gravity is not available.'});
|
||||
}
|
||||
|
||||
if(!isAvailable.rotationRateAvailable) {
|
||||
console.log({message:'Device rotation rate is not available.'});
|
||||
}
|
||||
|
||||
start_gn();
|
||||
}).catch(function(e){
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === "ArrowLeft") {
|
||||
leftRightMove = -10.0;
|
||||
} else if (e.key === "ArrowRight") {
|
||||
leftRightMove = 10.0;
|
||||
} else if (e.key === "ArrowDown") {
|
||||
upDownMove = -10.0;
|
||||
} else if (e.key === "ArrowUp") {
|
||||
upDownMove = 10.0;
|
||||
}
|
||||
});
|
||||
document.addEventListener('keyup', e => {
|
||||
if (e.key === "ArrowLeft") {
|
||||
leftRightMove = 0;
|
||||
} else if (e.key === "ArrowRight") {
|
||||
leftRightMove = 0;
|
||||
} else if (e.key === "ArrowDown") {
|
||||
upDownMove = 0;
|
||||
} else if (e.key === "ArrowUp") {
|
||||
upDownMove = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stop_gn() {
|
||||
gn.stop();
|
||||
}
|
||||
|
||||
function start_gn() {
|
||||
gn.start(gnCallBack);
|
||||
}
|
||||
|
||||
function gnCallBack(data) {
|
||||
if (!headSet) {
|
||||
betaReference = data.do.beta;
|
||||
} else {
|
||||
let leftRight = data.do.alpha > 180.0 ? "right" : "left";
|
||||
leftRightMove = data.do.alpha > 180.0 ? 360.0 - data.do.alpha : -data.do.alpha;
|
||||
let upDown = data.do.beta > betaReference ? "up" : "down";
|
||||
upDownMove = data.do.beta - betaReference;
|
||||
}
|
||||
}
|
||||
|
||||
function set_head_gn() {
|
||||
gn.setHeadDirection();
|
||||
headSet = true;
|
||||
}
|
||||
|
||||
document.getElementById("set_head").addEventListener("click", () => {
|
||||
document.getElementById("log").innerHTML += "clicked button<br>";
|
||||
set_head_gn();
|
||||
});
|
||||
|
||||
init_gn();
|
||||
|
||||
import * as THREE from './three.module.js';
|
||||
|
||||
// import { GUI } from './jsm/libs/dat.gui.module.js';
|
||||
|
||||
//import { OrbitControls } from './three/examples/jsm/controls/OrbitControls.js';
|
||||
|
||||
//import { EffectComposer } from './three/examples/jsm/postprocessing/EffectComposer.js';
|
||||
//import { RenderPass } from './three/examples/jsm/postprocessing/RenderPass.js';
|
||||
//import { ShaderPass } from '/three/examples/jsm/postprocessing/ShaderPass.js';
|
||||
//import { LuminosityShader } from '/three/examples/jsm/shaders/LuminosityShader.js';
|
||||
//import { SobelOperatorShader } from '/three/examples/jsm/shaders/SobelOperatorShader.js';
|
||||
|
||||
let scene, renderer, composer;
|
||||
const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 );
|
||||
|
||||
let effectSobel;
|
||||
|
||||
const params = {
|
||||
enable: true
|
||||
};
|
||||
const geometryCube = new THREE.BoxGeometry( 20, 20, 20 );
|
||||
const geometrySphere = new THREE.SphereGeometry(11, 32, 32);
|
||||
const geometrySphere2 = new THREE.SphereGeometry(13, 32, 32);
|
||||
const materialCube = new THREE.MeshBasicMaterial();
|
||||
const outlineMaterial = new THREE.MeshBasicMaterial( { color: 0x00ffff, side: THREE.BackSide } );
|
||||
const customMaterial = new THREE.ShaderMaterial({
|
||||
uniforms:
|
||||
{
|
||||
"c": { type: "f", value: 0.8 },
|
||||
"p": { type: "f", value: 2.4 },
|
||||
viewVector: { type: "v3", value: camera.position }
|
||||
},
|
||||
vertexShader: vertexShaderGlow,
|
||||
//vertexColors: true,
|
||||
fragmentShader: fragmentShaderGlow,
|
||||
side: THREE.FrontSide,
|
||||
blending: THREE.AdditiveBlending,
|
||||
transparent: true
|
||||
});
|
||||
|
||||
|
||||
const loader = new THREE.TextureLoader();
|
||||
const texture = loader.load('/bayer.png');
|
||||
texture.minFilter = THREE.NearestFilter;
|
||||
texture.magFilter = THREE.NearestFilter;
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
const uniforms = {
|
||||
iTime: { value: 0 },
|
||||
iResolution: { value: new THREE.Vector3(1, 1, 1) },
|
||||
iChannel0: { value: texture },
|
||||
};
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
uniforms,
|
||||
});
|
||||
const borderGeometry = new THREE.PlaneGeometry( 1, 1 );
|
||||
|
||||
const positions = [];
|
||||
const colors = [];
|
||||
const sizes = [];
|
||||
const color = new THREE.Color();
|
||||
|
||||
const cubes = [];
|
||||
const borders = [];
|
||||
|
||||
init();
|
||||
animate();
|
||||
|
||||
document.getElementById("start").onclick = () => {
|
||||
document.getElementById("start").style.zIndex = -10;
|
||||
speed = 5.0;
|
||||
score = 0;
|
||||
for (let i = 0; i < cubes.length; i++) {
|
||||
scene.remove(cubes[i]);
|
||||
}
|
||||
cubes.length = 0;
|
||||
removed = 0;
|
||||
running = true;
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
//
|
||||
|
||||
scene = new THREE.Scene();
|
||||
|
||||
camera.position.set( 0, 10, 25 );
|
||||
camera.lookAt( scene.position );
|
||||
|
||||
//const geometry = geometrySphere2;
|
||||
//geometry.setAttribute( 'positioned', new THREE.Float32BufferAttribute( positions, 3 ) );
|
||||
//geometry.setAttribute( 'glowColor', new THREE.Float32BufferAttribute( colors, 3 ) );
|
||||
////geometry.setAttribute( 'size', new THREE.Float32BufferAttribute( sizes, 1 ).setUsage( THREE.DynamicDrawUsage ) );
|
||||
//const particleSystem = new THREE.Mesh( geometry, customMaterial );
|
||||
//scene.add(particleSystem);
|
||||
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
//const meshCube = new THREE.Mesh( geometryCube, materialCube );
|
||||
//meshCube.position.z = -30;
|
||||
//meshCube.position.x = 40;
|
||||
//scene.add(meshCube);
|
||||
//cubes.push(meshCube);
|
||||
|
||||
//
|
||||
|
||||
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.7 );
|
||||
scene.add( ambientLight );
|
||||
|
||||
const pointLight = new THREE.PointLight( 0xffffff, 0.8 );
|
||||
camera.add( pointLight );
|
||||
scene.add( camera );
|
||||
|
||||
//
|
||||
|
||||
renderer = new THREE.WebGLRenderer();
|
||||
renderer.setPixelRatio( window.devicePixelRatio );
|
||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
||||
document.body.appendChild( renderer.domElement );
|
||||
|
||||
// postprocessing
|
||||
|
||||
//composer = new EffectComposer( renderer );
|
||||
//const renderPass = new RenderPass( scene, camera );
|
||||
//composer.addPass( renderPass );
|
||||
|
||||
// color to grayscale conversion
|
||||
|
||||
//const effectGrayScale = new ShaderPass( LuminosityShader );
|
||||
//composer.addPass( effectGrayScale );
|
||||
|
||||
// you might want to use a gaussian blur filter before
|
||||
// the next pass to improve the result of the Sobel operator
|
||||
|
||||
// Sobel operator
|
||||
|
||||
//effectSobel = new ShaderPass( SobelOperatorShader );
|
||||
//effectSobel.uniforms[ 'resolution' ].value.x = window.innerWidth * window.devicePixelRatio;
|
||||
//effectSobel.uniforms[ 'resolution' ].value.y = window.innerHeight * window.devicePixelRatio;
|
||||
//composer.addPass( effectSobel );
|
||||
|
||||
//const controls = new OrbitControls( camera, renderer.domElement );
|
||||
//controls.minDistance = 10;
|
||||
//controls.maxDistance = 100;
|
||||
|
||||
//
|
||||
|
||||
//const gui = new GUI();
|
||||
|
||||
//gui.add( params, 'enable' );
|
||||
//gui.open();
|
||||
|
||||
//
|
||||
|
||||
window.addEventListener( 'resize', onWindowResize );
|
||||
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
||||
//composer.setSize( window.innerWidth, window.innerHeight );
|
||||
|
||||
//effectSobel.uniforms[ 'resolution' ].value.x = window.innerWidth * window.devicePixelRatio;
|
||||
//effectSobel.uniforms[ 'resolution' ].value.y = window.innerHeight * window.devicePixelRatio;
|
||||
|
||||
}
|
||||
|
||||
function animate() {
|
||||
|
||||
requestAnimationFrame( animate );
|
||||
|
||||
uniforms.iTime.value = frameCount / 60.0;
|
||||
frameCount++;
|
||||
|
||||
if (running) {
|
||||
|
||||
for (let i = 0; i < cubes.length; i++) {
|
||||
if (cubes[i].geometry.parameters.radius !== geometrySphere.parameters.radius) {
|
||||
continue;
|
||||
}
|
||||
let x1 = cubes[i].position.x;
|
||||
let y1 = cubes[i].position.y;
|
||||
let z1 = cubes[i].position.z;
|
||||
let x2 = camera.position.x;
|
||||
let y2 = camera.position.y;
|
||||
let z2 = camera.position.z;
|
||||
if (z2 > z1) {
|
||||
// account for very fast speeds
|
||||
// (prevents clipping through obstacles)
|
||||
z2 = Math.max(z1, z2 - speed);
|
||||
}
|
||||
let dist_squared = (x1 - x2)**2 + (y1 - y2)**2 + (z1-z2)**2;
|
||||
if (dist_squared <= cubes[i].geometry.parameters.radius**2) {
|
||||
running = false;
|
||||
document.getElementById("score").innerText = document.getElementById("score").innerText + " - Game Over!";
|
||||
document.getElementById("start").style.zIndex = 0;
|
||||
document.getElementById("start").innerText = "Restart";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
camera.position.z -= 0.5 * speed;
|
||||
speed += 0.01;
|
||||
camera.position.x += 0.1 * leftRightMove;
|
||||
camera.position.y += 0.1 * upDownMove;
|
||||
|
||||
if (Math.random() < 0.09) {
|
||||
const meshCube = new THREE.Mesh( geometrySphere, materialCube );
|
||||
meshCube.position.z = camera.position.z - 930;
|
||||
meshCube.position.x = (2 * Math.random() - 1.0) * diameterOfTunnel;
|
||||
meshCube.position.y = (2 * Math.random() - 1.0) * 0.9 * diameterOfTunnel;
|
||||
|
||||
const outlineMesh = new THREE.Mesh( geometrySphere, customMaterial );
|
||||
outlineMesh.scale.multiplyScalar( 1.17 );
|
||||
outlineMesh.position.z = meshCube.position.z;
|
||||
outlineMesh.position.x = meshCube.position.x;
|
||||
outlineMesh.position.y = meshCube.position.y;
|
||||
meshCube.add( outlineMesh );
|
||||
scene.add(outlineMesh);
|
||||
cubes.push(outlineMesh);
|
||||
//positions.push( meshCube.position.x );
|
||||
//positions.push( meshCube.position.y );
|
||||
//positions.push( meshCube.position.z );
|
||||
//
|
||||
//color.setHSL( Math.random(), 1.0, 0.5 );
|
||||
//colors.push( color.r, color.g, color.b );
|
||||
|
||||
//sizes.push( 20 );
|
||||
|
||||
scene.add(meshCube);
|
||||
cubes.push(meshCube);
|
||||
}
|
||||
|
||||
}
|
||||
renderer.render( scene, camera );
|
||||
|
||||
|
||||
for (let i = 0; i < cubes.length; i++) {
|
||||
if (cubes[i].position.z > camera.position.z + cubes[i].geometry.parameters.radius) {
|
||||
scene.remove(cubes[i]);
|
||||
cubes.splice(i, 1);
|
||||
i--;
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
if (removed >= 2 || score == 0) {
|
||||
score += removed / 2;
|
||||
removed = removed % 2;
|
||||
document.getElementById("score").innerText = score;
|
||||
}
|
||||
if (borders.length == 0 || borders[borders.length - 1].position.z - camera.position.z > -600) {
|
||||
const newZ = borders.length == 0 ? -200 : borders[borders.length - 1].position.z - diameterOfTunnel * 2;
|
||||
|
||||
let border = new THREE.Mesh( borderGeometry, material );
|
||||
//border.position.x = -70;
|
||||
border.position.y = -diameterOfTunnel;
|
||||
border.rotation.x = -Math.PI / 2;
|
||||
border.position.z = newZ;
|
||||
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
||||
scene.add(border);
|
||||
borders.push(border);
|
||||
|
||||
border = new THREE.Mesh( borderGeometry, material );
|
||||
border.position.y = diameterOfTunnel;
|
||||
border.rotation.x = Math.PI / 2;
|
||||
border.position.z = newZ;
|
||||
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
||||
scene.add(border);
|
||||
borders.push(border);
|
||||
|
||||
border = new THREE.Mesh( borderGeometry, material );
|
||||
border.position.x = -diameterOfTunnel;
|
||||
border.rotation.y = Math.PI / 2;
|
||||
border.position.z = newZ;
|
||||
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
||||
scene.add(border);
|
||||
borders.push(border);
|
||||
|
||||
border = new THREE.Mesh( borderGeometry, material );
|
||||
border.position.x = diameterOfTunnel;
|
||||
border.rotation.y = -Math.PI / 2;
|
||||
border.position.z = newZ;
|
||||
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
||||
scene.add(border);
|
||||
borders.push(border);
|
||||
}
|
||||
//console.log(borders[borders.length - 1].position.z - camera.position.z);
|
||||
//console.log(borders[borders.length - 1] - camera.position.z);
|
||||
for (let i = 0; i < borders.length; i++) {
|
||||
if (borders[i].position.z > camera.position.z + diameterOfTunnel + 50) {
|
||||
scene.remove(borders[i]);
|
||||
borders.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
5
gyronorm.complete.min.js
vendored
Normal file
5
gyronorm.complete.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
93
main.css
Normal file
93
main.css
Normal file
@ -0,0 +1,93 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
font-family: Monospace;
|
||||
font-size: 13px;
|
||||
line-height: 24px;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
#start {
|
||||
width: 50vw;
|
||||
height: 20vh;
|
||||
position: absolute;
|
||||
top: 50vh;
|
||||
left: 50vw;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 15vh;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ff0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#info {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
z-index: 1; /* TODO Solve this in HTML */
|
||||
}
|
||||
|
||||
a, button, input, select {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.dg.ac {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
z-index: 2 !important; /* TODO Solve this in HTML */
|
||||
}
|
||||
|
||||
#overlay {
|
||||
position: absolute;
|
||||
font-size: 16px;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
background: rgba(0,0,0,0.7);
|
||||
}
|
||||
|
||||
#overlay button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border: 1px solid rgb(255, 255, 255);
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
padding: 12px 18px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#notSupported {
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
background-color: #f00;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
}
|
50447
three.module.js
Normal file
50447
three.module.js
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user