first test

main
km0 1 year ago
commit 96a08012c9

@ -0,0 +1,15 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "eslint:recommended",
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
}
}

2
.gitignore vendored

@ -0,0 +1,2 @@
node_modules
package-lock.json

53
3d.js

@ -0,0 +1,53 @@
import * as THREE from "three"
import {OrbitControls} from "https://unpkg.com/three@0.150.1/examples/jsm/controls/OrbitControls.js"
const container = document.querySelector('#container')
// Scene setup
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x88ccff)
// Light setup
const ambientLight = new THREE.AmbientLight(0xb3c3e6);
scene.add(ambientLight);
// Renderer setup
const renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Camera setup
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const controls = new OrbitControls( camera, renderer.domElement );
camera.position.z = 5;
// Objects setup
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add(cube);
container.appendChild(renderer.domElement);
// Animation loop setup
function animate() {
renderer.render(scene, camera)
controls.update()
requestAnimationFrame(animate)
cube.rotation.x +=0.01;
cube.rotation.y += 0.01;
}
animate()
// Window resize callback
window.addEventListener("resize", ()=>{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
})
export default {
scene: scene,
}

@ -0,0 +1,372 @@
import * as THREE from "https://cdn.skypack.dev/three@0.129.0";
import Stats from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/libs/stats.module.js";
import { GLTFLoader } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/DRACOLoader.js";
import { Octree } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/math/Octree.js";
import { Capsule } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/math/Capsule.js";
import { DeviceOrientationControls } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/controls/DeviceOrientationControls.js";
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
let mixer;
let controls;
const clock = new THREE.Clock();
const extScene = new THREE.Scene();
const intScene = new THREE.Scene();
let scene = extScene;
let intSceneisLoaded = false;
extScene.background = new THREE.Color(0x88ccff);
intScene.background = new THREE.Color(0xffcc88);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.rotation.order = "YXZ";
if (isMobile) {
controls = new DeviceOrientationControls(camera);
}
// LIGHTS
const ambientlight = new THREE.AmbientLight(0xb3c3e6);
extScene.add(ambientlight);
intScene.add(ambientlight.clone());
const fillLight1 = new THREE.DirectionalLight(0xffffee, 0.2);
fillLight1.position.set(-1, 2, 2);
extScene.add(fillLight1);
intScene.add(fillLight1.clone());
const fillLight2 = new THREE.DirectionalLight(0xffffee, 0.2);
fillLight2.position.set(0, 3, 0);
extScene.add(fillLight2);
intScene.add(fillLight2.clone());
const directionalLight = new THREE.DirectionalLight(0xffffaa, 0.5);
directionalLight.position.set(0, 5, 5);
directionalLight.castShadow = true;
directionalLight.shadow.camera.near = 0.01;
directionalLight.shadow.camera.far = 500;
directionalLight.shadow.camera.right = 30;
directionalLight.shadow.camera.left = -30;
directionalLight.shadow.camera.top = 30;
directionalLight.shadow.camera.bottom = -30;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.radius = 4;
directionalLight.shadow.bias = -0.00006;
extScene.add(directionalLight);
intScene.add(directionalLight.clone());
// RENDERER
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.VSMShadowMap;
const container = document.getElementById("container");
container.appendChild(renderer.domElement);
const stats = new Stats();
stats.domElement.classList.add("stats");
container.appendChild(stats.domElement);
const GRAVITY = 30;
let worldOctree = null;
let intWorldOctree = new Octree();
let extWorldOctree = new Octree();
const playerCollider = new Capsule(
new THREE.Vector3(0, 0.35, 0),
new THREE.Vector3(0, 1.5, 0),
0.35
);
const playerVelocity = new THREE.Vector3();
const playerDirection = new THREE.Vector3();
let playerOnFloor = false;
// mobile controls
const buttonStates = {};
// document.getElementById("forward").addEventListener("touchstart", (event) => {
// buttonStates.forward = true;
// });
// document.getElementById("forward").addEventListener("touchend", (event) => {
// buttonStates.forward = false;
// });
// document.getElementById("back").addEventListener("touchstart", (event) => {
// buttonStates["back"] = true;
// });
// document.getElementById("back").addEventListener("touchend", (event) => {
// buttonStates["back"] = false;
// });
// keyboard controls
const keyStates = {};
document.addEventListener("keydown", (event) => {
keyStates[event.code] = true;
});
document.addEventListener("keyup", (event) => {
keyStates[event.code] = false;
});
if (!isMobile) {
document.addEventListener("mousedown", () => {
document.body.requestPointerLock();
});
document.body.addEventListener("mousemove", (event) => {
if (document.pointerLockElement === document.body) {
camera.rotation.y -= event.movementX / 500;
camera.rotation.x -= event.movementY / 500;
}
});
}
window.addEventListener("resize", onWindowResize);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function playerCollitions() {
let result = worldOctree.capsuleIntersect(playerCollider);
playerOnFloor = false;
if (result) {
playerOnFloor = result.normal.y > 0;
if (!playerOnFloor) {
playerVelocity.addScaledVector(result.normal, -result.normal.dot(playerVelocity));
}
playerCollider.translate(result.normal.multiplyScalar(result.depth));
}
}
function updatePlayer(deltaTime) {
if (playerOnFloor) {
const damping = Math.exp(-3 * deltaTime) - 1;
playerVelocity.addScaledVector(playerVelocity, damping);
} else {
playerVelocity.y -= GRAVITY * deltaTime;
}
const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime);
playerCollider.translate(deltaPosition);
playerCollitions();
camera.position.copy(playerCollider.end);
}
function getForwardVector() {
camera.getWorldDirection(playerDirection);
playerDirection.y = 0;
playerDirection.normalize();
return playerDirection;
}
function getSideVector() {
camera.getWorldDirection(playerDirection);
playerDirection.y = 0;
playerDirection.normalize();
playerDirection.cross(camera.up);
return playerDirection;
}
function keyControls(deltaTime) {
const speed = 25;
if (playerOnFloor) {
if (keyStates["KeyW"]) {
playerVelocity.add(getForwardVector().multiplyScalar(speed * deltaTime));
}
if (keyStates["KeyS"]) {
playerVelocity.add(getForwardVector().multiplyScalar(-speed * deltaTime));
}
if (keyStates["KeyA"]) {
playerVelocity.add(getSideVector().multiplyScalar(-speed * deltaTime));
}
if (keyStates["KeyD"]) {
playerVelocity.add(getSideVector().multiplyScalar(speed * deltaTime));
}
if (keyStates["Space"]) {
playerVelocity.y = 10;
}
}
}
function mobileControls(deltaTime) {
const speed = 5;
if (playerOnFloor) {
if (buttonStates["forward"]) {
playerVelocity.add(getForwardVector().multiplyScalar(speed * deltaTime));
}
if (buttonStates["back"]) {
playerVelocity.add(getForwardVector().multiplyScalar(-speed * deltaTime));
}
}
}
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.4.1/");
const loader = new GLTFLoader().setPath("./assets/");
loader.setDRACOLoader(dracoLoader);
// SCENE 1
loader.load("model.glb", (gltf) => {
const model = gltf.scene;
extScene.add(model);
extWorldOctree.fromGraphNode(model);
worldOctree = extWorldOctree;
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
if (child.material.map) {
child.material.map.anisotropy = 8;
}
if (child.name === "Door") {
child.geometry.computeBoundingBox();
extDoor.copy(child.geometry.boundingBox).applyMatrix4(child.matrixWorld);
extDoorMesh = child;
door = extDoor;
}
}
});
mixer = new THREE.AnimationMixer(model);
gltf.animations.forEach((clip) => {
mixer.clipAction(clip).play();
});
animate();
});
// SCENE 2
// loader.load("modelCompress2.glb", (gltf) => {
// const model = gltf.scene;
// intScene.add(model);
// intWorldOctree.fromGraphNode(model);
// model.traverse((child) => {
// if (child.isMesh) {
// child.castShadow = true;
// child.receiveShadow = true;
// if (child.material.map) {
// child.material.map.anisotropy = 8;
// }
// if (child.name === "Door") {
// child.geometry.computeBoundingBox();
// intDoor.copy(child.geometry.boundingBox).applyMatrix4(child.matrixWorld);
// intDoorMesh = child;
// }
// }
// });
// intSceneisLoaded = true;
// });
function animate() {
const deltaTime = Math.min(0.1, clock.getDelta());
mixer.update(deltaTime);
if (isMobile) {
controls.update();
mobileControls(deltaTime);
} else keyControls(deltaTime);
let doorDistance = door.distanceToPoint(camera.position);
if (doorDistance < 1.5) {
testDoor();
}
if (isDoorActive == false && doorDistance > 2) {
isDoorActive = true;
}
updatePlayer(deltaTime);
renderer.render(scene, camera);
stats.update();
requestAnimationFrame(animate);
}
let door = null;
let isDoorActive = true;
let isInside = false;
let intDoor = new THREE.Box3();
let extDoor = new THREE.Box3();
let intDoorMesh = null;
let extDoorMesh = null;
function testDoor() {
if (intSceneisLoaded && isDoorActive) {
playerVelocity.y = 5;
scene = isInside ? extScene : intScene;
door = isInside ? extDoor : intDoor;
worldOctree = isInside ? extWorldOctree : intWorldOctree;
playerCollider.translate(
isInside
? new THREE.Vector3(0, 0, 0).copy(intDoorMesh.position).negate()
: new THREE.Vector3(0, 0, 0).copy(extDoorMesh.position).negate()
);
playerCollider.translate(isInside ? extDoorMesh.position : intDoorMesh.position);
camera.position.copy(playerCollider.end);
isInside = !isInside;
isDoorActive = false;
}
}

