Skip to content

Commit ba75bbe

Browse files
committed
纹理支持
1 parent 663795f commit ba75bbe

54 files changed

Lines changed: 978 additions & 35 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎assets/composition_builder/js/expression_runtime.js‎

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,52 @@ export function createExpressionRuntime(options = {}) {
3636
};
3737
};
3838
const runtimeVector3f = (x = 0, y = 0, z = 0) => ({ x: toNum(x), y: toNum(y), z: toNum(z) });
39+
const runtimeRandom = {
40+
nextInt(fromOrUntil, until) {
41+
if (until === undefined) {
42+
const u = Math.floor(toNum(fromOrUntil, 1));
43+
if (u <= 0) return 0;
44+
return Math.floor(Math.random() * u);
45+
}
46+
const f = Math.floor(toNum(fromOrUntil, 0));
47+
const u = Math.floor(toNum(until, 1));
48+
if (u <= f) return f;
49+
return f + Math.floor(Math.random() * (u - f));
50+
},
51+
nextDouble(fromOrUntil, until) {
52+
if (until === undefined) {
53+
const u = toNum(fromOrUntil, 1.0);
54+
if (u <= 0) return 0;
55+
return Math.random() * u;
56+
}
57+
const f = toNum(fromOrUntil, 0);
58+
const u = toNum(until, 1.0);
59+
if (u <= f) return f;
60+
return f + Math.random() * (u - f);
61+
},
62+
nextFloat(fromOrUntil, until) {
63+
return runtimeRandom.nextDouble(fromOrUntil, until);
64+
},
65+
nextBoolean() {
66+
return Math.random() < 0.5;
67+
},
68+
nextLong(fromOrUntil, until) {
69+
return runtimeRandom.nextInt(fromOrUntil, until);
70+
}
71+
};
3972
let baseVarsNoVector = Object.freeze({
4073
PI: Math.PI,
4174
RelativeLocation: runtimeRelativeLocation,
4275
Vec3: runtimeVec3,
43-
Vector3f: runtimeVector3f
76+
Vector3f: runtimeVector3f,
77+
Random: runtimeRandom
4478
});
4579
let baseVarsWithVector = Object.freeze({
4680
PI: Math.PI,
4781
RelativeLocation: runtimeRelativeLocation,
4882
Vec3: runtimeVec3,
49-
Vector3f: runtimeVector3f
83+
Vector3f: runtimeVector3f,
84+
Random: runtimeRandom
5085
});
5186

