primocode 10.5.0 → 10.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/primocode.js +117 -24
- package/lib/act.js +141 -9
- package/lib/agents-md.js +140 -0
- package/lib/auditoria.js +122 -0
- package/lib/browser.js +48 -1
- package/lib/catalogo.js +24 -4
- package/lib/comandos-extra.js +2 -0
- package/lib/config.js +32 -4
- package/lib/entrada.js +33 -11
- package/lib/estudio/colecao.js +34 -1
- package/lib/estudio/diretor.js +1 -0
- package/lib/estudio/falantes.js +30 -6
- package/lib/estudio/hibrido.js +144 -8
- package/lib/estudio/index.js +151 -59
- package/lib/estudio/kit/mundo.jsx +1060 -0
- package/lib/estudio/kit/voxel.jsx +315 -70
- package/lib/estudio/kit.js +1 -1
- package/lib/estudio/kokoro.js +18 -4
- package/lib/estudio/margem.js +1 -1
- package/lib/estudio/mundo.js +305 -0
- package/lib/estudio/personagem.js +164 -11
- package/lib/estudio/producao.js +107 -17
- package/lib/estudio/projeto.js +77 -22
- package/lib/leitura.js +176 -0
- package/lib/local/cerebro.json +180 -3
- package/lib/local/entrada.js +61 -4
- package/lib/mcp.js +296 -12
- package/lib/memoria-conversa.js +32 -3
- package/lib/mencoes.js +5 -1
- package/lib/nome-curto.js +126 -0
- package/lib/nome-projeto.js +5 -2
- package/lib/outra-pasta.js +161 -0
- package/lib/perigo.js +34 -2
- package/lib/referencia.js +40 -11
- package/lib/repeticao.js +20 -1
- package/lib/tela.js +1 -1
- package/lib/tools.js +76 -6
- package/lib/travado.js +141 -0
- package/lib/ui.js +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1060 @@
|
|
|
1
|
+
/* kit/mundo.jsx — o MUNDO do vídeo 3D em blocos: o lugar, as peças, os bichos e as coisas.
|
|
2
|
+
*
|
|
3
|
+
* O voxel.jsx desenha a pessoa e o mascote e decide a câmera; aqui mora tudo
|
|
4
|
+
* o que muda de um vídeo para o outro (mundo.js decide, isto só desenha):
|
|
5
|
+
* - Ceu, Chao, Mar e a luz de cada lugar (praia, cidade, espaço, cozinha…);
|
|
6
|
+
* - Peca: as peças de blocos do catálogo (palmeira, prédio, carro, foguete…);
|
|
7
|
+
* - IconeEmBlocos: QUALQUER coisa da biblioteca de ícones 3D, transformada em
|
|
8
|
+
* blocos na hora (o PNG vira uma grade de cubos com volume) — é o que deixa
|
|
9
|
+
* a cena montar o que não está no catálogo;
|
|
10
|
+
* - Animal: as espécies montadas de blocos, com boca que segue a voz.
|
|
11
|
+
* Tudo é geometria do three.js; o único arquivo de fora é o PNG do ícone. */
|
|
12
|
+
import React, { useMemo, useLayoutEffect, useRef, useState } from 'react';
|
|
13
|
+
import * as THREE from 'three';
|
|
14
|
+
import { delayRender, continueRender, staticFile } from 'remotion';
|
|
15
|
+
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js';
|
|
16
|
+
|
|
17
|
+
const clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x));
|
|
18
|
+
const hash = (n) => { const s = Math.sin(n * 127.1 + 311.7) * 43758.5453; return s - Math.floor(s); };
|
|
19
|
+
const escurecer = (c, k) => new THREE.Color(c).multiplyScalar(k).getStyle();
|
|
20
|
+
const misturar = (a, b, k) => new THREE.Color(a).lerp(new THREE.Color(b), k).getStyle();
|
|
21
|
+
|
|
22
|
+
const GEOS = new Map();
|
|
23
|
+
function geo(w, h, d, r) {
|
|
24
|
+
const raio = Math.min(r, w / 2 - 0.001, h / 2 - 0.001, d / 2 - 0.001);
|
|
25
|
+
const k = `${w}|${h}|${d}|${raio}`;
|
|
26
|
+
if (!GEOS.has(k)) GEOS.set(k, new RoundedBoxGeometry(w, h, d, 2, Math.max(0.001, raio)));
|
|
27
|
+
return GEOS.get(k);
|
|
28
|
+
}
|
|
29
|
+
export const B = ({ s, p = [0, 0, 0], rot, c, r = 0.03, rough = 0.7, metal = 0, e, ei = 1, sombra = true, op }) => (
|
|
30
|
+
<mesh position={p} rotation={rot} geometry={geo(s[0], s[1], s[2], r)} castShadow={sombra} receiveShadow>
|
|
31
|
+
<meshStandardMaterial color={c} roughness={rough} metalness={metal} {...(e ? { emissive: e, emissiveIntensity: ei } : {})} {...(op != null ? { transparent: true, opacity: op } : {})} />
|
|
32
|
+
</mesh>
|
|
33
|
+
);
|
|
34
|
+
const Olho = ({ p, s = 0.05, pisca = 0 }) => (
|
|
35
|
+
<group position={p}>
|
|
36
|
+
<mesh><boxGeometry args={[s, s * (1 - pisca * 0.85), 0.012]} /><meshStandardMaterial color="#141016" roughness={0.6} /></mesh>
|
|
37
|
+
<mesh position={[s * 0.22, s * 0.22, 0.004]}><boxGeometry args={[s * 0.3, s * 0.3 * (1 - pisca), 0.01]} /><meshStandardMaterial color="#ffffff" roughness={0.5} /></mesh>
|
|
38
|
+
</group>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
/* Muitas caixas iguais numa chamada só (o chão, as estrelas, o ícone em blocos). */
|
|
42
|
+
function Instancias({ itens, w = 1, h = 1, d = 1, r = 0, rough = 0.8, sombra = false, emissivo = 0 }) {
|
|
43
|
+
const ref = useRef();
|
|
44
|
+
const g = useMemo(() => (r > 0 ? new RoundedBoxGeometry(w, h, d, 2, r) : new THREE.BoxGeometry(w, h, d)), [w, h, d, r]);
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
const m = ref.current;
|
|
47
|
+
if (!m) return;
|
|
48
|
+
const o = new THREE.Object3D(), cor = new THREE.Color();
|
|
49
|
+
itens.forEach((it, i) => {
|
|
50
|
+
o.position.set(it.p[0], it.p[1], it.p[2]);
|
|
51
|
+
o.scale.set(...(it.s || [1, 1, 1]));
|
|
52
|
+
o.rotation.set(0, it.giro || 0, 0);
|
|
53
|
+
o.updateMatrix();
|
|
54
|
+
m.setMatrixAt(i, o.matrix);
|
|
55
|
+
m.setColorAt(i, cor.set(it.c));
|
|
56
|
+
});
|
|
57
|
+
m.instanceMatrix.needsUpdate = true;
|
|
58
|
+
if (m.instanceColor) m.instanceColor.needsUpdate = true;
|
|
59
|
+
}, [itens]);
|
|
60
|
+
if (!itens.length) return null;
|
|
61
|
+
return (
|
|
62
|
+
<instancedMesh ref={ref} args={[g, undefined, itens.length]} castShadow={sombra} receiveShadow>
|
|
63
|
+
<meshStandardMaterial roughness={rough} {...(emissivo ? { emissive: '#ffffff', emissiveIntensity: emissivo, toneMapped: false } : {})} />
|
|
64
|
+
</instancedMesh>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ── O CÉU: uma cúpula com o degradê do lugar, e sol, lua e estrelas ───── */
|
|
69
|
+
function texturaDegrade(alto, horizonte) {
|
|
70
|
+
const cv = document.createElement('canvas');
|
|
71
|
+
cv.width = 4; cv.height = 256;
|
|
72
|
+
const g = cv.getContext('2d');
|
|
73
|
+
const gr = g.createLinearGradient(0, 0, 0, 256);
|
|
74
|
+
gr.addColorStop(0, alto); gr.addColorStop(0.42, misturar(alto, horizonte, 0.55)); gr.addColorStop(0.5, horizonte); gr.addColorStop(1, horizonte);
|
|
75
|
+
g.fillStyle = gr; g.fillRect(0, 0, 4, 256);
|
|
76
|
+
const tx = new THREE.CanvasTexture(cv);
|
|
77
|
+
tx.colorSpace = THREE.SRGBColorSpace;
|
|
78
|
+
return tx;
|
|
79
|
+
}
|
|
80
|
+
function Ceu({ m, t }) {
|
|
81
|
+
const tx = useMemo(() => texturaDegrade(m.ceu[0], m.ceu[1]), [m.ceu[0], m.ceu[1]]);
|
|
82
|
+
const estrelas = useMemo(() => {
|
|
83
|
+
if (!m.estrelas) return [];
|
|
84
|
+
return Array.from({ length: 260 }, (_, i) => {
|
|
85
|
+
const az = hash(i * 3.1) * Math.PI * 2, el = 0.08 + hash(i * 7.7) * 1.2, R = 42;
|
|
86
|
+
const s = 0.06 + hash(i * 1.3) * 0.12;
|
|
87
|
+
return { p: [Math.cos(az) * Math.cos(el) * R, Math.sin(el) * R, Math.sin(az) * Math.cos(el) * R], s: [s, s, s], c: hash(i) > 0.8 ? '#ffd9a8' : '#ffffff' };
|
|
88
|
+
});
|
|
89
|
+
}, [m.estrelas]);
|
|
90
|
+
const solY = m.solBaixo ? 3.2 : 14, solCor = m.solBaixo ? '#ffb070' : '#fff6d8';
|
|
91
|
+
return (
|
|
92
|
+
<group>
|
|
93
|
+
<mesh scale={[-1, 1, 1]}>
|
|
94
|
+
<sphereGeometry args={[46, 32, 24]} />
|
|
95
|
+
<meshBasicMaterial map={tx} side={THREE.BackSide} fog={false} depthWrite={false} toneMapped={false} />
|
|
96
|
+
</mesh>
|
|
97
|
+
{estrelas.length > 0 && <Instancias itens={estrelas} w={1} h={1} d={1} emissivo={0.9} />}
|
|
98
|
+
{/* o sol e a lua são blocos que brilham: o bloom faz o halo */}
|
|
99
|
+
{m.sol && <B s={[2.6, 2.6, 0.4]} p={[-9, solY, -40]} c={solCor} e={solCor} ei={m.solBaixo ? 2.2 : 1.6} r={0.3} sombra={false} />}
|
|
100
|
+
{m.lua && <B s={[2, 2, 0.3]} p={[8, 12, -40]} c="#e9ecf5" e="#dfe6ff" ei={1.1} r={0.25} sombra={false} />}
|
|
101
|
+
{!m.subaquatico && !m.estrelas && [0, 1, 2, 3].map((i) => (
|
|
102
|
+
<group key={i} position={[(-14 + i * 9 + ((t * 0.25 + i * 3) % 6)) , 9 + hash(i) * 4, -30 - hash(i + 2) * 6]}>
|
|
103
|
+
<B s={[3.4, 0.8, 1.2]} c="#ffffff" r={0.3} rough={1} sombra={false} op={m.hora === 'noite' ? 0.25 : 0.92} />
|
|
104
|
+
<B s={[2, 0.9, 1.1]} p={[0.5, 0.5, 0]} c="#ffffff" r={0.3} rough={1} sombra={false} op={m.hora === 'noite' ? 0.25 : 0.92} />
|
|
105
|
+
</group>
|
|
106
|
+
))}
|
|
107
|
+
</group>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ── O CHÃO: ladrilhos de bloco, com a cor e o relevo do lugar ─────────── */
|
|
112
|
+
function Chao({ m }) {
|
|
113
|
+
const { tipo, cor, cor2 } = m.chao;
|
|
114
|
+
const itens = useMemo(() => {
|
|
115
|
+
const out = [];
|
|
116
|
+
const passo = tipo === 'ladrilho' ? 0.92 : tipo === 'madeira' ? 0.5 : 1;
|
|
117
|
+
const nx = m.dentro ? 12 : 26, nz = m.dentro ? 8 : 26;
|
|
118
|
+
for (let i = -nx; i <= nx; i++) {
|
|
119
|
+
for (let j = -nz; j <= 6; j++) {
|
|
120
|
+
const x = i * passo + (tipo === 'ladrilho' && j % 2 ? passo / 2 : 0), z = j * (tipo === 'madeira' ? 1.6 : passo);
|
|
121
|
+
if (m.agua && z < -4.6) continue;
|
|
122
|
+
if (m.dentro && z < -3.6) continue;
|
|
123
|
+
const k = hash(i * 13.1 + j * 7.3);
|
|
124
|
+
const natural = tipo === 'grama' || tipo === 'areia' || tipo === 'neve' || tipo === 'lua';
|
|
125
|
+
const alto = natural ? 0.12 + k * 0.08 : 0.1;
|
|
126
|
+
// O longe vai assentando: o relevo some no horizonte e não pisca.
|
|
127
|
+
let c = k > 0.5 ? cor : cor2;
|
|
128
|
+
if (tipo === 'grama' && k > 0.93) c = misturar(cor, '#e8e070', 0.35);
|
|
129
|
+
if (tipo === 'asfalto' && Math.abs(x) < 0.12 && j % 2 === 0 && z < -1) c = '#e8e4d8';
|
|
130
|
+
if (tipo === 'lua' && k > 0.9) c = escurecer(cor, 0.78);
|
|
131
|
+
out.push({ p: [x, -alto / 2, z], s: [tipo === 'madeira' ? 0.48 / passo : 0.98, alto, tipo === 'madeira' ? 1.56 / passo : 0.98], c });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}, [tipo, cor, cor2, m.agua, m.dentro]);
|
|
136
|
+
const passo = tipo === 'ladrilho' ? 0.92 : tipo === 'madeira' ? 0.5 : 1;
|
|
137
|
+
return (
|
|
138
|
+
<group>
|
|
139
|
+
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.2, 0]} receiveShadow>
|
|
140
|
+
<planeGeometry args={[120, 120]} /><meshStandardMaterial color={escurecer(cor2, 0.8)} roughness={0.95} />
|
|
141
|
+
</mesh>
|
|
142
|
+
<Instancias itens={itens} w={passo} h={1} d={passo} rough={tipo === 'ladrilho' ? 0.45 : tipo === 'neve' ? 0.9 : 0.85} />
|
|
143
|
+
</group>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* ── O MAR (praia): a água e as ondas de bloco que vão e voltam ─────────── */
|
|
148
|
+
function Mar({ m, t }) {
|
|
149
|
+
const ondas = useMemo(() => Array.from({ length: 60 }, (_, i) => ({ x: (i % 20 - 10) * 2.2 + hash(i) * 1.2, z: -5.6 - Math.floor(i / 20) * 2.6 - hash(i + 5), k: hash(i * 3) })), []);
|
|
150
|
+
return (
|
|
151
|
+
<group>
|
|
152
|
+
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.12, -26]}>
|
|
153
|
+
<planeGeometry args={[140, 44]} /><meshStandardMaterial color={m.agua} roughness={0.15} metalness={0.2} />
|
|
154
|
+
</mesh>
|
|
155
|
+
{/* a espuma da beira, que avança e recua */}
|
|
156
|
+
<B s={[60, 0.06, 0.5]} p={[0, -0.06, -4.75 + Math.sin(t * 0.9) * 0.18]} c="#f4fbff" r={0.02} rough={0.9} sombra={false} />
|
|
157
|
+
{ondas.map((o, i) => (
|
|
158
|
+
<B key={i} s={[1.4, 0.08, 0.3]} p={[o.x + Math.sin(t * 0.6 + o.k * 6) * 0.3, -0.05 + Math.sin(t * 1.6 + o.k * 9) * 0.04, o.z]} c={misturar(m.agua, '#ffffff', 0.45)} r={0.02} rough={0.5} sombra={false} />
|
|
159
|
+
))}
|
|
160
|
+
</group>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* ── O SET FECHADO (escritório, sala, cozinha, escola, palco): a parede ─── */
|
|
165
|
+
function Paredes({ m }) {
|
|
166
|
+
return (
|
|
167
|
+
<group>
|
|
168
|
+
<B s={[30, 6, 0.3]} p={[0, 2.9, -3.75]} c={m.parede} r={0.02} rough={0.95} sombra={false} />
|
|
169
|
+
<B s={[30, 0.18, 0.06]} p={[0, 0.09, -3.58]} c={escurecer(m.parede, 0.7)} r={0.01} sombra={false} />
|
|
170
|
+
{/* a janela de luz, que dá profundidade ao set fechado */}
|
|
171
|
+
{!m.holofotes && <group position={[3.4, 2.3, -3.58]}>
|
|
172
|
+
<B s={[1.9, 1.5, 0.06]} c="#cfe6ff" e="#cfe6ff" ei={0.6} r={0.02} sombra={false} />
|
|
173
|
+
<B s={[0.08, 1.5, 0.08]} c={escurecer(m.parede, 0.6)} r={0.01} sombra={false} />
|
|
174
|
+
<B s={[1.9, 0.08, 0.08]} c={escurecer(m.parede, 0.6)} r={0.01} sombra={false} />
|
|
175
|
+
</group>}
|
|
176
|
+
</group>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ── AS PEÇAS DO CATÁLOGO ───────────────────────────────────────────────
|
|
181
|
+
Cada uma é um punhado de blocos com a proporção certa. A base fica no
|
|
182
|
+
chão (y = 0); a escala e o giro vêm do mundo.js. */
|
|
183
|
+
const VERDE = '#3f8f3a', VERDE2 = '#2f7a2e', TRONCO = '#7a5232', CINZA = '#8b8f98';
|
|
184
|
+
function Arvore({ k = 0, copa = VERDE }) {
|
|
185
|
+
return (
|
|
186
|
+
<group>
|
|
187
|
+
<B s={[0.32, 1.4, 0.32]} p={[0, 0.7, 0]} c={TRONCO} r={0.04} />
|
|
188
|
+
<B s={[1.5, 1.1, 1.5]} p={[0, 1.85, 0]} c={copa} r={0.12} rough={0.9} />
|
|
189
|
+
<B s={[1.05, 0.8, 1.05]} p={[0.1, 2.6, -0.05]} c={misturar(copa, '#9fd86a', 0.2 + k * 0.2)} r={0.1} rough={0.9} />
|
|
190
|
+
</group>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function Pinheiro({ neve }) {
|
|
194
|
+
return (
|
|
195
|
+
<group>
|
|
196
|
+
<B s={[0.26, 0.7, 0.26]} p={[0, 0.35, 0]} c={TRONCO} r={0.04} />
|
|
197
|
+
{[0, 1, 2, 3].map((i) => (
|
|
198
|
+
<group key={i}>
|
|
199
|
+
<B s={[1.6 - i * 0.36, 0.55, 1.6 - i * 0.36]} p={[0, 0.9 + i * 0.55, 0]} c={i % 2 ? VERDE2 : '#2b6a32'} r={0.05} rough={0.9} />
|
|
200
|
+
{neve && <B s={[1.5 - i * 0.36, 0.12, 1.5 - i * 0.36]} p={[0, 1.2 + i * 0.55, 0]} c="#f6faff" r={0.04} rough={0.9} />}
|
|
201
|
+
</group>
|
|
202
|
+
))}
|
|
203
|
+
</group>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
function Palmeira({ t, k = 0 }) {
|
|
207
|
+
const balanco = Math.sin(t * 0.8 + k * 5) * 0.05;
|
|
208
|
+
return (
|
|
209
|
+
<group>
|
|
210
|
+
{[0, 1, 2, 3, 4, 5].map((i) => <B key={i} s={[0.3 - i * 0.015, 0.5, 0.3 - i * 0.015]} p={[i * i * 0.012, 0.25 + i * 0.5, 0]} c={i % 2 ? '#8a6238' : '#9b7044'} r={0.04} />)}
|
|
211
|
+
<group position={[0.36, 3.05, 0]} rotation={[0, 0, balanco]}>
|
|
212
|
+
{[0, 1, 2, 3, 4, 5].map((i) => {
|
|
213
|
+
const a = (i / 6) * Math.PI * 2;
|
|
214
|
+
return <B key={i} s={[1.6, 0.1, 0.42]} p={[Math.cos(a) * 0.75, -0.18, Math.sin(a) * 0.75]} rot={[0, -a, -0.35]} c={i % 2 ? '#3f9a3a' : '#2f8a34'} r={0.04} rough={0.85} />;
|
|
215
|
+
})}
|
|
216
|
+
<B s={[0.24, 0.24, 0.24]} p={[0.05, -0.2, 0.1]} c="#5b3b1c" r={0.06} />
|
|
217
|
+
<B s={[0.24, 0.24, 0.24]} p={[-0.1, -0.22, -0.08]} c="#5b3b1c" r={0.06} />
|
|
218
|
+
</group>
|
|
219
|
+
</group>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
function Predio({ k = 0, noite }) {
|
|
223
|
+
const andares = 4 + Math.floor(k * 6), cor = ['#c9ced8', '#9aa6b8', '#d8c8b0', '#7f8da3', '#b9a99a'][Math.floor(k * 5)];
|
|
224
|
+
const janelas = [];
|
|
225
|
+
for (let a = 0; a < andares; a++) for (let j = 0; j < 3; j++) {
|
|
226
|
+
const acesa = hash(a * 7 + j * 3 + k * 50) > (noite ? 0.45 : 1.1);
|
|
227
|
+
janelas.push(<B key={a + '-' + j} s={[0.36, 0.42, 0.04]} p={[(j - 1) * 0.55, 0.65 + a * 0.8, 0.93]} c={acesa ? '#ffd98a' : '#3b4a63'} e={acesa ? '#ffcc70' : undefined} ei={1.2} r={0.01} sombra={false} />);
|
|
228
|
+
}
|
|
229
|
+
return (
|
|
230
|
+
<group>
|
|
231
|
+
<B s={[1.9, andares * 0.8 + 0.4, 1.8]} p={[0, (andares * 0.8 + 0.4) / 2, 0]} c={cor} r={0.04} rough={0.85} />
|
|
232
|
+
{janelas}
|
|
233
|
+
</group>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
function Casa({ cor = '#e8d6b8', telhado = '#b5452f' }) {
|
|
237
|
+
return (
|
|
238
|
+
<group>
|
|
239
|
+
<B s={[2.2, 1.6, 1.8]} p={[0, 0.8, 0]} c={cor} r={0.04} />
|
|
240
|
+
<B s={[2.5, 0.35, 2.1]} p={[0, 1.78, 0]} c={telhado} r={0.04} />
|
|
241
|
+
<B s={[1.7, 0.35, 1.7]} p={[0, 2.12, 0]} c={telhado} r={0.04} />
|
|
242
|
+
<B s={[0.5, 0.9, 0.06]} p={[0, 0.45, 0.91]} c="#6b4423" r={0.02} />
|
|
243
|
+
<B s={[0.45, 0.4, 0.05]} p={[0.65, 1, 0.91]} c="#cfe6ff" r={0.02} />
|
|
244
|
+
</group>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
function Carro({ k = 0 }) {
|
|
248
|
+
const cor = ['#d0452f', '#2f4cdd', '#f1b434', '#1f8a5b', '#e8e2d6'][Math.floor(k * 5)];
|
|
249
|
+
return (
|
|
250
|
+
<group rotation={[0, Math.PI / 2, 0]}>
|
|
251
|
+
<B s={[1.9, 0.5, 0.95]} p={[0, 0.45, 0]} c={cor} r={0.1} rough={0.35} metal={0.2} />
|
|
252
|
+
<B s={[1.05, 0.42, 0.85]} p={[-0.1, 0.9, 0]} c={cor} r={0.1} rough={0.35} metal={0.2} />
|
|
253
|
+
<B s={[0.95, 0.32, 0.87]} p={[-0.1, 0.92, 0]} c="#2b3a4f" r={0.06} rough={0.2} />
|
|
254
|
+
{[[-0.6, 0.47], [0.6, 0.47], [-0.6, -0.47], [0.6, -0.47]].map(([x, z], i) => <B key={i} s={[0.36, 0.36, 0.14]} p={[x, 0.2, z]} c="#16161a" r={0.06} />)}
|
|
255
|
+
<B s={[0.06, 0.12, 0.2]} p={[0.96, 0.5, 0.3]} c="#fff6d0" e="#fff1c0" ei={0.8} r={0.02} sombra={false} />
|
|
256
|
+
<B s={[0.06, 0.12, 0.2]} p={[0.96, 0.5, -0.3]} c="#fff6d0" e="#fff1c0" ei={0.8} r={0.02} sombra={false} />
|
|
257
|
+
</group>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
function Poste({ noite }) {
|
|
261
|
+
return (
|
|
262
|
+
<group>
|
|
263
|
+
<B s={[0.14, 3, 0.14]} p={[0, 1.5, 0]} c="#2b2d33" r={0.03} />
|
|
264
|
+
<B s={[0.6, 0.1, 0.14]} p={[0.25, 3, 0]} c="#2b2d33" r={0.03} />
|
|
265
|
+
<B s={[0.3, 0.14, 0.24]} p={[0.48, 2.92, 0]} c="#fff1c0" e="#ffd98a" ei={noite ? 3 : 0.4} r={0.03} sombra={false} />
|
|
266
|
+
{noite && <pointLight position={[0.48, 2.6, 0]} intensity={6} distance={6} color="#ffd98a" />}
|
|
267
|
+
</group>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
function GuardaSol({ k = 0 }) {
|
|
271
|
+
const c1 = ['#e5484d', '#2f80ed', '#f1b434'][Math.floor(k * 3)];
|
|
272
|
+
return (
|
|
273
|
+
<group>
|
|
274
|
+
<B s={[0.08, 2, 0.08]} p={[0, 1, 0]} c="#e8e2d6" r={0.02} />
|
|
275
|
+
{[0, 1, 2, 3, 4, 5, 6, 7].map((i) => {
|
|
276
|
+
const a = (i / 8) * Math.PI * 2;
|
|
277
|
+
return <B key={i} s={[1.1, 0.08, 0.5]} p={[Math.cos(a) * 0.5, 1.92, Math.sin(a) * 0.5]} rot={[0, -a, -0.22]} c={i % 2 ? c1 : '#f6f2ea'} r={0.02} />;
|
|
278
|
+
})}
|
|
279
|
+
</group>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
function CadeiraDePraia() {
|
|
283
|
+
return (
|
|
284
|
+
<group>
|
|
285
|
+
<B s={[0.6, 0.06, 0.9]} p={[0, 0.3, 0]} c="#2f80ed" r={0.02} />
|
|
286
|
+
<B s={[0.6, 0.06, 0.7]} p={[0, 0.55, -0.55]} rot={[0.9, 0, 0]} c="#f6f2ea" r={0.02} />
|
|
287
|
+
{[-0.27, 0.27].map((x, i) => <B key={i} s={[0.05, 0.3, 0.05]} p={[x, 0.15, 0.3]} c="#c9c2b4" r={0.01} />)}
|
|
288
|
+
</group>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
function CasteloDeAreia() {
|
|
292
|
+
const a = '#d9be86';
|
|
293
|
+
return (
|
|
294
|
+
<group>
|
|
295
|
+
<B s={[0.9, 0.35, 0.9]} p={[0, 0.17, 0]} c={a} r={0.04} rough={1} />
|
|
296
|
+
{[[-0.35, -0.35], [0.35, -0.35], [-0.35, 0.35], [0.35, 0.35]].map(([x, z], i) => <B key={i} s={[0.26, 0.6, 0.26]} p={[x, 0.3, z]} c={a} r={0.04} rough={1} />)}
|
|
297
|
+
<B s={[0.36, 0.45, 0.36]} p={[0, 0.55, 0]} c={a} r={0.04} rough={1} />
|
|
298
|
+
<B s={[0.03, 0.3, 0.03]} p={[0, 0.9, 0]} c="#6b4423" r={0.01} />
|
|
299
|
+
<B s={[0.2, 0.12, 0.02]} p={[0.1, 1, 0]} c="#e5484d" r={0.01} />
|
|
300
|
+
</group>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function Pedra({ k = 0, cor = CINZA }) {
|
|
304
|
+
return (
|
|
305
|
+
<group>
|
|
306
|
+
<B s={[0.9 + k * 0.4, 0.55, 0.75]} p={[0, 0.27, 0]} c={cor} r={0.12} rough={1} />
|
|
307
|
+
<B s={[0.5, 0.35, 0.5]} p={[0.25, 0.6, 0.05]} c={escurecer(cor, 0.9)} r={0.1} rough={1} />
|
|
308
|
+
</group>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
function Cacto() {
|
|
312
|
+
const v = '#3f8f4a';
|
|
313
|
+
return (
|
|
314
|
+
<group>
|
|
315
|
+
<B s={[0.36, 2, 0.36]} p={[0, 1, 0]} c={v} r={0.08} />
|
|
316
|
+
<B s={[0.5, 0.24, 0.24]} p={[-0.38, 0.9, 0]} c={v} r={0.06} />
|
|
317
|
+
<B s={[0.24, 0.6, 0.24]} p={[-0.55, 1.25, 0]} c={v} r={0.06} />
|
|
318
|
+
<B s={[0.45, 0.22, 0.22]} p={[0.35, 1.3, 0]} c={v} r={0.06} />
|
|
319
|
+
<B s={[0.22, 0.5, 0.22]} p={[0.5, 1.6, 0]} c={v} r={0.06} />
|
|
320
|
+
</group>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
function Montanha({ neve, cor = '#7b8b6a' }) {
|
|
324
|
+
return (
|
|
325
|
+
<group scale={[2.6, 2.6, 2.6]}>
|
|
326
|
+
{[0, 1, 2, 3].map((i) => <B key={i} s={[3 - i * 0.7, 0.7, 3 - i * 0.7]} p={[0, 0.35 + i * 0.7, 0]} c={i === 3 && neve !== false ? '#f6faff' : misturar(cor, '#5c6b54', i * 0.15)} r={0.08} rough={1} sombra={false} />)}
|
|
327
|
+
</group>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
function Mesa({ cor = '#8a5a3c' }) {
|
|
331
|
+
return (
|
|
332
|
+
<group>
|
|
333
|
+
<B s={[1.4, 0.08, 0.8]} p={[0, 0.76, 0]} c={cor} r={0.02} />
|
|
334
|
+
{[[-0.62, -0.32], [0.62, -0.32], [-0.62, 0.32], [0.62, 0.32]].map(([x, z], i) => <B key={i} s={[0.07, 0.74, 0.07]} p={[x, 0.37, z]} c={escurecer(cor, 0.8)} r={0.01} />)}
|
|
335
|
+
</group>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
function Cadeira({ cor = '#2b2d33' }) {
|
|
339
|
+
return (
|
|
340
|
+
<group>
|
|
341
|
+
<B s={[0.5, 0.07, 0.5]} p={[0, 0.48, 0]} c={cor} r={0.02} />
|
|
342
|
+
<B s={[0.5, 0.55, 0.07]} p={[0, 0.78, -0.22]} c={cor} r={0.02} />
|
|
343
|
+
{[[-0.2, -0.2], [0.2, -0.2], [-0.2, 0.2], [0.2, 0.2]].map(([x, z], i) => <B key={i} s={[0.05, 0.46, 0.05]} p={[x, 0.23, z]} c={escurecer(cor, 0.8)} r={0.01} />)}
|
|
344
|
+
</group>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
function Sofa({ cor = '#4f6dd8' }) {
|
|
348
|
+
return (
|
|
349
|
+
<group>
|
|
350
|
+
<B s={[2, 0.45, 0.85]} p={[0, 0.3, 0]} c={cor} r={0.08} rough={0.95} />
|
|
351
|
+
<B s={[2, 0.6, 0.25]} p={[0, 0.75, -0.32]} c={cor} r={0.08} rough={0.95} />
|
|
352
|
+
{[-0.95, 0.95].map((x, i) => <B key={i} s={[0.22, 0.6, 0.85]} p={[x, 0.45, 0]} c={escurecer(cor, 0.9)} r={0.08} rough={0.95} />)}
|
|
353
|
+
</group>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
function Planta() {
|
|
357
|
+
return (
|
|
358
|
+
<group>
|
|
359
|
+
<B s={[0.45, 0.5, 0.45]} p={[0, 0.25, 0]} c="#c9784a" r={0.05} />
|
|
360
|
+
{[0, 1, 2, 3, 4].map((i) => <B key={i} s={[0.16, 0.7 + hash(i) * 0.3, 0.16]} p={[Math.cos(i * 1.3) * 0.13, 0.85, Math.sin(i * 1.3) * 0.13]} rot={[Math.sin(i) * 0.3, 0, Math.cos(i) * 0.3]} c={i % 2 ? VERDE : VERDE2} r={0.04} />)}
|
|
361
|
+
</group>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
function Estante({ k = 0 }) {
|
|
365
|
+
const livros = [];
|
|
366
|
+
for (let a = 0; a < 3; a++) for (let i = 0; i < 6; i++) {
|
|
367
|
+
const h = 0.32 + hash(a * 9 + i + k * 20) * 0.12;
|
|
368
|
+
livros.push(<B key={a + '-' + i} s={[0.13, h, 0.3]} p={[-0.45 + i * 0.18, 0.12 + a * 0.62 + h / 2, 0.02]} c={['#c2452f', '#2f4cdd', '#1f8a5b', '#d08a1e', '#7b5cff', '#e8e2d6'][(a * 2 + i) % 6]} r={0.01} sombra={false} />);
|
|
369
|
+
}
|
|
370
|
+
return (
|
|
371
|
+
<group>
|
|
372
|
+
<B s={[1.3, 1.95, 0.4]} p={[0, 0.97, -0.02]} c="#6b4a2f" r={0.03} />
|
|
373
|
+
{livros}
|
|
374
|
+
</group>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
function Computador() {
|
|
378
|
+
return (
|
|
379
|
+
<group>
|
|
380
|
+
<Mesa />
|
|
381
|
+
<B s={[0.75, 0.48, 0.05]} p={[0, 1.18, -0.15]} c="#16181f" r={0.02} />
|
|
382
|
+
<B s={[0.68, 0.4, 0.02]} p={[0, 1.18, -0.12]} c="#3d6fff" e="#3d6fff" ei={0.7} r={0.01} sombra={false} />
|
|
383
|
+
<B s={[0.08, 0.2, 0.08]} p={[0, 0.88, -0.15]} c="#16181f" r={0.01} />
|
|
384
|
+
<B s={[0.5, 0.03, 0.18]} p={[0, 0.81, 0.15]} c="#2b2d33" r={0.01} />
|
|
385
|
+
</group>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
function Luminaria() {
|
|
389
|
+
return (
|
|
390
|
+
<group>
|
|
391
|
+
<B s={[0.36, 0.06, 0.36]} p={[0, 0.03, 0]} c="#2b2d33" r={0.02} />
|
|
392
|
+
<B s={[0.06, 1.6, 0.06]} p={[0, 0.8, 0]} c="#2b2d33" r={0.01} />
|
|
393
|
+
<B s={[0.5, 0.36, 0.5]} p={[0, 1.7, 0]} c="#ffe8b8" e="#ffd98a" ei={1.4} r={0.06} sombra={false} />
|
|
394
|
+
<pointLight position={[0, 1.6, 0.3]} intensity={3} distance={4} color="#ffd98a" />
|
|
395
|
+
</group>
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
function Quadro({ k = 0, lousa }) {
|
|
399
|
+
return (
|
|
400
|
+
<group position={[0, lousa ? 1.6 : 2.1, 0]}>
|
|
401
|
+
<B s={[lousa ? 3 : 1.2, lousa ? 1.5 : 0.85, 0.06]} c={lousa ? '#5b3d22' : '#2b2d33'} r={0.02} sombra={false} />
|
|
402
|
+
<B s={[lousa ? 2.8 : 1.05, lousa ? 1.3 : 0.7, 0.02]} p={[0, 0, 0.03]} c={lousa ? '#24453a' : ['#f1b434', '#2f80ed', '#e5484d'][Math.floor(k * 3)]} r={0.01} sombra={false} />
|
|
403
|
+
</group>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
function Balcao() {
|
|
407
|
+
return (
|
|
408
|
+
<group>
|
|
409
|
+
<B s={[2.6, 1, 0.8]} p={[0, 0.5, 0]} c="#8a5a3c" r={0.03} />
|
|
410
|
+
<B s={[2.7, 0.08, 0.9]} p={[0, 1.04, 0]} c="#e8e2d6" r={0.02} />
|
|
411
|
+
{[-0.8, 0, 0.8].map((x, i) => <B key={i} s={[0.32, 0.25, 0.32]} p={[x, 1.2, 0]} c={['#d9a066', '#e8c48a', '#b5763e'][i]} r={0.08} />)}
|
|
412
|
+
</group>
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
function Cerca() {
|
|
416
|
+
return (
|
|
417
|
+
<group>
|
|
418
|
+
{[-0.9, -0.3, 0.3, 0.9].map((x, i) => <B key={i} s={[0.12, 0.9, 0.12]} p={[x, 0.45, 0]} c="#e8e2d6" r={0.02} />)}
|
|
419
|
+
<B s={[2.1, 0.1, 0.06]} p={[0, 0.6, 0.06]} c="#e8e2d6" r={0.02} />
|
|
420
|
+
<B s={[2.1, 0.1, 0.06]} p={[0, 0.3, 0.06]} c="#e8e2d6" r={0.02} />
|
|
421
|
+
</group>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
function Celeiro() { return <group scale={[1.2, 1.2, 1.2]}><Casa cor="#b5352a" telhado="#e8e2d6" /></group>; }
|
|
425
|
+
function Feno() { return <B s={[0.9, 0.6, 0.6]} p={[0, 0.3, 0]} c="#e2c35a" r={0.08} rough={1} />; }
|
|
426
|
+
function BonecoDeNeve() {
|
|
427
|
+
return (
|
|
428
|
+
<group>
|
|
429
|
+
<B s={[0.9, 0.8, 0.9]} p={[0, 0.4, 0]} c="#f8fbff" r={0.25} rough={0.9} />
|
|
430
|
+
<B s={[0.65, 0.6, 0.65]} p={[0, 1.05, 0]} c="#f8fbff" r={0.2} rough={0.9} />
|
|
431
|
+
<B s={[0.45, 0.45, 0.45]} p={[0, 1.55, 0]} c="#f8fbff" r={0.15} rough={0.9} />
|
|
432
|
+
<B s={[0.07, 0.07, 0.28]} p={[0, 1.55, 0.32]} c="#f97316" r={0.02} />
|
|
433
|
+
<Olho p={[-0.1, 1.64, 0.23]} s={0.06} /><Olho p={[0.1, 1.64, 0.23]} s={0.06} />
|
|
434
|
+
<B s={[0.55, 0.1, 0.55]} p={[0, 1.79, 0]} c="#16161a" r={0.02} /><B s={[0.36, 0.3, 0.36]} p={[0, 1.96, 0]} c="#16161a" r={0.02} />
|
|
435
|
+
</group>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
function Foguete({ t }) {
|
|
439
|
+
return (
|
|
440
|
+
<group position={[0, Math.sin(t * 0.7) * 0.05, 0]}>
|
|
441
|
+
<B s={[0.8, 2.6, 0.8]} p={[0, 1.6, 0]} c="#eef0f4" r={0.2} rough={0.4} metal={0.2} />
|
|
442
|
+
<B s={[0.56, 0.7, 0.56]} p={[0, 3.2, 0]} c="#e5484d" r={0.2} />
|
|
443
|
+
<B s={[0.36, 0.36, 0.06]} p={[0, 2.3, 0.41]} c="#3d6fff" e="#3d6fff" ei={0.6} r={0.08} sombra={false} />
|
|
444
|
+
{[0, 1, 2, 3].map((i) => <B key={i} s={[0.1, 0.8, 0.6]} p={[Math.cos(i * Math.PI / 2) * 0.48, 0.55, Math.sin(i * Math.PI / 2) * 0.48]} rot={[0, -i * Math.PI / 2, 0]} c="#e5484d" r={0.03} />)}
|
|
445
|
+
</group>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
function Planeta({ k = 0, t }) {
|
|
449
|
+
const cor = ['#f1b434', '#7b5cff', '#2fa4ff', '#e5484d'][Math.floor(k * 4)];
|
|
450
|
+
return (
|
|
451
|
+
<group position={[0, 4 + k * 3, 0]} rotation={[0.3, t * 0.15, 0.2]}>
|
|
452
|
+
<B s={[2.2, 2.2, 2.2]} c={cor} r={0.5} rough={0.8} />
|
|
453
|
+
<B s={[3.6, 0.08, 3.6]} c={misturar(cor, '#ffffff', 0.4)} r={0.04} rough={0.6} sombra={false} />
|
|
454
|
+
</group>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
function Cratera() {
|
|
458
|
+
return (
|
|
459
|
+
<group>
|
|
460
|
+
{[0, 1, 2, 3, 4, 5].map((i) => { const a = (i / 6) * Math.PI * 2; return <B key={i} s={[0.6, 0.18, 0.4]} p={[Math.cos(a) * 0.7, 0.09, Math.sin(a) * 0.7]} rot={[0, -a, 0]} c="#8a8a96" r={0.04} rough={1} />; })}
|
|
461
|
+
</group>
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
function Alga({ t, k = 0 }) {
|
|
465
|
+
return (
|
|
466
|
+
<group>
|
|
467
|
+
{[0, 1, 2, 3, 4, 5].map((i) => <B key={i} s={[0.16, 0.42, 0.16]} p={[Math.sin(t * 1.2 + i * 0.6 + k * 5) * 0.06 * i, 0.21 + i * 0.4, 0]} c={i % 2 ? '#2f9a5a' : '#3fae6a'} r={0.05} />)}
|
|
468
|
+
</group>
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
function Coral({ k = 0 }) {
|
|
472
|
+
const cor = ['#ff6f91', '#ff9f43', '#c56cf0'][Math.floor(k * 3)];
|
|
473
|
+
return (
|
|
474
|
+
<group>
|
|
475
|
+
{[[0, 0.3, 0, 0.6], [0.25, 0.55, 0.1, 0.5], [-0.2, 0.6, -0.05, 0.6], [0.05, 0.9, 0, 0.4]].map(([x, y, z, h], i) => <B key={i} s={[0.2, h, 0.2]} p={[x, y, z]} c={cor} r={0.06} rough={0.9} />)}
|
|
476
|
+
</group>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
function Bau() {
|
|
480
|
+
return (
|
|
481
|
+
<group>
|
|
482
|
+
<B s={[0.9, 0.55, 0.6]} p={[0, 0.27, 0]} c="#8a5a2c" r={0.04} />
|
|
483
|
+
<B s={[0.92, 0.25, 0.62]} p={[0, 0.66, -0.05]} rot={[-0.35, 0, 0]} c="#9b6a36" r={0.04} />
|
|
484
|
+
<B s={[0.7, 0.12, 0.4]} p={[0, 0.58, 0.02]} c="#f5c542" e="#f5c542" ei={0.5} r={0.04} sombra={false} />
|
|
485
|
+
</group>
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
function Cogumelo() {
|
|
489
|
+
return (
|
|
490
|
+
<group>
|
|
491
|
+
<B s={[0.22, 0.45, 0.22]} p={[0, 0.22, 0]} c="#f2e6d0" r={0.05} />
|
|
492
|
+
<B s={[0.7, 0.3, 0.7]} p={[0, 0.55, 0]} c="#d6332f" r={0.12} />
|
|
493
|
+
{[[0.15, 0.2], [-0.18, -0.1], [0.05, -0.22]].map(([x, z], i) => <B key={i} s={[0.1, 0.04, 0.1]} p={[x, 0.71, z]} c="#ffffff" r={0.02} />)}
|
|
494
|
+
</group>
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
function Fogueira({ t }) {
|
|
498
|
+
return (
|
|
499
|
+
<group>
|
|
500
|
+
{[0, 1, 2, 3].map((i) => <B key={i} s={[0.8, 0.14, 0.14]} p={[0, 0.07, 0]} rot={[0, i * Math.PI / 4, 0]} c="#6b4423" r={0.03} />)}
|
|
501
|
+
{[0, 1, 2].map((i) => <B key={'f' + i} s={[0.3 - i * 0.07, 0.35 + Math.sin(t * 9 + i * 2) * 0.08, 0.3 - i * 0.07]} p={[0, 0.3 + i * 0.2, 0]} c={['#ff7a1a', '#ffb31a', '#ffe08a'][i]} e={['#ff5a00', '#ff9a00', '#ffd060'][i]} ei={2.4} r={0.04} sombra={false} />)}
|
|
502
|
+
<pointLight position={[0, 0.8, 0]} intensity={8 + Math.sin(t * 11) * 2} distance={6} color="#ff9a3a" />
|
|
503
|
+
</group>
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
function Barraca() {
|
|
507
|
+
return (
|
|
508
|
+
<group>
|
|
509
|
+
<B s={[1.7, 0.1, 1.6]} p={[0, 0.05, 0]} c="#2f6a3a" r={0.02} />
|
|
510
|
+
<B s={[1.2, 0.08, 1.6]} p={[-0.38, 0.62, 0]} rot={[0, 0, 0.95]} c="#f08c2a" r={0.02} />
|
|
511
|
+
<B s={[1.2, 0.08, 1.6]} p={[0.38, 0.62, 0]} rot={[0, 0, -0.95]} c="#e27a1a" r={0.02} />
|
|
512
|
+
</group>
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
function Barco() {
|
|
516
|
+
return (
|
|
517
|
+
<group>
|
|
518
|
+
<B s={[2.2, 0.4, 0.9]} p={[0, 0.2, 0]} c="#e8e2d6" r={0.12} />
|
|
519
|
+
<B s={[0.08, 1.8, 0.08]} p={[0, 1.2, 0]} c="#6b4423" r={0.02} />
|
|
520
|
+
<B s={[0.9, 1.3, 0.04]} p={[0.48, 1.3, 0]} c="#ffffff" r={0.02} />
|
|
521
|
+
</group>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
function CaixaDeSom() {
|
|
525
|
+
return (
|
|
526
|
+
<group>
|
|
527
|
+
<B s={[0.8, 1.4, 0.6]} p={[0, 0.7, 0]} c="#16161a" r={0.04} />
|
|
528
|
+
<B s={[0.5, 0.5, 0.04]} p={[0, 0.95, 0.31]} c="#2b2d33" r={0.2} />
|
|
529
|
+
<B s={[0.3, 0.3, 0.04]} p={[0, 0.35, 0.31]} c="#2b2d33" r={0.12} />
|
|
530
|
+
</group>
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
function Banco() {
|
|
534
|
+
return (
|
|
535
|
+
<group>
|
|
536
|
+
<B s={[1.6, 0.08, 0.45]} p={[0, 0.45, 0]} c="#8a5a3c" r={0.02} />
|
|
537
|
+
<B s={[1.6, 0.35, 0.06]} p={[0, 0.72, -0.2]} c="#8a5a3c" r={0.02} />
|
|
538
|
+
{[-0.7, 0.7].map((x, i) => <B key={i} s={[0.08, 0.45, 0.4]} p={[x, 0.22, 0]} c="#2b2d33" r={0.02} />)}
|
|
539
|
+
</group>
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
function Flor({ k = 0 }) {
|
|
543
|
+
const cor = ['#ff6f91', '#f1b434', '#c56cf0', '#ffffff', '#ff8a3a'][Math.floor(k * 5)];
|
|
544
|
+
return (
|
|
545
|
+
<group>
|
|
546
|
+
{[0, 1, 2].map((i) => (
|
|
547
|
+
<group key={i} position={[(i - 1) * 0.22, 0, (i % 2) * 0.15]}>
|
|
548
|
+
<B s={[0.05, 0.35 + i * 0.05, 0.05]} p={[0, 0.18, 0]} c={VERDE2} r={0.01} />
|
|
549
|
+
<B s={[0.16, 0.12, 0.16]} p={[0, 0.4 + i * 0.05, 0]} c={cor} r={0.04} />
|
|
550
|
+
<B s={[0.06, 0.06, 0.06]} p={[0, 0.47 + i * 0.05, 0]} c="#f8d24a" r={0.02} />
|
|
551
|
+
</group>
|
|
552
|
+
))}
|
|
553
|
+
</group>
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
function Tapete() { return <B s={[2.4, 0.03, 1.6]} p={[0, 0.02, 1.2]} c="#b5452f" r={0.01} sombra={false} />; }
|
|
557
|
+
function Nuvem() { return <group position={[0, 3.2, 0]}><B s={[1.8, 0.6, 0.8]} c="#ffffff" r={0.25} rough={1} /><B s={[1, 0.6, 0.7]} p={[0.3, 0.4, 0]} c="#ffffff" r={0.25} rough={1} /></group>; }
|
|
558
|
+
|
|
559
|
+
const PECAS = {
|
|
560
|
+
arvore: (x) => <Arvore k={x.k} />, pinheiro: () => <Pinheiro />, 'pinheiro nevado': () => <Pinheiro neve />, palmeira: (x) => <Palmeira t={x.t} k={x.k} />,
|
|
561
|
+
arbusto: () => <B s={[1, 0.7, 0.9]} p={[0, 0.35, 0]} c={VERDE} r={0.15} rough={0.95} />, flor: (x) => <Flor k={x.k} />, pedra: (x) => <Pedra k={x.k} cor={x.m.lugar === 'espaco' ? '#9a9aa6' : CINZA} />,
|
|
562
|
+
cacto: () => <Cacto />, montanha: (x) => <Montanha neve={x.m.lugar !== 'deserto'} cor={x.m.lugar === 'deserto' ? '#c98a4a' : '#7b8b6a'} />, casa: () => <Casa />, predio: (x) => <Predio k={x.k} noite={x.m.hora === 'noite'} />,
|
|
563
|
+
celeiro: () => <Celeiro />, poste: (x) => <Poste noite={x.m.hora === 'noite' || x.m.hora === 'por_do_sol'} />, carro: (x) => <Carro k={x.k} />, 'guarda-sol': (x) => <GuardaSol k={x.k} />,
|
|
564
|
+
'cadeira de praia': () => <CadeiraDePraia />, 'castelo de areia': () => <CasteloDeAreia />, mesa: () => <Mesa />, cadeira: () => <Cadeira />, sofa: () => <Sofa />,
|
|
565
|
+
planta: () => <Planta />, estante: (x) => <Estante k={x.k} />, computador: () => <Computador />, luminaria: () => <Luminaria />, quadro: (x) => <Quadro k={x.k} />,
|
|
566
|
+
lousa: () => <Quadro lousa />, balcao: () => <Balcao />, tapete: () => <Tapete />, banco: () => <Banco />, cerca: () => <Cerca />, feno: () => <Feno />,
|
|
567
|
+
'boneco de neve': () => <BonecoDeNeve />, foguete: (x) => <Foguete t={x.t} />, planeta: (x) => <Planeta k={x.k} t={x.t} />, cratera: () => <Cratera />,
|
|
568
|
+
alga: (x) => <Alga t={x.t} k={x.k} />, coral: (x) => <Coral k={x.k} />, bau: () => <Bau />, cogumelo: () => <Cogumelo />, nuvem: () => <Nuvem />,
|
|
569
|
+
fogueira: (x) => <Fogueira t={x.t} />, barraca: () => <Barraca />, barco: () => <Barco />, 'caixa de som': () => <CaixaDeSom />,
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
/* ── O ÍCONE EM BLOCOS ──────────────────────────────────────────────────
|
|
573
|
+
O PNG do ícone 3D (colecao.js) é lido num canvas pequeno: cada pixel vira
|
|
574
|
+
um cubo, e o miolo ganha mais camadas que a borda — o objeto fica com
|
|
575
|
+
volume de verdade, e não um recorte chapado. É o que deixa "uma pizza",
|
|
576
|
+
"um violão" ou "um dragão" entrarem na cena no mesmo visual de blocos. */
|
|
577
|
+
const LADO = 34;
|
|
578
|
+
function lerIcone(src, pronto) {
|
|
579
|
+
const img = new Image();
|
|
580
|
+
img.crossOrigin = 'anonymous';
|
|
581
|
+
img.onload = () => {
|
|
582
|
+
const cv = document.createElement('canvas');
|
|
583
|
+
cv.width = LADO; cv.height = LADO;
|
|
584
|
+
const g = cv.getContext('2d');
|
|
585
|
+
g.drawImage(img, 0, 0, LADO, LADO);
|
|
586
|
+
const px = g.getImageData(0, 0, LADO, LADO).data;
|
|
587
|
+
const cheio = (x, y) => x >= 0 && y >= 0 && x < LADO && y < LADO && px[(y * LADO + x) * 4 + 3] > 110;
|
|
588
|
+
// A distância até a borda (até 3): o miolo é mais grosso.
|
|
589
|
+
const dist = (x, y) => { for (let d = 1; d <= 3; d++) if (!cheio(x - d, y) || !cheio(x + d, y) || !cheio(x, y - d) || !cheio(x, y + d)) return d; return 4; };
|
|
590
|
+
const cubos = [];
|
|
591
|
+
for (let y = 0; y < LADO; y++) for (let x = 0; x < LADO; x++) {
|
|
592
|
+
if (!cheio(x, y)) continue;
|
|
593
|
+
const i = (y * LADO + x) * 4;
|
|
594
|
+
const c = `rgb(${px[i]},${px[i + 1]},${px[i + 2]})`;
|
|
595
|
+
const camadas = dist(x, y);
|
|
596
|
+
for (let z = -camadas + 1; z < camadas; z++) {
|
|
597
|
+
const frente = z === camadas - 1;
|
|
598
|
+
cubos.push({ x, y, z, c: frente ? c : escurecer(c, 0.82) });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
pronto(cubos);
|
|
602
|
+
};
|
|
603
|
+
img.onerror = () => pronto([]);
|
|
604
|
+
img.src = staticFile(src);
|
|
605
|
+
}
|
|
606
|
+
/* Os ícones são lidos FORA do canvas 3D (useIcones, chamado pelo voxel.jsx
|
|
607
|
+
na árvore normal do React): dentro do canvas o filho monta depois que o
|
|
608
|
+
Remotion já decidiu que o quadro estava pronto, e o quadro saía sem o
|
|
609
|
+
ícone. Aqui o render espera todos os PNGs virarem blocos. */
|
|
610
|
+
export function useIcones(srcs) {
|
|
611
|
+
const chave = srcs.join('|');
|
|
612
|
+
const [mapa, setMapa] = useState({});
|
|
613
|
+
const [h] = useState(() => (srcs.length ? delayRender('ícones em blocos', { timeoutInMilliseconds: 60000 }) : null));
|
|
614
|
+
useLayoutEffect(() => {
|
|
615
|
+
if (!srcs.length) return;
|
|
616
|
+
let falta = srcs.length;
|
|
617
|
+
const out = {};
|
|
618
|
+
for (const src of srcs) lerIcone(src, (c) => { out[src] = c; if (--falta === 0) setMapa(out); });
|
|
619
|
+
}, [chave]);
|
|
620
|
+
useLayoutEffect(() => {
|
|
621
|
+
if (h != null && Object.keys(mapa).length === srcs.length) continueRender(h);
|
|
622
|
+
}, [mapa]);
|
|
623
|
+
return mapa;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
export function IconeEmBlocos({ cubos, alto = 1.2 }) {
|
|
627
|
+
const itens = useMemo(() => {
|
|
628
|
+
if (!cubos) return [];
|
|
629
|
+
const k = alto / LADO;
|
|
630
|
+
return cubos.map((q) => ({ p: [(q.x - LADO / 2) * k, (LADO - q.y) * k, q.z * k], c: q.c }));
|
|
631
|
+
}, [cubos, alto]);
|
|
632
|
+
if (!itens.length) return null;
|
|
633
|
+
const k = alto / LADO;
|
|
634
|
+
return <Instancias itens={itens} w={k * 0.98} h={k * 0.98} d={k * 0.98} r={k * 0.12} rough={0.6} sombra />;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/** Uma peça do mundo no lugar dela. */
|
|
638
|
+
function PecaNoLugar({ p, m, t, i, icones }) {
|
|
639
|
+
const x = { k: hash(i * 5.3 + p.x), t, m };
|
|
640
|
+
const corpo = p.src ? <IconeEmBlocos cubos={icones[p.src]} alto={1.3} /> : (PECAS[p.peca] ? PECAS[p.peca](x) : null);
|
|
641
|
+
if (!corpo) return null;
|
|
642
|
+
return <group position={[p.x, 0, p.z]} rotation={[0, p.giro || 0, 0]} scale={[p.escala || 1, p.escala || 1, p.escala || 1]}>{corpo}</group>;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* ── A LUZ DE CADA LUGAR ────────────────────────────────────────────────
|
|
646
|
+
Fora: o sol (direcional, com sombra) e o céu (hemisfério). Dentro: a luz
|
|
647
|
+
de estúdio da referência. Debaixo d'água: azul e névoa. */
|
|
648
|
+
function Luz({ m }) {
|
|
649
|
+
const f = m.forca || 1;
|
|
650
|
+
if (m.dentro) {
|
|
651
|
+
return (
|
|
652
|
+
<group>
|
|
653
|
+
<hemisphereLight args={[m.recorte, escurecer(m.chao.cor, 0.4), 0.55]} />
|
|
654
|
+
<spotLight position={[2.2, 5.2, 4.4]} angle={0.55} penumbra={0.8} intensity={120} distance={22} decay={2} color={m.luz} castShadow
|
|
655
|
+
shadow-mapSize-width={2048} shadow-mapSize-height={2048} shadow-bias={-0.0003} />
|
|
656
|
+
<spotLight position={[-2.5, 3.6, -2.4]} angle={0.7} penumbra={1} intensity={m.holofotes ? 90 : 40} distance={14} decay={2} color={m.recorte} />
|
|
657
|
+
{m.holofotes && <spotLight position={[2.6, 4.2, -2]} angle={0.5} penumbra={1} intensity={90} distance={14} decay={2} color={m.acento} />}
|
|
658
|
+
<directionalLight position={[-3, 2, 4]} intensity={0.35} color="#fff4e6" />
|
|
659
|
+
</group>
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
if (m.subaquatico) {
|
|
663
|
+
// Debaixo d'água: a luz vem de cima, azul, e a cor some com a distância.
|
|
664
|
+
return (
|
|
665
|
+
<group>
|
|
666
|
+
<hemisphereLight args={['#7fd8ff', '#0b3a4f', 1.1]} />
|
|
667
|
+
<directionalLight position={[0, 10, 3]} intensity={1.6} color="#bfefff" castShadow
|
|
668
|
+
shadow-mapSize-width={2048} shadow-mapSize-height={2048} shadow-camera-left={-9} shadow-camera-right={9} shadow-camera-top={9} shadow-camera-bottom={-9} />
|
|
669
|
+
<directionalLight position={[2, 3, 7]} intensity={0.5} color="#9fe6ff" />
|
|
670
|
+
</group>
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
const sol = m.solBaixo ? [-8, 3.5, -6] : m.lua ? [6, 9, 2] : [-5, 10, 6];
|
|
674
|
+
return (
|
|
675
|
+
<group>
|
|
676
|
+
<hemisphereLight args={[m.ceu[1], escurecer(m.chao.cor, 0.55), (m.lua ? 0.35 : 0.9) * f]} />
|
|
677
|
+
<directionalLight position={sol} intensity={(m.lua ? 0.7 : 2.6) * f} color={m.luz} castShadow
|
|
678
|
+
shadow-mapSize-width={2048} shadow-mapSize-height={2048} shadow-bias={-0.0004}
|
|
679
|
+
shadow-camera-left={-9} shadow-camera-right={9} shadow-camera-top={9} shadow-camera-bottom={-9} shadow-camera-near={0.5} shadow-camera-far={40} />
|
|
680
|
+
{/* o preenchimento da frente: o rosto não fica no breu contra o sol */}
|
|
681
|
+
<directionalLight position={[2, 3, 7]} intensity={(m.lua ? 0.35 : 0.6) * f} color="#ffffff" />
|
|
682
|
+
<spotLight position={[-2.5, 3.6, -2.4]} angle={0.7} penumbra={1} intensity={m.lua ? 30 : 18} distance={14} decay={2} color={m.recorte} />
|
|
683
|
+
</group>
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
/* ── A COBERTURA À NOITE (2ª referência, 11/10) ───────────────────────────
|
|
689
|
+
Atrás: a janela com a cidade acesa e as luzes grandes que a lente derrete
|
|
690
|
+
em bolas (bokeh). No meio: o parapeito escuro. Na frente: os cartões de
|
|
691
|
+
interface com o ASSUNTO do vídeo, entre a câmera e o elenco — desfocados,
|
|
692
|
+
eles dão a profundidade do quadro da referência. */
|
|
693
|
+
function texturaCartaoUso(titulo, linhas, k, uso) {
|
|
694
|
+
const cv = document.createElement('canvas');
|
|
695
|
+
cv.width = 640; cv.height = 400;
|
|
696
|
+
const g = cv.getContext('2d');
|
|
697
|
+
g.fillStyle = 'rgba(18,18,24,0.94)'; g.beginPath(); g.roundRect(4, 4, 632, 392, 26); g.fill();
|
|
698
|
+
g.strokeStyle = 'rgba(255,255,255,0.12)'; g.lineWidth = 2; g.stroke();
|
|
699
|
+
const F = 'Inter, "Helvetica Neue", Arial, sans-serif';
|
|
700
|
+
if (uso != null) {
|
|
701
|
+
g.fillStyle = 'rgba(235,235,245,0.75)'; g.font = `500 30px ${F}`; g.fillText('Uso da sessão', 34, 70);
|
|
702
|
+
g.fillStyle = 'rgba(255,255,255,0.12)'; g.beginPath(); g.roundRect(34, 100, 572, 26, 13); g.fill();
|
|
703
|
+
const gr = g.createLinearGradient(34, 0, 606, 0); gr.addColorStop(0, '#ff7a2b'); gr.addColorStop(1, '#ffc23a');
|
|
704
|
+
g.fillStyle = gr; g.beginPath(); g.roundRect(34, 100, 572 * uso, 26, 13); g.fill();
|
|
705
|
+
g.fillStyle = 'rgba(235,235,245,0.55)'; g.font = `500 24px ${F}`; g.fillText(`${Math.round(uso * 100)}%`, 34, 170);
|
|
706
|
+
return cv;
|
|
707
|
+
}
|
|
708
|
+
g.fillStyle = '#ff6a2b'; g.beginPath(); g.arc(46, 50, 9, 0, Math.PI * 2); g.fill();
|
|
709
|
+
g.fillStyle = '#f2f2f7'; g.font = `600 28px ${F}`; g.fillText(`Claude · sessão ${k + 1}`, 66, 60);
|
|
710
|
+
g.fillStyle = '#ffffff'; g.font = `700 30px ${F}`; g.fillText(String(titulo).slice(0, 30), 34, 134);
|
|
711
|
+
g.fillStyle = 'rgba(225,225,235,0.75)'; g.font = `400 24px ${F}`;
|
|
712
|
+
(linhas || []).slice(0, 3).forEach((l, i) => g.fillText('· ' + String(l).slice(0, 34), 34, 178 + i * 36));
|
|
713
|
+
g.fillStyle = '#ff6a2b'; g.font = `500 22px ${F}`; g.fillText(`● trabalhando · ${30 + k * 7}:${String(10 + k * 13).slice(0, 2)}`, 34, 312);
|
|
714
|
+
g.fillStyle = 'rgba(255,255,255,0.06)'; g.beginPath(); g.roundRect(34, 336, 572, 40, 12); g.fill();
|
|
715
|
+
g.fillStyle = 'rgba(225,225,235,0.45)'; g.font = `400 20px ${F}`; g.fillText('Pergunte ao Claude…', 50, 363);
|
|
716
|
+
return cv;
|
|
717
|
+
}
|
|
718
|
+
function CartoesDeUso({ paineis = [], t }) {
|
|
719
|
+
const texturas = useMemo(() => {
|
|
720
|
+
const lista = paineis.length ? paineis : [{ titulo: 'Arrumando a tela', linha: '' }];
|
|
721
|
+
const linhasDe = (i) => lista.filter((_, j) => j !== i).slice(0, 3).map((x) => x.titulo);
|
|
722
|
+
const fazer = (cv) => { const tx = new THREE.CanvasTexture(cv); tx.colorSpace = THREE.SRGBColorSpace; tx.anisotropy = 4; return tx; };
|
|
723
|
+
return [fazer(texturaCartaoUso(lista[0].titulo, linhasDe(0), 1)), fazer(texturaCartaoUso(null, null, 0, 0.62 + (lista.length % 4) * 0.08)),
|
|
724
|
+
fazer(texturaCartaoUso((lista[1] || lista[0]).titulo, linhasDe(1), 3))];
|
|
725
|
+
}, [paineis]);
|
|
726
|
+
// Três cartões na frente e embaixo, inclinados para a câmera, flutuando devagar.
|
|
727
|
+
const lugares = [[-1.25, 0.55, 1.35, 0.35], [0.15, 0.32, 1.7, 0.05], [1.35, 0.6, 1.2, -0.35]];
|
|
728
|
+
return (
|
|
729
|
+
<group>
|
|
730
|
+
{lugares.map(([x, y, z, giro], i) => (
|
|
731
|
+
<group key={i} position={[x, y + Math.sin(t * 0.8 + i * 2) * 0.02, z]} rotation={[-0.5, giro, 0]}>
|
|
732
|
+
<mesh>
|
|
733
|
+
<planeGeometry args={[1.28, 0.8]} />
|
|
734
|
+
<meshStandardMaterial map={texturas[i]} emissiveMap={texturas[i]} emissive="#ffffff" emissiveIntensity={0.55} transparent roughness={0.4} toneMapped />
|
|
735
|
+
</mesh>
|
|
736
|
+
</group>
|
|
737
|
+
))}
|
|
738
|
+
</group>
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
function JanelaDaCidade({ m, t }) {
|
|
742
|
+
const predios = useMemo(() => {
|
|
743
|
+
const out = [], janelas = [];
|
|
744
|
+
for (let i = 0; i < 46; i++) {
|
|
745
|
+
const fila = i % 3, x = -22 + (i / 46) * 44 + (hash(i) - 0.5) * 1.5;
|
|
746
|
+
const z = -9 - fila * 6 - hash(i + 3) * 3, w = 1.6 + hash(i + 7) * 2, h = 3 + hash(i + 11) * (fila === 2 ? 16 : 10);
|
|
747
|
+
out.push({ p: [x, h / 2 - 1.5, z], s: [w, h, 1.5], c: ['#0b1024', '#0e1530', '#0a0e1e'][fila] });
|
|
748
|
+
for (let a = 0; a < h / 0.55; a++) for (let b = 0; b < w / 0.5; b++) {
|
|
749
|
+
if (hash(i * 131 + a * 17 + b * 7) > 0.3) continue;
|
|
750
|
+
janelas.push({ p: [x - w / 2 + 0.25 + b * 0.5, -1.2 + a * 0.55, z + 0.76], s: [0.22, 0.26, 0.02], c: hash(a + b + i) > 0.25 ? '#c9a46a' : '#9fb4d6' });
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return { out, janelas };
|
|
754
|
+
}, []);
|
|
755
|
+
// As luzes grandes: bolinhas que brilham a várias distâncias — a lente faz o bokeh.
|
|
756
|
+
// Poucas e GRANDES, perto do vidro: a lente as derrete nas bolas da referência.
|
|
757
|
+
const luzes = useMemo(() => Array.from({ length: 22 }, (_, i) => ({ p: [(hash(i * 5.1) - 0.5) * 16, 2 + hash(i * 2.3) * 6, -4.6 - hash(i * 7.7) * 5], r: 0.12 + hash(i) * 0.16, c: hash(i * 9) > 0.86 ? '#ff4a5a' : '#fff1d6' })), []);
|
|
758
|
+
return (
|
|
759
|
+
<group>
|
|
760
|
+
<mesh position={[0, 6, -32]}><planeGeometry args={[90, 40]} /><meshBasicMaterial color="#13246e" toneMapped={false} /></mesh>
|
|
761
|
+
<mesh position={[0, 16, -31]}><planeGeometry args={[90, 18]} /><meshBasicMaterial color="#050a24" toneMapped={false} /></mesh>
|
|
762
|
+
<Instancias itens={predios.out} rough={0.9} />
|
|
763
|
+
<Instancias itens={predios.janelas} emissivo={0.32} />
|
|
764
|
+
{luzes.map((l, i) => (
|
|
765
|
+
<mesh key={i} position={l.p}><sphereGeometry args={[l.r, 10, 8]} /><meshBasicMaterial color={l.c} toneMapped={false} /></mesh>
|
|
766
|
+
))}
|
|
767
|
+
{/* a esquadria do vidro: as colunas finas que dividem a janela */}
|
|
768
|
+
{[-6, -2, 2, 6].map((x) => <B key={x} s={[0.08, 8, 0.08]} p={[x, 3, -3.9]} c="#05060c" r={0.01} sombra={false} />)}
|
|
769
|
+
{/* o parapeito escuro onde o elenco está */}
|
|
770
|
+
<B s={[18, 0.95, 0.5]} p={[0, 0.47, -2.1]} c="#0d0f16" r={0.03} rough={0.5} metal={0.3} />
|
|
771
|
+
<B s={[18, 0.06, 0.56]} p={[0, 0.97, -2.1]} c="#1a1d28" r={0.02} rough={0.35} metal={0.4} />
|
|
772
|
+
</group>
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/** O mundo inteiro de um trecho: céu, chão, água, paredes, peças e a luz. */
|
|
777
|
+
export function Mundo({ m, t, icones = {}, paineis = [] }) {
|
|
778
|
+
const nevoa = m.cobertura ? ['#070c26', 14, 40] : m.subaquatico ? ['#0a4f6e', 2.5, 15] : m.dentro ? [m.parede || '#000', 9, 20] : [m.ceu ? m.ceu[1] : '#000', 16 - (m.neblina || 0) * 6, 46 - (m.neblina || 0) * 14];
|
|
779
|
+
// Fragmento, e não <group>: o "attach" do fundo e da névoa vai para o PAI — dentro de um grupo, eles iam para o grupo e sumiam.
|
|
780
|
+
return (
|
|
781
|
+
<>
|
|
782
|
+
<color attach="background" args={[m.subaquatico ? '#0a4f6e' : m.ceu ? m.ceu[1] : m.parede || '#07061a']} />
|
|
783
|
+
<fog attach="fog" args={nevoa} />
|
|
784
|
+
<Luz m={m} />
|
|
785
|
+
{m.ceu && !m.dentro && !m.subaquatico && <Ceu m={m} t={t} />}
|
|
786
|
+
<Chao m={m} />
|
|
787
|
+
{m.agua && <Mar m={m} t={t} />}
|
|
788
|
+
{m.dentro && !m.telas && !m.cobertura && <Paredes m={m} />}
|
|
789
|
+
{m.cobertura && <JanelaDaCidade m={m} t={t} />}
|
|
790
|
+
{m.cobertura && <CartoesDeUso paineis={paineis} t={t} />}
|
|
791
|
+
{m.props.map((p, i) => <PecaNoLugar key={i} p={p} m={m} t={t} i={i} icones={icones} />)}
|
|
792
|
+
{m.subaquatico && Array.from({ length: 24 }, (_, i) => (
|
|
793
|
+
<B key={'b' + i} s={[0.07, 0.07, 0.07]} p={[(hash(i) - 0.5) * 10, (t * 0.4 + hash(i + 3) * 6) % 6, -1 - hash(i + 7) * 6]} c="#dff6ff" op={0.6} r={0.02} sombra={false} />
|
|
794
|
+
))}
|
|
795
|
+
</>
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/* ── OS BICHOS ──────────────────────────────────────────────────────────
|
|
800
|
+
Cada espécie é uma receita de blocos: corpo, pernas, cabeça, focinho
|
|
801
|
+
(a mandíbula de baixo abre com a voz), orelha e rabo (que abana quando o
|
|
802
|
+
bicho fala ou está feliz). forma: 4 (quatro patas), ave, pinguim, peixe, dino. */
|
|
803
|
+
const ESP = {
|
|
804
|
+
cachorro: { forma: 4, corpo: [0.4, 0.36, 0.78], perna: 0.3, cab: 0.38, foc: [0.2, 0.16, 0.22], orelha: 'caida', rabo: 'reto', cor: '#b07a45', cor2: '#f1dfc4' },
|
|
805
|
+
gato: { forma: 4, corpo: [0.3, 0.28, 0.62], perna: 0.26, cab: 0.32, foc: [0.14, 0.09, 0.07], orelha: 'pontuda', rabo: 'longo', cor: '#e09a4a', cor2: '#fff1dc' },
|
|
806
|
+
cavalo: { forma: 4, corpo: [0.5, 0.56, 1.3], perna: 0.85, cab: 0.36, foc: [0.26, 0.26, 0.42], orelha: 'pontuda', rabo: 'fofo', cor: '#7a4a2a', cor2: '#3a2414', pescoco: 0.55, crina: true },
|
|
807
|
+
vaca: { forma: 4, corpo: [0.72, 0.66, 1.3], perna: 0.6, cab: 0.46, foc: [0.36, 0.24, 0.16], orelha: 'lado', rabo: 'reto', cor: '#f4f2ee', cor2: '#1d1b1a', manchas: true, chifre: true, focCor: '#f2a9b4' },
|
|
808
|
+
porco: { forma: 4, corpo: [0.56, 0.5, 0.9], perna: 0.24, cab: 0.44, foc: [0.22, 0.17, 0.08], orelha: 'pontuda', rabo: 'enrolado', cor: '#f2a9b4', cor2: '#e98f9d', focCor: '#e98f9d' },
|
|
809
|
+
ovelha: { forma: 4, corpo: [0.62, 0.56, 0.86], perna: 0.3, cab: 0.32, foc: [0.16, 0.14, 0.1], orelha: 'lado', rabo: 'curto', cor: '#f6f4ee', cor2: '#2b2622', cabCor: '#2b2622', fofo: true },
|
|
810
|
+
raposa: { forma: 4, corpo: [0.34, 0.32, 0.72], perna: 0.3, cab: 0.34, foc: [0.16, 0.13, 0.2], orelha: 'pontuda', rabo: 'fofo', cor: '#e2702a', cor2: '#fff4e6' },
|
|
811
|
+
lobo: { forma: 4, corpo: [0.44, 0.42, 0.9], perna: 0.42, cab: 0.4, foc: [0.2, 0.16, 0.24], orelha: 'pontuda', rabo: 'fofo', cor: '#8a8f98', cor2: '#e6e8ec' },
|
|
812
|
+
urso: { forma: 4, corpo: [0.8, 0.74, 1.1], perna: 0.42, cab: 0.56, foc: [0.26, 0.2, 0.16], orelha: 'redonda', rabo: 'curto', cor: '#6b4423', cor2: '#c9a27a' },
|
|
813
|
+
panda: { forma: 4, corpo: [0.8, 0.74, 1.05], perna: 0.42, cab: 0.56, foc: [0.24, 0.18, 0.14], orelha: 'redonda', rabo: 'curto', cor: '#f6f4ee', cor2: '#1d1b1a', panda: true },
|
|
814
|
+
coelho: { forma: 4, corpo: [0.32, 0.32, 0.42], perna: 0.12, cab: 0.32, foc: [0.12, 0.08, 0.06], orelha: 'longa', rabo: 'bola', cor: '#f2ece4', cor2: '#f2b8c4' },
|
|
815
|
+
leao: { forma: 4, corpo: [0.56, 0.52, 1.1], perna: 0.5, cab: 0.44, foc: [0.22, 0.16, 0.16], orelha: 'redonda', rabo: 'longo', cor: '#e0a24a', cor2: '#8a4a1a', juba: true },
|
|
816
|
+
tigre: { forma: 4, corpo: [0.56, 0.52, 1.1], perna: 0.5, cab: 0.44, foc: [0.22, 0.16, 0.16], orelha: 'redonda', rabo: 'longo', cor: '#f08a2a', cor2: '#1d1b1a', listras: true },
|
|
817
|
+
elefante: { forma: 4, corpo: [1.1, 1, 1.5], perna: 0.8, cab: 0.8, foc: [0.22, 0.22, 0.2], orelha: 'grande', rabo: 'curto', cor: '#9aa0aa', cor2: '#c9ced6', tromba: true },
|
|
818
|
+
girafa: { forma: 4, corpo: [0.56, 0.6, 1.05], perna: 1.15, cab: 0.3, foc: [0.18, 0.18, 0.26], orelha: 'pontuda', rabo: 'curto', cor: '#e8b85a', cor2: '#8a5a2c', pescoco: 1.3, manchas: true },
|
|
819
|
+
tartaruga: { forma: 4, corpo: [0.7, 0.36, 0.8], perna: 0.14, cab: 0.26, foc: [0.12, 0.1, 0.1], orelha: 'nenhuma', rabo: 'curto', cor: '#7fae5a', cor2: '#5a7a3a', casco: true },
|
|
820
|
+
galinha: { forma: 'ave', corpo: [0.32, 0.34, 0.4], perna: 0.18, cab: 0.22, cor: '#f6f4ee', bico: '#f1b434', crista: '#e5484d' },
|
|
821
|
+
pato: { forma: 'ave', corpo: [0.34, 0.3, 0.46], perna: 0.12, cab: 0.24, cor: '#f8d24a', bico: '#f97316', bicoLargo: true },
|
|
822
|
+
passaro: { forma: 'ave', corpo: [0.22, 0.22, 0.28], perna: 0.1, cab: 0.18, cor: '#2f80ed', bico: '#f1b434', voa: true },
|
|
823
|
+
papagaio: { forma: 'ave', corpo: [0.26, 0.34, 0.3], perna: 0.12, cab: 0.22, cor: '#2fae5a', bico: '#2b2622', voa: true, rabo2: '#e5484d' },
|
|
824
|
+
coruja: { forma: 'ave', corpo: [0.36, 0.42, 0.34], perna: 0.08, cab: 0.32, cor: '#8a6a4a', bico: '#f1b434', olhao: true },
|
|
825
|
+
pinguim: { forma: 'pinguim', corpo: [0.42, 0.6, 0.36], perna: 0.06, cab: 0.3, cor: '#1d1f26', cor2: '#f6f4ee', bico: '#f97316' },
|
|
826
|
+
peixe: { forma: 'peixe', corpo: [0.18, 0.4, 0.6], cor: '#f97316', cor2: '#ffffff' },
|
|
827
|
+
tubarao: { forma: 'peixe', corpo: [0.4, 0.5, 1.6], cor: '#7f8ea3', cor2: '#eef0f4', barbatana: true },
|
|
828
|
+
golfinho: { forma: 'peixe', corpo: [0.34, 0.4, 1.3], cor: '#7fa6c9', cor2: '#eef4fb', barbatana: true },
|
|
829
|
+
dinossauro: { forma: 'dino', corpo: [0.6, 0.7, 0.9], perna: 0.5, cab: 0.5, cor: '#4fae5a', cor2: '#f1d27a' },
|
|
830
|
+
sapo: { forma: 4, corpo: [0.4, 0.26, 0.42], perna: 0.08, cab: 0.36, foc: [0.3, 0.06, 0.04], orelha: 'nenhuma', rabo: 'nenhum', cor: '#4fae5a', cor2: '#d8f0a0', olhoAlto: true },
|
|
831
|
+
macaco: { forma: 4, corpo: [0.36, 0.42, 0.42], perna: 0.36, cab: 0.36, foc: [0.22, 0.14, 0.08], orelha: 'redonda', rabo: 'longo', cor: '#7a5232', cor2: '#e6c9a0', focCor: '#e6c9a0' },
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
/** A altura da cabeça (para a câmera mirar no rosto do bicho). */
|
|
835
|
+
export function alturaDoBicho(especie) {
|
|
836
|
+
const e = ESP[especie] || ESP.cachorro;
|
|
837
|
+
if (e.forma === 'peixe') return 1.2;
|
|
838
|
+
if (e.forma === 'ave') return e.perna + e.corpo[1] + e.cab * 0.6 + (e.voa ? 0.9 : 0);
|
|
839
|
+
if (e.forma === 'pinguim') return e.corpo[1] + e.cab * 0.7;
|
|
840
|
+
if (e.forma === 'dino') return e.perna + e.corpo[1] + e.cab * 0.9;
|
|
841
|
+
return e.perna + e.corpo[1] * 0.9 + (e.pescoco || 0) * 0.9 + e.cab * 0.5;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function Rabo({ e, t, abana, cor }) {
|
|
845
|
+
const giro = Math.sin(t * (abana ? 14 : 3)) * (abana ? 0.6 : 0.15);
|
|
846
|
+
if (e.rabo === 'nenhum') return null;
|
|
847
|
+
if (e.rabo === 'bola') return <B s={[0.14, 0.14, 0.14]} p={[0, 0, -0.04]} c="#ffffff" r={0.05} />;
|
|
848
|
+
if (e.rabo === 'enrolado') return <group rotation={[0, giro, 0]}><B s={[0.06, 0.12, 0.06]} p={[0, 0.04, -0.04]} c={cor} r={0.02} /><B s={[0.08, 0.06, 0.06]} p={[0.04, 0.1, -0.06]} c={cor} r={0.02} /></group>;
|
|
849
|
+
const L = e.rabo === 'curto' ? 0.12 : e.rabo === 'longo' ? 0.5 : e.rabo === 'fofo' ? 0.42 : 0.3;
|
|
850
|
+
const W = e.rabo === 'fofo' ? 0.16 : 0.07;
|
|
851
|
+
return (
|
|
852
|
+
<group rotation={[0.7, giro, 0]}>
|
|
853
|
+
<B s={[W, W, L]} p={[0, 0, -L / 2]} c={cor} r={W * 0.4} />
|
|
854
|
+
{e.rabo === 'fofo' && <B s={[W * 0.9, W * 0.9, 0.12]} p={[0, 0, -L - 0.03]} c={e.cor2} r={0.04} />}
|
|
855
|
+
</group>
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/* A cabeça de quatro patas: o cubo, os olhos de pixel, o focinho com a
|
|
860
|
+
mandíbula que abre na vogal, a orelha de cada espécie. */
|
|
861
|
+
function CabecaDeBicho({ e, est, cor }) {
|
|
862
|
+
const s = e.cab;
|
|
863
|
+
const [abre] = est.boca;
|
|
864
|
+
const pisca = clamp(est.pisca);
|
|
865
|
+
const focCor = e.focCor || e.cor2;
|
|
866
|
+
const fz = s / 2 + e.foc[2] / 2;
|
|
867
|
+
return (
|
|
868
|
+
<group>
|
|
869
|
+
<B s={[s, s, s]} c={e.cabCor || cor} r={0.05} rough={0.75} />
|
|
870
|
+
{e.panda && [-1, 1].map((k) => <B key={'p' + k} s={[s * 0.26, s * 0.3, 0.01]} p={[k * s * 0.22, s * 0.08, s / 2 + 0.003]} c="#1d1b1a" r={0.01} sombra={false} />)}
|
|
871
|
+
<Olho p={[-s * 0.24, s * (e.olhoAlto ? 0.45 : 0.12), s / 2 + 0.006]} s={s * 0.15} pisca={pisca} />
|
|
872
|
+
<Olho p={[s * 0.24, s * (e.olhoAlto ? 0.45 : 0.12), s / 2 + 0.006]} s={s * 0.15} pisca={pisca} />
|
|
873
|
+
{/* o focinho: em cima fixo, a mandíbula de baixo abre com a voz */}
|
|
874
|
+
<B s={[e.foc[0], e.foc[1] * 0.6, e.foc[2]]} p={[0, -s * 0.12, fz]} c={focCor} r={0.03} />
|
|
875
|
+
<B s={[e.foc[0] * 0.5, e.foc[1] * 0.3, 0.03]} p={[0, -s * 0.12 + e.foc[1] * 0.2, s / 2 + e.foc[2] + 0.005]} c="#1d1617" r={0.01} sombra={false} />
|
|
876
|
+
<group position={[0, -s * 0.12 - e.foc[1] * 0.3, s / 2]} rotation={[abre * 0.55, 0, 0]}>
|
|
877
|
+
<B s={[e.foc[0] * 0.92, e.foc[1] * 0.4, e.foc[2]]} p={[0, -e.foc[1] * 0.2, e.foc[2] / 2]} c={escurecer(focCor, 0.95)} r={0.03} />
|
|
878
|
+
{abre > 0.25 && <B s={[e.foc[0] * 0.6, 0.02, e.foc[2] * 0.7]} p={[0, 0.0, e.foc[2] / 2]} c="#c2455a" r={0.005} sombra={false} />}
|
|
879
|
+
</group>
|
|
880
|
+
{e.orelha === 'caida' && [-1, 1].map((k) => <B key={k} s={[0.07, s * 0.6, s * 0.36]} p={[k * (s / 2 + 0.035), s * 0.05, -0.02]} rot={[0, 0, k * 0.12]} c={escurecer(cor, 0.75)} r={0.03} />)}
|
|
881
|
+
{e.orelha === 'pontuda' && [-1, 1].map((k) => <B key={k} s={[s * 0.24, s * 0.3, 0.07]} p={[k * s * 0.3, s * 0.62, -0.02]} rot={[0, 0, -k * 0.2]} c={cor} r={0.02} />)}
|
|
882
|
+
{e.orelha === 'redonda' && [-1, 1].map((k) => <B key={k} s={[s * 0.26, s * 0.24, 0.08]} p={[k * s * 0.38, s * 0.56, -0.02]} c={e.panda ? '#1d1b1a' : escurecer(cor, 0.85)} r={0.04} />)}
|
|
883
|
+
{e.orelha === 'longa' && [-1, 1].map((k) => <B key={k} s={[s * 0.2, s * 1.1, 0.07]} p={[k * s * 0.22, s * 0.95, -0.04]} rot={[0, 0, -k * 0.12]} c={cor} r={0.03} />)}
|
|
884
|
+
{e.orelha === 'lado' && [-1, 1].map((k) => <B key={k} s={[s * 0.32, 0.1, s * 0.2]} p={[k * s * 0.62, s * 0.2, -0.02]} c={escurecer(cor, 0.9)} r={0.03} />)}
|
|
885
|
+
{e.orelha === 'grande' && [-1, 1].map((k) => <B key={k} s={[0.08, s * 0.95, s * 0.8]} p={[k * (s / 2 + 0.05), 0, -0.06]} rot={[0, k * 0.3, 0]} c={escurecer(cor, 0.92)} r={0.04} />)}
|
|
886
|
+
{e.chifre && [-1, 1].map((k) => <B key={'c' + k} s={[0.06, 0.16, 0.06]} p={[k * s * 0.34, s * 0.58, 0]} c="#efe6d0" r={0.02} />)}
|
|
887
|
+
{e.juba && <B s={[s * 1.55, s * 1.45, s * 0.5]} p={[0, 0.02, -s * 0.32]} c={e.cor2} r={0.1} rough={1} />}
|
|
888
|
+
{e.tromba && <group position={[0, -s * 0.2, s / 2]} rotation={[0.25 + abre * 0.4, 0, 0]}>{[0, 1, 2, 3].map((i) => <B key={i} s={[0.16 - i * 0.02, 0.18, 0.16 - i * 0.02]} p={[0, -0.12 - i * 0.16, 0.06 + i * 0.03]} c={cor} r={0.04} />)}</group>}
|
|
889
|
+
</group>
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function Quadrupede({ e, est, cor }) {
|
|
894
|
+
const [W, H, D] = e.corpo;
|
|
895
|
+
const anda = est.acao === 'andar' || est.acao === 'correr' || est.acao === 'pular';
|
|
896
|
+
const passo = est.acao === 'correr' ? 14 : 8;
|
|
897
|
+
const pulo = est.acao === 'pular' || est.acao === 'comemorar' ? Math.abs(Math.sin(est.t * 6)) * 0.25 : 0;
|
|
898
|
+
const respira = Math.sin(est.t * 2.2) * 0.008;
|
|
899
|
+
const y0 = e.perna + H / 2;
|
|
900
|
+
const [cy, cp] = est.cabeca;
|
|
901
|
+
const pescoco = e.pescoco || 0;
|
|
902
|
+
const cabPos = [0, y0 + H * 0.35 + pescoco * 0.85, D / 2 + e.cab * 0.25 + pescoco * 0.25];
|
|
903
|
+
// O corpo vira de lado, e a cabeça volta para a câmera: o bicho se lê inteiro em 3/4.
|
|
904
|
+
return (
|
|
905
|
+
<group position={[0, pulo, 0]}>
|
|
906
|
+
<group rotation={[0, 0.55, 0]}>
|
|
907
|
+
<B s={[W, H, D]} p={[0, y0 + respira, 0]} c={cor} r={0.06} rough={e.fofo ? 1 : 0.75} />
|
|
908
|
+
{e.fofo && [0, 1, 2].map((i) => <B key={i} s={[W * 1.06, H * 0.32, D * 0.3]} p={[0, y0 + H * 0.2, -D * 0.3 + i * D * 0.3]} c={e.cor} r={0.08} rough={1} />)}
|
|
909
|
+
{/* a barriga clara, as manchas, as listras ou o casco */}
|
|
910
|
+
{!e.fofo && !e.casco && <B s={[W * 0.8, H * 0.2, D * 0.8]} p={[0, y0 - H * 0.42, 0]} c={e.cor2} r={0.03} sombra={false} />}
|
|
911
|
+
{e.manchas && [0, 1, 2, 3].map((i) => <B key={'m' + i} s={[W * 1.01, H * 0.32, D * 0.2]} p={[0, y0 + H * (hash(i) - 0.4) * 0.6, -D * 0.35 + i * D * 0.22]} c={e.cor2} r={0.02} sombra={false} />)}
|
|
912
|
+
{e.listras && [0, 1, 2, 3, 4].map((i) => <B key={'l' + i} s={[W * 1.01, H * 0.85, 0.05]} p={[0, y0 + H * 0.05, -D * 0.38 + i * D * 0.19]} c={e.cor2} r={0.01} sombra={false} />)}
|
|
913
|
+
{e.casco && <B s={[W * 1.08, H * 0.9, D * 1.02]} p={[0, y0 + H * 0.2, 0]} c={e.cor2} r={0.14} rough={0.6} />}
|
|
914
|
+
{[[-1, 1], [1, 1], [-1, -1], [1, -1]].map(([kx, kz], i) => {
|
|
915
|
+
const balanca = anda ? Math.sin(est.t * passo + (i === 0 || i === 3 ? 0 : Math.PI)) * 0.5 : 0;
|
|
916
|
+
return (
|
|
917
|
+
<group key={i} position={[kx * (W / 2 - 0.07), e.perna, kz * (D / 2 - 0.1)]} rotation={[balanca, 0, 0]}>
|
|
918
|
+
<B s={[0.12 + W * 0.08, e.perna, 0.12 + W * 0.08]} p={[0, -e.perna / 2, 0]} c={e.panda ? '#1d1b1a' : escurecer(cor, 0.92)} r={0.03} />
|
|
919
|
+
</group>
|
|
920
|
+
);
|
|
921
|
+
})}
|
|
922
|
+
<group position={[0, y0 + H * 0.2, -D / 2]}><Rabo e={e} t={est.t} abana={est.falando || est.emocao === 'feliz' || est.emocao === 'animado'} cor={escurecer(cor, 0.9)} /></group>
|
|
923
|
+
{pescoco > 0 && <B s={[W * 0.4, pescoco + 0.2, W * 0.4]} p={[0, y0 + H * 0.3 + pescoco / 2, D / 2 - 0.05]} rot={[-0.35, 0, 0]} c={cor} r={0.04} />}
|
|
924
|
+
{e.crina && <B s={[0.06, pescoco + 0.15, W * 0.3]} p={[0, y0 + H * 0.45 + pescoco / 2, D / 2 - 0.2]} rot={[-0.35, 0, 0]} c={e.cor2} r={0.02} />}
|
|
925
|
+
</group>
|
|
926
|
+
<group position={[cabPos[0] + Math.sin(0.55) * cabPos[2], cabPos[1], Math.cos(0.55) * cabPos[2]]} rotation={[cp, cy - 0.15, 0]}>
|
|
927
|
+
<CabecaDeBicho e={e} est={est} cor={cor} />
|
|
928
|
+
</group>
|
|
929
|
+
</group>
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function Ave({ e, est, cor }) {
|
|
934
|
+
const [W, H, D] = e.corpo;
|
|
935
|
+
const [abre] = est.boca;
|
|
936
|
+
const voo = e.voa ? 0.9 + Math.sin(est.t * 2) * 0.08 : 0;
|
|
937
|
+
const bate = e.voa ? Math.sin(est.t * 18) * 0.7 : (est.falando ? Math.sin(est.t * 9) * 0.3 : 0);
|
|
938
|
+
const y0 = e.perna + H / 2 + voo;
|
|
939
|
+
const s = e.cab;
|
|
940
|
+
return (
|
|
941
|
+
<group>
|
|
942
|
+
{!e.voa && [-1, 1].map((k) => <B key={k} s={[0.04, e.perna, 0.04]} p={[k * W * 0.2, e.perna / 2, 0]} c={e.bico} r={0.01} />)}
|
|
943
|
+
<B s={[W, H, D]} p={[0, y0, 0]} c={cor} r={0.06} />
|
|
944
|
+
{[-1, 1].map((k) => <group key={'a' + k} position={[k * W / 2, y0 + H * 0.15, 0]} rotation={[0, 0, k * (0.2 + bate)]}><B s={[0.06, H * 0.7, D * 0.75]} p={[k * 0.03, -H * 0.3, 0]} c={escurecer(cor, 0.85)} r={0.02} /></group>)}
|
|
945
|
+
{e.rabo2 && <B s={[W * 0.4, 0.06, D * 0.7]} p={[0, y0 - H * 0.2, -D * 0.6]} rot={[0.4, 0, 0]} c={e.rabo2} r={0.02} />}
|
|
946
|
+
<group position={[0, y0 + H / 2 + s * 0.4, D * 0.15]} rotation={[est.cabeca[1], est.cabeca[0], 0]}>
|
|
947
|
+
<B s={[s, s, s]} c={cor} r={0.04} />
|
|
948
|
+
<Olho p={[-s * 0.25, s * 0.12, s / 2 + 0.006]} s={s * (e.olhao ? 0.3 : 0.17)} pisca={est.pisca} />
|
|
949
|
+
<Olho p={[s * 0.25, s * 0.12, s / 2 + 0.006]} s={s * (e.olhao ? 0.3 : 0.17)} pisca={est.pisca} />
|
|
950
|
+
<B s={[s * (e.bicoLargo ? 0.6 : 0.3), s * 0.12, s * 0.3]} p={[0, -s * 0.08 + abre * 0.02, s / 2 + s * 0.14]} c={e.bico} r={0.02} />
|
|
951
|
+
<B s={[s * (e.bicoLargo ? 0.55 : 0.26), s * 0.1, s * 0.26]} p={[0, -s * 0.2 - abre * 0.05, s / 2 + s * 0.12]} c={escurecer(e.bico, 0.85)} r={0.02} />
|
|
952
|
+
{e.crista && <B s={[0.05, s * 0.4, s * 0.5]} p={[0, s * 0.6, 0]} c={e.crista} r={0.02} />}
|
|
953
|
+
</group>
|
|
954
|
+
</group>
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function Pinguim({ e, est }) {
|
|
959
|
+
const [W, H, D] = e.corpo;
|
|
960
|
+
const [abre] = est.boca;
|
|
961
|
+
const ginga = Math.sin(est.t * (est.acao === 'andar' ? 8 : 2)) * (est.acao === 'andar' ? 0.12 : 0.03);
|
|
962
|
+
const s = e.cab;
|
|
963
|
+
return (
|
|
964
|
+
<group rotation={[0, 0, ginga]}>
|
|
965
|
+
{[-1, 1].map((k) => <B key={k} s={[0.14, 0.05, 0.2]} p={[k * 0.1, 0.025, 0.06]} c={e.bico} r={0.02} />)}
|
|
966
|
+
<B s={[W, H, D]} p={[0, H / 2 + 0.04, 0]} c={e.cor} r={0.12} />
|
|
967
|
+
<B s={[W * 0.72, H * 0.8, 0.04]} p={[0, H / 2, D / 2 + 0.005]} c={e.cor2} r={0.06} sombra={false} />
|
|
968
|
+
{[-1, 1].map((k) => <B key={'a' + k} s={[0.06, H * 0.6, D * 0.5]} p={[k * (W / 2 + 0.02), H * 0.55, 0]} rot={[0, 0, k * (0.15 + (est.falando ? Math.abs(Math.sin(est.t * 7)) * 0.4 : 0))]} c={e.cor} r={0.02} />)}
|
|
969
|
+
<group position={[0, H + s * 0.4, 0]} rotation={[est.cabeca[1], est.cabeca[0], 0]}>
|
|
970
|
+
<B s={[s, s, s]} c={e.cor} r={0.06} />
|
|
971
|
+
<B s={[s * 0.7, s * 0.5, 0.03]} p={[0, -0.02, s / 2 + 0.003]} c={e.cor2} r={0.02} sombra={false} />
|
|
972
|
+
<Olho p={[-s * 0.2, s * 0.08, s / 2 + 0.02]} s={s * 0.16} pisca={est.pisca} />
|
|
973
|
+
<Olho p={[s * 0.2, s * 0.08, s / 2 + 0.02]} s={s * 0.16} pisca={est.pisca} />
|
|
974
|
+
<B s={[s * 0.26, s * 0.08, s * 0.24]} p={[0, -s * 0.12 + abre * 0.02, s / 2 + 0.1]} c={e.bico} r={0.02} />
|
|
975
|
+
<B s={[s * 0.22, s * 0.07, s * 0.2]} p={[0, -s * 0.22 - abre * 0.05, s / 2 + 0.09]} c={escurecer(e.bico, 0.85)} r={0.02} />
|
|
976
|
+
</group>
|
|
977
|
+
</group>
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function Peixe({ e, est }) {
|
|
982
|
+
const [W, H, D] = e.corpo;
|
|
983
|
+
const [abre] = est.boca;
|
|
984
|
+
const nado = Math.sin(est.t * 4) * 0.2;
|
|
985
|
+
return (
|
|
986
|
+
<group position={[0, 1.2 + Math.sin(est.t * 1.5) * 0.06, 0]} rotation={[0, 0.5 + nado * 0.3, 0]}>
|
|
987
|
+
<B s={[W, H, D]} c={e.cor} r={Math.min(W, H) * 0.35} />
|
|
988
|
+
<B s={[W * 0.9, H * 0.35, D * 0.8]} p={[0, -H * 0.3, 0]} c={e.cor2} r={0.04} sombra={false} />
|
|
989
|
+
<group position={[0, 0, -D / 2]} rotation={[0, nado * 1.6, 0]}><B s={[0.05, H * 0.9, D * 0.3]} p={[0, 0, -D * 0.12]} c={e.cor} r={0.02} /></group>
|
|
990
|
+
{e.barbatana && <B s={[0.05, H * 0.6, D * 0.25]} p={[0, H * 0.65, -D * 0.05]} rot={[-0.4, 0, 0]} c={e.cor} r={0.02} />}
|
|
991
|
+
<Olho p={[-W / 2 - 0.004, H * 0.15, D * 0.3]} s={H * 0.18} pisca={est.pisca} />
|
|
992
|
+
<Olho p={[W / 2 + 0.004, H * 0.15, D * 0.3]} s={H * 0.18} pisca={est.pisca} />
|
|
993
|
+
<B s={[W * 0.5, H * 0.12 + abre * H * 0.15, 0.03]} p={[0, -H * 0.1, D / 2 + 0.01]} c="#3a1416" r={0.01} sombra={false} />
|
|
994
|
+
</group>
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function Dino({ e, est, cor }) {
|
|
999
|
+
const [W, H, D] = e.corpo;
|
|
1000
|
+
const [abre] = est.boca;
|
|
1001
|
+
const s = e.cab;
|
|
1002
|
+
const y0 = e.perna + H / 2;
|
|
1003
|
+
return (
|
|
1004
|
+
<group rotation={[0, 0.35, 0]}>
|
|
1005
|
+
{[-1, 1].map((k) => <B key={k} s={[0.22, e.perna, 0.3]} p={[k * W * 0.3, e.perna / 2, 0]} c={escurecer(cor, 0.9)} r={0.04} />)}
|
|
1006
|
+
<B s={[W, H, D]} p={[0, y0, 0]} rot={[-0.35, 0, 0]} c={cor} r={0.08} />
|
|
1007
|
+
<B s={[W * 0.7, H * 0.6, 0.04]} p={[0, y0 - 0.05, D / 2 - 0.02]} rot={[-0.35, 0, 0]} c={e.cor2} r={0.02} sombra={false} />
|
|
1008
|
+
{[0, 1, 2].map((i) => <B key={'r' + i} s={[W * (0.6 - i * 0.15), H * (0.5 - i * 0.12), 0.4]} p={[0, y0 - 0.2 - i * 0.1, -D / 2 - 0.15 - i * 0.35]} c={cor} r={0.05} />)}
|
|
1009
|
+
{[-1, 1].map((k) => <B key={'b' + k} s={[0.08, 0.22, 0.08]} p={[k * W * 0.4, y0 + H * 0.1, D / 2]} rot={[0.8, 0, 0]} c={cor} r={0.02} />)}
|
|
1010
|
+
<group position={[0, y0 + H / 2 + s * 0.35, D * 0.4]} rotation={[est.cabeca[1], est.cabeca[0] - 0.2, 0]}>
|
|
1011
|
+
<B s={[s, s * 0.7, s * 1.1]} c={cor} r={0.06} />
|
|
1012
|
+
<Olho p={[-s * 0.3, s * 0.2, s * 0.4]} s={s * 0.14} pisca={est.pisca} />
|
|
1013
|
+
<Olho p={[s * 0.3, s * 0.2, s * 0.4]} s={s * 0.14} pisca={est.pisca} />
|
|
1014
|
+
<group position={[0, -s * 0.3, -s * 0.1]} rotation={[abre * 0.5, 0, 0]}>
|
|
1015
|
+
<B s={[s * 0.9, s * 0.22, s]} p={[0, -0.04, s * 0.1]} c={escurecer(cor, 0.9)} r={0.04} />
|
|
1016
|
+
{[-1, 0, 1].map((k) => <B key={k} s={[0.04, 0.05, 0.04]} p={[k * s * 0.25, 0.06, s * 0.5]} c="#ffffff" r={0.01} sombra={false} />)}
|
|
1017
|
+
</group>
|
|
1018
|
+
</group>
|
|
1019
|
+
</group>
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/** O bicho da partitura: {especie, cor?, cor2?}; est é o mesmo estado da pessoa (boca, piscada, cabeça, ação). */
|
|
1024
|
+
export function Animal({ p, est }) {
|
|
1025
|
+
const e = { ...(ESP[p.especie] || ESP.cachorro), ...(p.cor2 ? { cor2: p.cor2 } : {}) };
|
|
1026
|
+
const cor = p.cor || e.cor;
|
|
1027
|
+
if (e.forma === 'ave') return <Ave e={e} est={est} cor={cor} />;
|
|
1028
|
+
if (e.forma === 'pinguim') return <Pinguim e={e} est={est} />;
|
|
1029
|
+
if (e.forma === 'peixe') return <Peixe e={{ ...e, cor }} est={est} />;
|
|
1030
|
+
if (e.forma === 'dino') return <Dino e={e} est={est} cor={cor} />;
|
|
1031
|
+
return <Quadrupede e={e} est={est} cor={cor} />;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/** A COISA que fala: o ícone em blocos, que estica com a voz e flutua. */
|
|
1035
|
+
export function Coisa({ p, est, icones = {} }) {
|
|
1036
|
+
const [abre] = est.boca;
|
|
1037
|
+
const bob = Math.sin(est.t * 1.9 + 0.7) * 0.05;
|
|
1038
|
+
const estica = 1 + abre * 0.08, achata = 1 - abre * 0.04;
|
|
1039
|
+
const pulo = est.acao === 'pular' || est.acao === 'comemorar' ? Math.abs(Math.sin(est.t * 6)) * 0.25 : 0;
|
|
1040
|
+
if (!p.src) return null;
|
|
1041
|
+
return (
|
|
1042
|
+
<group position={[0, 0.45 + bob + pulo, 0]} rotation={[0, est.cabeca[0] * 0.6, Math.sin(est.t * 1.3) * 0.04]}>
|
|
1043
|
+
<group scale={[achata, estica, achata]}>
|
|
1044
|
+
<IconeEmBlocos cubos={icones[p.src]} alto={1.25} />
|
|
1045
|
+
{/* o rosto de personagem: dois olhos que piscam e a boca que abre com a voz,
|
|
1046
|
+
na frente do ícone (o miolo tem 3 camadas de bloco: z = 0,12) */}
|
|
1047
|
+
{p.rosto !== false && (
|
|
1048
|
+
<group position={[0, 0.66, 0.135]}>
|
|
1049
|
+
<Olho p={[-0.16, 0.06, 0]} s={0.11} pisca={est.pisca} />
|
|
1050
|
+
<Olho p={[0.16, 0.06, 0]} s={0.11} pisca={est.pisca} />
|
|
1051
|
+
<mesh position={[0, -0.14 - abre * 0.03, 0]}><boxGeometry args={[0.14 + abre * 0.04, 0.03 + abre * 0.1, 0.012]} /><meshStandardMaterial color="#3a1416" roughness={0.6} /></mesh>
|
|
1052
|
+
</group>
|
|
1053
|
+
)}
|
|
1054
|
+
</group>
|
|
1055
|
+
<B s={[0.7, 0.04, 0.5]} p={[0, -0.43 - bob - pulo, 0]} c="#000000" op={0.18} r={0.02} sombra={false} />
|
|
1056
|
+
</group>
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
export const ESPECIES_3D = Object.keys(ESP);
|