@ -0,0 +1,54 @@
1
00:00:00,000 --> 00:00:05,529
The average prehistoric person could make a nice living
in about a fifteen-hour work week.
2
00:00:05,800 --> 00:00:10,920
Fifteen hours a week for subsistence leaves
a lot of time for other things
3
00:00:11,514 --> 00:00:14,200
So much time
4
00:00:15,057 --> 00:00:19,100
that maybe the restless ones
5
00:00:20,171 --> 00:00:27,221
to enliven their life, or skill in making
or cooking or singing
6
00:00:27,971 --> 00:00:33,057
or very interesting thoughts to think
7
00:00:33,629 --> 00:00:37,614
decided to slope off and hunt mammoths
8
00:00:38,157 --> 00:00:40,443
The skillful hunters then
9
00:00:40,929 --> 00:00:44,871
would come staggering back with a load of meat
10
00:00:45,186 --> 00:00:47,329
a lot of ivory
11
00:00:47,529 --> 00:00:49,514
and a story
12
00:00:49,636 --> 00:00:51,914
It wasn't the meat that made the difference.
13
00:00:52,300 --> 00:00:53,760
It was the story

Binary file not shown.

Binary file not shown.

@ -0,0 +1,25 @@
import {BoxGeometry, MeshBasicMaterial, Mesh} from "https://unpkg.com/three@0.150.1/build/three.module.js";
import context from './3d.js'
// Mapping
export default {
1: () => addCube(),
2: () => addCube(),
3: () => addCube()
}
// Functions
const addCube = () => {
const geometry = new BoxGeometry(1,1,1);
const material = new MeshBasicMaterial({color: 0xff0000})
const cube = new Mesh(geometry,material)
cube.position.x = Math.random() * 10 - 5;
cube.position.y = Math.random() * 10 - 5;
cube.position.z = Math.random() * 10 - 5;
context.scene.add(cube)
}

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Subtitles player</title>
<link rel="stylesheet" href="style.css">
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.150.1/build/three.module.js"
}
}
</script>
<script type="module" defer src="3d.js"></script>
<script type="module" defer src="index.js" ></script>
<script type="module" defer src="functions.js"></script>
</head>
<body>
<div id="container">
<div id="sub">Hello</div>
</div>
<audio src="assets/polypupatic.mp3" id="player" controls ></audio>
</body>
</html>

