Skip to content

Commit

Permalink
Merge pull request #142 from musehq/dev
Browse files Browse the repository at this point in the history
v2.7.1
  • Loading branch information
alex-shortt authored Nov 28, 2022
2 parents 2e12ab7 + e4664ff commit 0d42532
Show file tree
Hide file tree
Showing 15 changed files with 663 additions and 12 deletions.
5 changes: 5 additions & 0 deletions examples/worlds/Workshop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Switch,
LostWorld,
Camera,
Dialogue,
} from "spacesvr";
import Title from "../ideas/Title";
import Link from "../ideas/Link";
Expand Down Expand Up @@ -62,6 +63,10 @@ export default function Workshop() {
</Link>
</group>
<group position-x={-6} position-z={-3}>
<Dialogue
position={[0, 1, 5]}
dialogue={[{ key: "init", text: "hello world" }]}
/>
<mesh position-y={0.5}>
<boxBufferGeometry args={[1, 1, 1]} />
<meshNormalMaterial />
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spacesvr",
"version": "2.7.0",
"version": "2.7.1",
"private": true,
"description": "A standardized reality for future of the 3D Web",
"keywords": [
Expand Down
1 change: 1 addition & 0 deletions src/ideas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./modifiers/Floating";
export * from "./modifiers/LookAtPlayer";
export * from "./modifiers/Spinning";
export * from "./modifiers/VisualEffect";
export * from "./ui/Dialogue";
export * from "./ui/TextInput";
export * from "./ui/Arrow";
export * from "./ui/Button";
Expand Down
8 changes: 3 additions & 5 deletions src/ideas/modifiers/Tool/modifiers/Draggable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ export default function Draggable(props: DraggableProps) {
const { set, distance, enabled, children } = props;

const toolbelt = useToolbelt();
const { viewport, size, gl } = useThree();
const { size, gl } = useThree();
const { device } = useEnvironment();
const { raycaster } = usePlayer();

const group = useRef<Group>(null);

const aspect = size.width / viewport.width;

const DOWN_SWIPE_DIST = size.height * 0.28;
const SIDE_SWIPE_DIST = size.width * 0.3;

Expand All @@ -46,8 +44,8 @@ export default function Draggable(props: DraggableProps) {

set({
pos: [
(delta.x / aspect) * distance * 0.7,
(-delta.y / aspect) * distance * (delta.y < 0 ? 0.15 : 0.5),
delta.x * 0.003 * distance * 0.7,
-delta.y * 0.003 * distance * (delta.y < 0 ? 0.15 : 0.5),
0,
],
});
Expand Down
3 changes: 2 additions & 1 deletion src/ideas/ui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ export function Button(props: ButtonProps) {
// keep dimensions up to date
useLayoutEffect(() => {
textRef.current.addEventListener("synccomplete", () => {
const info = textRef.current.textRenderInfo;
const info = textRef.current?.textRenderInfo;
if (!info) return;
const w = info.blockBounds[2] - info.blockBounds[0];
const h = info.blockBounds[3] - info.blockBounds[1];
setDims([w, h]);
Expand Down
67 changes: 67 additions & 0 deletions src/ideas/ui/Dialogue/ideas/Bubbles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useLimitedFrame } from "../../../../logic/limiter";
import {
Group,
InstancedMesh,
MathUtils,
Object3D,
SphereBufferGeometry,
Vector3,
} from "three";
import { useIdeaMaterial } from "../logic/ideaMat";
import { GroupProps, useThree } from "@react-three/fiber";

type BubblesProps = {
numStops: number;
enabled: boolean;
offset: GroupProps["position"];
};

export default function Bubbles(props: BubblesProps) {
const { numStops, enabled, offset } = props;

const group = useRef<Group>(null);
const mesh = useRef<InstancedMesh>(null);

const clock = useThree((st) => st.clock);

const [pos] = useState(new Vector3());
const [obj] = useState(new Object3D());
const startTime = useRef(0);
useEffect(() => {
startTime.current = clock.elapsedTime;
}, [enabled]);
useLimitedFrame(40, ({ clock }) => {
if (!mesh.current || !group.current) return;

group.current.updateMatrix();
group.current.matrix.decompose(pos, obj.quaternion, obj.scale);
for (let i = 0; i < numStops; i++) {
const perc = i / (numStops - 1);
obj.position.set(perc * pos.x, perc * pos.y, perc * pos.z);
const sc = 0.01 + perc * 0.05;
const delay = 60 / 1000;
const time = 400 / 1000;
const delta = clock.elapsedTime - startTime.current;
const iter = enabled ? i : numStops - i - 1;
const x = MathUtils.clamp((delta - iter * delay) / time, 0, 1);
let val = (Math.cos(Math.PI * x) + 1) / 2;
if (enabled) val = 1 - val;
obj.scale.setScalar(sc * 0.2 * val);
obj.updateMatrix();
mesh.current.setMatrixAt(i, obj.matrix);
}

mesh.current.instanceMatrix.needsUpdate = true;
});

const geo = useMemo(() => new SphereBufferGeometry(4, 32, 16), []);
const mat = useIdeaMaterial(undefined, 4);

return (
<>
<group position={offset} ref={group} />
<instancedMesh args={[geo, mat, numStops]} ref={mesh} />
</>
);
}
159 changes: 159 additions & 0 deletions src/ideas/ui/Dialogue/ideas/VisualDecisions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Decision } from "../logic/types";
import { useEffect, useMemo, useState } from "react";
// @ts-ignore
import { Text as TextImpl } from "troika-three-text";
import { Triplet } from "@react-three/cannon";
import { Idea } from "../../../../logic/basis";
import { Button } from "../../../../ideas/ui/Button";
import { FacePlayer } from "../../../../ideas/modifiers/FacePlayer";

type DecisionProps = {
decisions: Decision[];
width: number;
setCurKey: (key: string) => void;
};

export default function VisualDecisions(props: DecisionProps) {
const { decisions, setCurKey, width } = props;

const FONT_FILE =
"https://d27rt3a60hh1lx.cloudfront.net/fonts/Quicksand_Bold.otf";
const FONT_SIZE = 0.05;
const OUTLINE_WIDTH = FONT_SIZE * 0.1;
const PADDING_Y = 0.065;
const SPACING_Y = 0.015;
const SPACING_X = 0.08;
const PADDING_X = 0.025;

const [dimMap] = useState(() => new Map<string, { w: number; h: number }>());
const [ready, setReady] = useState(false);

// for every new set of values, generate a new text object and store width
// keep ready state in sync with whether all values have been measured
useEffect(() => {
if (decisions.every((d) => dimMap.has(d.name))) return;
setReady(false);
for (const decision of decisions) {
if (dimMap.has(decision.name)) continue;
setReady(false);
const t = new TextImpl();
t.text = decision.name;
t.font = FONT_FILE;
t.fontSize = FONT_SIZE;
t.maxWidth = width;
t.outlineWidth = OUTLINE_WIDTH;
t.sync(() => {
const { blockBounds } = t.textRenderInfo;
const w = blockBounds[2] - blockBounds[0];
const h = blockBounds[3] - blockBounds[1];
dimMap.set(decision.name, { w, h });
if (decisions.every((d) => dimMap.has(d.name))) setReady(true);
});
}
}, [decisions, dimMap, FONT_SIZE, FONT_FILE, width, OUTLINE_WIDTH]);

const objValues = useMemo(() => {
type Line = { y: number; decisions: Decision[] };
const lines: Line[] = [];
let thisLineWidth = 0;
let thisLineIndex = 0;
let y = -FONT_SIZE;
let lastHei = 0;

// calculate lines and y positions
for (const decision of decisions) {
const wid =
(dimMap.get(decision.name)?.w || 0) + PADDING_X * 2 + SPACING_X;
const hei = dimMap.get(decision.name)?.h || 0;

if (thisLineWidth + wid <= width) {
if (!lines[thisLineIndex]) lines.push({ y, decisions: [] });
lines[thisLineIndex].decisions.push(decision);
lastHei = hei;
thisLineWidth += wid;
} else {
// by default, overflow means new line
thisLineIndex++;
const hei = dimMap.get(decision.name)?.h || 0;
y -= lastHei / 2 + SPACING_Y + PADDING_Y + hei / 2;

if (hei > FONT_SIZE + OUTLINE_WIDTH * 2) {
// if it's taller than one line, force it to be on its own line
lines.push({ y, decisions: [decision] });
y -= hei / 2 + PADDING_Y + SPACING_Y;
thisLineIndex++;
thisLineWidth = 0;
lastHei = hei;
} else {
// add to this new line
lines.push({ y, decisions: [decision] });
thisLineWidth += wid;
lastHei = hei;
}
}
}

// from lines, calculate x positions by centering each decision within its line
type ObjEntry = { decision: Decision; position: Triplet };
const objMap: ObjEntry[] = [];
for (const line of lines) {
const lineObjMap: ObjEntry[] = [];
// place each decision in the center then shift left
let x = 0;
for (const decision of line.decisions) {
const wid = dimMap.get(decision.name)?.w || 0;
x -= wid / 2;
lineObjMap.push({ decision, position: [x, line.y, 0] });
x -= wid / 2 + PADDING_X * 2 + SPACING_X;
}
// shift all decisions in the line to the right
const lineWid = -x - PADDING_X * 2 - SPACING_X;
const shift = lineWid / 2;
for (const obj of lineObjMap) {
obj.position[0] += shift;
}
objMap.push(...lineObjMap);
}

return objMap;
}, [decisions, dimMap, width, ready]);

const [offset] = useState(Math.random());
const ideaMap = useMemo(() => {
const map = new Map<string, Idea>();
for (const decision of decisions) {
const m = (offset + decisions.indexOf(decision) / decisions.length) % 1;
const s = 0.7;
map.set(decision.name, new Idea(m, s, decision.utility || 0.8));
}
return map;
}, [decisions, offset]);

if (!ready) return null;

return (
<>
{objValues.map(({ decision, position }, i) => (
<group
key={decision.name + i + position.toString()}
position={position}
>
<FacePlayer>
<Button
font={FONT_FILE}
fontSize={FONT_SIZE}
maxWidth={width}
idea={ideaMap.get(decision.name)}
onClick={() => {
if (decision.onClick) decision.onClick();
if (decision.nextKey) setCurKey(decision.nextKey || "");
}}
>
{decision.name}
</Button>
</FacePlayer>
</group>
))}
</>
);
}
Loading

1 comment on commit 0d42532

@vercel
Copy link

@vercel vercel bot commented on 0d42532 Nov 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.