5287
const toIdentifier = (rawName) => {
@@ -103,7 +138,8 @@ export function createExpressionRuntime(options = {}) {
103138
PI: Math.PI,
104139
RelativeLocation: runtimeRelativeLocation,
105140
Vec3: runtimeVec3,
106-
Vector3f: runtimeVector3f
141+
Vector3f: runtimeVector3f,
142+
Random: runtimeRandom
107143
};
108144
const vecMap = new Map();
109145

‎assets/composition_builder/js/main.js‎

Lines changed: 121 additions & 17 deletions
Large diffs are not rendered by default.

‎assets/composition_builder/js/preview_runtime_mixin.js‎

Lines changed: 312 additions & 6 deletions
Large diffs are not rendered by default.

‎assets/emitter_generator/js/generate.js‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ import {
3838
validateDoTickExpressionSource,
3939
normalizeDoTickExpression,
4040
} from "./do_tick_expression.js";
41+
import {
42+
loadAllParticleData,
43+
getParticleDataByName,
44+
getParticleEffectNames,
45+
setBasePath,
46+
} from "../../particles/particle_data_loader.js";
4147

4248
(() => {
4349
const EMITTER_TYPE_META = {
@@ -86,6 +92,7 @@ import {
8692
"particle.velSpeedMax": "速度倍率上限。",
8793
"particle.visibleRange": "可见范围(作用于游戏渲染距离)。",
8894

95+
"template.effectClass": "粒子效果类(决定粒子纹理和渲染方式)。",
8996
"template.alpha": "透明度(0~1)。",
9097
"template.light": "亮度(0~15)。",
9198
"template.sign": "粒子 sign 标识(用于 Command 过滤)。",
@@ -137,6 +144,7 @@ import {
137144
"emitter.spiral.axis.x": "螺旋轴 X(会 normalize)。",
138145
"emitter.spiral.axis.y": "螺旋轴 Y(会 normalize)。",
139146
"emitter.spiral.axis.z": "螺旋轴 Z(会 normalize)。",
147+
"template.useTexture": "预览是否使用纹理渲染;关闭后始终使用纯色点渲染。",
140148
};
141149

142150
const uid = () => (Math.random().toString(16).slice(2) + Date.now().toString(16)).slice(0, 12);
@@ -186,6 +194,8 @@ import {
186194
colorEnd: "#d04dff",
187195
},
188196
template: {
197+
effectClass: "ControlableEndRodEffect",
198+
useTexture: true,
189199
alpha: 1.0,
190200
light: 15,
191201
faceToCamera: true,
@@ -209,6 +219,7 @@ import {
209219
emitters: [makeDefaultEmitterCard()],
210220
playing: true,
211221
autoPaused: false,
222+
focusedEmitterId: "",
212223
ticksPerSecond: 20,
213224
fullscreen: false,
214225
kotlin: {
@@ -614,6 +625,8 @@ import {
614625

615626

616627
const template = card.template || (card.template = {});
628+
template.effectClass = String(template.effectClass || "ControlableEndRodEffect").trim();
629+
template.useTexture = (template.useTexture !== false);
617630
template.alpha = normalizeNumExpr(template.alpha, 1.0, { min: 0, max: 1 });
618631
template.light = normalizeNumExpr(template.light, 15, { int: true, min: 0, max: 15 });
619632
template.faceToCamera = (template.faceToCamera !== false);
@@ -2410,6 +2423,17 @@ function setVelocitySection($card, mode) {
24102423
});
24112424
}
24122425

2426+
function buildEffectClassOptionsHtml(selected) {
2427+
const dynamicNames = getParticleEffectNames();
2428+
const base = ["ControlableEndRodEffect", "ControlableCloudEffect"];
2429+
const all = Array.from(new Set([...base, ...dynamicNames, String(selected || "").trim()].filter(Boolean)));
2430+
return all.map((name) => {
2431+
const pData = getParticleDataByName(name);
2432+
const label = pData && pData.displayName ? `${name} (${pData.displayName})` : name;
2433+
return `<option value="${escapeHtml(name)}" ${name === selected ? "selected" : ""}>${escapeHtml(label)}</option>`;
2434+
}).join("");
2435+
}
2436+
24132437
function renderEmitterList() {
24142438
const $list = $("#emitList");
24152439
$list.empty();
@@ -2589,6 +2613,19 @@ function setVelocitySection($card, mode) {
25892613
` </div>`,
25902614

25912615
` <div class="panel-subtitle">模板参数(高级)</div>`,
2616+
` <div class="field" style="margin-bottom:6px">`,
2617+
` <label>粒子效果类</label>`,
2618+
` <select class="emitInput" data-key="template.effectClass" data-type="string">`,
2619+
` ${buildEffectClassOptionsHtml(card.template && card.template.effectClass)}`,
2620+
` </select>`,
2621+
` </div>`,
2622+
` <div class="field" style="margin-bottom:6px">`,
2623+
` <label>预览纹理渲染</label>`,
2624+
` <select class="emitInput" data-key="template.useTexture" data-type="bool">`,
2625+
` <option value="1" ${(card.template && card.template.useTexture === false) ? "" : "selected"}>启用</option>`,
2626+
` <option value="0" ${(card.template && card.template.useTexture === false) ? "selected" : ""}>关闭</option>`,
2627+
` </select>`,
2628+
` </div>`,
25922629
` <div class="grid3">`,
25932630
` <div class="field">`,
25942631
` <label>alpha(0~1)</label>`,
@@ -2836,6 +2873,7 @@ function setVelocitySection($card, mode) {
28362873
$card.find('[data-key="externalTemplate"]').val(card.externalTemplate ? "1" : "0");
28372874
$card.find('[data-key="externalData"]').val(card.externalData ? "1" : "0");
28382875
$card.find('[data-key="particle.velMode"]').val(card.particle && card.particle.velMode ? card.particle.velMode : "fixed");
2876+
$card.find('[data-key="template.useTexture"]').val((card.template && card.template.useTexture === false) ? "0" : "1");
28392877
$card.find('[data-key="template.faceToCamera"]').val((card.template && card.template.faceToCamera === false) ? "0" : "1");
28402878
$card.find('[data-key="emitter.box.surface"]').val(card.emitter.box.surface ? "1" : "0");
28412879

@@ -3071,6 +3109,8 @@ function setVelocitySection($card, mode) {
30713109
}
30723110

30733111
const EXTERNAL_TEMPLATE_SYNC_FIELDS = [
3112+
"template.effectClass",
3113+
"template.useTexture",
30743114
"template.alpha",
30753115
"template.light",
30763116
"template.faceToCamera",
@@ -7887,6 +7927,7 @@ function setVelocitySection($card, mode) {
78877927
if (focusedEmitterId !== id) {
78887928
handleEmitterCollapseFocusChange(focusedEmitterId, id);
78897929
focusedEmitterId = id;
7930+
state.focusedEmitterId = id;
78907931
}
78917932
});
78927933

@@ -7896,6 +7937,7 @@ function setVelocitySection($card, mode) {
78967937
if (!focusedEmitterId) return;
78977938
handleEmitterCollapseFocusChange(focusedEmitterId, null);
78987939
focusedEmitterId = null;
7940+
state.focusedEmitterId = "";
78997941
});
79007942

79017943
$("#emitList").on("click", ".emitHead", function (e) {
@@ -7943,6 +7985,7 @@ function setVelocitySection($card, mode) {
79437985
return;
79447986
}
79457987
state.emitters = state.emitters.filter((it) => it.id !== id);
7988+
if (state.focusedEmitterId === id) state.focusedEmitterId = "";
79467989
if (emitterSync) removeEmitterSyncTarget(id);
79477990
cardHistory.push();
79487991
renderEmitterList();
@@ -8754,6 +8797,11 @@ function setVelocitySection($card, mode) {
87548797
syncEmitterBuilderVarContextStorage();
87558798
const returnedEmitterId = consumeEmitterBuilderReturnState();
87568799

8800+
setBasePath("./");
8801+
loadAllParticleData().then(() => {
8802+
renderEmitterList();
8803+
});
8804+
87578805
initPanelTools();
87588806
applyStateToForm();
87598807
// Queue list page can be the default workspace. Render once to bind

‎assets/emitter_generator/js/kotlin_gen.js‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,14 @@ function buildLocalDataApplyLines(card) {
929929
push("");
930930
}
931931

932+
const effectClass = String(card?.template?.effectClass || "ControlableEndRodEffect").trim();
933+
if (effectClass) {
934+
push(`${pad2}${tVar}.apply {`);
935+
push(`${pad2} effect = ${safeIdent(effectClass, "ControlableEndRodEffect")}(uuid)`);
936+
push(`${pad2}}`);
937+
push("");
938+
}
939+
932940
// res.addAll block
933941
push(`${pad2}res.addAll(`);
934942

@@ -978,4 +986,3 @@ function buildLocalDataApplyLines(card) {
978986

979987
return out.join("\n");
980988
}
981-

0 commit comments

Comments
 (0)