Fluid backdrop behind the 3D model
New 'Sfondo Modello 3D' selector (Media tab): gradient or any of the five fluid modes. The fluid sim paints the canvas first, then the model renders on top clearing only the depth buffer (overlay mode skips its own backdrop pass). Palette and speed are shared with the preset; persisted and re-sent when the output restarts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
173d85b681
commit
b45c7078dc
5 files changed
+41
-5
No files matched your search
@@ -131,6 +131,15 @@
|
|||||||
<button id="btn-glb-load" class="wide">🧊 Carica un modello .glb…</button>
|
<button id="btn-glb-load" class="wide">🧊 Carica un modello .glb…</button>
|
||||||
<input type="file" id="glb-input" accept=".glb" hidden />
|
<input type="file" id="glb-input" accept=".glb" hidden />
|
||||||
<div class="hint-mini" id="glb-name">Il modello ruota a tempo di musica nella famiglia “Modello 3D”.</div>
|
<div class="hint-mini" id="glb-name">Il modello ruota a tempo di musica nella famiglia “Modello 3D”.</div>
|
||||||
|
<label>Sfondo Modello 3D</label>
|
||||||
|
<select id="glb-bg">
|
||||||
|
<option value="gradient">Gradiente</option>
|
||||||
|
<option value="ink">Fluido</option>
|
||||||
|
<option value="fire">Fluido Fuoco</option>
|
||||||
|
<option value="ring">Fluido Anello</option>
|
||||||
|
<option value="vortex">Fluido Vortici</option>
|
||||||
|
<option value="wave">Fluido Onda</option>
|
||||||
|
</select>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="span2" id="effect-seq-section">
|
<section class="span2" id="effect-seq-section">
|
||||||
|
|||||||
+11
-1
@@ -146,13 +146,23 @@ $('#svg-input').addEventListener('change', (e) => {
|
|||||||
// --- 3D model (GLB) for the 'Modello 3D' family -----------------------------
|
// --- 3D model (GLB) for the 'Modello 3D' family -----------------------------
|
||||||
// The path persists and is re-sent when the output window (re)starts.
|
// The path persists and is re-sent when the output window (re)starts.
|
||||||
let glbPath = localStorage.getItem('glb3d') || '';
|
let glbPath = localStorage.getItem('glb3d') || '';
|
||||||
function glbSend() { if (glbPath) send({ type: 'glb', path: glbPath }); }
|
let glbBg = localStorage.getItem('glbbg') || 'gradient';
|
||||||
|
function glbSend() {
|
||||||
|
if (glbPath) send({ type: 'glb', path: glbPath });
|
||||||
|
send({ type: 'modelBg', mode: glbBg });
|
||||||
|
}
|
||||||
function glbLabel(name) {
|
function glbLabel(name) {
|
||||||
$('#glb-name').textContent = name
|
$('#glb-name').textContent = name
|
||||||
? '🧊 ' + name + ' — attiva la famiglia “Modello 3D” per vederlo.'
|
? '🧊 ' + name + ' — attiva la famiglia “Modello 3D” per vederlo.'
|
||||||
: 'Il modello ruota a tempo di musica nella famiglia “Modello 3D”.';
|
: 'Il modello ruota a tempo di musica nella famiglia “Modello 3D”.';
|
||||||
}
|
}
|
||||||
if (glbPath) glbLabel(glbPath.split('/').pop());
|
if (glbPath) glbLabel(glbPath.split('/').pop());
|
||||||
|
$('#glb-bg').value = glbBg;
|
||||||
|
$('#glb-bg').addEventListener('change', (e) => {
|
||||||
|
glbBg = e.target.value;
|
||||||
|
localStorage.setItem('glbbg', glbBg);
|
||||||
|
send({ type: 'modelBg', mode: glbBg });
|
||||||
|
});
|
||||||
$('#btn-glb-load').addEventListener('click', () => $('#glb-input').click());
|
$('#btn-glb-load').addEventListener('click', () => $('#glb-input').click());
|
||||||
$('#glb-input').addEventListener('change', (e) => {
|
$('#glb-input').addEventListener('change', (e) => {
|
||||||
const f = e.target.files[0];
|
const f = e.target.files[0];
|
||||||
|
|||||||
+11
-3
@@ -929,7 +929,9 @@ class ModelSim {
|
|||||||
// pose (optional, from the camera-interactive mode): { yaw, leanX, hopY,
|
// pose (optional, from the camera-interactive mode): { yaw, leanX, hopY,
|
||||||
// squash, rim } — extra rotation, sideways lean, jump height (in radii),
|
// squash, rim } — extra rotation, sideways lean, jump height (in radii),
|
||||||
// vertical squash & stretch, touch-glow 0..1.
|
// vertical squash & stretch, touch-glow 0..1.
|
||||||
render(timeSec, audio, e, canvas, pose) {
|
// overlay = true: another engine (e.g. the fluid sim) already painted the
|
||||||
|
// canvas this frame — draw only the model on top (clear depth, no backdrop).
|
||||||
|
render(timeSec, audio, e, canvas, pose, overlay) {
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
const speed = Math.min(2.5, e.speed || 1);
|
const speed = Math.min(2.5, e.speed || 1);
|
||||||
const mix = e.audioMix !== undefined ? e.audioMix : 1;
|
const mix = e.audioMix !== undefined ? e.audioMix : 1;
|
||||||
@@ -938,10 +940,15 @@ class ModelSim {
|
|||||||
|
|
||||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||||
gl.disable(gl.BLEND);
|
gl.disable(gl.BLEND);
|
||||||
gl.clearColor(0, 0, 0, 1);
|
if (overlay) {
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
gl.clear(gl.DEPTH_BUFFER_BIT);
|
||||||
|
} else {
|
||||||
|
gl.clearColor(0, 0, 0, 1);
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
// backdrop
|
// backdrop
|
||||||
|
if (!overlay) {
|
||||||
gl.useProgram(this.progBg);
|
gl.useProgram(this.progBg);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
|
||||||
gl.enableVertexAttribArray(this.aBg);
|
gl.enableVertexAttribArray(this.aBg);
|
||||||
@@ -953,6 +960,7 @@ class ModelSim {
|
|||||||
gl.uniform1f(this.ub.uBass, bass);
|
gl.uniform1f(this.ub.uBass, bass);
|
||||||
gl.uniform1f(this.ub.uBeat, beat);
|
gl.uniform1f(this.ub.uBeat, beat);
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
// model
|
// model
|
||||||
const asp = canvas.width/Math.max(1, canvas.height);
|
const asp = canvas.width/Math.max(1, canvas.height);
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ djv.onControl(async (m) => {
|
|||||||
djv.report({ type: 'autoVj', on: autoVj });
|
djv.report({ type: 'autoVj', on: autoVj });
|
||||||
break;
|
break;
|
||||||
case 'svg': loadCustomTexture(m.dataUrl); break;
|
case 'svg': loadCustomTexture(m.dataUrl); break;
|
||||||
|
case 'modelBg': viz.modelBg = m.mode || 'gradient'; break;
|
||||||
case 'glb':
|
case 'glb':
|
||||||
try {
|
try {
|
||||||
const bytes = await djv.readFile(m.path);
|
const bytes = await djv.readFile(m.path);
|
||||||
|
|||||||
+9
-1
@@ -114,7 +114,15 @@ class Visualizer {
|
|||||||
if (e.isModel3d && window.ModelSim) {
|
if (e.isModel3d && window.ModelSim) {
|
||||||
if (!this.model3d) this.model3d = new window.ModelSim(gl);
|
if (!this.model3d) this.model3d = new window.ModelSim(gl);
|
||||||
if (this._pendingGlb) { this.model3d.setModel(this._pendingGlb); this._pendingGlb = null; }
|
if (this._pendingGlb) { this.model3d.setModel(this._pendingGlb); this._pendingGlb = null; }
|
||||||
this.model3d.render(timeSec, audio, e, this.canvas);
|
// optional fluid backdrop behind the model (modelBg: ink/fire/ring/...)
|
||||||
|
const bg = this.modelBg || 'gradient';
|
||||||
|
if (bg !== 'gradient' && window.FluidSim) {
|
||||||
|
if (!this.fluid) this.fluid = new window.FluidSim(gl);
|
||||||
|
this.fluid.render(timeSec, audio, Object.assign({}, e, { fluidMode: bg }), this.canvas);
|
||||||
|
this.model3d.render(timeSec, audio, e, this.canvas, null, true);
|
||||||
|
} else {
|
||||||
|
this.model3d.render(timeSec, audio, e, this.canvas);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (e.isInteractive && window.InteractiveSim) {
|
if (e.isInteractive && window.InteractiveSim) {
|
||||||
|
|||||||
Reference in new issue
Block a user