mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
fixed media scene text interaction, also added ability to dynamically display baseline offsets on fonts.
This commit is contained in:
parent
bbbc929801
commit
813063953a
13 changed files with 12183 additions and 12046 deletions
|
@ -26,10 +26,10 @@ const App = () => {
|
||||||
<span className="canvas">
|
<span className="canvas">
|
||||||
<EventStateManager />
|
<EventStateManager />
|
||||||
<Canvas concurrent>
|
<Canvas concurrent>
|
||||||
{/*<Suspense fallback={null}>*/}
|
<Suspense fallback={null}>
|
||||||
{/* <MediaOverlay />*/}
|
<MediaOverlay />
|
||||||
{/*</Suspense>*/}
|
</Suspense>
|
||||||
<MainScene />
|
{/*<MainScene />*/}
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</span>
|
</span>
|
||||||
<MediaPlayer />
|
<MediaPlayer />
|
||||||
|
|
|
@ -202,7 +202,10 @@ const MediaOverlay = () => {
|
||||||
<spriteMaterial attach="material" map={mediaHudOverlayTex} />
|
<spriteMaterial attach="material" map={mediaHudOverlayTex} />
|
||||||
</sprite>
|
</sprite>
|
||||||
<group position={[0.4, -0.3, 0]}>
|
<group position={[0.4, -0.3, 0]}>
|
||||||
<TextRenderer />
|
<group position={[0, 0, 13]} scale={[1, 1, 1]}>
|
||||||
|
<TextRenderer />
|
||||||
|
</group>
|
||||||
|
|
||||||
<pointLight intensity={1.2} color={0xffffff} position={[-2, 0, 3]} />
|
<pointLight intensity={1.2} color={0xffffff} position={[-2, 0, 3]} />
|
||||||
|
|
||||||
<GrayCube position={[-2.7, -1.6, 0.6]} active={grayCubesActive} />
|
<GrayCube position={[-2.7, -1.6, 0.6]} active={grayCubesActive} />
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import React, { useCallback, useEffect } from "react";
|
||||||
|
import { useMediaStore } from "../../store";
|
||||||
|
import { StateManagerProps } from "./EventStateManager";
|
||||||
|
import media_scene_directions from "../../resources/media_scene_directions.json";
|
||||||
|
|
||||||
|
const ActiveMediaElementStateManager = (props: StateManagerProps) => {
|
||||||
|
const setActiveMediaElement = useMediaStore(
|
||||||
|
(state) => state.setActiveMediaElement
|
||||||
|
);
|
||||||
|
|
||||||
|
const dispatchObject = useCallback(
|
||||||
|
(event: string, targetMediaElement: string) => {
|
||||||
|
const dispatcherObjects = {
|
||||||
|
setActivePlay: {
|
||||||
|
action: setActiveMediaElement,
|
||||||
|
value: targetMediaElement,
|
||||||
|
},
|
||||||
|
setActiveExit: {
|
||||||
|
action: setActiveMediaElement,
|
||||||
|
value: targetMediaElement,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return dispatcherObjects[event as keyof typeof dispatcherObjects];
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.eventState) {
|
||||||
|
const eventObject =
|
||||||
|
media_scene_directions[
|
||||||
|
props.eventState as keyof typeof media_scene_directions
|
||||||
|
];
|
||||||
|
|
||||||
|
if (eventObject) {
|
||||||
|
const eventAction = eventObject.action;
|
||||||
|
|
||||||
|
const targetMediaElement = eventObject.target_media_element;
|
||||||
|
|
||||||
|
const dispatchedObject = dispatchObject(
|
||||||
|
eventAction,
|
||||||
|
targetMediaElement
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dispatchedObject) {
|
||||||
|
dispatchedObject.action(dispatchedObject.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [props.eventState, dispatchObject]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ActiveMediaElementStateManager;
|
|
@ -8,6 +8,7 @@ import YellowTextStateManager from "./YellowTextStateManager";
|
||||||
import { useBlueOrbStore, useMediaStore } from "../../store";
|
import { useBlueOrbStore, useMediaStore } from "../../store";
|
||||||
import MediaSceneStateManager from "./MediaSceneStateManager";
|
import MediaSceneStateManager from "./MediaSceneStateManager";
|
||||||
import GreenTextStateManager from "./GreenTextStateManager";
|
import GreenTextStateManager from "./GreenTextStateManager";
|
||||||
|
import ActiveMediaElementStateManager from "./ActiveMediaElementStateManager";
|
||||||
|
|
||||||
const getKeyCodeAssociation = (keyCode: number): string => {
|
const getKeyCodeAssociation = (keyCode: number): string => {
|
||||||
const keyCodeAssocs = {
|
const keyCodeAssocs = {
|
||||||
|
@ -24,6 +25,16 @@ export type StateManagerProps = {
|
||||||
eventState: string;
|
eventState: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EventObject = {
|
||||||
|
action: string;
|
||||||
|
|
||||||
|
target_blue_orb_id?: string;
|
||||||
|
target_hud_id?: string;
|
||||||
|
target_media_element?: string;
|
||||||
|
target_media_element_text?: string;
|
||||||
|
target_media_element_text_position?: number[];
|
||||||
|
};
|
||||||
|
|
||||||
const EventStateManager = () => {
|
const EventStateManager = () => {
|
||||||
const [eventState, setEventState] = useState<string>();
|
const [eventState, setEventState] = useState<string>();
|
||||||
const activeBlueOrb = useBlueOrbStore((state) => state.blueOrbId);
|
const activeBlueOrb = useBlueOrbStore((state) => state.blueOrbId);
|
||||||
|
@ -44,7 +55,7 @@ const EventStateManager = () => {
|
||||||
// from blue_orb_directions.json file.
|
// from blue_orb_directions.json file.
|
||||||
// const eventId = `${activeBlueOrb}_${keyPress}`;
|
// const eventId = `${activeBlueOrb}_${keyPress}`;
|
||||||
//
|
//
|
||||||
const eventId = `${activeBlueOrb}_${keyPress}`;
|
const eventId = `${activeMediaElement}_${keyPress}`;
|
||||||
setEventState(eventId);
|
setEventState(eventId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -68,7 +79,7 @@ const EventStateManager = () => {
|
||||||
<SiteStateManager eventState={eventState!} />
|
<SiteStateManager eventState={eventState!} />
|
||||||
<LainStateManager eventState={eventState!} />
|
<LainStateManager eventState={eventState!} />
|
||||||
<MiddleRingStateManager eventState={eventState!} />
|
<MiddleRingStateManager eventState={eventState!} />
|
||||||
<MediaSceneStateManager eventState={eventState!} />
|
<ActiveMediaElementStateManager eventState={eventState!} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ const GreenTextStateManager = (props: StateManagerProps) => {
|
||||||
toggleGreenText();
|
toggleGreenText();
|
||||||
}, delay);
|
}, delay);
|
||||||
},
|
},
|
||||||
[]
|
[setGreenText, toggleGreenText]
|
||||||
);
|
);
|
||||||
|
|
||||||
const dispatchObject = useCallback(
|
const dispatchObject = useCallback(
|
||||||
|
|
|
@ -3,6 +3,8 @@ import blue_orb_huds from "../../resources/blue_orb_huds.json";
|
||||||
import site_a from "../../resources/site_a.json";
|
import site_a from "../../resources/site_a.json";
|
||||||
import { useTextRendererStore } from "../../store";
|
import { useTextRendererStore } from "../../store";
|
||||||
import blue_orb_directions from "../../resources/blue_orb_directions.json";
|
import blue_orb_directions from "../../resources/blue_orb_directions.json";
|
||||||
|
import media_scene_directions from "../../resources/media_scene_directions.json";
|
||||||
|
import { EventObject } from "./EventStateManager";
|
||||||
|
|
||||||
type AnimateYellowTextWithMove = (
|
type AnimateYellowTextWithMove = (
|
||||||
yellowLetterPosYOffset: number,
|
yellowLetterPosYOffset: number,
|
||||||
|
@ -15,8 +17,16 @@ type AnimateYellowTextWithoutMove = (
|
||||||
targetBlueOrbId: string
|
targetBlueOrbId: string
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
type AnimateMediaYellowText = (
|
||||||
|
targetMediaText: string,
|
||||||
|
targetMediaTextPos: number[]
|
||||||
|
) => void;
|
||||||
|
|
||||||
type YellowTextDispatchData = {
|
type YellowTextDispatchData = {
|
||||||
action: AnimateYellowTextWithMove | AnimateYellowTextWithoutMove;
|
action:
|
||||||
|
| AnimateYellowTextWithMove
|
||||||
|
| AnimateYellowTextWithoutMove
|
||||||
|
| AnimateMediaYellowText;
|
||||||
value: any;
|
value: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,6 +36,8 @@ type YellowTextDispatcher = {
|
||||||
moveLeft: YellowTextDispatchData;
|
moveLeft: YellowTextDispatchData;
|
||||||
moveRight: YellowTextDispatchData;
|
moveRight: YellowTextDispatchData;
|
||||||
changeBlueOrbFocus: YellowTextDispatchData;
|
changeBlueOrbFocus: YellowTextDispatchData;
|
||||||
|
setActivePlay: YellowTextDispatchData;
|
||||||
|
setActiveExit: YellowTextDispatchData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const YellowTextStateManager = (props: any) => {
|
const YellowTextStateManager = (props: any) => {
|
||||||
|
@ -125,8 +137,41 @@ const YellowTextStateManager = (props: any) => {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const animateMediaYellowText: AnimateMediaYellowText = useCallback(
|
||||||
|
(targetMediaElementText: string, targetMediaElementTextPos: number[]) => {
|
||||||
|
// make current text shrink
|
||||||
|
setYellowTextOffsetXCoeff(-1);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setYellowTextPosX(targetMediaElementTextPos[0]);
|
||||||
|
setYellowTextPosY(targetMediaElementTextPos[1]);
|
||||||
|
}, 400);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setYellowText(targetMediaElementText);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// unshrink text
|
||||||
|
setYellowTextOffsetXCoeff(0);
|
||||||
|
}, 1200);
|
||||||
|
},
|
||||||
|
[
|
||||||
|
setYellowText,
|
||||||
|
setYellowTextOffsetXCoeff,
|
||||||
|
setYellowTextPosX,
|
||||||
|
setYellowTextPosY,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const dispatchObject = useCallback(
|
const dispatchObject = useCallback(
|
||||||
(event: string, targetBlueOrbHudId: string, targetBlueOrbId: string) => {
|
(
|
||||||
|
event: string,
|
||||||
|
targetBlueOrbHudId: string | undefined,
|
||||||
|
targetBlueOrbId: string | undefined,
|
||||||
|
targetMediaElementText: string | undefined,
|
||||||
|
targetMediaElementTextPos: number[] | undefined
|
||||||
|
) => {
|
||||||
const dispatcherObjects: YellowTextDispatcher = {
|
const dispatcherObjects: YellowTextDispatcher = {
|
||||||
moveUp: {
|
moveUp: {
|
||||||
action: animateYellowTextWithMove,
|
action: animateYellowTextWithMove,
|
||||||
|
@ -148,29 +193,53 @@ const YellowTextStateManager = (props: any) => {
|
||||||
action: animateYellowTextWithoutMove,
|
action: animateYellowTextWithoutMove,
|
||||||
value: [targetBlueOrbHudId, targetBlueOrbId],
|
value: [targetBlueOrbHudId, targetBlueOrbId],
|
||||||
},
|
},
|
||||||
|
setActivePlay: {
|
||||||
|
action: animateMediaYellowText,
|
||||||
|
value: [targetMediaElementText, targetMediaElementTextPos],
|
||||||
|
},
|
||||||
|
setActiveExit: {
|
||||||
|
action: animateMediaYellowText,
|
||||||
|
value: [targetMediaElementText, targetMediaElementTextPos],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return dispatcherObjects[event as keyof typeof dispatcherObjects];
|
return dispatcherObjects[event as keyof typeof dispatcherObjects];
|
||||||
},
|
},
|
||||||
[animateYellowTextWithMove, animateYellowTextWithoutMove]
|
[
|
||||||
|
animateYellowTextWithMove,
|
||||||
|
animateYellowTextWithoutMove,
|
||||||
|
animateMediaYellowText,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.eventState) {
|
if (props.eventState) {
|
||||||
const eventObject =
|
const eventObject: EventObject =
|
||||||
blue_orb_directions[
|
blue_orb_directions[
|
||||||
props.eventState as keyof typeof blue_orb_directions
|
props.eventState as keyof typeof blue_orb_directions
|
||||||
|
] ||
|
||||||
|
media_scene_directions[
|
||||||
|
props.eventState as keyof typeof media_scene_directions
|
||||||
];
|
];
|
||||||
|
|
||||||
if (eventObject) {
|
if (eventObject) {
|
||||||
const eventAction = eventObject.action;
|
const eventAction = eventObject.action;
|
||||||
|
|
||||||
|
// main scene specific
|
||||||
const targetBlueOrbId = eventObject.target_blue_orb_id;
|
const targetBlueOrbId = eventObject.target_blue_orb_id;
|
||||||
const targetBlueOrbHudId = eventObject.target_hud_id;
|
const targetBlueOrbHudId = eventObject.target_hud_id;
|
||||||
|
|
||||||
|
// media scene specific
|
||||||
|
const targetMediaElementText = eventObject.target_media_element_text;
|
||||||
|
const targetMediaElementTextPos =
|
||||||
|
eventObject.target_media_element_text_position;
|
||||||
|
|
||||||
const dispatchedObject = dispatchObject(
|
const dispatchedObject = dispatchObject(
|
||||||
eventAction,
|
eventAction,
|
||||||
targetBlueOrbHudId,
|
targetBlueOrbHudId,
|
||||||
targetBlueOrbId
|
targetBlueOrbId,
|
||||||
|
targetMediaElementText,
|
||||||
|
targetMediaElementTextPos
|
||||||
);
|
);
|
||||||
|
|
||||||
if (dispatchedObject) {
|
if (dispatchedObject) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import orangeFont from "../../static/sprite/orange_font_texture.png";
|
||||||
import yellowFont from "../../static/sprite/yellow_font_texture.png";
|
import yellowFont from "../../static/sprite/yellow_font_texture.png";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useLoader } from "react-three-fiber";
|
import { useLoader } from "react-three-fiber";
|
||||||
import orange_font_json from "../../resources/orange_font.json";
|
import orange_font_json from "../../resources/big_font.json";
|
||||||
import { a, useSpring } from "@react-spring/three";
|
import { a, useSpring } from "@react-spring/three";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { LetterProps } from "./TextRenderer";
|
import { LetterProps } from "./TextRenderer";
|
||||||
|
@ -48,7 +48,7 @@ const BigLetter = (props: BigLetterProps) => {
|
||||||
const lineYOffsets = {
|
const lineYOffsets = {
|
||||||
1: 0.884,
|
1: 0.884,
|
||||||
2: 0.765,
|
2: 0.765,
|
||||||
3: 0.65,
|
3: 0.648,
|
||||||
4: 0.47,
|
4: 0.47,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,14 +70,13 @@ const BigLetter = (props: BigLetterProps) => {
|
||||||
|
|
||||||
v =
|
v =
|
||||||
(v * letterData[3]) / 136 +
|
(v * letterData[3]) / 136 +
|
||||||
letterData[4] / 136 +
|
lineYOffsets[getLineNum(props.letter)] -
|
||||||
lineYOffsets[getLineNum(props.letter) as keyof typeof lineYOffsets] -
|
|
||||||
letterData[4] / 136;
|
letterData[4] / 136;
|
||||||
|
|
||||||
uvAttribute.setXY(i, u, v);
|
uvAttribute.setXY(i, u, v);
|
||||||
}
|
}
|
||||||
return geometry;
|
return geometry;
|
||||||
}, []);
|
}, [letterData, lineYOffsets, props.letter]);
|
||||||
|
|
||||||
const textRendererState = useSpring({
|
const textRendererState = useSpring({
|
||||||
letterOffsetXCoeff:
|
letterOffsetXCoeff:
|
||||||
|
@ -92,8 +91,8 @@ const BigLetter = (props: BigLetterProps) => {
|
||||||
return (
|
return (
|
||||||
<a.mesh
|
<a.mesh
|
||||||
position-x={textRendererState.letterOffsetXCoeff}
|
position-x={textRendererState.letterOffsetXCoeff}
|
||||||
position-y={props.letterIdx === 0 ? -0.03 : 0}
|
position-y={props.letterIdx === 0 ? -0.03 : 0 - letterData[4] / 12.5}
|
||||||
scale={props.letterIdx === 0 ? [1.7, 1, 1.7] : [1, 1, 1]}
|
scale={props.letterIdx === 0 ? [1.5, 1, 1.5] : [1, 1, 1]}
|
||||||
geometry={geom}
|
geometry={geom}
|
||||||
renderOrder={props.letterIdx === 0 ? 4 : 3}
|
renderOrder={props.letterIdx === 0 ? 4 : 3}
|
||||||
>
|
>
|
||||||
|
|
|
@ -60,7 +60,6 @@ const MediumLetter = (props: LetterProps) => {
|
||||||
|
|
||||||
v =
|
v =
|
||||||
(v * letterData[3]) / 136 +
|
(v * letterData[3]) / 136 +
|
||||||
letterData[4] / 136 +
|
|
||||||
lineYOffsets[getLineNum(props.letter)] -
|
lineYOffsets[getLineNum(props.letter)] -
|
||||||
letterData[4] / 136;
|
letterData[4] / 136;
|
||||||
|
|
||||||
|
@ -73,6 +72,7 @@ const MediumLetter = (props: LetterProps) => {
|
||||||
return (
|
return (
|
||||||
<a.mesh
|
<a.mesh
|
||||||
position-x={props.letterIdx * 1.6}
|
position-x={props.letterIdx * 1.6}
|
||||||
|
position-y={0 - letterData[4] / 12.5}
|
||||||
scale={[1.7, 1, 1.7]}
|
scale={[1.7, 1, 1.7]}
|
||||||
geometry={geom}
|
geometry={geom}
|
||||||
renderOrder={100}
|
renderOrder={100}
|
||||||
|
|
|
@ -44,7 +44,7 @@ const TextRenderer = () => {
|
||||||
// ==================================== GREEN TEXT ============================================
|
// ==================================== GREEN TEXT ============================================
|
||||||
|
|
||||||
const greenText = useTextRendererStore((state) => state.greenText);
|
const greenText = useTextRendererStore((state) => state.greenText);
|
||||||
const greenTextArr = useMemo(() => greenText.split(""), []);
|
const greenTextArr = useMemo(() => greenText.split(""), [greenText]);
|
||||||
const greenTextActive = useTextRendererStore(
|
const greenTextActive = useTextRendererStore(
|
||||||
(state) => state.greenTextActive
|
(state) => state.greenTextActive
|
||||||
);
|
);
|
||||||
|
|
|
@ -47,10 +47,10 @@
|
||||||
"f": [90, 34, 9, 14, 3],
|
"f": [90, 34, 9, 14, 3],
|
||||||
"g": [100, 34, 11, 17, 0],
|
"g": [100, 34, 11, 17, 0],
|
||||||
"h": [112, 34, 11, 14, 0],
|
"h": [112, 34, 11, 14, 0],
|
||||||
"i": [125, 34, 4, 14, 0],
|
"i": [123, 34, 7, 14, 0],
|
||||||
"j": [132, 34, 7, 17, 3],
|
"j": [132, 34, 7, 17, 3],
|
||||||
"k": [140, 34, 11, 14, 0],
|
"k": [140, 34, 11, 14, 0],
|
||||||
"l": [153, 34, 4, 14, 0],
|
"l": [152, 34, 7, 15, 0],
|
||||||
"m": [160, 34, 14, 14, 0],
|
"m": [160, 34, 14, 14, 0],
|
||||||
"n": [176, 34, 11, 14, 0],
|
"n": [176, 34, 11, 14, 0],
|
||||||
"o": [188, 34, 12, 14, 0],
|
"o": [188, 34, 12, 14, 0],
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
"v": [22, 58, 13, 14, 0],
|
"v": [22, 58, 13, 14, 0],
|
||||||
"w": [36, 58, 17, 14, 0],
|
"w": [36, 58, 17, 14, 0],
|
||||||
"x": [54, 58, 11, 14, 0],
|
"x": [54, 58, 11, 14, 0],
|
||||||
"y": [66, 58, 13, 17, 3],
|
"y": [65, 58, 13, 14, 3],
|
||||||
"z": [80, 58, 10, 14, 0],
|
"z": [80, 58, 10, 14, 0],
|
||||||
",": [92, 58, 5, 16, 2],
|
",": [92, 58, 5, 16, 2],
|
||||||
".": [100, 58, 5, 14, 0],
|
".": [100, 58, 5, 14, 0],
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"play_down": {
|
"play_down": {
|
||||||
"action": "setActiveMediaElement",
|
"action": "setActivePlay",
|
||||||
"target_media_element": "exit",
|
"target_media_element": "exit",
|
||||||
"target_media_element_text": "Exit",
|
"target_media_element_text": "Exit",
|
||||||
"target_media_element_text_position": [-3.5, -1.6, 0.6]
|
"target_media_element_text_position": [-0.8, -0.08, 0.6]
|
||||||
},
|
},
|
||||||
"exit_up": {
|
"exit_up": {
|
||||||
"action": "setActiveMediaElement",
|
"action": "setActiveExit",
|
||||||
"target_media_element": "play",
|
"target_media_element": "play",
|
||||||
"target_media_element_text": "Play",
|
"target_media_element_text": "Play",
|
||||||
"target_media_element_text_position": [-2.7, -0.9, 0.6]
|
"target_media_element_text_position": [-0.8, 0.05, 0.6]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -114,7 +114,7 @@ type TextRendererState = {
|
||||||
|
|
||||||
export const useTextRendererStore = create<TextRendererState>((set) => ({
|
export const useTextRendererStore = create<TextRendererState>((set) => ({
|
||||||
// yellow text
|
// yellow text
|
||||||
yellowText: "Tda028",
|
yellowText: "Play",
|
||||||
yellowTextPosY: 0.23,
|
yellowTextPosY: 0.23,
|
||||||
yellowTextPosX: -0.35,
|
yellowTextPosX: -0.35,
|
||||||
yellowTextOffsetXCoeff: 0,
|
yellowTextOffsetXCoeff: 0,
|
||||||
|
|
Loading…
Reference in a new issue