morfeo 0.3.0 → 0.3.3
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 +1 -1
- package/bases/2d/Mundo.tsx +43 -0
- package/bases/2d/guion.mjs +32 -0
- package/bases/3d/Mundo.tsx +45 -0
- package/bases/3d/guion.mjs +26 -0
- package/bin/morfeo.mjs +23 -4
- package/index.d.ts +10 -3
- package/lib/config.mjs +48 -8
- package/lib/entorno.mjs +22 -2
- package/lib/estructura.mjs +1 -1
- package/lib/guion.mjs +2 -2
- package/lib/hoja.mjs +3 -0
- package/lib/nuevo-video.mjs +65 -0
- package/lib/palabras.mjs +79 -0
- package/lib/render.mjs +211 -88
- package/lib/salida.mjs +84 -0
- package/lib/whisper.mjs +47 -3
- package/package.json +5 -4
- package/plantilla/morfeo.config.mjs +3 -1
- package/python/musica.py +40 -7
- package/python/preparar.py +10 -1
- package/remotion/kit/datos.tsx +180 -0
- package/remotion/kit/escenarios2d.tsx +250 -0
- package/remotion/kit/index.ts +9 -2
- package/remotion/kit/mundo3d.tsx +251 -0
- package/remotion/kit/personas.tsx +439 -0
- package/remotion/kit/tres.tsx +11 -6
- package/remotion/motor/capas.tsx +27 -2
- package/remotion/motor/modelo3d.tsx +8 -3
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// Más piezas 3D para `Lienzo3D` (kit/tres.tsx): personas low-poly con cara y expresiones (Persona3D), ciudad, cuarto y camino.
|
|
2
|
+
// `Persona` (kit/diorama.tsx) es la figura de maqueta (casco, chaleco); `Persona3D` es la de historias, con expresiones.
|
|
3
|
+
// Mismo estilo que Arbol/Casa: geometría facetada y materiales mate. Todo depende de props y de `t`.
|
|
4
|
+
//
|
|
5
|
+
// <Persona3D pos={[0, 0, 2]} t={t} ropa="#2F6FE0" expresion="sorpresa" pose="señalar" />
|
|
6
|
+
// <Ciudad t={t} noche /> <Cuarto3D /> <Camino largo={30} /> (el coche es `Coche` de kit/diorama.tsx)
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import * as THREE from 'three';
|
|
9
|
+
import {azarN} from './ilustrado';
|
|
10
|
+
import type {Expresion, Pose} from './personas';
|
|
11
|
+
import {grupo, type Pieza, type V3} from './tres';
|
|
12
|
+
|
|
13
|
+
const Mate: React.FC<{c: string; r?: number; m?: number; e?: number}> = ({c, r = 0.8, m = 0, e = 0}) => (
|
|
14
|
+
<meshStandardMaterial color={c} roughness={r} metalness={m} emissive={e ? c : '#000000'} emissiveIntensity={e} flatShading />
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/** Ángulos (grados) de brazos y piernas por pose; los brazos rotan en el plano del cuerpo (z) y hacia adelante (x). */
|
|
18
|
+
function pose3d(pose: Pose, t: number, fase: number) {
|
|
19
|
+
switch (pose) {
|
|
20
|
+
case 'caminar': {
|
|
21
|
+
const p = Math.sin(t * 7 + fase);
|
|
22
|
+
return {bi: [p * 30, 8], bd: [-p * 30, 8], pi: -p * 28, pd: p * 28, sube: Math.abs(Math.cos(t * 7 + fase)) * 0.05};
|
|
23
|
+
}
|
|
24
|
+
case 'señalar':
|
|
25
|
+
return {bi: [0, 6], bd: [-80, 10], pi: 0, pd: 0, sube: 0};
|
|
26
|
+
case 'brazosArriba':
|
|
27
|
+
return {bi: [0, 160 + Math.sin(t * 6 + fase) * 10], bd: [0, 160 + Math.sin(t * 6 + fase + 1) * 10], pi: 0, pd: 0, sube: Math.abs(Math.sin(t * 6 + fase)) * 0.06};
|
|
28
|
+
case 'pensar':
|
|
29
|
+
return {bi: [-40, 12], bd: [-110, 20], pi: 0, pd: 0, sube: 0};
|
|
30
|
+
case 'cruzado':
|
|
31
|
+
return {bi: [-75, 35], bd: [-75, 35], pi: 0, pd: 0, sube: 0};
|
|
32
|
+
case 'sentado':
|
|
33
|
+
return {bi: [-45, 10], bd: [-45, 10], pi: -85, pd: -85, sube: -0.38};
|
|
34
|
+
default:
|
|
35
|
+
return {bi: [Math.sin(t * 1.5 + fase) * 3, 6], bd: [-Math.sin(t * 1.5 + fase) * 3, 6], pi: 0, pd: 0, sube: 0};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Persona low-poly de ~1.8 unidades de alto (pies en y = 0). Cara con ojos y boca según `expresion`;
|
|
41
|
+
* `habla` = intervalos [inicio, fin] en segundos en que mueve la boca.
|
|
42
|
+
*/
|
|
43
|
+
export const Persona3D: React.FC<
|
|
44
|
+
Pieza & {
|
|
45
|
+
t?: number;
|
|
46
|
+
piel?: string;
|
|
47
|
+
pelo?: string;
|
|
48
|
+
ropa?: string;
|
|
49
|
+
pantalon?: string;
|
|
50
|
+
expresion?: Expresion;
|
|
51
|
+
pose?: Pose;
|
|
52
|
+
estados?: {en: number; expresion?: Expresion; pose?: Pose}[];
|
|
53
|
+
habla?: [number, number][];
|
|
54
|
+
semilla?: number;
|
|
55
|
+
cabezaGrande?: boolean;
|
|
56
|
+
}
|
|
57
|
+
> = ({t = 0, piel = '#E8B48F', pelo = '#2B1B12', ropa = '#2F6FE0', pantalon = '#2E3A59', expresion: e0 = 'neutral', pose: p0 = 'parado', estados = [], habla = [], semilla = 0, cabezaGrande = true, ...p}) => {
|
|
58
|
+
let expresion = e0;
|
|
59
|
+
let pose = p0;
|
|
60
|
+
for (const e of [...estados].sort((a, b) => a.en - b.en))
|
|
61
|
+
if (t >= e.en) {
|
|
62
|
+
if (e.expresion) expresion = e.expresion;
|
|
63
|
+
if (e.pose) pose = e.pose;
|
|
64
|
+
}
|
|
65
|
+
const fase = azarN(semilla + 5) * 6.28;
|
|
66
|
+
const P = pose3d(pose, t, fase);
|
|
67
|
+
const R = Math.PI / 180;
|
|
68
|
+
const hc = cabezaGrande ? 0.36 : 0.26; // radio de la cabeza
|
|
69
|
+
const parpadea = (t + azarN(semilla) * 3) % 3.4 < 0.12 || expresion === 'dormido';
|
|
70
|
+
const hablando = habla.some(([a, b]) => t >= a && t <= b);
|
|
71
|
+
const boca = hablando ? 0.03 + 0.05 * Math.abs(Math.sin(t * 13)) : expresion === 'sorpresa' || expresion === 'miedo' ? 0.06 : 0.018;
|
|
72
|
+
const ancho = expresion === 'feliz' ? 0.13 : expresion === 'triste' || expresion === 'enojado' ? 0.09 : 0.08;
|
|
73
|
+
const curva = expresion === 'feliz' ? -0.02 : expresion === 'triste' ? 0.02 : 0;
|
|
74
|
+
const ceja = expresion === 'enojado' ? 0.35 : expresion === 'triste' || expresion === 'miedo' ? -0.35 : 0;
|
|
75
|
+
const brazo = (lado: 1 | -1, [adelante, afuera]: number[]) => (
|
|
76
|
+
<group position={[lado * 0.27, 1.12, 0]} rotation={[adelante * R, 0, lado * afuera * R]}>
|
|
77
|
+
<mesh position={[0, -0.27, 0]} castShadow>
|
|
78
|
+
<capsuleGeometry args={[0.075, 0.42, 4, 8]} />
|
|
79
|
+
<Mate c={ropa} />
|
|
80
|
+
</mesh>
|
|
81
|
+
<mesh position={[0, -0.55, 0]} castShadow>
|
|
82
|
+
<sphereGeometry args={[0.08, 10, 8]} />
|
|
83
|
+
<Mate c={piel} />
|
|
84
|
+
</mesh>
|
|
85
|
+
</group>
|
|
86
|
+
);
|
|
87
|
+
const pierna = (lado: 1 | -1, ang: number) => (
|
|
88
|
+
<group position={[lado * 0.11, 0.62, 0]} rotation={[ang * R, 0, 0]}>
|
|
89
|
+
<mesh position={[0, -0.3, 0]} castShadow>
|
|
90
|
+
<capsuleGeometry args={[0.09, 0.44, 4, 8]} />
|
|
91
|
+
<Mate c={pantalon} />
|
|
92
|
+
</mesh>
|
|
93
|
+
<mesh position={[0, -0.6, 0.06]} castShadow>
|
|
94
|
+
<boxGeometry args={[0.16, 0.08, 0.26]} />
|
|
95
|
+
<Mate c="#2A2420" />
|
|
96
|
+
</mesh>
|
|
97
|
+
</group>
|
|
98
|
+
);
|
|
99
|
+
return (
|
|
100
|
+
<group {...grupo(p)}>
|
|
101
|
+
<group position={[0, P.sube, 0]}>
|
|
102
|
+
{pierna(-1, P.pi)}
|
|
103
|
+
{pierna(1, P.pd)}
|
|
104
|
+
<mesh position={[0, 0.92, 0]} castShadow>
|
|
105
|
+
<capsuleGeometry args={[0.24, 0.4, 4, 10]} />
|
|
106
|
+
<Mate c={ropa} />
|
|
107
|
+
</mesh>
|
|
108
|
+
{brazo(-1, P.bi)}
|
|
109
|
+
{brazo(1, P.bd)}
|
|
110
|
+
<group position={[0, 1.38 + hc * 0.75, 0]} rotation={[0, Math.sin(t * 0.9 + fase) * 0.12, pose === 'pensar' ? -0.12 : 0]}>
|
|
111
|
+
<mesh castShadow>
|
|
112
|
+
<icosahedronGeometry args={[hc, 1]} />
|
|
113
|
+
<Mate c={piel} />
|
|
114
|
+
</mesh>
|
|
115
|
+
{/* pelo: casquete encima */}
|
|
116
|
+
<mesh position={[0, hc * 0.28, -hc * 0.06]} scale={[1.04, 0.72, 1.04]} castShadow>
|
|
117
|
+
<icosahedronGeometry args={[hc, 1]} />
|
|
118
|
+
<Mate c={pelo} />
|
|
119
|
+
</mesh>
|
|
120
|
+
{/* ojos */}
|
|
121
|
+
{[-1, 1].map((l) => (
|
|
122
|
+
<group key={l} position={[l * hc * 0.36, hc * 0.08, hc * 0.9]}>
|
|
123
|
+
{parpadea ? (
|
|
124
|
+
<mesh scale={[1, 0.18, 0.4]}>
|
|
125
|
+
<sphereGeometry args={[hc * 0.16, 10, 8]} />
|
|
126
|
+
<Mate c="#1E1410" />
|
|
127
|
+
</mesh>
|
|
128
|
+
) : (
|
|
129
|
+
<>
|
|
130
|
+
<mesh scale={[1, 1.15, 0.45]}>
|
|
131
|
+
<sphereGeometry args={[hc * 0.19, 12, 10]} />
|
|
132
|
+
<Mate c="#FFFFFF" r={0.4} />
|
|
133
|
+
</mesh>
|
|
134
|
+
<mesh position={[0, -hc * 0.01, hc * 0.07]} scale={[1, 1, 0.5]}>
|
|
135
|
+
<sphereGeometry args={[hc * (expresion === 'sorpresa' || expresion === 'miedo' ? 0.07 : 0.1), 10, 8]} />
|
|
136
|
+
<Mate c="#1E1410" r={0.3} />
|
|
137
|
+
</mesh>
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
<mesh position={[0, hc * 0.27, hc * 0.03]} rotation={[0, 0, -l * ceja]}>
|
|
141
|
+
<boxGeometry args={[hc * 0.32, hc * 0.06, hc * 0.06]} />
|
|
142
|
+
<Mate c={pelo} />
|
|
143
|
+
</mesh>
|
|
144
|
+
</group>
|
|
145
|
+
))}
|
|
146
|
+
{/* boca */}
|
|
147
|
+
<mesh position={[0, -hc * 0.38 + curva, hc * 0.92]} rotation={[0, 0, 0]}>
|
|
148
|
+
<boxGeometry args={[ancho * (hc / 0.36), boca * (hc / 0.36) * 1.4, 0.03]} />
|
|
149
|
+
<Mate c="#5A1E1E" />
|
|
150
|
+
</mesh>
|
|
151
|
+
</group>
|
|
152
|
+
</group>
|
|
153
|
+
</group>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/** Ciudad: manzanas de edificios con ventanas (encendidas de noche), en una cuadrícula alrededor del centro. */
|
|
158
|
+
export const Ciudad: React.FC<Pieza & {t?: number; noche?: boolean; filas?: number; columnas?: number; separacion?: number; semilla?: number; hueco?: number}> = ({t = 0, noche = false, filas = 4, columnas = 6, separacion = 4.2, semilla = 0, hueco = 1, ...p}) => {
|
|
159
|
+
const colores = noche ? ['#2A2F55', '#323A66', '#262B4A'] : ['#E8DCC8', '#CFD8E3', '#F2E6D0', '#D9C7B0'];
|
|
160
|
+
return (
|
|
161
|
+
<group {...grupo(p)}>
|
|
162
|
+
{Array.from({length: filas * columnas}, (_, i) => {
|
|
163
|
+
const f = Math.floor(i / columnas);
|
|
164
|
+
const c = i % columnas;
|
|
165
|
+
// deja libre la calle del centro (columna central) para la acción
|
|
166
|
+
if (Math.abs(c - (columnas - 1) / 2) < hueco * 0.5) return null;
|
|
167
|
+
const s = semilla + i * 7;
|
|
168
|
+
const alto = 2 + azarN(s) * 6;
|
|
169
|
+
const w = 2.2 + azarN(s + 1) * 1.2;
|
|
170
|
+
const x = (c - (columnas - 1) / 2) * separacion;
|
|
171
|
+
const z = -f * separacion - 2;
|
|
172
|
+
const luz = noche ? 0.5 + 0.5 * Math.sin(t * 0.4 + i) : 0;
|
|
173
|
+
return (
|
|
174
|
+
<group key={i} position={[x, 0, z]}>
|
|
175
|
+
<mesh position={[0, alto / 2, 0]} castShadow receiveShadow>
|
|
176
|
+
<boxGeometry args={[w, alto, w]} />
|
|
177
|
+
<Mate c={colores[i % colores.length]} />
|
|
178
|
+
</mesh>
|
|
179
|
+
{Array.from({length: Math.floor(alto / 0.7)}, (_, k) => (
|
|
180
|
+
<mesh key={k} position={[0, 0.5 + k * 0.7, w / 2 + 0.01]}>
|
|
181
|
+
<boxGeometry args={[w * 0.75, 0.28, 0.02]} />
|
|
182
|
+
<Mate c={noche ? (azarN(s + k) > 0.45 ? '#FFD27A' : '#1A1E36') : '#8EC5E8'} e={noche && azarN(s + k) > 0.45 ? 0.6 + luz * 0.3 : 0} r={0.3} />
|
|
183
|
+
</mesh>
|
|
184
|
+
))}
|
|
185
|
+
</group>
|
|
186
|
+
);
|
|
187
|
+
})}
|
|
188
|
+
<mesh position={[0, 0, -filas * separacion * 0.5]} rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
|
|
189
|
+
<planeGeometry args={[columnas * separacion + 20, filas * separacion + 30]} />
|
|
190
|
+
<Mate c={noche ? '#1E2030' : '#9AA0A8'} />
|
|
191
|
+
</mesh>
|
|
192
|
+
</group>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** Calle recta a lo largo de z, con líneas pintadas y banquetas. */
|
|
197
|
+
export const Camino: React.FC<Pieza & {largo?: number; ancho?: number; color?: string}> = ({largo = 30, ancho = 4, color = '#3E4250', ...p}) => (
|
|
198
|
+
<group {...grupo(p)}>
|
|
199
|
+
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.01, -largo / 2 + 4]} receiveShadow>
|
|
200
|
+
<planeGeometry args={[ancho, largo]} />
|
|
201
|
+
<Mate c={color} />
|
|
202
|
+
</mesh>
|
|
203
|
+
{Array.from({length: Math.floor(largo / 2.2)}, (_, i) => (
|
|
204
|
+
<mesh key={i} rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.02, 3 - i * 2.2]}>
|
|
205
|
+
<planeGeometry args={[0.14, 1.1]} />
|
|
206
|
+
<Mate c="#F2D04A" />
|
|
207
|
+
</mesh>
|
|
208
|
+
))}
|
|
209
|
+
{[-1, 1].map((l) => (
|
|
210
|
+
<mesh key={l} position={[l * (ancho / 2 + 0.5), 0.08, -largo / 2 + 4]} receiveShadow castShadow>
|
|
211
|
+
<boxGeometry args={[1, 0.16, largo]} />
|
|
212
|
+
<Mate c="#C9C4BA" />
|
|
213
|
+
</mesh>
|
|
214
|
+
))}
|
|
215
|
+
</group>
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
/** Cuarto (interior abierto hacia la cámara): piso, dos paredes, ventana y lámpara de techo. */
|
|
219
|
+
export const Cuarto3D: React.FC<Pieza & {ancho?: number; fondo?: number; alto?: number; pared?: string; piso?: string; noche?: boolean}> = ({ancho = 10, fondo = 8, alto = 4.5, pared = '#2E5A4C', piso = '#7A4A2A', noche = true, ...p}) => (
|
|
220
|
+
<group {...grupo(p)}>
|
|
221
|
+
<mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
|
|
222
|
+
<planeGeometry args={[ancho, fondo]} />
|
|
223
|
+
<Mate c={piso} />
|
|
224
|
+
</mesh>
|
|
225
|
+
<mesh position={[0, alto / 2, -fondo / 2]} receiveShadow>
|
|
226
|
+
<boxGeometry args={[ancho, alto, 0.2]} />
|
|
227
|
+
<Mate c={pared} />
|
|
228
|
+
</mesh>
|
|
229
|
+
<mesh position={[-ancho / 2, alto / 2, 0]} receiveShadow>
|
|
230
|
+
<boxGeometry args={[0.2, alto, fondo]} />
|
|
231
|
+
<Mate c={new THREE.Color(pared).multiplyScalar(0.85).getStyle()} />
|
|
232
|
+
</mesh>
|
|
233
|
+
<group position={[ancho * 0.2, alto * 0.55, -fondo / 2 + 0.12]}>
|
|
234
|
+
<mesh>
|
|
235
|
+
<boxGeometry args={[1.8, 1.5, 0.05]} />
|
|
236
|
+
<Mate c="#E9E1D2" />
|
|
237
|
+
</mesh>
|
|
238
|
+
<mesh position={[0, 0, 0.03]}>
|
|
239
|
+
<boxGeometry args={[1.6, 1.3, 0.02]} />
|
|
240
|
+
<Mate c={noche ? '#1A2350' : '#9FD4F5'} e={noche ? 0.1 : 0.3} />
|
|
241
|
+
</mesh>
|
|
242
|
+
</group>
|
|
243
|
+
<mesh position={[0, alto - 0.1, 0]}>
|
|
244
|
+
<cylinderGeometry args={[0.35, 0.5, 0.25, 16]} />
|
|
245
|
+
<Mate c="#F2D49B" e={0.5} />
|
|
246
|
+
</mesh>
|
|
247
|
+
<pointLight position={[0, alto - 0.5, 0]} intensity={noche ? 6 : 2} distance={12} color="#FFD9A0" castShadow />
|
|
248
|
+
</group>
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
export type {V3};
|