venkat_glb_creation / scripts /scene_parser.js
Antigravity AI
Initial HF commit
37ed7e4
Raw
History Blame Contribute Delete
14.8 kB
// Enhanced Natural Language Scene Parser
// Converts user descriptions into comprehensive structured scene data
export class SceneParser {
constructor() {
this.keywords = {
environments: {
forest: ['forest', 'woods', 'jungle', 'trees', 'woodland'],
beach: ['beach', 'shore', 'ocean', 'seaside', 'coast'],
desert: ['desert', 'sand', 'dunes', 'arid'],
city: ['city', 'urban', 'street', 'building', 'metropolis'],
lab: ['lab', 'laboratory', 'sci-fi', 'futuristic', 'science'],
room: ['room', 'indoor', 'interior', 'chamber'],
space: ['space', 'planet', 'alien', 'cosmic', 'astronomical'],
underwater: ['underwater', 'ocean floor', 'sea', 'aquatic'],
magical: ['magical', 'enchanted', 'mystical', 'fantasy']
},
lighting: {
day: ['day', 'daylight', 'sunny', 'bright'],
night: ['night', 'dark', 'moonlight', 'stars', 'nocturnal'],
sunset: ['sunset', 'dusk', 'evening', 'golden hour', 'twilight'],
sunrise: ['sunrise', 'dawn', 'morning', 'aurora'],
indoor: ['indoor', 'artificial', 'fluorescent', 'lamp'],
blue: ['blue light', 'blue ambient', 'cyan', 'azure'],
warm: ['warm', 'orange', 'amber', 'firelight']
},
objects: {
tree: ['tree', 'palm', 'oak', 'pine', 'cedar'],
mushroom: ['mushroom', 'fungus', 'toadstool', 'glowing mushroom'],
fire: ['fire', 'campfire', 'flame', 'bonfire', 'torch'],
rock: ['rock', 'stone', 'boulder', 'pebble'],
water: ['water', 'ocean', 'sea', 'lake', 'river', 'pond'],
building: ['building', 'house', 'structure', 'tower'],
vehicle: ['car', 'vehicle', 'truck', 'bike', 'automobile'],
firefly: ['firefly', 'fireflies', 'lightning bug', 'glowing bug'],
wizard: ['wizard', 'mage', 'sorcerer', 'magician'],
character: ['character', 'person', 'human', 'avatar', 'npc']
},
materials: {
metal: ['metal', 'metallic', 'steel', 'iron', 'bronze'],
wood: ['wood', 'wooden', 'timber', 'oak', 'pine'],
glass: ['glass', 'transparent', 'clear', 'crystal'],
fabric: ['fabric', 'cloth', 'textile', 'fabric'],
stone: ['stone', 'rock', 'concrete', 'marble', 'granite'],
glowing: ['glowing', 'emissive', 'luminous', 'radiant', 'shining'],
magical: ['magical', 'enchanted', 'mystical', 'ethereal']
},
effects: {
fog: ['fog', 'mist', 'haze', 'vapor', 'cloud'],
rain: ['rain', 'rainy', 'storm', 'precipitation'],
snow: ['snow', 'snowy', 'blizzard', 'flurry'],
bloom: ['bloom', 'glow', 'bright', 'halo'],
particles: ['particles', 'sparkles', 'dust', 'debris']
},
audio: {
ambient: ['ambient', 'background', 'music', 'soundtrack', 'atmosphere'],
spatial: ['spatial', 'positional', '3d sound', 'surround'],
nature: ['nature', 'birds', 'wind', 'water', 'forest sounds'],
magical: ['magical', 'mystical', 'enchanted', 'fantasy music']
},
animations: {
walk: ['walk', 'walking', 'stroll', 'pace'],
run: ['run', 'running', 'sprint'],
sit: ['sit', 'sitting', 'rest'],
stand: ['stand', 'standing', 'idle'],
fly: ['fly', 'flying', 'hover', 'float'],
glow: ['glow', 'pulse', 'flicker', 'animate']
}
};
}
parse(description) {
const lowerDesc = description.toLowerCase();
const scene = {
sceneName: this.extractSceneName(description),
environment: this.detectEnvironment(lowerDesc),
lighting: this.detectLighting(lowerDesc),
objects: this.extractObjects(lowerDesc, description),
materials: this.detectMaterials(lowerDesc),
effects: this.detectEffects(lowerDesc),
avatar: this.extractAvatar(lowerDesc, description),
audio: this.extractAudio(lowerDesc, description),
camera: this.detectCamera(lowerDesc)
};
// Fill defaults
this.fillDefaults(scene);
return scene;
}
extractSceneName(description) {
// Try to extract a name from quotes or first few words
const match = description.match(/"([^"]+)"/);
if (match) return match[1];
const words = description.split(/\s+/).slice(0, 3);
return words.join('_').replace(/[^a-z0-9_]/gi, '') || 'generated_scene';
}
detectEnvironment(desc) {
for (const [env, keywords] of Object.entries(this.keywords.environments)) {
if (keywords.some(kw => desc.includes(kw))) {
return env;
}
}
return 'room'; // default
}
detectLighting(desc) {
const lighting = {
type: 'day',
hdri: null,
sun_energy: 3.0,
sun_angle: 0.5,
ambient_strength: 0.3,
color_temperature: 5500,
color: [1, 1, 1],
lights: []
};
if (this.keywords.lighting.night.some(kw => desc.includes(kw))) {
lighting.type = 'night';
lighting.sun_energy = 0.5;
lighting.ambient_strength = 0.1;
lighting.color_temperature = 2000;
lighting.hdri = 'night_sky';
lighting.color = [0.2, 0.2, 0.4];
} else if (this.keywords.lighting.sunset.some(kw => desc.includes(kw))) {
lighting.type = 'sunset';
lighting.sun_energy = 2.0;
lighting.color_temperature = 3000;
lighting.hdri = 'sunset_sky';
lighting.color = [1, 0.6, 0.3];
} else if (this.keywords.lighting.sunrise.some(kw => desc.includes(kw))) {
lighting.type = 'sunrise';
lighting.sun_energy = 2.5;
lighting.color_temperature = 4000;
lighting.hdri = 'sunrise_sky';
lighting.color = [1, 0.8, 0.5];
}
// Detect blue ambient light
if (this.keywords.lighting.blue.some(kw => desc.includes(kw))) {
lighting.color = [0.3, 0.5, 1.0];
lighting.ambient_strength = 0.4;
lighting.lights.push({
type: 'AMBIENT',
color: [0.2, 0.3, 0.8],
energy: 0.5
});
}
return lighting;
}
extractObjects(desc, fullDesc) {
const objects = [];
let objIndex = 0;
// Count objects - improved matching
const counts = {};
for (const [type, keywords] of Object.entries(this.keywords.objects)) {
const matches = keywords.filter(kw => desc.includes(kw));
if (matches.length > 0) {
// Try to find number before the keyword
const countMatch = fullDesc.match(new RegExp(`(\\d+)\\s+${matches[0]}`, 'i'));
const count = countMatch ? parseInt(countMatch[1]) :
(type === 'firefly' ? 10 : type === 'mushroom' ? 5 : 1);
counts[type] = count;
}
}
// Create objects
for (const [type, count] of Object.entries(counts)) {
for (let i = 0; i < count; i++) {
objects.push(this.createObject(type, objIndex++, objects.length));
}
}
// Extract custom objects from description
const customMatches = fullDesc.match(/(\d+)\s+(\w+)/gi);
if (customMatches) {
customMatches.forEach(match => {
const parts = match.match(/(\d+)\s+(\w+)/i);
if (parts && !this.keywords.objects[parts[2].toLowerCase()]) {
const count = parseInt(parts[1]);
for (let i = 0; i < count; i++) {
objects.push(this.createObject('cube', objIndex++, objects.length, parts[2]));
}
}
});
}
return objects;
}
createObject(type, index, position, name = null) {
const base = {
type: 'cube',
name: name || `${type}_${index}`,
location: [
(position % 5 - 2) * 2 + (Math.random() - 0.5) * 0.5,
Math.floor(position / 5) * 2 + (Math.random() - 0.5) * 0.5,
0.5
],
rotation: [0, 0, 0],
scale: [1, 1, 1],
color: [0.8, 0.8, 0.8, 1],
metallic: 0.0,
roughness: 0.5
};
// Type-specific defaults
switch (type) {
case 'tree':
base.type = 'cylinder';
base.scale = [0.3, 0.3, 3];
base.location[2] = 1.5;
base.color = [0.2, 0.6, 0.2, 1];
base.roughness = 0.8;
break;
case 'mushroom':
base.type = 'sphere';
base.scale = [0.3, 0.3, 0.4];
base.location[2] = 0.2;
base.color = [0.8, 0.2, 0.8, 1];
base.emission = [0.5, 0.1, 0.5, 1];
base.emissionStrength = 2.0;
base.roughness = 0.3;
break;
case 'fire':
base.type = 'cone';
base.scale = [0.5, 0.5, 1];
base.location[2] = 0.5;
base.color = [1, 0.3, 0, 1];
base.emission = [1, 0.5, 0, 1];
base.emissionStrength = 3.0;
base.roughness = 0.2;
break;
case 'firefly':
base.type = 'sphere';
base.scale = [0.05, 0.05, 0.05];
base.location[2] = 1.5 + Math.random() * 0.5;
base.color = [1, 1, 0.5, 1];
base.emission = [1, 1, 0.3, 1];
base.emissionStrength = 5.0;
base.roughness = 0.1;
base.animation = { type: 'float', speed: 1.0 };
break;
case 'rock':
base.type = 'sphere';
base.scale = [0.8, 0.8, 0.6];
base.color = [0.4, 0.4, 0.4, 1];
base.roughness = 0.9;
break;
case 'water':
base.type = 'plane';
base.scale = [10, 10, 1];
base.location[2] = 0;
base.color = [0.2, 0.5, 0.8, 0.7];
base.metallic = 0.1;
base.roughness = 0.1;
break;
}
return base;
}
detectMaterials(desc) {
const materials = {};
for (const [mat, keywords] of Object.entries(this.keywords.materials)) {
if (keywords.some(kw => desc.includes(kw))) {
materials[mat] = true;
}
}
return materials;
}
detectEffects(desc) {
const effects = {};
for (const [effect, keywords] of Object.entries(this.keywords.effects)) {
if (keywords.some(kw => desc.includes(kw))) {
effects[effect] = true;
}
}
return effects;
}
extractAvatar(desc, fullDesc) {
const avatar = {
present: desc.includes('avatar') || desc.includes('character') || desc.includes('person') ||
desc.includes('wizard') || desc.includes('mage') || desc.includes('sorcerer'),
action: 'stand',
position: [0, 0, 0],
name: 'Avatar',
scale: 1.0
};
if (avatar.present) {
// Detect wizard/mage
if (desc.includes('wizard') || desc.includes('mage') || desc.includes('sorcerer')) {
avatar.name = 'Wizard';
avatar.scale = 1.0;
}
// Detect actions
for (const [action, keywords] of Object.entries(this.keywords.animations)) {
if (keywords.some(kw => desc.includes(kw))) {
avatar.action = action;
break;
}
}
// Position based on action
if (avatar.action === 'sit') {
avatar.position[2] = 0.5;
} else if (avatar.action === 'walk') {
avatar.position = [0, 0, 0];
avatar.animation = { type: 'walk', speed: 1.0 };
}
}
return avatar;
}
extractAudio(desc, fullDesc) {
const audio = {
ambient: [],
spatial: []
};
// Detect ambient sounds
if (desc.includes('wave') || desc.includes('ocean')) {
audio.ambient.push({ type: 'ocean_waves', volume: 0.5, loop: true });
}
if (desc.includes('wind') || desc.includes('breeze')) {
audio.ambient.push({ type: 'wind', volume: 0.3, loop: true });
}
if (desc.includes('music') || desc.includes('soundtrack') || desc.includes('background')) {
const isMagical = desc.includes('magical') || desc.includes('mystical') || desc.includes('enchanted');
audio.ambient.push({
type: isMagical ? 'magical_ambient' : 'ambient_music',
volume: 0.4,
loop: true
});
}
if (this.keywords.audio.magical.some(kw => desc.includes(kw))) {
audio.ambient.push({ type: 'magical_ambient', volume: 0.5, loop: true });
}
// Detect spatial sounds
if (desc.includes('fire') || desc.includes('campfire')) {
audio.spatial.push({ type: 'fire_crackle', position: [0, 0, 0], volume: 0.4, loop: true });
}
return audio;
}
detectCamera(desc) {
const camera = {
position: [4, -4, 2.2],
rotation: [1.05, 0, 0.78],
fov: 50
};
if (desc.includes('close') || desc.includes('close-up')) {
camera.position = [2, -2, 1.5];
} else if (desc.includes('far') || desc.includes('wide')) {
camera.position = [8, -8, 4];
camera.fov = 70;
}
return camera;
}
fillDefaults(scene) {
// Ensure lighting exists
if (!scene.lighting.lights) {
scene.lighting.lights = [];
}
// Add environment-specific objects
if (scene.environment === 'forest' && scene.objects.filter(o => o.name.includes('tree')).length === 0) {
for (let i = 0; i < 5; i++) {
scene.objects.push(this.createObject('tree', i, scene.objects.length));
}
}
if (scene.environment === 'beach' && scene.objects.filter(o => o.name.includes('water')).length === 0) {
scene.objects.push(this.createObject('water', 0, scene.objects.length));
}
// Add magical forest elements
if (scene.environment === 'magical' || scene.environment === 'forest') {
const hasMushrooms = scene.objects.some(o => o.name.includes('mushroom'));
const hasFireflies = scene.objects.some(o => o.name.includes('firefly'));
if (!hasMushrooms && scene.effects && !scene.effects.fog) {
// Add some mushrooms if not present
for (let i = 0; i < 3; i++) {
scene.objects.push(this.createObject('mushroom', i, scene.objects.length));
}
}
}
// Add ground plane
scene.ground = {
type: 'plane',
size: 20,
texture: scene.environment === 'beach' ? 'sand' :
scene.environment === 'forest' || scene.environment === 'magical' ? 'grass' : 'concrete',
color: scene.environment === 'beach' ? [0.9, 0.8, 0.6, 1] :
scene.environment === 'forest' || scene.environment === 'magical' ? [0.2, 0.4, 0.2, 1] :
[0.5, 0.5, 0.5, 1],
roughness: 0.8,
metallic: 0.0
};
// Add default sun if not specified
if (scene.lighting.type === 'day' && scene.lighting.lights.filter(l => l.type === 'SUN').length === 0) {
scene.lighting.lights.push({
type: 'SUN',
location: [10, -10, 10],
energy: scene.lighting.sun_energy,
angle: scene.lighting.sun_angle,
color: scene.lighting.color
});
}
// Add night lighting if needed
if (scene.lighting.type === 'night' && scene.lighting.lights.length === 0) {
scene.lighting.lights.push({
type: 'POINT',
location: [0, 0, 3],
energy: 50,
color: [0.3, 0.3, 0.5]
});
}
}
}