@ -0,0 +1,43 @@
// 1. Import dependencies, select DOM elements, define global var
import srtParser2 from 'https://cdn.skypack.dev/srt-parser-2';
// 4. Interactive functions to attach to the subtitle id
// see functions.js for more info
import functions from './functions.js'
const player = document.querySelector('#player')
const sub = document.querySelector('#sub')
const printText = (text) => {
sub.innerHTML = text
}
// 3. Parse .srt file and setup audio player callback
const readSRT = (srt) => {
let parser = new srtParser2();
let srt_array = parser.fromSrt(srt)
console.log(srt_array)
let currentId = 0
player.addEventListener('timeupdate', (e)=>{
let current = srt_array.find(
caption =>
caption.startSeconds <= e.target.currentTime &&
caption.endSeconds >= e.target.currentTime
)
if (current != undefined && currentId != parseInt(current.id)){
currentId = parseInt(current.id)
printText(current.text)
// Check if the srtFunctions object has some callback for the current index
if(Object.hasOwn( functions, current.id))
functions[current.id]()
}
})
}
// 2. Open .srt file and trigger readSRT() function
fetch('assets/mammoth.srt').then(res=>res.text()).then(data=>readSRT(data))

@ -0,0 +1,14 @@
{
"name": "3dsub",
"version": "1.0.0",
"description": "3d env with interactive sub",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"eslint": "^8.35.0"
}
}

@ -0,0 +1,33 @@
html,
body {
margin: 0;
max-width: 100vw;
max-height: 100vh;
overflow: hidden;
font-family: sans-serif;
font-size: 32px;
line-height: 1.4;
}
#container {
position: relative;
}
#sub {
position: absolute;
bottom: 25%;
left: 50%;
translate: -50% 0;
color: white;
text-shadow: 0px 0px 4px black;
max-width: 60ch;
text-align: center;
}
audio {
position: absolute;
bottom: 0;
left: 0;
z-index: 50;
}
Loading…
Cancel
Save