rewrote starfield

This commit is contained in:
ad044 2020-12-06 18:02:09 +04:00
parent f27ab6d4f8
commit abed73e236
8 changed files with 184 additions and 384 deletions

View file

@ -9,7 +9,7 @@ import moveRightSpriteSheet from "../../static/sprite/move_right.png";
import standingSpriteSheet from "../../static/sprite/standing.png";
import introSpriteSheet from "../../static/sprite/intro.png";
import throwNodeSpriteSheet from "../../static/sprite/throw_node.png";
import { useLainStore } from "../../store";
import { useLainStore, useMainSceneStore } from "../../store";
type LainConstructorProps = {
sprite: string;

View file

@ -1,11 +1,12 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import * as THREE from "three";
import PauseSquare from "./PauseSquare";
import StaticBigLetter from "../TextRenderer/StaticBigLetter";
import { usePauseStore } from "../../store";
import StaticBigLetter from "../../TextRenderer/StaticBigLetter";
import { usePauseStore } from "../../../store";
import { useLoader } from "react-three-fiber";
const Pause = (props: { visible: boolean }) => {
const exit = usePauseStore((state) => state.exitAnimation);
const [showActiveComponent, setShowActiveComponent] = useState(false);
const [animation, setAnimation] = useState(false);
const [intro, setIntro] = useState(true);
@ -77,7 +78,7 @@ const Pause = (props: { visible: boolean }) => {
[0, 1, 2, 3, 4, 5, 6].map((col: number) => {
if (rowIdx === 5 && col > 0 && col < 5) {
return col === 1 ? (
<>
<React.Fragment key={`Lfragment`}>
<StaticBigLetter
color={"white"}
letter={"L"}
@ -85,7 +86,7 @@ const Pause = (props: { visible: boolean }) => {
position={[0.35, 1.8, 0]}
scale={[0.25, 0.25, 0.25]}
active={!(activeComponent === "load")}
key={col}
key={"whiteL"}
rowIdx={rowIdx}
colIdx={col}
intro={intro}
@ -94,24 +95,24 @@ const Pause = (props: { visible: boolean }) => {
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
key={col}
key={"whiteLsquare"}
shouldDisappear={true}
intro={intro}
/>
</>
</React.Fragment>
) : (
<PauseSquare
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
active={!(activeComponent === "load")}
key={col}
key={`${rowIdx}${col}L`}
intro={intro}
/>
);
} else if (rowIdx === 4 && col > 4 && col < 7) {
return col === 5 ? (
<>
<React.Fragment key={"AFragment"}>
<StaticBigLetter
color={"white"}
letter={"A"}
@ -119,7 +120,7 @@ const Pause = (props: { visible: boolean }) => {
position={[1.78, 1.43, 0]}
scale={[0.25, 0.25, 0]}
active={!(activeComponent === "about")}
key={col}
key={"whiteA"}
rowIdx={rowIdx}
colIdx={col}
intro={intro}
@ -128,24 +129,24 @@ const Pause = (props: { visible: boolean }) => {
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
key={col}
key={"whiteAsquare"}
shouldDisappear={true}
intro={intro}
/>
</>
</React.Fragment>
) : (
<PauseSquare
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
active={!(activeComponent === "about")}
key={col}
key={`${rowIdx}${col}A`}
intro={intro}
/>
);
} else if (rowIdx === 3 && col > 2 && col < 7) {
return col === 3 ? (
<>
<React.Fragment key={"CFragment"}>
<StaticBigLetter
color={"white"}
letter={"C"}
@ -153,7 +154,7 @@ const Pause = (props: { visible: boolean }) => {
position={[1.05, 1.07, 0]}
scale={[0.25, 0.25, 0]}
active={!(activeComponent === "change")}
key={col}
key={"whiteC"}
rowIdx={rowIdx}
colIdx={col}
intro={intro}
@ -162,24 +163,24 @@ const Pause = (props: { visible: boolean }) => {
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
key={col}
key={"whiteCsquare"}
shouldDisappear={true}
intro={intro}
/>
</>
</React.Fragment>
) : (
<PauseSquare
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
active={!(activeComponent === "change")}
key={col}
key={`${rowIdx}${col}C`}
intro={intro}
/>
);
} else if (rowIdx === 1 && col > 0 && col < 5) {
return col === 1 ? (
<>
<React.Fragment key={"Sfragment"}>
<StaticBigLetter
color={"white"}
letter={"S"}
@ -187,7 +188,7 @@ const Pause = (props: { visible: boolean }) => {
position={[0.35, 0.35, 0]}
scale={[0.25, 0.25, 0]}
active={!(activeComponent === "save")}
key={col}
key={"whiteS"}
rowIdx={rowIdx}
colIdx={col}
intro={intro}
@ -196,24 +197,24 @@ const Pause = (props: { visible: boolean }) => {
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
key={col}
key={"whiteSsquare"}
shouldDisappear={true}
intro={intro}
/>
</>
</React.Fragment>
) : (
<PauseSquare
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
active={!(activeComponent === "save")}
key={col}
key={`${rowIdx}${col}S`}
intro={intro}
/>
);
} else if (rowIdx === 0 && col > 4 && col < 7) {
return col === 5 ? (
<>
<React.Fragment key={"Efragment"}>
<StaticBigLetter
color={"white"}
letter={"E"}
@ -221,7 +222,7 @@ const Pause = (props: { visible: boolean }) => {
position={[1.78, 0, 0]}
scale={[0.25, 0.25, 0]}
active={!(activeComponent === "exit")}
key={col}
key={"whiteE"}
rowIdx={1}
colIdx={col}
intro={intro}
@ -230,18 +231,18 @@ const Pause = (props: { visible: boolean }) => {
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
key={col}
key={"whiteEsquare"}
shouldDisappear={true}
intro={intro}
/>
</>
</React.Fragment>
) : (
<PauseSquare
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
active={!(activeComponent === "exit")}
key={col}
key={`${rowIdx}${col}E`}
intro={intro}
/>
);
@ -251,7 +252,7 @@ const Pause = (props: { visible: boolean }) => {
geometry={squareGeoms[row][col]}
rowIdx={rowIdx}
colIdx={col}
key={col}
key={`${rowIdx}${col}r`}
active={true}
intro={intro}
/>
@ -319,7 +320,7 @@ const Pause = (props: { visible: boolean }) => {
/>
))}
<group visible={showActiveComponent}>
<group visible={!exit}>
<sprite position={[0.5, -0.8, 0]} scale={[3, 1, 0]} renderOrder={100}>
<spriteMaterial
attach="material"

View file

@ -1,9 +1,9 @@
import React, { useMemo } from "react";
import * as THREE from "three";
import { useLoader } from "react-three-fiber";
import pauseGrayBoxes from "../../static/sprite/pause_gray_boxes.png";
import pauseGrayBoxes from "../../../static/sprite/pause_gray_boxes.png";
import { a, useSpring } from "@react-spring/three";
import { usePauseStore } from "../../store";
import { usePauseStore } from "../../../store";
type PauseSquareProps = {
colIdx: number;

View file

@ -1,346 +0,0 @@
import { a, useSpring } from "@react-spring/three";
import React, { createRef, memo, RefObject, useMemo, useRef } from "react";
import { useFrame } from "react-three-fiber";
import * as THREE from "three";
import { useStarfieldStore } from "../../store";
type StarRefsAndInitialPoses = [
React.MutableRefObject<React.RefObject<THREE.Object3D>[]>,
number[][]
][];
type StarfieldObjectData = {
starPoses: number[][];
ref: React.MutableRefObject<React.RefObject<THREE.Object3D>[]>;
rotation: number[];
positionSpecifier: number[];
uniform?:
| { color1: { value: THREE.Color }; color2: { value: THREE.Color } }
| undefined;
};
type IntroStarfieldObjectData = {
starPoses: number[][];
ref: React.MutableRefObject<React.RefObject<THREE.Object3D>[]>;
uniform?:
| { color1: { value: THREE.Color }; color2: { value: THREE.Color } }
| undefined;
};
const Starfield = memo(() => {
const introStarfieldGroupRef = useRef<THREE.Object3D>();
const introStarfieldVisible = useStarfieldStore(
(state) => state.introStarfieldVisible
);
const mainStarfieldVisible = useStarfieldStore(
(state) => state.mainStarfieldVisible
);
const mainStarfieldBoostVal = useStarfieldStore(
(state) => state.mainStarfieldBoostVal
);
const starfieldState = useSpring({
starfieldBoostVal: mainStarfieldBoostVal,
config: { duration: 1200 },
});
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform vec3 color1;
uniform vec3 color2;
uniform float alpha;
varying vec2 vUv;
void main() {
float alpha = smoothstep(0.0, 1.0, vUv.y);
float colorMix = smoothstep(1.0, 2.0, 1.8);
gl_FragColor = vec4(mix(color1, color2, colorMix), alpha);
}
`;
const LCG = (a: number, c: number, m: number, s: number) => () =>
(s = (s * a + c) % m);
const lcgInstance = LCG(1664525, 1013904223, 2 ** 32, 2);
const uniformConstructor = (col: string) => {
return {
color1: {
value: new THREE.Color("white"),
},
color2: {
value: new THREE.Color(col),
},
};
};
const [blueUniforms, cyanUniforms, whiteUniforms] = [
"blue",
"cyan",
"gray",
// eslint-disable-next-line react-hooks/rules-of-hooks
].map((color: string) => useMemo(() => uniformConstructor(color), [color]));
const [
posesBlueFromRight,
posesBlueFromLeft,
posesCyanFromRight,
posesCyanFromLeft,
posesWhiteFromRight,
posesWhiteFromLeft,
] = [5, 5, 5, 5, 5, 5].map((x) =>
Array.from({ length: x }, () => [
lcgInstance() / 1000000000,
lcgInstance() / 1000000000 - 1,
lcgInstance() / 1000000000,
])
);
const [
blueFromRightRef,
blueFromLeftRef,
cyanFromRightRef,
cyanFromLeftRef,
whiteFromRightRef,
whiteFromLeftRef,
] = [
posesBlueFromRight,
posesBlueFromLeft,
posesCyanFromRight,
posesCyanFromLeft,
posesWhiteFromRight,
posesWhiteFromLeft,
].map((poses) =>
// eslint-disable-next-line react-hooks/rules-of-hooks
useRef<RefObject<THREE.Object3D>[]>(
poses.map(() => createRef<THREE.Object3D>())
)
);
const [introPosesBlue, introPosesCyan, introPosesWhite] = [
80,
80,
60,
].map((x) =>
Array.from({ length: x }, () => [
lcgInstance() / 1000000050,
lcgInstance() / 100000099,
lcgInstance() / 1000000050,
])
);
const [introBlueRef, introCyanRef, introWhiteRef] = [
introPosesBlue,
introPosesCyan,
introPosesWhite,
].map((poses) =>
// eslint-disable-next-line react-hooks/rules-of-hooks
useRef<RefObject<THREE.Object3D>[]>(
poses.map(() => createRef<THREE.Object3D>())
)
);
const fromRightStarRefsAndInitialPoses: StarRefsAndInitialPoses = [
[blueFromRightRef, posesBlueFromRight],
[cyanFromRightRef, posesCyanFromRight],
[whiteFromRightRef, posesWhiteFromRight],
];
const fromLeftStarRefsAndInitialPoses: StarRefsAndInitialPoses = [
[blueFromLeftRef, posesBlueFromLeft],
[cyanFromLeftRef, posesCyanFromLeft],
[whiteFromLeftRef, posesWhiteFromLeft],
];
const starSpeeds = Array.from(
{ length: 60 },
() => lcgInstance() / 100000000000 + 0.03
);
useFrame(() => {
if (introStarfieldVisible) {
introStarfieldGroupRef.current!.position.y += 0.2;
}
if (mainStarfieldVisible) {
// planes (stars) coming from right move to positive X and negative Z direction
fromRightStarRefsAndInitialPoses.forEach((el) => {
el[0].current.forEach(
(posRef: RefObject<THREE.Object3D>, idx: number) => {
if (posRef.current!.position.x < -1) {
posRef.current!.position.x = el[1][idx][0] + 6;
posRef.current!.position.z = el[1][idx][2] - 2.5;
}
posRef.current!.position.x -=
starSpeeds[idx] + starfieldState.starfieldBoostVal.get();
posRef.current!.position.z +=
0.035 + starfieldState.starfieldBoostVal.get() * 0.5;
}
);
});
// the ones that are coming from left move to negative X and Z
fromLeftStarRefsAndInitialPoses.forEach((el) => {
el[0].current.forEach(
(posRef: RefObject<THREE.Object3D>, idx: number) => {
if (posRef.current!.position.x > 3) {
posRef.current!.position.x = el[1][idx][0] - 9;
posRef.current!.position.z = el[1][idx][2] - 0.5;
} else {
posRef.current!.position.x +=
starSpeeds[idx] + starfieldState.starfieldBoostVal.get();
posRef.current!.position.z +=
0.015 + starfieldState.starfieldBoostVal.get() * 0.5;
}
}
);
});
}
});
const mainStarfieldObjects = [
{
starPoses: posesBlueFromRight,
ref: blueFromRightRef,
rotation: [1.7, 0, 1],
positionSpecifier: [6, 0, -2.5],
uniform: blueUniforms,
},
{
starPoses: posesBlueFromLeft,
ref: blueFromLeftRef,
rotation: [1.7, 0, -1.2],
positionSpecifier: [-2.4, -0.5, 2],
uniform: blueUniforms,
},
{
starPoses: posesCyanFromRight,
ref: cyanFromRightRef,
rotation: [1.7, 0, 1],
positionSpecifier: [6, 0, -2.5],
uniform: cyanUniforms,
},
{
starPoses: posesCyanFromLeft,
ref: cyanFromLeftRef,
rotation: [1.7, 0, -1.2],
positionSpecifier: [-1.3, 0, 2.5],
uniform: cyanUniforms,
},
{
starPoses: posesWhiteFromLeft,
ref: whiteFromLeftRef,
rotation: [1.7, 0, -1.2],
positionSpecifier: [-1.3, 0.5, 2.3],
uniform: whiteUniforms,
},
{
starPoses: posesWhiteFromRight,
ref: whiteFromRightRef,
rotation: [1.7, 0, 1],
positionSpecifier: [-1.3, 0.5, -2.5],
uniform: whiteUniforms,
},
];
const introStarfieldObjects = [
{
starPoses: introPosesBlue,
ref: introBlueRef,
uniform: blueUniforms,
},
{
starPoses: introPosesCyan,
ref: introCyanRef,
uniform: cyanUniforms,
},
{
starPoses: introPosesWhite,
ref: introWhiteRef,
uniform: whiteUniforms,
},
];
return (
<>
<a.group
ref={introStarfieldGroupRef}
position={[-2, -35, -3.2]}
rotation={[0, 0, 0]}
visible={introStarfieldVisible}
>
{introStarfieldObjects.map((obj: IntroStarfieldObjectData) =>
obj.starPoses.map((pos: number[], idx: number) => {
return (
<mesh
ref={obj.ref.current[idx]}
scale={[0.005, 2, 0.005]}
position={[pos[0], pos[1], pos[2]]}
key={pos[0]}
renderOrder={-1}
>
<boxBufferGeometry attach="geometry" />
<shaderMaterial
attach="material"
uniforms={obj.uniform}
fragmentShader={fragmentShader}
vertexShader={vertexShader}
side={THREE.DoubleSide}
transparent={true}
depthWrite={false}
/>
</mesh>
);
})
)}
</a.group>
<a.group
position={[-0.7, 0, -5]}
rotation={[0, 0, 0]}
position-y={-1}
visible={mainStarfieldVisible}
>
{mainStarfieldObjects.map((obj: StarfieldObjectData) =>
obj.starPoses.map((pos: number[], idx: number) => {
return (
<mesh
ref={obj.ref.current[idx]}
position={[
pos[0] + obj.positionSpecifier[0],
pos[1] + obj.positionSpecifier[1],
pos[2] + obj.positionSpecifier[2],
]}
rotation={obj.rotation as [number, number, number]}
scale={[0.01, 2, 0.01]}
renderOrder={-1}
key={pos[0]}
>
<boxBufferGeometry attach="geometry" args={[1, 1, 1]} />
<a.shaderMaterial
attach="material"
uniforms={obj.uniform}
fragmentShader={fragmentShader}
vertexShader={vertexShader}
transparent={true}
depthWrite={false}
/>
</mesh>
);
})
)}
</a.group>
</>
);
});
export default Starfield;

View file

@ -0,0 +1,80 @@
import React, { useRef } from "react";
import { a } from "@react-spring/three";
import * as THREE from "three";
import { useFrame } from "react-three-fiber";
type StarProps = {
position: number[];
color: string;
};
const Star = (props: StarProps) => {
const uniformConstructor = (col: string) => {
return {
color1: {
value: new THREE.Color("white"),
},
color2: {
value: new THREE.Color(col),
},
};
};
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform vec3 color1;
uniform vec3 color2;
uniform float alpha;
varying vec2 vUv;
void main() {
float alpha = smoothstep(0.0, 1.0, vUv.y);
float colorMix = smoothstep(1.0, 2.0, 1.8);
gl_FragColor = vec4(mix(color1, color2, colorMix), alpha) * 0.8;
}
`;
const starRef = useRef<THREE.Object3D>();
const amp = useRef(Math.random() / 10);
useFrame(() => {
if (starRef.current) {
if (starRef.current.position.y > 4) {
starRef.current.position.y = props.position[1];
}
starRef.current.position.y += (0.01 + amp.current);
}
});
return (
<mesh
position={props.position as [number, number, number]}
scale={[0.01, 2, 0.01]}
renderOrder={-1}
ref={starRef}
>
<boxBufferGeometry attach="geometry" args={[1, 1, 1]} />
<a.shaderMaterial
attach="material"
fragmentShader={fragmentShader}
vertexShader={vertexShader}
transparent={true}
depthWrite={false}
uniforms={uniformConstructor(props.color)}
/>
</mesh>
);
};
export default Star;

View file

@ -0,0 +1,57 @@
import React from "react";
import Star from "./Star";
type StarfieldProps = {
visible: boolean;
}
const Starfield = (props: StarfieldProps) => {
const LCG = (a: number, c: number, m: number, s: number) => () =>
(s = (s * a + c) % m);
const lcgInstance = LCG(1664525, 1013904223, 2 ** 32, 2);
const [
posesBlueFromRight,
posesBlueFromLeft,
posesCyanFromRight,
posesCyanFromLeft,
posesWhiteFromRight,
posesWhiteFromLeft,
] = [5, 5, 5, 5, 5, 5].map((x) =>
Array.from({ length: x }, () => [
lcgInstance() / 1000000000,
lcgInstance() / 10000000000 - 10,
lcgInstance() / 1000000000,
])
);
return (
<group position={[0, -1, 2]} visible={props.visible}>
<group rotation={[0, 0.75, Math.PI / 2]} position={[-0.7, -1, -5]}>
{posesBlueFromLeft.map((poses, idx) => (
<Star position={poses} color={"blue"} key={idx} />
))}
{posesWhiteFromLeft.map((poses, idx) => (
<Star position={poses} color={"white"} key={idx} />
))}
{posesCyanFromLeft.map((poses, idx) => (
<Star position={poses} color={"cyan"} key={idx} />
))}
</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} />
))}
{posesWhiteFromRight.map((poses, idx) => (
<Star position={poses} color={"white"} key={idx} />
))}
{posesCyanFromRight.map((poses, idx) => (
<Star position={poses} color={"cyan"} key={idx} />
))}
</group>
</group>
);
};
export default Starfield;

View file

@ -15,35 +15,41 @@ const SubsceneManager = (props: StateManagerProps) => {
return {
action: setMainSubscene,
value: "site",
delay: 0,
};
case "toggle_level_selection":
return {
action: setMainSubscene,
value: "level_selection",
delay: 0,
};
case "toggle_pause":
return {
action: setMainSubscene,
value: "pause",
delay: 0,
};
case "pause_exit_select":
return {
action: setMainSubscene,
value: "site",
delay: 1800,
};
case "authorize_user_back":
case "load_data_no_select":
return {
action: setBootSubscene,
value: "main_menu",
delay: 0,
};
case "authorize_user_select":
return {
action: setBootSubscene,
value: "authorize_user",
delay: 0,
};
case "load_data_select":
return { action: setBootSubscene, value: "load_data" };
return { action: setBootSubscene, value: "load_data", delay: 0 };
}
},
[setBootSubscene, setMainSubscene]
@ -56,7 +62,9 @@ const SubsceneManager = (props: StateManagerProps) => {
const dispatchedObject = dispatchObject(eventAction);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value);
setTimeout(() => {
dispatchedObject.action(dispatchedObject.value);
}, dispatchedObject.delay);
}
}
}, [props.eventState, dispatchObject]);

View file

@ -7,7 +7,6 @@ import Preloader from "../components/Preloader";
import MainSceneIntro from "../components/MainSceneIntro";
import GrayPlanes from "../components/MainScene/GrayPlanes";
import MiddleRing from "../components/MainScene/MiddleRing";
import Starfield from "../components/MainScene/Starfield";
import { useHudStore, useLainStore, useMainSceneStore } from "../store";
import GreenTextRenderer from "../components/TextRenderer/GreenTextRenderer";
import HUD from "../components/MainScene/HUD";
@ -15,7 +14,8 @@ import YellowOrb from "../components/MainScene/YellowOrb";
import ActiveLevelNodes from "../components/MainScene/ActiveLevelNodes";
import YellowTextRenderer from "../components/TextRenderer/YellowTextRenderer";
import LevelSelection from "../components/MainScene/LevelSelection";
import Pause from "../components/MainScene/Pause";
import Pause from "../components/MainScene/PauseSubscene/Pause";
import Starfield from "../components/MainScene/Starfield/Starfield";
const MainScene = () => {
const currentSubscene = useMainSceneStore((state) => state.subscene);
@ -35,18 +35,18 @@ const MainScene = () => {
<Suspense fallback={null}>
<MainSceneIntro />
<a.group>
<Preloader />
{/*<Preloader />*/}
<Site />
<ActiveLevelNodes />
<HUD visible={!isPaused} />
<GreenTextRenderer visible={!isPaused} />
<YellowTextRenderer visible={!isPaused} />
<YellowOrb visible={!isPaused} />
<Starfield />
<GrayPlanes visible={!isPaused} />
<MiddleRing />
<LevelSelection />
<Pause visible={isPaused} />
<Starfield visible={!isPaused}/>
<OrbitControls />
<pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} />
<pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} />