Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<script src="js/f2d/solver.js"></script>
<script src="js/f2d/fileloader.js"></script>
<script src="js/f2d/mouse.js"></script>
<script src="js/f2d/main.js"></script>
<script type="module" src="js/f2d/main.js"></script>
<script type="module" src="js/f2d/vectorFieldTemplates.js"></script>
</body>
</html>
Binary file added js/.DS_Store
Binary file not shown.
Binary file added js/f2d/.DS_Store
Binary file not shown.
73 changes: 71 additions & 2 deletions js/f2d/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// -- main.js
// 새롭게 만든 벡터 필드 템플릿 코드를 사용하도록 수정
// 벡터 필드 템플릿 코드 가져오기
// import { TEMPLATE_1, TEMPLATE_2 } from './vectorFieldTemplates.js';

(function() {
"use strict";

Expand All @@ -19,9 +24,10 @@
document.body.appendChild(stats.domElement);

var grid = {
size: new THREE.Vector2(512, 256),
// size: new THREE.Vector2(512, 256),
size: new THREE.Vector2(256, 128),
scale: 1,
applyBoundaries: true
applyBoundaries: false,
};
var time = {
step: 1,
Expand All @@ -33,6 +39,24 @@
var solver, gui;
var mouse = new F2D.Mouse(grid);

// 템플릿 확인 코드
let vectorTemplates = [];
fetch('/fluids-2d/js/f2d/vector_templates.json')
.then(response => {
if (!response.ok) {
throw new Error('네트워크 응답이 올바르지 않습니다.');
}
return response.json();
})
.then(data => {
vectorTemplates = data;
console.log('벡터 템플릿 로딩 성공!', vectorTemplates.length, '개');
// init(shaders);
})
.catch(error => {
console.error('벡터 템플릿 로딩 실패!', error);
});

function init(shaders) {
solver = F2D.Solver.make(grid, time, windowSize, shaders);

Expand Down Expand Up @@ -90,10 +114,55 @@
requestAnimationFrame(update);
}

// 테스트용 키보드 이벤트 추가
let currentTemplate = null;
// ==================================================
window.addEventListener('keydown', (event) => {
// 키보드에서 누른 키를 숫자로 변환
const key = parseInt(event.key);

// 1~5번 키를 눌렀고, vectorTemplates가 그만큼의 데이터를 가지고 있다면
if (key >= 1 && key <= vectorTemplates.length) {
console.log(`JSON 템플릿 ${key}번 활성화!`);
// 키보드 숫자 '1'을 배열 인덱스 '0'에 매핑
currentTemplate = vectorTemplates[key - 1];
} else if (event.key === '0') {
// 0번 키를 누르면 힘을 비활성화
console.log("외력 없음");
currentTemplate = null;
}
});

// window.addEventListener("keydown", function(event) {
// switch (event.key) {
// case "1":
// currentTemplate = TEMPLATE_1;
// break;
// case "2":
// currentTemplate = TEMPLATE_2;
// break;
// case "0":
// currentTemplate = null;
// break;
// }
// });

// 키에서 손을 떼면 템플릿을 비움!
window.addEventListener('keyup', (event) => {
// 1번이나 2번 키에서 손을 뗐을 때만 null로 바꿈
if (event.key === '1' || event.key === '2') {
currentTemplate = null;
}
});

function update() {
stats.begin();

solver.step(renderer, mouse);

// applyForceField 함수를 통해 벡터 템플릿 적용
solver.applyForceField(renderer, currentTemplate);

render();

stats.end();
Expand Down
5 changes: 5 additions & 0 deletions js/f2d/mouse.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// -- mouse.js
// 마우스 왼쪽 버튼을 눌렀을 때는 1번 템플릿
// 마우스 오른쪽 버튼을 눌렀을 때는 2번 템플릿이 나타나도록
// 간단한 테스트용 수정코드.

var F2D = F2D === undefined ? {} : F2D;

(function(F2D) {
Expand Down
90 changes: 82 additions & 8 deletions js/f2d/solver.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ var F2D = F2D === undefined ? {} : F2D;
var temp = this.advect.dissipation;
this.advect.dissipation = 1;
this.advect.compute(renderer, this.velocity, this.velocity, this.velocity);
this.boundary.compute(renderer, this.velocity, -1, this.velocity);
// open boundary로 수정하기
this.boundary.compute(renderer, this.velocity, 0, this.velocity);

this.advect.dissipation = temp;
this.advect.compute(renderer, this.velocity, this.density, this.density);
// this.advect.compute(renderer, this.velocity, this.density, this.density);

this.addForces(renderer, mouse);

Expand All @@ -59,7 +60,7 @@ var F2D = F2D === undefined ? {} : F2D;
this.velocityVorticity,
this.velocity
);
this.boundary.compute(renderer, this.velocity, -1, this.velocity);
this.boundary.compute(renderer, this.velocity, 0, this.velocity);
}

if (this.applyViscosity && this.viscosity > 0) {
Expand Down Expand Up @@ -87,9 +88,9 @@ var F2D = F2D === undefined ? {} : F2D;

if (motion.left) {
force.set(
motion.drag.x,
motion.drag.x,
-motion.drag.y,
0
0
);
this.splat.compute(
renderer,
Expand All @@ -98,7 +99,7 @@ var F2D = F2D === undefined ? {} : F2D;
point,
this.velocity
);
this.boundary.compute(renderer, this.velocity, -1, this.velocity);
this.boundary.compute(renderer, this.velocity, 0, this.velocity);
}

if (motion.right) {
Expand Down Expand Up @@ -142,13 +143,86 @@ var F2D = F2D === undefined ? {} : F2D;
this.velocity,
this.velocity
);
this.boundary.compute(renderer, this.velocity, -1, this.velocity);
this.boundary.compute(renderer, this.velocity, 0, this.velocity);
},

clearSlab: function(renderer, slab) {
renderer.clearTarget(slab.write, true, false, false);
slab.swap();
}
},

// -- applyForceField 함수 추가하기
applyForceField: function(renderer, forceField) {
// forceField 데이터가 없으면(null) 아무것도 하지 않음
if (!forceField) return;

const w = this.grid.size.x;
const h = this.grid.size.y;
const force = new THREE.Vector3();
const point = new THREE.Vector2();

// forceField는 이제 평탄화된 배열: [vx1, vy1, vx2, vy2, ...]
// 배열 길이의 절반만큼만 반복하면서, 한 번에 2개씩 데이터를 읽음
for (let i = 0; i < forceField.length / 2; i++) {
const gridX = i % w;
const gridY = Math.floor(i / w);

const yOffset = 30;

// ▼▼▼ 여기가 핵심! 평탄화된 배열에서 vx, vy를 올바르게 가져옴 ▼▼▼
const forceX = forceField[i * 2];
const forceY = forceField[i * 2 + 1];
// ▲▲▲ 여기까지 ▲▲▲

if (forceX === 0 && forceY === 0) continue;

point.set(gridX, gridY + yOffset);
force.set(forceX, forceY, 0);

this.splat.compute(
renderer,
this.velocity,
force,
point,
this.velocity
);
}
},
// applyForceField: function(renderer, forceField) {
// // forceField 데이터가 없으면 아무것도 하지 않음
// if (!forceField || forceField.length === 0) {
// return;
// }

// const w = this.grid.size.x;
// const force = new THREE.Vector3();
// const point = new THREE.Vector2();

// // 모든 격자점을 순회하면서 힘을 하나씩 'Splat'으로 찍어줌
// for (let i = 0; i < forceField.length / 2; i++) {
// const gridX = i % w;
// const gridY = Math.floor(i / w);

// const forceX = forceField[i * 2];
// const forceY = forceField[i * 2 + 1];

// // 힘이 0인 지점은 계산할 필요가 없으므로 건너뛰기 (성능 최적화)
// if (forceX === 0 && forceY === 0) {
// continue;
// }

// point.set(gridX, gridY);
// force.set(forceX, forceY, 0);

// this.splat.compute(
// renderer,
// this.velocity,
// force,
// point,
// this.velocity
// );
// }
// },
};

F2D.Solver.make = function(grid, time, windowSize, shaders) {
Expand Down
58 changes: 58 additions & 0 deletions js/f2d/vectorFieldTemplates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// -- vectorFieldTemplates.js
// 새롭게 만든 벡터 필드 템플릿 코드

// 격자 크기에 맞게 설정
const GRID_WIDTH = 256;
const GRID_HEIGHT = 128;
const GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;

// // --- 템플릿 1번: 전체적으로 오른쪽으로 부는 바람 ---
// export const TEMPLATE_1 = [];
// const WIND_SOURCE_WIDTH = 10; // 바람이 시작되는 영역의 너비 (픽셀)

// for (let y = 0; y < GRID_HEIGHT; y++) {
// for (let x = 0; x < GRID_WIDTH; x++) {
// // 왼쪽 가장자리(x 좌표가 10보다 작은 영역)에서만 힘을 가함!
// if (x < WIND_SOURCE_WIDTH) {
// TEMPLATE_1.push(0.5, 0); // 오른쪽으로 미는 힘
// } else {
// TEMPLATE_1.push(0, 0); // 나머지 영역은 힘 0
// }
// }
// }

// // --- 템플릿 2번: 중앙에서 소용돌이치는 바람 ---
// export const TEMPLATE_2 = [];
// //손바람 크기(반경), 중심 위치
// const HAND_RADIUS = 10;
// const STRENGTH = 1;
// const centerX = GRID_WIDTH / 2;
// const centerY = GRID_HEIGHT / 2;

// const BORDER_MARGIN = 20; // 가장자리에서 5픽셀 안쪽에는 힘을 주지 않음

// for (let y = 0; y < GRID_HEIGHT; y++) {
// for (let x = 0; x < GRID_WIDTH; x++) {
// const dx = x - centerX;
// const dy = y - centerY;
// // 중심으로부터의 거리 계산
// const distance = Math.sqrt(dx * dx + dy * dy);

// // 3. 손바람 반경(RADIUS) 안에 있을 때만 힘을 계산!
// if (distance < HAND_RADIUS &&
// x > BORDER_MARGIN && x < GRID_WIDTH - BORDER_MARGIN &&
// y > BORDER_MARGIN && y < GRID_HEIGHT - BORDER_MARGIN &&
// distance > 0) {
// // 4. (보너스) 가장자리로 갈수록 부드럽게 힘 줄이기 (Falloff)
// const falloff = 1.0 - (distance / HAND_RADIUS);

// const forceX = (dx / distance) * STRENGTH * falloff;
// const forceY = (dy / distance) * STRENGTH * falloff;

// TEMPLATE_2.push(forceX, forceY);
// } else {
// // 5. 반경 밖은 힘을 0으로 설정
// TEMPLATE_2.push(0, 0);
// }
// }
// }
1 change: 1 addition & 0 deletions js/f2d/vector_templates.json

Large diffs are not rendered by default.