adding endroll

This commit is contained in:
ad044 2020-12-29 21:18:49 +04:00
parent b2ce4b74bf
commit aafcd90c35
16 changed files with 302 additions and 173 deletions

View file

@ -12,6 +12,7 @@ import SSknScene from "./scenes/SSknScene";
import PolytanScene from "./scenes/PolytanScene";
import TaKScene from "./scenes/TaKScene";
import ChangeDiscScene from "./scenes/ChangeDiscScene";
import EndScene from "./scenes/EndScene";
const App = () => {
const currentScene = useSceneStore((state) => state.currentScene);
@ -30,6 +31,7 @@ const App = () => {
polytan: <PolytanScene />,
tak: <TaKScene />,
change_disc: <ChangeDiscScene />,
end: <EndScene />,
};
}, []);
@ -43,7 +45,7 @@ const App = () => {
</Suspense>
</Canvas>
</span>
<MediaPlayer />
{/*<MediaPlayer />*/}
</div>
);
};

View file

@ -0,0 +1,59 @@
import React from "react";
import * as THREE from "three";
import { useLoader } from "react-three-fiber";
import mainCylinder from "../../static/sprite/end_cylinder_1.png";
const EndCylinder = () => {
const mainCylinderTex = useLoader(THREE.TextureLoader, mainCylinder);
return (
<>
<mesh position={[0, 2.5, 0]} renderOrder={4}>
<cylinderBufferGeometry
args={[1.5, 2, 1.5, 64, 64, true]}
attach="geometry"
/>
<meshStandardMaterial
attach="material"
map={mainCylinderTex}
color={0xffffff}
transparent={true}
side={THREE.DoubleSide}
depthTest={false}
/>
</mesh>
<mesh position={[0, 0.8, 0]} renderOrder={4}>
<cylinderBufferGeometry
args={[2, 2, 1.2, 64, 64, true]}
attach="geometry"
/>
<meshStandardMaterial
attach="material"
map={mainCylinderTex}
color={0xffffff}
transparent={true}
depthTest={false}
side={THREE.DoubleSide}
/>
</mesh>
<mesh position={[0, -0.9, 0]} renderOrder={4}>
<cylinderBufferGeometry
args={[2, 1.5, 1.5, 64, 64, true]}
attach="geometry"
/>
<meshStandardMaterial
attach="material"
map={mainCylinderTex}
color={0xffffff}
transparent={true}
depthTest={false}
side={THREE.DoubleSide}
/>
</mesh>
</>
);
};
export default EndCylinder;

View file

@ -0,0 +1,37 @@
import React, { useRef } from "react";
import * as THREE from "three";
import { useFrame, useLoader } from "react-three-fiber";
import secondCylinder from "../../static/sprite/end_cylinder_2.png";
type EndSphereProps = {
position: number[];
};
const EndSphere = (props: EndSphereProps) => {
const secondCylinderTex = useLoader(THREE.TextureLoader, secondCylinder);
const meshRef = useRef<THREE.Object3D>();
useFrame(() => {
if (meshRef.current) meshRef.current.rotation.y += 0.005;
});
return (
<mesh
position={props.position as [number, number, number]}
ref={meshRef}
renderOrder={3}
>
<sphereBufferGeometry args={[1, 16, 16]} attach="geometry" />
<meshStandardMaterial
attach="material"
map={secondCylinderTex}
color={0xffffff}
transparent={true}
side={THREE.DoubleSide}
depthTest={false}
opacity={0.7}
/>
</mesh>
);
};
export default EndSphere;

View file

