diff --git a/src/game/scenery/isometric/metadata/models.ts b/src/game/scenery/isometric/metadata/models.ts index f2312d27f..3b1b1c6d6 100644 --- a/src/game/scenery/isometric/metadata/models.ts +++ b/src/game/scenery/isometric/metadata/models.ts @@ -15,6 +15,7 @@ import { getParams } from '../../../../params'; import { loadFx } from './fx/loader'; import Game from '../../../Game'; import { Fx } from './fx/Fx'; +import { createTwinklingStar } from '../misc/twinkling_star'; const exporter = new GLTFExporter(); @@ -95,6 +96,47 @@ export async function loadFullSceneModel( } } }); + + if (entry === 117) { + // Twinkling star effects for entry 117 (scene 173, Kurtz house) + // Specs: pos (Vector3), size (scale), color (hex Color), speed (twinkle rate) + const starSpecs = [ + { + pos: new THREE.Vector3(41.3, 3.85, 35), + size: 1.0, + color: 0xefb810, + speed: 1.514 + }, + { + pos: new THREE.Vector3(41.17, 3.4, 35.5), + size: 0.6, + color: 0x3c341e, + speed: 1.259 + } + // Add more stars by adding additional objects to this array. + ]; + + for (const spec of starSpecs) { + const { pos, size, color, speed } = spec; + // Create a twinkling star (defined in ../misc/twinkling_star.ts) + const starEffect = await createTwinklingStar( + pos, + new THREE.Color(color), + size, + speed + ); + // Add the star's mesh to the scene + threeObject.add(starEffect.threeObject); + // Register the star's update function to animate its twinkle each frame + effects.push({ + init: () => {}, // no extra initialization needed + update: (_game, _scene, time) => { + starEffect.update(time); // update star effect (uses time.elapsed in shader) + } + }); + } + } + const mixer = new THREE.AnimationMixer(threeObject); applyAnimationUpdaters(threeObject, model.animations); each(model.animations, (clip) => { diff --git a/src/game/scenery/isometric/misc/shaders/star_twinkle.frag.glsl b/src/game/scenery/isometric/misc/shaders/star_twinkle.frag.glsl new file mode 100644 index 000000000..044c9896e --- /dev/null +++ b/src/game/scenery/isometric/misc/shaders/star_twinkle.frag.glsl @@ -0,0 +1,31 @@ +precision highp float; + +// Varyings from the vertex shader +varying float vIntensity; +varying float vSparkle; +varying vec2 vUv; + +// Uniforms set via Three.js +uniform sampler2D starTex; +uniform vec3 uColor; +uniform float time; +uniform float uSpeed; + +void main() { + // Compute twinkle flicker factor + float flicker = sin(time * uSpeed + vSparkle * 10.0) * vSparkle; + float finalIntensity = vIntensity + flicker; + + // Sample the star texture and apply the color tint + vec4 tex = texture2D(starTex, vUv); + vec3 invertedRGB = vec3(1.0) - tex.rgb; + vec3 tinted = invertedRGB * uColor; + + // Apply the final intensity to color and alpha + vec4 color = vec4(tinted * finalIntensity, tex.a * finalIntensity); + + // Discard nearly transparent pixels for cleaner edges + if (color.a < 0.01) discard; + + gl_FragColor = color; +} diff --git a/src/game/scenery/isometric/misc/shaders/star_twinkle.vert.glsl b/src/game/scenery/isometric/misc/shaders/star_twinkle.vert.glsl new file mode 100644 index 000000000..b00a0be85 --- /dev/null +++ b/src/game/scenery/isometric/misc/shaders/star_twinkle.vert.glsl @@ -0,0 +1,39 @@ +precision highp float; + +// Standard vertex attributes (not automatically provided in RawShaderMaterial) +attribute vec3 position; +attribute vec2 uv; + +// Custom attributes for brightness and sparkle effect +attribute float intensity; +attribute float size; +attribute float sparkle; + +// Varyings to pass data to the fragment shader +varying float vIntensity; +varying float vSparkle; +varying vec2 vUv; + +// Transformation matrices (provided by Three.js) +uniform mat4 modelViewMatrix; +uniform mat4 projectionMatrix; + +void main() { + // Pass custom attribute values to the fragment shader via varyings + vIntensity = intensity; + vSparkle = sparkle; + vUv = uv; + + // Center UV coordinates at (0.5, 0.5) and scale by size + vec2 centeredUV = uv - vec2(0.5); + vec2 offset = centeredUV * size; + + // Compute vertex position in camera space + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); + + // Offset the X and Y position by the computed offset (billboard quad) + mvPosition.xy += offset; + + // Project the vertex to clip space + gl_Position = projectionMatrix * mvPosition; +} diff --git a/src/game/scenery/isometric/misc/twinkling_star.ts b/src/game/scenery/isometric/misc/twinkling_star.ts new file mode 100644 index 000000000..3139dc687 --- /dev/null +++ b/src/game/scenery/isometric/misc/twinkling_star.ts @@ -0,0 +1,90 @@ +import * as THREE from 'three'; +import STAR_VERT from './shaders/star_twinkle.vert.glsl'; +import STAR_FRAG from './shaders/star_twinkle.frag.glsl'; + +const loader = new THREE.TextureLoader(); + +/** + * Create a twinkling star effect at a given position. + * Returns an object with the star's Three.js mesh and an update function. + */ +export async function createTwinklingStar( + position: THREE.Vector3, + color: number | string | THREE.Color, + size = 1.0, + speed = 1.5 +) { + // Load star texture (with alpha channel for transparency) + const starTexture = await new Promise((resolve) => { + loader.load('images/stars/B_OPC1.png', resolve); + }); + + // Create a RawShaderMaterial for the star using custom shaders + const starMaterial = new THREE.RawShaderMaterial({ + vertexShader: STAR_VERT, + fragmentShader: STAR_FRAG, + uniforms: { + starTex: { value: starTexture }, + uColor: { value: new THREE.Color(color) }, + time: { value: 0.0 }, + uSpeed: { value: speed } + }, + transparent: true, + blending: THREE.AdditiveBlending, + depthWrite: false, + side: THREE.FrontSide + }); + + // Define star geometry as a quad (two triangles at the given position) + const starGeo = new THREE.BufferGeometry(); + const positions: number[] = []; + const uvs: number[] = []; + const intensities: number[] = []; + const sizes: number[] = []; + const sparkles: number[] = []; + + // Base attributes for the star + const baseIntensity = 0.4; // base brightness intensity + const baseSize = 0.1 * size; // base sprite size in world units + const baseSparkle = 0.7; // base sparkle variation factor + + // Add 6 vertices at the star position (two triangles forming a quad) + for (let j = 0; j < 6; j += 1) { + positions.push(position.x, position.y, position.z); + intensities.push(baseIntensity); + sizes.push(baseSize); + sparkles.push(baseSparkle); + } + // Set UV coordinates for the two triangles (covering the full texture) + uvs.push( + 0, 0, + 0, 1, + 1, 0, + 1, 1, + 1, 0, + 0, 1 + ); + + starGeo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + starGeo.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); + starGeo.setAttribute('intensity', new THREE.Float32BufferAttribute(intensities, 1)); + starGeo.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); + starGeo.setAttribute('sparkle', new THREE.Float32BufferAttribute(sparkles, 1)); + // Set the index buffer to form two triangles (0-1-2 and 3-4-5) + starGeo.setIndex([5, 4, 3, 2, 1, 0]); + + // Create the star mesh with its geometry and material + const starMesh = new THREE.Mesh(starGeo, starMaterial); + starMesh.name = 'twinkling_star'; + starMesh.frustumCulled = false; // always render (disable frustum culling) + starMesh.renderOrder = 2; // draw after scene objects to avoid overlap + + // Return the Three.js mesh and an update function for the twinkle animation + return { + threeObject: starMesh, + update: (time: { elapsed: number }) => { + // Update the time uniform to animate the flicker effect + starMaterial.uniforms.time.value = time.elapsed; + } + }; +} diff --git a/www/metadata/lba2/grids.json b/www/metadata/lba2/grids.json index 927a9b9d4..f11da5573 100644 --- a/www/metadata/lba2/grids.json +++ b/www/metadata/lba2/grids.json @@ -6250,6 +6250,20 @@ 0, 6.123233995736766e-17 ] + }, + { + "name": "FI_053_liquor_bottle_and_mug.glb", + "position": { + "x": 41, + "y": 1.5, + "z": 31.5 + }, + "quaternion": [ + 0, + 0.7071067811865475, + 0, + 0.7071067811865476 + ] } ], "patches": { @@ -7317,6 +7331,12 @@ }, "14x1x37": { "hide": true + }, + "41x3x32": { + "hide": true + }, + "41x4x32": { + "hide": true } } }, diff --git a/www/metadata/lba2/layouts.json b/www/metadata/lba2/layouts.json index 5a1d38138..85df24a71 100644 --- a/www/metadata/lba2/layouts.json +++ b/www/metadata/lba2/layouts.json @@ -5285,6 +5285,16 @@ } }, "15": { + "0": { + "replace": true, + "file": "FI_000_001_010_011_padded_armchair.glb", + "orientation": 0 + }, + "1": { + "replace": true, + "file": "FI_000_001_010_011_padded_armchair.glb", + "orientation": 1 + }, "2": { "replace": true, "file": "FI_002_003_francos_building_wall.glb", @@ -5320,6 +5330,26 @@ "file": "FI_008_francos_building_wall_round_corner.glb", "orientation": 0 }, + "9": { + "replace": true, + "file": "FI_009_padded_armchair_long.glb", + "orientation": 0 + }, + "10": { + "replace": true, + "file": "FI_000_001_010_011_padded_armchair.glb", + "orientation": 3 + }, + "11": { + "replace": true, + "file": "FI_000_001_010_011_padded_armchair.glb", + "orientation": 2 + }, + "12": { + "replace": true, + "file": "FI_012_padded_beanbag.glb", + "orientation": 0 + }, "17": { "replace": true, "file": "FI_017_francos_building_wall_window_big.glb", @@ -5340,6 +5370,11 @@ "file": "FI_020_francos_building_wall_window.glb", "orientation": 0 }, + "22": { + "replace": true, + "file": "FI_022_padded_stool.glb", + "orientation": 0 + }, "23": { "replace": true, "file": "FI_023_francos_doorframe.glb", @@ -5350,6 +5385,11 @@ "file": "FI_024_francos_doorframe.glb", "orientation": 0 }, + "25": { + "replace": true, + "file": "FI_025_round_table.glb", + "orientation": 0 + }, "26": { "replace": true, "file": "FI_026_ice_creams_and_cup.glb", @@ -5485,11 +5525,26 @@ "file": "FI_069_short_stool.glb", "orientation": 0 }, + "70": { + "replace": true, + "file": "FI_070_baby_cushion.glb", + "orientation": 0 + }, + "71": { + "replace": true, + "file": "FI_071_red_soup.glb", + "orientation": 0 + }, "72": { "replace": true, "file": "FI_072_baby_bottles.glb", "orientation": 0 }, + "73": { + "replace": true, + "file": "FI_073_little_doll.glb", + "orientation": 0 + }, "74": { "replace": true, "file": "FI_074_abacus.glb", @@ -5515,6 +5570,11 @@ "file": "FI_079_big_candle.glb", "orientation": 0 }, + "80": { + "replace": true, + "file": "FI_080_big_dry_flower_pot.glb", + "orientation": 0 + }, "81": { "replace": true, "file": "FI_081_francos_building_wall_window_covered.glb", @@ -5525,6 +5585,11 @@ "file": "FI_082_francos_building_wall_window_big_covered.glb", "orientation": 0 }, + "83": { + "replace": true, + "file": "FI_083_shiny_globe.glb", + "orientation": 0 + }, "84": { "replace": true, "file": "FI_084_dark_monk_altar.glb", diff --git a/www/models/lba2/iso_scenes/115.glb b/www/models/lba2/iso_scenes/115.glb index b83275df3..f949ec5bf 100644 Binary files a/www/models/lba2/iso_scenes/115.glb and b/www/models/lba2/iso_scenes/115.glb differ diff --git a/www/models/lba2/iso_scenes/116.glb b/www/models/lba2/iso_scenes/116.glb index 0ad7af38e..37f3eab18 100644 Binary files a/www/models/lba2/iso_scenes/116.glb and b/www/models/lba2/iso_scenes/116.glb differ diff --git a/www/models/lba2/iso_scenes/117.glb b/www/models/lba2/iso_scenes/117.glb index d9e67355d..e21bf1504 100644 Binary files a/www/models/lba2/iso_scenes/117.glb and b/www/models/lba2/iso_scenes/117.glb differ diff --git a/www/models/lba2/iso_scenes/118.glb b/www/models/lba2/iso_scenes/118.glb index e59e40bbd..8e3e997d5 100644 Binary files a/www/models/lba2/iso_scenes/118.glb and b/www/models/lba2/iso_scenes/118.glb differ diff --git a/www/models/lba2/iso_scenes/119.glb b/www/models/lba2/iso_scenes/119.glb index 7bc088afd..e1681765f 100644 Binary files a/www/models/lba2/iso_scenes/119.glb and b/www/models/lba2/iso_scenes/119.glb differ diff --git a/www/models/lba2/layouts/FI_000_001_010_011_padded_armchair.glb b/www/models/lba2/layouts/FI_000_001_010_011_padded_armchair.glb new file mode 100644 index 000000000..5ad57c6a3 Binary files /dev/null and b/www/models/lba2/layouts/FI_000_001_010_011_padded_armchair.glb differ diff --git a/www/models/lba2/layouts/FI_009_padded_armchair_long.glb b/www/models/lba2/layouts/FI_009_padded_armchair_long.glb new file mode 100644 index 000000000..c8a419006 Binary files /dev/null and b/www/models/lba2/layouts/FI_009_padded_armchair_long.glb differ diff --git a/www/models/lba2/layouts/FI_012_padded_beanbag.glb b/www/models/lba2/layouts/FI_012_padded_beanbag.glb new file mode 100644 index 000000000..bd9b41971 Binary files /dev/null and b/www/models/lba2/layouts/FI_012_padded_beanbag.glb differ diff --git a/www/models/lba2/layouts/FI_022_padded_stool.glb b/www/models/lba2/layouts/FI_022_padded_stool.glb new file mode 100644 index 000000000..f01c0940c Binary files /dev/null and b/www/models/lba2/layouts/FI_022_padded_stool.glb differ diff --git a/www/models/lba2/layouts/FI_025_round_table.glb b/www/models/lba2/layouts/FI_025_round_table.glb new file mode 100644 index 000000000..c5a7410ff Binary files /dev/null and b/www/models/lba2/layouts/FI_025_round_table.glb differ diff --git a/www/models/lba2/layouts/FI_070_baby_cushion.glb b/www/models/lba2/layouts/FI_070_baby_cushion.glb new file mode 100644 index 000000000..653599aaa Binary files /dev/null and b/www/models/lba2/layouts/FI_070_baby_cushion.glb differ diff --git a/www/models/lba2/layouts/FI_071_red_soup.glb b/www/models/lba2/layouts/FI_071_red_soup.glb new file mode 100644 index 000000000..b2a9112e3 Binary files /dev/null and b/www/models/lba2/layouts/FI_071_red_soup.glb differ diff --git a/www/models/lba2/layouts/FI_073_little_doll.glb b/www/models/lba2/layouts/FI_073_little_doll.glb new file mode 100644 index 000000000..ae62ef5e8 Binary files /dev/null and b/www/models/lba2/layouts/FI_073_little_doll.glb differ diff --git a/www/models/lba2/layouts/FI_080_big_dry_flower_pot.glb b/www/models/lba2/layouts/FI_080_big_dry_flower_pot.glb new file mode 100644 index 000000000..1d36d4398 Binary files /dev/null and b/www/models/lba2/layouts/FI_080_big_dry_flower_pot.glb differ diff --git a/www/models/lba2/layouts/FI_083_shiny_globe.glb b/www/models/lba2/layouts/FI_083_shiny_globe.glb new file mode 100644 index 000000000..dd43c7113 Binary files /dev/null and b/www/models/lba2/layouts/FI_083_shiny_globe.glb differ