morfeo 0.6.4 → 0.6.6
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/index.d.ts +4 -1
- package/lib/config.mjs +4 -1
- package/lib/diccionario.mjs +86 -9
- package/lib/recursos.mjs +96 -3
- package/package.json +1 -1
- package/remotion/motor/modelo3d.tsx +13 -2
- package/remotion/motor/objetos-ciencia.tsx +777 -0
- package/remotion/motor/objetos-ingenieria.tsx +620 -0
- package/remotion/motor/objetos-varios.tsx +789 -0
- package/remotion/motor/objetos3d.tsx +12 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ la voz, la música, los efectos de sonido, los subtítulos, las animaciones y la
|
|
|
21
21
|
|
|
22
22
|
- **Voces:** Kokoro (gratis, en tu computadora, más de 50 voces y mezclas), ElevenLabs, OpenAI, tu propia
|
|
23
23
|
grabación por escena o cualquier servicio que conectes con una función.
|
|
24
|
-
- **Objetos 2D y 3D:** una biblioteca con diccionario temático en español (`{tipo: 'recurso', buscar: 'tubería', estilo: '3d'}`) que busca en Kenney, Poly Haven e Iconify (miles de modelos e ilustraciones libres), más objetos 3D y piezas de construcción hechos en código (tuberías con agua, muros, vigas…) y escenarios ilustrados animados. Los emojis quedan como último recurso.
|
|
24
|
+
- **Objetos 2D y 3D:** una biblioteca con diccionario temático en español (`{tipo: 'recurso', buscar: 'tubería', estilo: '3d'}`) que busca en Kenney, Quaternius, KayKit, Poly Haven e Iconify (miles de modelos e ilustraciones libres), más objetos 3D y piezas de construcción hechos en código (tuberías con agua, muros, vigas…) y escenarios ilustrados animados. Los emojis quedan como último recurso.
|
|
25
25
|
- **Música:** 7 estilos generados sin derechos de autor, o tu propia pista (mp3, wav, flac, ogg). Baja sola cuando habla la voz.
|
|
26
26
|
- **Guiones:** los escribes tú, o Claude escribe el video completo a partir de un tema o de tu texto.
|
|
27
27
|
- **Subtítulos:** palabra por palabra; con Whisper quedan sincronizados con la voz real.
|
package/index.d.ts
CHANGED
|
@@ -27,7 +27,10 @@ interface EscenaBase {
|
|
|
27
27
|
export type Estilo = 'datos-curiosos' | 'explicativo' | 'documental' | 'ilustrado' | 'historia' | 'mesa' | 'serie' | 'infografia' | '3d' | 'maqueta' | 'minimal' | (string & {});
|
|
28
28
|
|
|
29
29
|
/** Objetos 3D incluidos para capas {tipo: 'modelo', forma}: mejor opción que un emoji. */
|
|
30
|
-
export type Objeto3D = 'corazon' | 'estrella' | 'rayo' | 'diamante' | 'trofeo' | 'cohete' | 'foco' | 'reloj' | 'planeta' | 'arbol' | 'nube' | 'regalo' | 'llave' | 'candado' | 'casa' | 'libro' | 'globo' | 'medalla' | 'escudo' | 'pin' | 'tuberia' | 'muro' | 'viga' | 'columna' | 'escalera' | 'losa' | 'tanque' | 'suelo'
|
|
30
|
+
export type Objeto3D = 'corazon' | 'estrella' | 'rayo' | 'diamante' | 'trofeo' | 'cohete' | 'foco' | 'reloj' | 'planeta' | 'arbol' | 'nube' | 'regalo' | 'llave' | 'candado' | 'casa' | 'libro' | 'globo' | 'medalla' | 'escudo' | 'pin' | 'tuberia' | 'muro' | 'viga' | 'columna' | 'escalera' | 'losa' | 'tanque' | 'suelo'
|
|
31
|
+
| 'valvula' | 'grifo' | 'tinaco' | 'calentador' | 'bomba' | 'alcantarilla' | 'codo' | 'interruptor' | 'enchufe' | 'tablero' | 'medidor' | 'transformador' | 'poste' | 'bateria' | 'turbina' | 'torre_electrica' | 'aire' | 'chip' | 'placa' | 'resistor' | 'capacitor' | 'led' | 'disco_duro' | 'tarjeta_grafica' | 'disipador' | 'ram' | 'router' | 'servidor' | 'fuente_poder' | 'engrane' | 'tornillo' | 'tuerca' | 'motor' | 'resorte' | 'piston'
|
|
32
|
+
| 'atomo' | 'molecula' | 'adn' | 'celula' | 'virus' | 'cerebro' | 'neurona' | 'pulmon' | 'ojo' | 'diente' | 'hueso' | 'jeringa' | 'matraz' | 'telescopio' | 'iman' | 'sol' | 'tierra' | 'volcan' | 'tornado' | 'iceberg' | 'agujero_negro' | 'disco' | 'usb' | 'tarjeta_sd' | 'cable_hdmi' | 'microfono' | 'visor_vr' | 'cargador' | 'dron' | 'tanque_gas' | 'calentador_solar' | 'termostato' | 'detector_humo' | 'camara_seguridad' | 'antena_celular' | 'presa' | 'torre_enfriamiento' | 'ascensor' | 'fusible' | 'fibra_optica' | 'buzon' | 'autobus' | 'helicoptero' | 'submarino' | 'bicicleta' | 'lapiz' | 'regla' | 'dado' | 'ancla' | 'paraguas'
|
|
33
|
+
| 'guitarra' | 'piano' | 'tambor' | 'trompeta' | 'zapato' | 'camisa' | 'pulpo' | 'medusa' | 'tortuga' | 'pajaro' | 'buho' | 'moto' | 'pesa' | 'caminadora' | 'raqueta' | 'silbato' | 'golf' | 'tijeras' | 'mapa' | 'sobre' | 'tetera' | 'periodico' | 'pasaporte' | 'ropero' | 'tarjeta' | 'alcancia' | 'grafica' | 'calculadora' | 'pluma' | 'diploma' | 'birrete' | 'estetoscopio' | 'gota' | 'ramen' | 'planta_tratamiento' | 'refineria' | 'pozo_petrolero' | 'subestacion' | 'fosa_septica' | 'tubo_pvc' | 'granero' | 'parada' | 'hospital' | 'galaxia' | 'cometa' | 'lluvia' | 'fosil' | 'wifi' | 'huella' | 'telarana' | 'arbol_navidad';
|
|
31
34
|
|
|
32
35
|
/** Momento: segundos desde que empieza la escena, una palabra de `say` ("orcas"), "voz", "fin", o "palabra+0.3". */
|
|
33
36
|
export type Momento = number | string;
|
package/lib/config.mjs
CHANGED
|
@@ -30,7 +30,10 @@ export const ESTILOS = ['datos-curiosos', 'explicativo', 'documental', 'ilustrad
|
|
|
30
30
|
export const TIPOS_CAPA = ['imagen', 'video', 'texto', 'contador', 'chip', 'etiqueta', 'numero', 'tarjeta', 'telefono', 'forma', 'icono', 'emoji', 'marca', 'particulas', 'grupo', 'modelo', 'componente', 'ambiente', 'barra', 'medidor', 'votacion', 'estado', 'noticia', 'chat', 'onda', 'globo', 'seguir', 'personaje', 'escena3d', 'ventana', 'escenario', 'recurso', 'dato', 'medida', 'trazo', 'contorno', 'fuente', 'seccion', 'insignia', 'sello', 'cursor', 'terminal', 'nombre', 'accion', 'reloj', 'cronometro', 'termometro', 'recipientes', 'ficha', 'checklist', 'mapa', 'bloqueo', 'llamada', 'recuento', 'capitulo', 'vigilancia', 'cintillo', 'fecha', 'calendario', 'dividida', 'tierra', 'linea'];
|
|
31
31
|
export const AMBIENTES = ['cuarto-gamer', 'sala', 'arcade', 'taller', 'oficina', 'tienda', 'calle'];
|
|
32
32
|
export const ESCENARIOS = ['bosque', 'cuarto', 'departamento', 'tunel', 'pasillo'];
|
|
33
|
-
export const FORMAS_3D = ['moneda', 'billete', 'lingote', 'cubo', 'esfera', 'corazon', 'estrella', 'rayo', 'diamante', 'trofeo', 'cohete', 'foco', 'reloj', 'planeta', 'arbol', 'nube', 'regalo', 'llave', 'candado', 'casa', 'libro', 'globo', 'medalla', 'escudo', 'pin', 'tuberia', 'muro', 'viga', 'columna', 'escalera', 'losa', 'tanque', 'suelo'
|
|
33
|
+
export const FORMAS_3D = ['moneda', 'billete', 'lingote', 'cubo', 'esfera', 'corazon', 'estrella', 'rayo', 'diamante', 'trofeo', 'cohete', 'foco', 'reloj', 'planeta', 'arbol', 'nube', 'regalo', 'llave', 'candado', 'casa', 'libro', 'globo', 'medalla', 'escudo', 'pin', 'tuberia', 'muro', 'viga', 'columna', 'escalera', 'losa', 'tanque', 'suelo',
|
|
34
|
+
'valvula', 'grifo', 'tinaco', 'calentador', 'bomba', 'alcantarilla', 'codo', 'interruptor', 'enchufe', 'tablero', 'medidor', 'transformador', 'poste', 'bateria', 'turbina', 'torre_electrica', 'aire', 'chip', 'placa', 'resistor', 'capacitor', 'led', 'disco_duro', 'tarjeta_grafica', 'disipador', 'ram', 'router', 'servidor', 'fuente_poder', 'engrane', 'tornillo', 'tuerca', 'motor', 'resorte', 'piston',
|
|
35
|
+
'atomo', 'molecula', 'adn', 'celula', 'virus', 'cerebro', 'neurona', 'pulmon', 'ojo', 'diente', 'hueso', 'jeringa', 'matraz', 'telescopio', 'iman', 'sol', 'tierra', 'volcan', 'tornado', 'iceberg', 'agujero_negro', 'disco', 'usb', 'tarjeta_sd', 'cable_hdmi', 'microfono', 'visor_vr', 'cargador', 'dron', 'tanque_gas', 'calentador_solar', 'termostato', 'detector_humo', 'camara_seguridad', 'antena_celular', 'presa', 'torre_enfriamiento', 'ascensor', 'fusible', 'fibra_optica', 'buzon', 'autobus', 'helicoptero', 'submarino', 'bicicleta', 'lapiz', 'regla', 'dado', 'ancla', 'paraguas',
|
|
36
|
+
'guitarra', 'piano', 'tambor', 'trompeta', 'zapato', 'camisa', 'pulpo', 'medusa', 'tortuga', 'pajaro', 'buho', 'moto', 'pesa', 'caminadora', 'raqueta', 'silbato', 'golf', 'tijeras', 'mapa', 'sobre', 'tetera', 'periodico', 'pasaporte', 'ropero', 'tarjeta', 'alcancia', 'grafica', 'calculadora', 'pluma', 'diploma', 'birrete', 'estetoscopio', 'gota', 'ramen', 'planta_tratamiento', 'refineria', 'pozo_petrolero', 'subestacion', 'fosa_septica', 'tubo_pvc', 'granero', 'parada', 'hospital', 'galaxia', 'cometa', 'lluvia', 'fosil', 'wifi', 'huella', 'telarana', 'arbol_navidad'];
|
|
34
37
|
export const PROVEEDORES_VOZ = ['kokoro', 'elevenlabs', 'openai'];
|
|
35
38
|
export const MUSICAS = ['lofi', 'lofi2', 'chillpop', 'synth', 'house', 'ballad', 'trap'];
|
|
36
39
|
|
package/lib/diccionario.mjs
CHANGED
|
@@ -13,14 +13,14 @@ export const TEMAS = {
|
|
|
13
13
|
vela_aromatica: ['candle'], jarron: ['vase'], maceta: ['pot', 'plant pot'], tetera: ['teapot'], olla: ['pot', 'cooking pot'], sarten: ['pan', 'frying pan'],
|
|
14
14
|
cuchara: ['spoon'], tenedor: ['fork'], frasco: ['jar'], lata: ['can'], extintor: ['fire extinguisher'], botiquin: ['first aid kit'], escalera_mano: ['ladder'],
|
|
15
15
|
silla_ruedas: ['wheelchair'], muleta: ['crutch'], carrito: ['cart', 'shopping cart'], tienda_campana: ['tent'], fogata: ['campfire'], hacha: ['axe'],
|
|
16
|
-
arco: ['bow'], flecha: ['arrow'], bote_basura: ['trash can', '
|
|
16
|
+
arco: ['bow'], flecha: ['arrow'], bote_basura: ['trashcan', 'dumpster', 'trash can'], bote_de_basura: ['trashcan', 'dumpster'], periodico: ['newspaper'], revista: ['magazine'], boleto: ['ticket'], pasaporte: ['passport'],
|
|
17
17
|
},
|
|
18
18
|
hogar: {
|
|
19
19
|
casa: ['house', 'home'], cuarto: ['bedroom', 'room'], cama: ['bed'], sillon: ['sofa', 'couch', 'armchair'], sofa: ['sofa', 'couch'],
|
|
20
20
|
silla: ['chair'], mesa: ['table'], escritorio: ['desk'], lampara: ['lamp'], librero: ['bookcase', 'shelf'], refrigerador: ['fridge', 'refrigerator'],
|
|
21
21
|
estufa: ['stove', 'oven'], horno: ['oven'], lavabo: ['sink', 'bathroom sink'], fregadero: ['kitchen sink', 'sink'], inodoro: ['toilet'], banera: ['bathtub'],
|
|
22
22
|
regadera: ['shower'], espejo: ['mirror'], puerta: ['door'], ventana: ['window'], televisor: ['television', 'tv', 'monitor'], television: ['television', 'tv', 'monitor'], tele: ['television', 'tv', 'monitor'], tv: ['tv', 'television', 'monitor'], planta: ['plant', 'potted plant'],
|
|
23
|
-
alfombra: ['rug'], ropero: ['wardrobe', 'closet'], buro: ['nightstand'], cajon: ['drawer', 'cabinet'], lavadora: ['washer', 'washing machine'], microondas: ['microwave'], licuadora: ['blender'], ventilador: ['fan'], almohada: ['pillow'], cortina: ['curtain'], reloj: ['clock'], llave: ['key'], candado: ['padlock', 'lock'], cocina: ['kitchen'], bano: ['bathroom'],
|
|
23
|
+
alfombra: ['rug', 'carpet'], chimenea: ['fireplace'], ropero: ['wardrobe', 'closet'], buro: ['nightstand'], cajon: ['drawer', 'cabinet'], lavadora: ['washer', 'washing machine'], microondas: ['microwave'], licuadora: ['blender'], ventilador: ['fan'], ventilador_de_techo: ['ceiling fan'], generador: ['generator'], bocina: ['speaker'], boton: ['button'], satelite_antena: ['satellite dish'], almohada: ['pillow'], cortina: ['curtain'], reloj: ['clock'], llave: ['key'], candado: ['padlock', 'lock'], cocina: ['kitchen'], bano: ['bathroom'],
|
|
24
24
|
},
|
|
25
25
|
construccion: {
|
|
26
26
|
tuberia: ['pipe', 'pipes'], tubo: ['pipe'], valvula: ['valve'], llave_de_agua: ['faucet', 'tap'], drenaje: ['drain', 'sewer'], manguera: ['hose'],
|
|
@@ -29,17 +29,19 @@ export const TEMAS = {
|
|
|
29
29
|
martillo: ['hammer'], taladro: ['drill'], llave_inglesa: ['wrench'], desarmador: ['screwdriver'], sierra: ['saw'], pala: ['shovel'], carretilla: ['wheelbarrow'],
|
|
30
30
|
cono: ['traffic cone', 'cone'], barrera: ['barrier'], tunel: ['tunnel'], puente: ['bridge'], tanque: ['tank'], cable: ['cable', 'wire'], tornillo: ['screw', 'bolt'],
|
|
31
31
|
molino: ['windmill', 'building mill', 'mill'], granja: ['farm', 'barn'], granero: ['barn'], pozo: ['well'], muelle: ['dock', 'pier'], mina: ['mine'], castillo_piedra: ['castle'],
|
|
32
|
+
fabrica: ['factory machine', 'machine', 'factory'], maquina: ['machine'], banda_transportadora: ['conveyor'], brazo_robotico: ['robot arm'], palanca: ['lever'], tolva: ['hopper'], escaner: ['scanner'], pasarela: ['catwalk'],
|
|
33
|
+
chimenea_industrial: ['chimney'], canaleta: ['gutter'], contenedor: ['container', 'cargo container'], carguero: ['ship cargo'], boya: ['buoy'], via: ['railroad'], riel: ['railroad', 'rail'], vagon: ['train carriage', 'wagon'], locomotora: ['locomotive', 'train'],
|
|
32
34
|
herramienta: ['tool', 'toolbox'], caja_de_herramientas: ['toolbox'], valla: ['fence'], cerca: ['fence'],
|
|
33
35
|
},
|
|
34
36
|
ciudad: {
|
|
35
37
|
edificio: ['building', 'skyscraper'], rascacielos: ['skyscraper'], tienda: ['shop', 'store'], calle: ['road', 'street'], semaforo: ['traffic light'],
|
|
36
|
-
farola: ['street light', 'lamp post'], banca: ['bench'], parada: ['bus stop'], hidrante: ['fire hydrant', 'hydrant'], basura: ['
|
|
38
|
+
farola: ['street light', 'lamp post'], banca: ['bench'], parada: ['bus stop'], hidrante: ['fire hydrant', 'hydrant'], basura: ['trashcan', 'dumpster', 'garbage'], camion_de_basura: ['garbage truck'],
|
|
37
39
|
buzon: ['mailbox'], hospital_edificio: ['hospital'], escuela_edificio: ['school'], iglesia: ['church'], mercado: ['market'], torre: ['tower'], fuente: ['fountain'], letrero: ['sign', 'billboard'], estacion: ['station'], metro: ['subway', 'train'],
|
|
38
40
|
},
|
|
39
41
|
transporte: {
|
|
40
42
|
auto: ['car'], carro: ['car'], coche: ['car'], camion: ['truck'], autobus: ['bus'], ambulancia: ['ambulance'], patrulla: ['police car', 'police'],
|
|
41
43
|
bomberos: ['firetruck', 'fire truck'], taxi: ['taxi'], moto: ['motorcycle', 'motorbike'], bicicleta: ['bicycle', 'bike'], tren: ['train'], avion: ['airplane', 'plane'],
|
|
42
|
-
barco: ['boat', 'ship'], cohete: ['rocket'], helicoptero: ['helicopter'], submarino: ['submarine'],
|
|
44
|
+
barco: ['boat', 'ship'], velero: ['sail ship', 'boat'], crucero: ['cruise ship'], balsa: ['raft'], bote: ['boat'], cohete: ['rocket'], helicoptero: ['helicopter'], submarino: ['submarine'],
|
|
43
45
|
},
|
|
44
46
|
naturaleza: {
|
|
45
47
|
arbol: ['tree'], pino: ['pine tree', 'pine'], flor: ['flower'], pasto: ['grass'], roca: ['rock', 'stone'], piedra: ['stone', 'rock'], montana: ['mountain'],
|
|
@@ -51,9 +53,11 @@ export const TEMAS = {
|
|
|
51
53
|
concha: ['shell', 'seashell'], coral: ['coral'], ancla: ['anchor'], faro: ['lighthouse'], burbuja: ['bubble'],
|
|
52
54
|
},
|
|
53
55
|
animales: {
|
|
54
|
-
perro: ['dog'], gato: ['cat'], oso: ['bear'], leon: ['lion'], tigre: ['tiger'], elefante: ['elephant'], caballo: ['horse'], vaca: ['cow'], cerdo: ['pig'],
|
|
56
|
+
perro: ['husky', 'shiba', 'dog'], gato: ['cat'], oso: ['bear'], leon: ['lion'], tigre: ['tiger'], elefante: ['elephant'], caballo: ['horse'], vaca: ['cow'], cerdo: ['pig'],
|
|
55
57
|
oveja: ['sheep'], gallina: ['chicken'], pato: ['duck'], conejo: ['rabbit', 'bunny'], raton: ['mouse'], mono: ['monkey'], pajaro: ['bird'], buho: ['owl'],
|
|
56
|
-
serpiente: ['snake'], arana: ['spider'], abeja: ['bee'], mariposa: ['butterfly'], dinosaurio: ['dinosaur'], zorro: ['fox'], lobo: ['wolf'], pinguino: ['penguin'],
|
|
58
|
+
serpiente: ['snake'], arana: ['spider'], abeja: ['bee'], mariposa: ['butterfly'], dinosaurio: ['trex', 'dinosaur'], zorro: ['fox'], lobo: ['wolf'], pinguino: ['penguin'],
|
|
59
|
+
rana: ['frog'], rata: ['rat', 'mouse'], avispa: ['wasp', 'bee'], venado: ['deer', 'stag'], burro: ['donkey'], toro: ['bull'], alpaca: ['alpaca'], llama: ['alpaca'],
|
|
60
|
+
tiranosaurio: ['trex'], triceratops: ['triceratops'], velociraptor: ['velociraptor'], pirana: ['piranha'], pez_payaso: ['clownfish'], pez_globo: ['puffer'], pez_espada: ['swordfish'], atun: ['tuna'],
|
|
57
61
|
},
|
|
58
62
|
comida: {
|
|
59
63
|
manzana: ['apple'], platano: ['banana'], pizza: ['pizza'], hamburguesa: ['burger', 'hamburger'], papas: ['fries', 'french fries'], pan: ['bread'],
|
|
@@ -67,7 +71,7 @@ export const TEMAS = {
|
|
|
67
71
|
oro: ['gold', 'gold bar'], diamante: ['diamond', 'gem'], grafica: ['chart', 'graph'], cofre: ['chest', 'treasure chest'], tesoro: ['treasure'], caja_fuerte: ['safe'],
|
|
68
72
|
},
|
|
69
73
|
tecnologia: {
|
|
70
|
-
computadora: ['computer', 'laptop'], laptop: ['laptop'], celular: ['phone', 'smartphone'], telefono: ['phone'], robot: ['robot'], chip: ['chip', 'microchip'],
|
|
74
|
+
computadora: ['computer', 'laptop'], laptop: ['laptop'], celular: ['phone', 'smartphone'], telefono: ['phone'], robot: ['robot', 'mech'], radio: ['radio'], chip: ['chip', 'microchip'],
|
|
71
75
|
bateria: ['battery'], enchufe: ['plug', 'socket'], foco: ['light bulb', 'bulb'], camara: ['camera'], audifonos: ['headphones'], microfono: ['microphone'],
|
|
72
76
|
control: ['gamepad', 'controller'], consola: ['game console', 'console', 'gamepad'], consola_portatil: ['handheld console', 'nintendo switch', 'tablet', 'gamepad'],
|
|
73
77
|
tablet: ['tablet'], monitor: ['monitor', 'screen'], pantalla: ['screen', 'monitor'], disco: ['disc', 'cd', 'compact disc'], cd: ['disc', 'cd', 'compact disc'], dvd: ['disc', 'dvd'], memoria_usb: ['usb', 'flash drive'], antena: ['antenna'], satelite: ['satellite'], dron: ['drone'], servidor: ['server'],
|
|
@@ -75,7 +79,7 @@ export const TEMAS = {
|
|
|
75
79
|
},
|
|
76
80
|
espacio: {
|
|
77
81
|
planeta: ['planet'], estrella: ['star'], astronauta: ['astronaut'], ovni: ['ufo'], galaxia: ['galaxy'], meteorito: ['meteor', 'asteroid'], telescopio: ['telescope'],
|
|
78
|
-
nave: ['spaceship', 'spacecraft'], tierra: ['earth', 'globe'], marte: ['mars'], cometa: ['comet'],
|
|
82
|
+
nave: ['spaceship', 'spacecraft'], base_espacial: ['base module', 'base'], panel_solar: ['solar panel'], modulo_lunar: ['lander'], domo: ['dome'], radar: ['radar'], tierra: ['earth', 'globe'], marte: ['mars'], cometa: ['comet'],
|
|
79
83
|
},
|
|
80
84
|
cuerpo: {
|
|
81
85
|
corazon: ['heart'], cerebro: ['brain'], pulmon: ['lungs'], hueso: ['bone'], craneo: ['skull'], diente: ['tooth'], ojo: ['eye'], mano: ['hand'], musculo: ['muscle'],
|
|
@@ -99,6 +103,9 @@ export const TEMAS = {
|
|
|
99
103
|
videojuego: ['video game', 'gamepad'], dado: ['dice'], carta: ['playing card', 'card'], ajedrez: ['chess'], moneda_juego: ['coin'], corona: ['crown'], espada: ['sword'],
|
|
100
104
|
escudo: ['shield'], cofre_juego: ['chest'], gema: ['gem', 'crystal'], pocion: ['potion'], bandera: ['flag'], meta: ['finish line', 'flag'],
|
|
101
105
|
cartucho: ['game cartridge', 'cartridge', 'sd card', 'floppy disk'], joystick: ['joystick', 'gamepad'], arcade: ['arcade machine', 'arcade', 'gamepad'], jefe_final: ['monster', 'dragon', 'skull'],
|
|
106
|
+
caballero: ['knight'], esqueleto: ['skeleton'], mago: ['mage', 'wizard'], barbaro: ['barbarian'], enemigo: ['enemy', 'monster'], mecha: ['mech', 'robot'], torreta: ['turret'], pistola: ['pistol', 'gun'], escopeta: ['shotgun'],
|
|
107
|
+
armadura: ['armor'], daga: ['dagger'], baston_magico: ['staff', 'wand'], varita: ['wand'], ballesta: ['crossbow'], pergamino: ['parchment', 'scroll'], caliz: ['chalice'], collar: ['necklace'], lingote: ['gold ingots', 'ingot'],
|
|
108
|
+
catapulta: ['catapult'], taberna: ['tavern'], herreria: ['blacksmith'], estandarte: ['banner'], antorcha: ['torch'], botin: ['lootbox', 'chest'],
|
|
102
109
|
vida: ['heart', 'life'], nivel: ['level', 'block', 'platform'], bloque: ['block', 'cube', 'brick'], llave_juego: ['key'], hongo: ['mushroom'], estrella_juego: ['star'],
|
|
103
110
|
},
|
|
104
111
|
fiestas: {
|
|
@@ -128,5 +135,75 @@ export function traducir(termino) {
|
|
|
128
135
|
return {tema: null, termino: k, buscar: [String(termino).trim()]};
|
|
129
136
|
}
|
|
130
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Piezas de ingeniería hechas en código (remotion/motor/objetos-ingenieria.tsx): forma → términos que la piden.
|
|
140
|
+
* No están en ninguna biblioteca libre, así que el buscador las revisa antes que a las bibliotecas.
|
|
141
|
+
*/
|
|
142
|
+
export const PIEZAS = {
|
|
143
|
+
valvula: ['valvula', 'llave_de_paso', 'valvula_de_agua'], grifo: ['grifo', 'llave_de_agua', 'llave_del_agua'], tinaco: ['tinaco', 'tanque_de_agua'],
|
|
144
|
+
calentador: ['calentador', 'boiler', 'caldera', 'calentador_de_agua'], bomba: ['bomba_de_agua', 'bomba_hidraulica'], alcantarilla: ['alcantarilla', 'coladera', 'tapa_de_drenaje'],
|
|
145
|
+
codo: ['codo', 'codo_de_tuberia'], interruptor: ['interruptor', 'apagador', 'switch'], enchufe: ['enchufe', 'contacto', 'tomacorriente'],
|
|
146
|
+
tablero: ['tablero_electrico', 'centro_de_carga', 'caja_de_fusibles', 'pastillas', 'breaker'], medidor: ['medidor', 'medidor_de_luz'], transformador: ['transformador'],
|
|
147
|
+
poste: ['poste_de_luz', 'poste'], bateria: ['bateria', 'pila', 'pila_aa'], turbina: ['turbina', 'aerogenerador', 'turbina_eolica'], torre_electrica: ['torre_electrica', 'torre_de_alta_tension'],
|
|
148
|
+
aire: ['aire_acondicionado', 'minisplit', 'clima'], chip: ['chip', 'microchip', 'procesador', 'cpu'], placa: ['placa_madre', 'tarjeta_madre', 'placa_base', 'motherboard', 'placa'],
|
|
149
|
+
resistor: ['resistor', 'resistencia'], capacitor: ['capacitor', 'condensador'], led: ['led'], disco_duro: ['disco_duro', 'hdd'],
|
|
150
|
+
tarjeta_grafica: ['tarjeta_grafica', 'tarjeta_de_video', 'gpu'], disipador: ['disipador', 'ventilador_de_pc', 'cooler'], ram: ['ram', 'memoria_ram'], router: ['router', 'modem'],
|
|
151
|
+
servidor: ['servidor', 'rack'], fuente_poder: ['fuente_de_poder', 'fuente_de_alimentacion'], engrane: ['engrane', 'engranaje'], tornillo: ['tornillo'], tuerca: ['tuerca'],
|
|
152
|
+
motor: ['motor', 'motor_electrico'], resorte: ['resorte'], piston: ['piston'],
|
|
153
|
+
// ciencia y cuerpo
|
|
154
|
+
atomo: ['atomo'], molecula: ['molecula', 'molecula_de_agua'], adn: ['adn', 'dna', 'genes'], celula: ['celula'], virus: ['virus', 'bacteria', 'microbio'], cerebro: ['cerebro'],
|
|
155
|
+
neurona: ['neurona'], pulmon: ['pulmon', 'pulmones'], ojo: ['ojo'], diente: ['diente', 'muela'], hueso: ['hueso'], jeringa: ['jeringa', 'vacuna', 'inyeccion'],
|
|
156
|
+
matraz: ['matraz', 'quimica', 'tubo_de_ensayo'], telescopio: ['telescopio'], iman: ['iman'], sol: ['sol'], tierra: ['tierra', 'globo_terraqueo', 'mundo', 'planeta_tierra'],
|
|
157
|
+
volcan: ['volcan'], tornado: ['tornado', 'huracan'], iceberg: ['iceberg'], agujero_negro: ['agujero_negro'],
|
|
158
|
+
// consola y tecnología
|
|
159
|
+
disco: ['disco', 'cd', 'dvd', 'disco_de_juego', 'blu_ray'], usb: ['usb', 'memoria_usb'], tarjeta_sd: ['tarjeta_sd', 'memoria_sd', 'tarjeta_de_memoria'], cable_hdmi: ['cable_hdmi', 'hdmi'],
|
|
160
|
+
microfono: ['microfono'], visor_vr: ['visor_vr', 'realidad_virtual', 'lentes_vr', 'vr'], cargador: ['cargador'], dron: ['dron'],
|
|
161
|
+
// casa y ciudad
|
|
162
|
+
tanque_gas: ['tanque_de_gas', 'gas', 'tanque_estacionario'], calentador_solar: ['calentador_solar'], termostato: ['termostato'], detector_humo: ['detector_de_humo', 'alarma_de_humo'],
|
|
163
|
+
camara_seguridad: ['camara_de_seguridad', 'camara_de_vigilancia'], antena_celular: ['antena_celular', 'antena_de_telefonia', 'torre_celular'], presa: ['presa', 'represa'],
|
|
164
|
+
torre_enfriamiento: ['planta_nuclear', 'torre_de_enfriamiento', 'central_nuclear'], ascensor: ['ascensor', 'elevador'], fusible: ['fusible'], fibra_optica: ['fibra_optica'], buzon: ['buzon'],
|
|
165
|
+
// transporte
|
|
166
|
+
autobus: ['autobus', 'camion_de_pasajeros'], helicoptero: ['helicoptero'], submarino: ['submarino'], bicicleta: ['bicicleta', 'bici'],
|
|
167
|
+
// escuela y objetos
|
|
168
|
+
lapiz: ['lapiz'], regla: ['regla'], dado: ['dado'], ancla: ['ancla'], paraguas: ['paraguas', 'sombrilla'],
|
|
169
|
+
// música, ropa y animales
|
|
170
|
+
guitarra: ['guitarra'], piano: ['piano', 'teclado_musical'], tambor: ['tambor', 'bateria_musical'], trompeta: ['trompeta'], zapato: ['zapato', 'tenis', 'tenis_deportivo', 'calzado'],
|
|
171
|
+
camisa: ['camisa', 'playera', 'camiseta', 'ropa'], pulpo: ['pulpo'], medusa: ['medusa'], tortuga: ['tortuga'], pajaro: ['pajaro', 'ave'], buho: ['buho', 'lechuza'],
|
|
172
|
+
// transporte y deporte
|
|
173
|
+
moto: ['moto', 'motocicleta'], pesa: ['pesa', 'mancuerna', 'gimnasio'], caminadora: ['caminadora'], raqueta: ['raqueta', 'tenis_deporte'], silbato: ['silbato'], golf: ['golf', 'palo_de_golf'],
|
|
174
|
+
// oficina, escuela y casa
|
|
175
|
+
tijeras: ['tijeras', 'tijera'], mapa: ['mapa'], sobre: ['sobre', 'carta_correo', 'correo'], tetera: ['tetera'], periodico: ['periodico', 'noticias'], pasaporte: ['pasaporte'],
|
|
176
|
+
ropero: ['ropero', 'closet', 'armario'], tarjeta: ['tarjeta', 'tarjeta_de_credito', 'tarjeta_bancaria'], alcancia: ['alcancia', 'cochinito'], grafica: ['grafica', 'grafica_de_barras', 'estadistica'],
|
|
177
|
+
calculadora: ['calculadora'], pluma: ['pluma', 'boligrafo', 'lapicero'], diploma: ['diploma', 'titulo'], birrete: ['birrete', 'graduacion'], estetoscopio: ['estetoscopio'],
|
|
178
|
+
gota: ['gota', 'sangre', 'gota_de_sangre'], ramen: ['ramen', 'fideos'],
|
|
179
|
+
// industria y ciudad
|
|
180
|
+
planta_tratamiento: ['planta_de_tratamiento', 'planta_de_agua', 'planta_tratadora'], refineria: ['refineria'], pozo_petrolero: ['pozo_petrolero', 'petroleo', 'bomba_de_petroleo'],
|
|
181
|
+
subestacion: ['subestacion', 'subestacion_electrica'], fosa_septica: ['fosa_septica', 'fosa'], tubo_pvc: ['tubo_pvc', 'pvc', 'tubo_de_pvc'], granero: ['granero'],
|
|
182
|
+
parada: ['parada', 'parada_de_autobus', 'parada_de_camion'], hospital: ['hospital', 'hospital_edificio', 'clinica'],
|
|
183
|
+
// naturaleza, espacio y otros
|
|
184
|
+
galaxia: ['galaxia', 'via_lactea'], cometa: ['cometa'], lluvia: ['lluvia', 'tormenta'], fosil: ['fosil'], wifi: ['wifi', 'senal', 'internet'], huella: ['huella'],
|
|
185
|
+
telarana: ['telarana', 'arana_red'], arbol_navidad: ['arbol_navidad', 'arbol_de_navidad', 'navidad'],
|
|
186
|
+
};
|
|
187
|
+
const PIEZA_DE = new Map();
|
|
188
|
+
for (const [forma, terminos] of Object.entries(PIEZAS)) for (const t of terminos) PIEZA_DE.set(t, forma);
|
|
189
|
+
|
|
190
|
+
/** Formas básicas de Morfeo (objetos3d.tsx) por término: se usan solo si ninguna biblioteca tiene el objeto. */
|
|
191
|
+
const BASICAS = {corazon: ['corazon'], estrella: ['estrella'], rayo: ['rayo', 'electricidad'], diamante: ['diamante', 'gema'], trofeo: ['trofeo', 'copa'], cohete: ['cohete'], foco: ['foco', 'bombilla', 'foco_led'], reloj: ['reloj'],
|
|
192
|
+
planeta: ['planeta', 'marte', 'saturno'], arbol: ['arbol'], nube: ['nube'], regalo: ['regalo'], llave: ['llave'], candado: ['candado'], casa: ['casa'], libro: ['libro'], globo: ['globo'], medalla: ['medalla'], escudo: ['escudo'], tanque: ['cisterna']};
|
|
193
|
+
const BASICA_DE = new Map();
|
|
194
|
+
for (const [forma, terminos] of Object.entries(BASICAS)) for (const t of terminos) BASICA_DE.set(t, forma);
|
|
195
|
+
export function basicaDe(termino) {
|
|
196
|
+
const k = clave(termino);
|
|
197
|
+
for (const s of [k, k.replace(/es$/, ''), k.replace(/s$/, '')]) if (BASICA_DE.has(s)) return BASICA_DE.get(s);
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Forma hecha en código que corresponde a un término ("llave de agua" → "grifo"), o null. */
|
|
202
|
+
export function piezaDe(termino) {
|
|
203
|
+
const k = clave(termino);
|
|
204
|
+
for (const s of [k, k.replace(/es$/, ''), k.replace(/s$/, '')]) if (PIEZA_DE.has(s)) return PIEZA_DE.get(s);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
131
208
|
/** Lista de temas con sus términos (para `morfeo recursos temas` y para la IA). */
|
|
132
|
-
export const listaTemas = () => Object.fromEntries(Object.entries(TEMAS).map(([t, v]) => [t, Object.keys(v)]));
|
|
209
|
+
export const listaTemas = () => ({...Object.fromEntries(Object.entries(TEMAS).map(([t, v]) => [t, Object.keys(v)])), ingenieria: Object.values(PIEZAS).map((v) => v[0])});
|
package/lib/recursos.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import fs from 'node:fs';
|
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
import {ErrorMorfeo} from './config.mjs';
|
|
16
16
|
import {HOME} from './entorno.mjs';
|
|
17
|
-
import {traducir} from './diccionario.mjs';
|
|
17
|
+
import {traducir, piezaDe, basicaDe} from './diccionario.mjs';
|
|
18
18
|
|
|
19
19
|
export const BIBLIOTECA = path.join(HOME, 'biblioteca');
|
|
20
20
|
const UA = {'user-agent': 'morfeo (https://github.com/UreckChan/morfeo)'};
|
|
@@ -136,6 +136,82 @@ async function bajarKenney(r) {
|
|
|
136
136
|
return {archivos: [local], principal: local, credito: `${r.nombre} · Kenney (kenney.nl) · CC0`};
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// ---------------------------------------------------------------- Quaternius y KayKit (3D low-poly, CC0)
|
|
140
|
+
// Las dos viven en GitHub: el índice sale del árbol de cada repositorio y los archivos de raw.githubusercontent.
|
|
141
|
+
// Quaternius no tiene repositorio propio: se usa un espejo que conserva los paquetes y sus licencias (todas CC0).
|
|
142
|
+
const KAYKIT = ['Character-Pack-Adventures', 'Character-Pack-Skeletons', 'City-Builder-Bits', 'Dungeon-Remastered', 'Furniture-Bits', 'Halloween-Bits', 'Medieval-Hexagon-Pack', 'Prototype-Bits', 'Restaurant-Bits', 'Space-Base-Bits'];
|
|
143
|
+
const REPOS = {
|
|
144
|
+
quaternius: [{repo: 'trebeljahr/quaternius-showcase', rama: 'main', filtro: /^public\/glb\/.+\.glb$/i, kit: (ruta) => ruta.split('/')[2].replace(/_/g, ' ')}],
|
|
145
|
+
kaykit: KAYKIT.map((n) => ({repo: `KayKit-Game-Assets/KayKit-${n}-1.0`, rama: 'main', filtro: /\/gltf\/.+\.(gltf|glb)$/i, kit: () => n.replace(/-/g, ' ')})),
|
|
146
|
+
};
|
|
147
|
+
// Kenney completo (49 kits): el espejo de arriba solo trae 12. De este se toman los kits que aquel no tiene
|
|
148
|
+
// (fábrica, ciudad industrial, edificios, trenes, estación espacial, caminos…), para no duplicar resultados.
|
|
149
|
+
const KENNEY_YA = ['car', 'fantasy-town', 'food', 'furniture', 'graveyard', 'hexagon', 'holiday', 'nature', 'platformer', 'racing', 'minigolf', 'tower-defense'];
|
|
150
|
+
REPOS['kenney-mas'] = [{repo: 'shorepine/kenney', rama: 'main', filtro: new RegExp(`^3d/(?!(${KENNEY_YA.join('|')})/)[^/]+/[^/]+\\.glb$`, 'i'), kit: (ruta) => ruta.split('/')[1].replace(/-/g, ' ')}];
|
|
151
|
+
const CREDITO_REPO = {'kenney-mas': 'Kenney (kenney.nl)', quaternius: 'Quaternius (quaternius.com)', kaykit: 'KayKit · Kay Lousberg (kaylousberg.com)'};
|
|
152
|
+
|
|
153
|
+
const repoIndice = (fuente) =>
|
|
154
|
+
indice(fuente, async () => {
|
|
155
|
+
const out = [];
|
|
156
|
+
let fallos = 0;
|
|
157
|
+
for (const r of REPOS[fuente]) {
|
|
158
|
+
try {
|
|
159
|
+
const arbol = await pedirJSON(`https://api.github.com/repos/${r.repo}/git/trees/${r.rama}?recursive=1`);
|
|
160
|
+
for (const n of arbol.tree) if (r.filtro.test(n.path)) out.push({repo: r.repo, rama: r.rama, ruta: n.path, nombre: n.path.split('/').pop().replace(/(\.gltf)?\.(glb|gltf)$/i, '').replace(/-transformed$/, ''), kit: r.kit(n.path)});
|
|
161
|
+
} catch {
|
|
162
|
+
fallos++; // un repositorio que falla no tira a los demás
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!out.length) throw new ErrorMorfeo(`No pude leer el índice de ${fuente}`);
|
|
166
|
+
void fallos;
|
|
167
|
+
return out;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
async function buscarRepo(fuente, consultas) {
|
|
171
|
+
const idx = await repoIndice(fuente);
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const [i, q] of consultas.entries())
|
|
174
|
+
for (const m of idx) {
|
|
175
|
+
// "bed_double_A" / "Tree_03": la letra o el número de variante no es el sustantivo
|
|
176
|
+
const nombre = palabras(m.nombre).filter((w) => w.length > 1);
|
|
177
|
+
// el paquete también cuenta, con menos peso: "dinosaur" trae todo el paquete de dinosaurios aunque se llamen "Trex"
|
|
178
|
+
const p = Math.max(puntaje(nombre, q), puntaje([...nombre, ...palabras(m.kit).filter((w) => !['pack', 'kit', 'bits', 'ultimate', 'modular'].includes(w))], q) - 6);
|
|
179
|
+
if (p > 0) out.push({fuente, id: `${m.repo}/${m.ruta}`, repo: m.repo, rama: m.rama, ruta: m.ruta, nombre: m.nombre, coleccion: m.kit, licencia: 'CC0', puntaje: p - i * 3, tipo: 'modelo'});
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** El JSON de un .glb (primer bloque del archivo), o null si no se puede leer. */
|
|
185
|
+
function jsonDeGlb(archivo) {
|
|
186
|
+
try {
|
|
187
|
+
const b = fs.readFileSync(archivo);
|
|
188
|
+
if (b.readUInt32LE(0) !== 0x46546c67 || b.readUInt32LE(16) !== 0x4e4f534a) return null;
|
|
189
|
+
return JSON.parse(b.subarray(20, 20 + b.readUInt32LE(12)).toString('utf8'));
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function bajarRepo(r) {
|
|
196
|
+
const cruda = (ruta) => `https://raw.githubusercontent.com/${r.repo}/${r.rama}/${ruta.split('/').map(encodeURIComponent).join('/')}`;
|
|
197
|
+
const local = (ruta) => path.join(BIBLIOTECA, r.fuente, r.repo.split('/')[1], ruta.replace(/[^A-Za-z0-9._/-]/g, '_'));
|
|
198
|
+
const principal = await bajar(cruda(r.ruta), local(r.ruta));
|
|
199
|
+
const archivos = [principal];
|
|
200
|
+
// un .gltf trae su geometría (.bin) y sus texturas en archivos aparte, con rutas relativas; un .glb también
|
|
201
|
+
// puede apuntar a una textura de fuera (los de Kenney comparten "Textures/colormap.png" por kit)
|
|
202
|
+
const g = /\.gltf$/i.test(r.ruta) ? JSON.parse(fs.readFileSync(principal, 'utf8')) : jsonDeGlb(principal);
|
|
203
|
+
if (g) {
|
|
204
|
+
const dir = path.posix.dirname(r.ruta);
|
|
205
|
+
for (const x of [...(g.buffers ?? []), ...(g.images ?? [])]) {
|
|
206
|
+
if (!x.uri || x.uri.startsWith('data:')) continue;
|
|
207
|
+
const rel = path.posix.normalize(path.posix.join(dir, decodeURIComponent(x.uri)));
|
|
208
|
+
if (rel.startsWith('..')) continue;
|
|
209
|
+
archivos.push(await bajar(cruda(rel), local(rel)));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return {archivos, principal, credito: `${r.nombre} · ${r.coleccion} · ${CREDITO_REPO[r.fuente]} · CC0`};
|
|
213
|
+
}
|
|
214
|
+
|
|
139
215
|
// ---------------------------------------------------------------- Poly Haven (3D realista, CC0)
|
|
140
216
|
const polyhavenIndice = () => indice('polyhaven', () => pedirJSON('https://api.polyhaven.com/assets?t=models'));
|
|
141
217
|
|
|
@@ -211,7 +287,7 @@ async function bajarPolypizza(r) {
|
|
|
211
287
|
|
|
212
288
|
// ---------------------------------------------------------------- búsqueda y descarga
|
|
213
289
|
/** Orden de fuentes por estilo. */
|
|
214
|
-
const FUENTES = {'3d': ['kenney', 'polypizza', 'polyhaven'], realista: ['polyhaven', 'polypizza', 'kenney'], '2d': ['iconify']};
|
|
290
|
+
const FUENTES = {'3d': ['kenney', 'kenney-mas', 'quaternius', 'kaykit', 'polypizza', 'polyhaven'], realista: ['polyhaven', 'polypizza', 'kenney', 'kenney-mas', 'quaternius', 'kaykit'], '2d': ['iconify']};
|
|
215
291
|
|
|
216
292
|
/**
|
|
217
293
|
* Busca un término en las bibliotecas.
|
|
@@ -221,15 +297,25 @@ export async function buscarRecursos(termino, {estilo = '3d', variante, fuente}
|
|
|
221
297
|
const {buscar} = traducir(termino);
|
|
222
298
|
const fuentes = fuente ? [fuente] : FUENTES[estilo] ?? FUENTES['3d'];
|
|
223
299
|
const todos = [];
|
|
300
|
+
// piezas de ingeniería hechas en código (válvula, interruptor, placa madre…): van primero porque ninguna
|
|
301
|
+
// biblioteca las tiene y lo que devuelven en su lugar no sirve ("tornillo" → unas pinzas)
|
|
302
|
+
const pieza = estilo !== '2d' && (!fuente || fuente === 'morfeo') ? piezaDe(termino) : null;
|
|
303
|
+
if (pieza) todos.push({fuente: 'morfeo', id: pieza, nombre: pieza, coleccion: 'piezas de ingeniería', licencia: 'incluida', puntaje: 1000, tipo: 'forma'});
|
|
304
|
+
if (fuente === 'morfeo') return todos;
|
|
224
305
|
for (const f of fuentes) {
|
|
225
306
|
try {
|
|
226
|
-
const r = f === 'kenney' ? await buscarKenney(buscar) : f === 'polyhaven' ? await buscarPolyhaven(buscar) : f === 'polypizza' ? await buscarPolypizza(buscar) : await buscarIconify(buscar, variante);
|
|
307
|
+
const r = f === 'kenney' ? await buscarKenney(buscar) : REPOS[f] ? await buscarRepo(f, buscar) : f === 'polyhaven' ? await buscarPolyhaven(buscar) : f === 'polypizza' ? await buscarPolypizza(buscar) : await buscarIconify(buscar, variante);
|
|
227
308
|
// el orden de las fuentes pesa: la primera fuente con resultados buenos gana
|
|
228
309
|
todos.push(...r.map((x) => ({...x, puntaje: x.puntaje - fuentes.indexOf(f) * 6})));
|
|
229
310
|
} catch (e) {
|
|
230
311
|
if (fuente) throw e; // con fuente explícita, el error importa; si no, se prueba la siguiente
|
|
231
312
|
}
|
|
232
313
|
}
|
|
314
|
+
// formas básicas de siempre (corazón, foco, planeta…): solo si ninguna biblioteca tuvo el objeto
|
|
315
|
+
// (en `3d`, un modelo realista de Poly Haven no cuenta: un foco de vidrio oscuro no combina con lo demás)
|
|
316
|
+
const hayLowPoly = todos.some((r) => estilo === 'realista' || r.fuente !== 'polyhaven');
|
|
317
|
+
const basica = estilo !== '2d' && !fuente && !hayLowPoly ? basicaDe(termino) : null;
|
|
318
|
+
if (basica) todos.push({fuente: 'morfeo', id: basica, nombre: basica, coleccion: 'formas básicas', licencia: 'incluida', puntaje: 500, tipo: 'forma'});
|
|
233
319
|
const vistos = new Set();
|
|
234
320
|
return todos
|
|
235
321
|
.sort((a, b) => b.puntaje - a.puntaje)
|
|
@@ -239,6 +325,7 @@ export async function buscarRecursos(termino, {estilo = '3d', variante, fuente}
|
|
|
239
325
|
/** Descarga un resultado. Devuelve {principal, archivos, base?, credito}. */
|
|
240
326
|
export async function bajarRecurso(r, {color} = {}) {
|
|
241
327
|
if (r.fuente === 'kenney') return bajarKenney(r);
|
|
328
|
+
if (REPOS[r.fuente]) return bajarRepo(r);
|
|
242
329
|
if (r.fuente === 'polyhaven') return bajarPolyhaven(r);
|
|
243
330
|
if (r.fuente === 'polypizza') return bajarPolypizza(r);
|
|
244
331
|
return bajarIconify(r, color);
|
|
@@ -279,6 +366,12 @@ export async function resolverRecursos(video, assetsPublico, log = console.log)
|
|
|
279
366
|
return null;
|
|
280
367
|
}
|
|
281
368
|
const r = res[Math.min(n, res.length - 1)];
|
|
369
|
+
if (r.tipo === 'forma') {
|
|
370
|
+
log(` recurso "${buscar}" → ${r.nombre} (pieza de Morfeo)`);
|
|
371
|
+
// quietas y en tres cuartos: varias son planas (placa, chip, tapa) y girando quedaban de canto; las que
|
|
372
|
+
// tienen movimiento propio (engrane, ventiladores, turbina) se siguen moviendo
|
|
373
|
+
return {tipo: 'modelo', forma: r.id, ancho: 620, alto: 620, girar3d: [0, 0, 0], rotacion: [6, -28, 0], reflejos: 0.5, luzAmbiente: 0.42, ...resto};
|
|
374
|
+
}
|
|
282
375
|
const d = await bajarRecurso(r, {color});
|
|
283
376
|
creditos.add(d.credito);
|
|
284
377
|
// se copia a public/assets/biblioteca/<fuente>/... conservando la carpeta (los glTF de Poly Haven traen texturas aparte)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "morfeo",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "Genera videos verticales animados (Remotion + Three.js) con voz, música y subtítulos, desde un guion en código o escrito por IA.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -11,6 +11,7 @@ import React, {useEffect, useLayoutEffect, useMemo, useState} from 'react';
|
|
|
11
11
|
import {continueRender, delayRender} from 'remotion';
|
|
12
12
|
import * as THREE from 'three';
|
|
13
13
|
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
14
|
+
import {clone as clonarConEsqueleto} from 'three/examples/jsm/utils/SkeletonUtils.js';
|
|
14
15
|
import {RoomEnvironment} from 'three/examples/jsm/environments/RoomEnvironment.js';
|
|
15
16
|
import {clamp, easeOut} from '../estilo';
|
|
16
17
|
import {cuando, recurso, type CtxEscena} from './util';
|
|
@@ -30,7 +31,15 @@ const cargar = (url: string) => cargarGltf(url).then((g) => g.scene);
|
|
|
30
31
|
* hilos sueltos (el cable de un control, la cuerda de un globo), encuadra esa zona y no la caja completa.
|
|
31
32
|
*/
|
|
32
33
|
function cajaPrincipal(obj: THREE.Object3D): THREE.Box3 {
|
|
34
|
+
obj.updateMatrixWorld(true);
|
|
33
35
|
const completa = new THREE.Box3().setFromObject(obj);
|
|
36
|
+
// con esqueleto (animales, personajes), los vértices crudos están en la pose de reposo y a otra escala que la
|
|
37
|
+
// que se dibuja: la caja buena es la que calcula three con los huesos
|
|
38
|
+
let conHuesos = false;
|
|
39
|
+
obj.traverse((o) => {
|
|
40
|
+
if ((o as THREE.SkinnedMesh).isSkinnedMesh) conHuesos = true;
|
|
41
|
+
});
|
|
42
|
+
if (conHuesos) return completa;
|
|
34
43
|
const pts: number[] = [];
|
|
35
44
|
const v = new THREE.Vector3();
|
|
36
45
|
obj.updateMatrixWorld(true);
|
|
@@ -67,7 +76,9 @@ const Glb: React.FC<{src: string; levantar?: boolean}> = ({src, levantar}) => {
|
|
|
67
76
|
cargar(url)
|
|
68
77
|
.then((s) => {
|
|
69
78
|
// centrado y escalado a ~2 unidades para que cualquier modelo quepa en el lienzo
|
|
70
|
-
|
|
79
|
+
// clone() a secas deja la malla atada a los huesos del original: el modelo se dibujaba sin centrar ni escalar
|
|
80
|
+
const copia = clonarConEsqueleto(s) as THREE.Group;
|
|
81
|
+
copia.updateMatrixWorld(true);
|
|
71
82
|
const caja = levantar ? cajaPrincipal(copia) : new THREE.Box3().setFromObject(copia);
|
|
72
83
|
const tam = caja.getSize(new THREE.Vector3());
|
|
73
84
|
const centro = caja.getCenter(new THREE.Vector3());
|
|
@@ -244,7 +255,7 @@ const ModeloEscena: React.FC<{c: Record<string, any>; ctx: CtxEscena}> = ({c, ct
|
|
|
244
255
|
useEffect(() => {
|
|
245
256
|
cargarGltf(url)
|
|
246
257
|
.then((gl) => {
|
|
247
|
-
const scene = gl.scene.
|
|
258
|
+
const scene = clonarConEsqueleto(gl.scene) as THREE.Group;
|
|
248
259
|
let mixer: THREE.AnimationMixer | null = null;
|
|
249
260
|
if (c.animacion && gl.animations.length) {
|
|
250
261
|
mixer = new THREE.AnimationMixer(scene);
|