@ -1,21 +1,21 @@
import React, { useMemo, useRef } from "react";
import { useFrame, useLoader } from "react-three-fiber";
import * as THREE from "three";
import takIntro from "../../static/sprite/tak_intro.png";
import takOutro from "../../static/sprite/tak_outro.png";
import mouth1 from "../../static/sprite/mouth_1.png";
import mouth2 from "../../static/sprite/mouth_2.png";
import mouth3 from "../../static/sprite/mouth_3.png";
import mouth4 from "../../static/sprite/mouth_4.png";
import { useMediaStore } from "../../store";
import { LainConstructor } from "../MainScene/Lain";
import takIntro from "../static/sprite/tak_intro.png";
import takOutro from "../static/sprite/tak_outro.png";
import mouth1 from "../static/sprite/mouth_1.png";
import mouth2 from "../static/sprite/mouth_2.png";
import mouth3 from "../static/sprite/mouth_3.png";
import mouth4 from "../static/sprite/mouth_4.png";
import { useMediaStore } from "../store";
import { LainConstructor } from "./MainScene/Lain";
type LainTaKProps = {
intro: boolean;
outro: boolean;
};
const LainTaK = (props: LainTaKProps) => {
const LainSpeak = (props: LainTaKProps) => {
const mouth1Tex = useLoader(THREE.TextureLoader, mouth1);
const mouth2Tex = useLoader(THREE.TextureLoader, mouth2);
const mouth3Tex = useLoader(THREE.TextureLoader, mouth3);
@ -80,10 +80,11 @@ const LainTaK = (props: LainTaKProps) => {
map={mouth4Tex}
alphaTest={0.01}
ref={mouthRef}
depthTest={false}
/>
</sprite>
</>
);
};
export default LainTaK;
export default LainSpeak;

View file

@ -135,7 +135,7 @@ const Lain = (props: LainProps) => {
useEffect(() => {
setTimeout(() => {
setIntroFinished(true);
}, 4000);
}, 3900);
}, []);
const stopIntroAnim = useMemo(() => {

View file

@ -17,10 +17,17 @@ const SyncedComponentLoader = (props: SyncedComponentLoaderProps) => {
const [introFinished, setIntroFinished] = useState(false);
useEffect(() => {
if (!props.shouldIntro) {
document.getElementsByTagName("canvas")[0].className =
"main-scene-background";
}
setTimeout(() => {
setIntroFinished(true);
document.getElementsByTagName("canvas")[0].className =
"main-scene-background";
}, 4000);
}, []);
}, [props.shouldIntro]);
const visible = useMemo(() => {
if (props.paused) {
@ -40,7 +47,10 @@ const SyncedComponentLoader = (props: SyncedComponentLoaderProps) => {
<LevelSelection />
<GrayPlanes />
</group>
<Starfield />
<Starfield
shouldIntro={props.shouldIntro}
introFinished={introFinished}
/>
<Site shouldIntro={props.shouldIntro} introFinished={introFinished} />
</>
);

View file

@ -1,7 +1,12 @@
import React, { useEffect, useState } from "react";
import Star from "./Starfield/Star";
const Starfield = () => {
type StarfieldProps = {
shouldIntro: boolean;
introFinished: boolean;
};
const Starfield = (props: StarfieldProps) => {
const LCG = (a: number, c: number, m: number, s: number) => () =>
(s = (s * a + c) % m);
@ -23,65 +28,103 @@ const Starfield = () => {
);
const [posesBlueFromBottom, posesCyanFromBottom, posesWhiteFromBottom] = [
60,
60,
60,
80,
80,
80,
].map((x) =>
Array.from({ length: x }, () => [
lcgInstance() / 1000000050,
lcgInstance() / 100000099 - 10,
lcgInstance() / 100000099 - 15,
lcgInstance() / 1000000050,
])
);
const [mainVisible, setMainVisible] = useState(false);
const [introVisible, setIntroVisible] = useState(true);
useEffect(() => {
setTimeout(() => {
setMainVisible(true);
}, 3700);
}, 2800);
setTimeout(() => {
setIntroVisible(false);
}, 3200);
}, []);
return (
<>
<group position={[0, -1, 2]} visible={mainVisible}>
<group
position={[0, -1, 2]}
visible={props.shouldIntro ? mainVisible : true}
>
<group rotation={[0, 0.75, Math.PI / 2]} position={[-0.7, -1, -5]}>
{posesBlueFromLeft.map((poses, idx) => (
<Star position={poses} color={"blue"} key={idx} />
<Star
position={poses}
color={"blue"}
key={idx}
shouldIntro={props.shouldIntro}
/>
))}
{posesWhiteFromLeft.map((poses, idx) => (
<Star position={poses} color={"white"} key={idx} />
<Star
position={poses}
color={"white"}
key={idx}
shouldIntro={props.shouldIntro}
/>
))}
{posesCyanFromLeft.map((poses, idx) => (
<Star position={poses} color={"cyan"} key={idx} />
<Star
position={poses}
color={"cyan"}
key={idx}
shouldIntro={props.shouldIntro}
/>
))}
</group>
<group rotation={[0, 2.5, Math.PI / 2]} position={[-0.7, -1, -1]}>
{posesBlueFromRight.map((poses, idx) => (
<Star position={poses} color={"blue"} key={idx} />
<Star
position={poses}
color={"blue"}
key={idx}
shouldIntro={props.shouldIntro}
/>
))}
{posesWhiteFromRight.map((poses, idx) => (
<Star position={poses} color={"white"} key={idx} />
<Star
position={poses}
color={"white"}
key={idx}
shouldIntro={props.shouldIntro}
/>
))}
{posesCyanFromRight.map((poses, idx) => (
<Star position={poses} color={"cyan"} key={idx} />
<Star
position={poses}
color={"cyan"}
key={idx}
shouldIntro={props.shouldIntro}
/>
))}
</group>
</group>
<group
position={[-2, -15, -19]}
rotation={[Math.PI / 4, 0, 0]}
>
{posesBlueFromBottom.map((poses, idx) => (
<Star position={poses} color={"blue"} key={idx} introStar={true} />
))}
{posesWhiteFromBottom.map((poses, idx) => (
<Star position={poses} color={"white"} key={idx} introStar={true} />
))}
{posesCyanFromBottom.map((poses, idx) => (
<Star position={poses} color={"cyan"} key={idx} introStar={true} />
))}
</group>
{introVisible && props.shouldIntro ? (
<group position={[-2, -15, -30]} rotation={[Math.PI / 3, 0, 0]}>
{posesBlueFromBottom.map((poses, idx) => (
<Star position={poses} color={"blue"} key={idx} introStar={true} />
))}
{posesWhiteFromBottom.map((poses, idx) => (
<Star position={poses} color={"white"} key={idx} introStar={true} />
))}
{posesCyanFromBottom.map((poses, idx) => (
<Star position={poses} color={"cyan"} key={idx} introStar={true} />
))}
</group>
) : (
<></>
)}
</>
);
};

View file

@ -7,6 +7,7 @@ type StarProps = {
position: number[];
color: string;
introStar?: boolean;
shouldIntro?: boolean;
};
const Star = (props: StarProps) => {
@ -49,7 +50,7 @@ const Star = (props: StarProps) => {
const amp = useRef(Math.random() / 10);
const introAmpRef = useRef(1);
const introAmpRef = useRef(props.shouldIntro ? 1 : 0);
useFrame(() => {
if (starRef.current) {
@ -61,7 +62,7 @@ const Star = (props: StarProps) => {
}
starRef.current.position.y += 0.01 + amp.current + introAmpRef.current;
if (introAmpRef.current > 0) {
introAmpRef.current -= 0.003;
introAmpRef.current -= 0.004;
}
}
}

View file

@ -57,11 +57,9 @@ const Images = () => {
"../../static/media_images/" + currentSite + "/" + img[1] + ".png"
).then((imageSrc: { default: string }) => {
imgArr.splice(parseInt(img[0]), 0, imageSrc);
console.log(imgTries);
if (imgTries === 3) {
setSceneImages(imgArr);
new THREE.TextureLoader().load(imgArr[0].default, setActiveImage);
console.log(imgArr);
}
});
}

View file

@ -2,6 +2,7 @@ import React, {
createRef,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
@ -21,6 +22,8 @@ const MediaPlayer = () => {
const [mediaSource, setMediaSource] = useState<any>();
const currentScene = useSceneStore((state) => state.currentScene);
const setScene = useSceneStore((state) => state.setScene);
const setPercentageElapsed = useMediaStore(
(state) => state.setPercentageElapsed
);
@ -33,7 +36,9 @@ const MediaPlayer = () => {
const currentSite = useSiteStore((state) => state.currentSite);
const siteData = currentSite === "a" ? site_a : site_b;
const siteData = useMemo(() => (currentSite === "a" ? site_a : site_b), [
currentSite,
]);
const updateTime = useCallback(() => {
(requestRef.current as any) = requestAnimationFrame(updateTime);
@ -45,12 +50,26 @@ const MediaPlayer = () => {
if (percentageElapsed % 5 === 0) {
setPercentageElapsed(percentageElapsed);
if (percentageElapsed === 100) {
videoRef.current.pause();
videoRef.current.currentTime = 0;
if (
(siteData as SiteType)[activeLevel][activeNodeId]
.triggers_final_video === 1
) {
setScene("end");
} else {
videoRef.current.pause();
}
}
}
}
}, [setPercentageElapsed, videoRef]);
}, [
activeLevel,
activeNodeId,
setPercentageElapsed,
setScene,
siteData,
videoRef,
]);
useEffect(() => {
(requestRef.current as any) = requestAnimationFrame(updateTime);
@ -59,7 +78,15 @@ const MediaPlayer = () => {
}, [updateTime]);
useEffect(() => {
if (currentScene === "media" || currentScene === "tak") {
if (currentScene === "end") {
import("../../static/movie/b/ENDROLL1.STR[0].webm").then((media) => {
setMediaSource(media.default);
if (videoRef.current) {
videoRef.current.load();
videoRef.current.play();
}
});
} else if (currentScene === "media" || currentScene === "tak") {
const nodeMedia = (siteData as SiteType)[activeLevel][activeNodeId]
.media_file;
if (nodeMedia.includes("XA")) {
@ -98,7 +125,11 @@ const MediaPlayer = () => {
controls
id="media"
ref={videoRef}
style={{ display: currentScene === "media" ? "block" : "none" }}
style={{
display: ["media", "tak", "end"].includes(currentScene)
? "block"
: "none",
}}
>
<source src={mediaSource} />
<track src={t} kind="captions" default />

View file

@ -4,99 +4,25 @@ import grayTextureFile from "../../../../static/sprite/gray_box.png";
import darkGrayTextureFile from "../../../../static/sprite/dark_gray_box.png";
import React, { useRef } from "react";
import { ShapeProps } from "../LeftSide";
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
type GLTFResult = GLTF & {
nodes: {
Cube001: THREE.Mesh;
};
materials: {
["Material.001"]: THREE.MeshStandardMaterial;
};
};
const TriangularPrism = (props: ShapeProps) => {
const grayTex = useLoader(THREE.TextureLoader, grayTextureFile);
const darkGrayTex = useLoader(THREE.TextureLoader, darkGrayTextureFile);
const { nodes } = useLoader<GLTFResult>(GLTFLoader, "models/cutcube.glb");
const prismRef = useRef<THREE.Object3D>();
const verts = new Float32Array([
// Top
0,
0,
0,
1,
0,
0,
1,
1,
0,
0,
0,
0,
1,
1,
0,
0,
1,
0,
// Bottom
0,
0,
0,
0,
0,
1,
0,
1,
1,
0,
0,
0,
0,
1,
0,
0,
1,
1,
// Sides
0,
0,
1,
1,
0,
0,
0,
0,
0,
0,
1,
1,
0,
1,
0,
1,
1,
0,
// Hypotenuse
1,
0,
0,
1,
1,
0,
0,
0,
1,
0,
0,
1,
1,
1,
0,
0,
1,
1,
]);
useFrame(() => {
if (props.selectable && props.active) {
prismRef.current!.rotation.y -= 0.015;
@ -107,41 +33,19 @@ const TriangularPrism = (props: ShapeProps) => {
return (
<mesh
scale={[0.45, 0.5, 0.45]}
scale={[0.45/2, 0.5/2, 0.45/2]}
position={props.position as [number, number, number]}
rotation-y={0.15}
rotation-z={-0.02}
ref={prismRef}
geometry={nodes["Cube001"].geometry}
>
<boxBufferGeometry args={[1, 1, 1]} attach="geometry" />
<meshLambertMaterial
attach="material"
map={props.active ? grayTex : darkGrayTex}
/>
</mesh>
// <mesh
// scale={[0.45, 0.5, 0.45]}
// position={props.position as [number, number, number]}
// rotation-y={0.15}
// rotation-x={-1.45}
// ref={prismRef}
// >
// <bufferGeometry attach="geometry">
// <bufferAttribute
// array={verts}
// itemSize={3}
// count={verts.length / 3}
// attachObject={["attributes", "position"]}
// />
// </bufferGeometry>
// <meshBasicMaterial
// side={THREE.DoubleSide}
// attach="material"
// map={props.active ? grayTex : darkGrayTex}
// />
// </mesh>
);
};
export default TriangularPrism;
export default TriangularPrism;

38
src/scenes/EndScene.tsx Normal file
View file

@ -0,0 +1,38 @@
import React, { useRef, useState } from "react";
import * as THREE from "three";
import { useFrame } from "react-three-fiber";
import EndCylinder from "../components/EndScene/EndCylinder";
import EndSphere from "../components/EndScene/EndSphere";
import LainSpeak from "../components/LainSpeak";
const EndScene = () => {
const mainCylinderRef = useRef<THREE.Object3D>();
useFrame(() => {
if (mainCylinderRef.current) {
mainCylinderRef.current.rotation.y -= 0.01;
}
});
const [isIntro, setIsIntro] = useState(true);
const [isOutro, setIsOutro] = useState(false);
return (
<>
<pointLight position={[0, 0, 5]} intensity={0.9} />
<pointLight position={[0, 0, -5]} intensity={0.9} />
<group ref={mainCylinderRef} position={[0, -1, 2.2]}>
<EndCylinder />
</group>
<EndSphere position={[-1.8, -1.6, 1.4]} />
<EndSphere position={[1.8, -0.5, 0]} />
<EndSphere position={[2, -1.7, 1]} />
<EndSphere position={[-1.6, 1.4, 1.5]} />
<EndSphere position={[2, 1.7, -0.5]} />
<LainSpeak intro={isIntro} outro={isOutro} />
</>
);
};
export default EndScene;

View file

@ -18,12 +18,10 @@ const MainScene = () => {
]);
useEffect(() => {
document.getElementsByTagName("body")[0].className = "main-body";
return () => {
document.getElementsByTagName("body")[0].className = "";
document.getElementsByTagName("canvas")[0].className = "";
};
}, []);
return (
<perspectiveCamera position-z={3}>
<Suspense fallback={null}>

View file

@ -1,12 +1,16 @@
import React, { useCallback, useEffect, useState } from "react";
import {useLevelStore, useMediaStore, useNodeStore, useSiteStore} from "../store";
import React, { useCallback, useEffect } from "react";
import {
useLevelStore,
useMediaStore,
useNodeStore,
useSiteStore,
} from "../store";
import LeftSide from "../components/MediaScene/Selectables/LeftSide";
import RightSide from "../components/MediaScene/Selectables/RightSide";
import AudioVisualizer from "../components/MediaScene/AudioVisualizer/AudioVisualizer";
import MediaLoadingBar from "../components/MediaScene/MediaLoadingBar";
import NodeNameContainer from "../components/MediaScene/NodeNameContainer";
import Lof from "../components/MediaScene/Lof";
import { OrbitControls } from "@react-three/drei";
import Images from "../components/MediaScene/Images";
import MediumLetter from "../components/TextRenderer/MediumLetter";
import site_a from "../resources/site_a.json";
@ -45,12 +49,15 @@ const MediaScene = () => {
useEffect(() => {
document.getElementsByTagName("canvas")[0].className =
"media-scene-background";
return () => {
document.getElementsByTagName("canvas")[0].className = "";
};
}, []);
return (
<perspectiveCamera position-z={3}>
<group position={[0.4, -0.3, 0]}>
<OrbitControls />
<pointLight intensity={1.2} color={0xffffff} position={[-2, 0, 0]} />
<LeftSide activeMediaComponent={activeMediaComponent} />
<group position={[0, 0.5, -3]}>

View file

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import LainTaK from "../components/TaKScene/LainTaK";
import LainSpeak from "../components/LainSpeak";
import * as THREE from "three";
import { useMediaStore, useSceneStore } from "../store";
@ -7,7 +7,7 @@ const TaKScene = () => {
const setAudioAnalyser = useMediaStore((state) => state.setAudioAnalyser);
const setScene = useSceneStore((state) => state.setScene);
const [isIntro, setIntro] = useState(true);
const [isIntro, setIsIntro] = useState(true);
const [isOutro, setIsOutro] = useState(false);
const percentageElapsed = useMediaStore(
@ -36,12 +36,12 @@ const TaKScene = () => {
if (mediaElement) {
mediaElement.play();
setIntro(false);
setIsIntro(false);
}
}, 3800);
}, [setAudioAnalyser]);
return <LainTaK intro={isIntro} outro={isOutro} />;
return <LainSpeak intro={isIntro} outro={isOutro} />;
};
export default TaKScene;

View file

@ -444,7 +444,7 @@ export const useSSknStore = create<SSknState>((set) => ({
}));
export const useSceneStore = create<SceneState>((set) => ({
currentScene: "main",
currentScene: "end",
setScene: (to) => set(() => ({ currentScene: to })),
}));