@adatechnology/game-interactables-three 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.global.js +2337 -225
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +62 -173
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -697,6 +697,10 @@ function createCord(THREE, points, radius, material, radialSegments = 5) {
|
|
|
697
697
|
return mesh;
|
|
698
698
|
}
|
|
699
699
|
|
|
700
|
+
// src/mesh.ts
|
|
701
|
+
import { createShapeRecorder } from "@adatechnology/game-props3d";
|
|
702
|
+
import { colorChannels, generate } from "@adatechnology/game-shapes";
|
|
703
|
+
|
|
700
704
|
// src/types.ts
|
|
701
705
|
var MATERIAL_KEYS = ["plain", "metal", "wood", "glass", "fabric", "plant", "water", "emissive", "atlas_picture", "atlas_floor"];
|
|
702
706
|
|
|
@@ -1055,214 +1059,99 @@ function createDefaultMaterials(THREE, quality = "high") {
|
|
|
1055
1059
|
atlas_floor: std({ roughness: 1 })
|
|
1056
1060
|
};
|
|
1057
1061
|
}
|
|
1058
|
-
var
|
|
1062
|
+
var colorSpaces = /* @__PURE__ */ new WeakMap();
|
|
1063
|
+
function colorSpaceOf(THREE) {
|
|
1064
|
+
const known = colorSpaces.get(THREE.Color);
|
|
1065
|
+
if (known) return known;
|
|
1066
|
+
const space = detectColorSpace(THREE);
|
|
1067
|
+
colorSpaces.set(THREE.Color, space);
|
|
1068
|
+
return space;
|
|
1069
|
+
}
|
|
1070
|
+
function detectColorSpace(THREE) {
|
|
1071
|
+
const color = new THREE.Color();
|
|
1072
|
+
for (let c = 0; c < 256; c++) {
|
|
1073
|
+
color.setHex(c * 65793);
|
|
1074
|
+
if (color.r !== colorChannels(c * 65793, "linear")[0]) {
|
|
1075
|
+
if (color.r !== c / 255) throw new Error("createMeshApi: this three.js stores colours in a space game-shapes does not know");
|
|
1076
|
+
return "srgb";
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return "linear";
|
|
1080
|
+
}
|
|
1059
1081
|
function createMeshApi(THREE, options = {}) {
|
|
1060
1082
|
const quality = options.quality ?? "high";
|
|
1061
1083
|
const shadows = options.shadows !== false;
|
|
1062
1084
|
const materials = { ...createDefaultMaterials(THREE, quality), ...stripUndefined(options.materials) };
|
|
1063
1085
|
if (options.doubleSidedPlanes) for (const k of MATERIAL_KEYS) materials[k].side = THREE.DoubleSide;
|
|
1064
|
-
const root = { id: null, pivot: [0, 0, 0], ry: 0, acc: /* @__PURE__ */ new Map(), lights: [] };
|
|
1065
|
-
const partTargets = /* @__PURE__ */ new Map();
|
|
1066
|
-
const stack = [];
|
|
1067
|
-
let current = root;
|
|
1068
1086
|
let finished = false;
|
|
1069
|
-
const
|
|
1070
|
-
const
|
|
1071
|
-
let t = templates.get(key);
|
|
1072
|
-
if (!t) {
|
|
1073
|
-
const g = make();
|
|
1074
|
-
t = {
|
|
1075
|
-
pos: g.getAttribute("position").array,
|
|
1076
|
-
nor: g.getAttribute("normal").array,
|
|
1077
|
-
uv: g.getAttribute("uv").array,
|
|
1078
|
-
idx: g.index ? g.index.array : Array.from({ length: g.getAttribute("position").count }, (_, i) => i)
|
|
1079
|
-
};
|
|
1080
|
-
templates.set(key, t);
|
|
1081
|
-
}
|
|
1082
|
-
return t;
|
|
1083
|
-
};
|
|
1084
|
-
const segs = quality === "high" ? { cyl: 12, sphereW: 12, sphereH: 8 } : { cyl: 8, sphereW: 8, sphereH: 6 };
|
|
1085
|
-
const tBox = () => template("box", () => new THREE.BoxGeometry(1, 1, 1));
|
|
1086
|
-
const tCyl = (shape) => {
|
|
1087
|
-
switch (shape) {
|
|
1088
|
-
case "cyl6":
|
|
1089
|
-
return template("cyl6", () => new THREE.CylinderGeometry(1, 1, 1, 6));
|
|
1090
|
-
case "cone":
|
|
1091
|
-
return template("cone", () => new THREE.CylinderGeometry(0, 1, 1, segs.cyl));
|
|
1092
|
-
case "sphere":
|
|
1093
|
-
return template("sphere", () => new THREE.SphereGeometry(1, segs.sphereW, segs.sphereH));
|
|
1094
|
-
default:
|
|
1095
|
-
return template("cyl", () => new THREE.CylinderGeometry(1, 1, 1, segs.cyl));
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1098
|
-
const tPlane = () => template("plane", () => new THREE.PlaneGeometry(1, 1));
|
|
1099
|
-
const tLeaf = () => {
|
|
1100
|
-
let t = templates.get("leaf");
|
|
1101
|
-
if (!t) {
|
|
1102
|
-
const f = [0, 0, 0, -0.5, 0.4, 0, 0.5, 0.4, 0, 0, 1, 0];
|
|
1103
|
-
const pos = [];
|
|
1104
|
-
const nor = [];
|
|
1105
|
-
const uv = [];
|
|
1106
|
-
const idx = [];
|
|
1107
|
-
for (const side of [1, -1]) {
|
|
1108
|
-
const base = pos.length / 3;
|
|
1109
|
-
for (let i = 0; i < 4; i++) {
|
|
1110
|
-
pos.push(f[i * 3], f[i * 3 + 1], f[i * 3 + 2]);
|
|
1111
|
-
nor.push(0, 0, side);
|
|
1112
|
-
uv.push(f[i * 3] + 0.5, f[i * 3 + 1]);
|
|
1113
|
-
}
|
|
1114
|
-
if (side === 1) idx.push(base, base + 2, base + 1, base + 1, base + 2, base + 3);
|
|
1115
|
-
else idx.push(base, base + 1, base + 2, base + 1, base + 3, base + 2);
|
|
1116
|
-
}
|
|
1117
|
-
t = { pos: new Float32Array(pos), nor: new Float32Array(nor), uv: new Float32Array(uv), idx };
|
|
1118
|
-
templates.set("leaf", t);
|
|
1119
|
-
}
|
|
1120
|
-
return t;
|
|
1121
|
-
};
|
|
1122
|
-
const m4 = new THREE.Matrix4();
|
|
1123
|
-
const mItem = new THREE.Matrix4();
|
|
1124
|
-
const mTotal = new THREE.Matrix4();
|
|
1125
|
-
const n3 = new THREE.Matrix3();
|
|
1126
|
-
const v = new THREE.Vector3();
|
|
1127
|
-
const quat = new THREE.Quaternion();
|
|
1128
|
-
const euler = new THREE.Euler();
|
|
1129
|
-
const scl = new THREE.Vector3();
|
|
1130
|
-
const color = new THREE.Color();
|
|
1131
|
-
const canonical = /* @__PURE__ */ new Map();
|
|
1087
|
+
const recorder = quality === "high" ? createShapeRecorder({ cylinderSegments: 12, sphereSegments: [12, 8] }) : createShapeRecorder({ cylinderSegments: 8, sphereSegments: [8, 6] });
|
|
1088
|
+
const materialAlias = {};
|
|
1132
1089
|
for (const k of MATERIAL_KEYS) {
|
|
1133
1090
|
const first = MATERIAL_KEYS.find((o) => materials[o] === materials[k]) ?? k;
|
|
1134
|
-
|
|
1091
|
+
if (first !== k) materialAlias[k] = first;
|
|
1135
1092
|
}
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
if (!a) {
|
|
1140
|
-
a = { pos: [], nor: [], col: [], uv: [], idx: [] };
|
|
1141
|
-
current.acc.set(key, a);
|
|
1142
|
-
}
|
|
1143
|
-
return a;
|
|
1144
|
-
};
|
|
1145
|
-
const frame = (M) => {
|
|
1146
|
-
mItem.makeRotationY(M?.ry ?? 0);
|
|
1147
|
-
mItem.setPosition(M?.x ?? 0, 0, M?.z ?? 0);
|
|
1148
|
-
return mItem;
|
|
1149
|
-
};
|
|
1150
|
-
const emit = (requested, M, local, t, hexColor, uv) => {
|
|
1151
|
-
const key = requested in materials ? requested : "plain";
|
|
1152
|
-
const a = accOf(key);
|
|
1153
|
-
const total = mTotal.copy(frame(M)).multiply(local);
|
|
1154
|
-
n3.getNormalMatrix(total);
|
|
1155
|
-
const base = a.pos.length / 3;
|
|
1156
|
-
color.setHex(hexColor);
|
|
1157
|
-
const count = t.pos.length / 3;
|
|
1158
|
-
const e = total.elements;
|
|
1159
|
-
const nm = n3.elements;
|
|
1160
|
-
const u = uv ?? IDENT_UV;
|
|
1161
|
-
for (let i = 0; i < count; i++) {
|
|
1162
|
-
const x = t.pos[i * 3];
|
|
1163
|
-
const y = t.pos[i * 3 + 1];
|
|
1164
|
-
const z = t.pos[i * 3 + 2];
|
|
1165
|
-
a.pos.push(e[0] * x + e[4] * y + e[8] * z + e[12], e[1] * x + e[5] * y + e[9] * z + e[13], e[2] * x + e[6] * y + e[10] * z + e[14]);
|
|
1166
|
-
const nx = t.nor[i * 3];
|
|
1167
|
-
const ny = t.nor[i * 3 + 1];
|
|
1168
|
-
const nz = t.nor[i * 3 + 2];
|
|
1169
|
-
v.set(nm[0] * nx + nm[3] * ny + nm[6] * nz, nm[1] * nx + nm[4] * ny + nm[7] * nz, nm[2] * nx + nm[5] * ny + nm[8] * nz).normalize();
|
|
1170
|
-
a.nor.push(v.x, v.y, v.z);
|
|
1171
|
-
a.col.push(color.r, color.g, color.b);
|
|
1172
|
-
a.uv.push(u.u0 + t.uv[i * 2] * (u.u1 - u.u0), u.v0 + t.uv[i * 2 + 1] * (u.v1 - u.v0));
|
|
1173
|
-
}
|
|
1174
|
-
for (let i = 0; i < t.idx.length; i++) a.idx.push(base + t.idx[i]);
|
|
1175
|
-
};
|
|
1176
|
-
const compose = (x, y, z, rx, ry, rz, sx, sy, sz) => {
|
|
1177
|
-
euler.set(rx, ry, rz, "YXZ");
|
|
1178
|
-
quat.setFromEuler(euler);
|
|
1179
|
-
return m4.compose(v.set(x, y, z), quat, scl.set(sx, sy, sz));
|
|
1180
|
-
};
|
|
1181
|
-
const api = {
|
|
1182
|
-
box(M, material, x, y, z, w, h, d, col, ry, rx, rz) {
|
|
1183
|
-
emit(material, M, compose(x, y + h / 2, z, rx, ry, rz, w, h, d), tBox(), col);
|
|
1184
|
-
},
|
|
1185
|
-
cylinder(M, material, x, y, z, r, h, col, rx, rz, shape) {
|
|
1186
|
-
const tilted = rx !== 0 || rz !== 0;
|
|
1187
|
-
const cy = shape === "sphere" ? y + h / 2 : tilted ? y : y + h / 2;
|
|
1188
|
-
const sy = shape === "sphere" ? h / 2 : h;
|
|
1189
|
-
emit(material, M, compose(x, cy, z, rx, 0, rz, r, sy, r), tCyl(shape), col);
|
|
1190
|
-
},
|
|
1191
|
-
leaf(M, x, y, z, w, h, col, ry, rx) {
|
|
1192
|
-
emit("plant", M, compose(x, y, z, rx, ry, 0, w, h, 1), tLeaf(), col);
|
|
1193
|
-
},
|
|
1194
|
-
plane(M, material, x, y, z, w, h, col, ry, rx, rz, uv) {
|
|
1195
|
-
emit(material, M, compose(x, y, z, rx, ry, rz, w, h, 1), tPlane(), col, uv);
|
|
1196
|
-
},
|
|
1197
|
-
light(M, x, y, z, col, range) {
|
|
1198
|
-
const fr = frame(M);
|
|
1199
|
-
v.set(x, y, z).applyMatrix4(fr);
|
|
1200
|
-
current.lights.push({ x: v.x, y: v.y, z: v.z, color: col, range });
|
|
1201
|
-
},
|
|
1202
|
-
part(id, pivot, ry, draw) {
|
|
1203
|
-
let target = partTargets.get(id);
|
|
1204
|
-
if (!target) {
|
|
1205
|
-
target = { id, pivot: [pivot[0], pivot[1], pivot[2]], ry, acc: /* @__PURE__ */ new Map(), lights: [] };
|
|
1206
|
-
partTargets.set(id, target);
|
|
1207
|
-
}
|
|
1208
|
-
stack.push(current);
|
|
1209
|
-
current = target;
|
|
1210
|
-
try {
|
|
1211
|
-
draw();
|
|
1212
|
-
} finally {
|
|
1213
|
-
current = stack.pop() ?? root;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
};
|
|
1217
|
-
const build = (target, into, materialFor) => {
|
|
1218
|
-
for (const [key, a] of target.acc) {
|
|
1219
|
-
if (!a.idx.length) continue;
|
|
1093
|
+
const lightsOut = [];
|
|
1094
|
+
const build = (batches, points, id, into, materialFor) => {
|
|
1095
|
+
for (const batch of batches) {
|
|
1220
1096
|
const g = new THREE.BufferGeometry();
|
|
1221
|
-
g.setAttribute("position", new THREE.
|
|
1222
|
-
g.setAttribute("normal", new THREE.
|
|
1223
|
-
g.setAttribute("color", new THREE.
|
|
1224
|
-
g.setAttribute("uv", new THREE.
|
|
1225
|
-
g.setIndex(
|
|
1097
|
+
g.setAttribute("position", new THREE.BufferAttribute(batch.positions, 3));
|
|
1098
|
+
g.setAttribute("normal", new THREE.BufferAttribute(batch.normals, 3));
|
|
1099
|
+
g.setAttribute("color", new THREE.BufferAttribute(batch.colors, 3));
|
|
1100
|
+
g.setAttribute("uv", new THREE.BufferAttribute(batch.uvs, 2));
|
|
1101
|
+
g.setIndex(new THREE.BufferAttribute(batch.indices, 1));
|
|
1226
1102
|
g.computeBoundingSphere();
|
|
1227
1103
|
g.computeBoundingBox();
|
|
1228
|
-
const
|
|
1229
|
-
mesh
|
|
1230
|
-
|
|
1104
|
+
const key = batch.material;
|
|
1105
|
+
const mesh = new THREE.Mesh(g, materialFor?.(key, id) ?? materials[key]);
|
|
1106
|
+
mesh.name = id === null ? `static:${key}` : `part:${id}`;
|
|
1107
|
+
if (id !== null) mesh.userData.part = id;
|
|
1231
1108
|
mesh.userData.material = key;
|
|
1232
1109
|
mesh.castShadow = shadows;
|
|
1233
1110
|
mesh.receiveShadow = shadows;
|
|
1234
1111
|
into.add(mesh);
|
|
1235
1112
|
}
|
|
1236
|
-
for (const
|
|
1113
|
+
for (const point of points) {
|
|
1114
|
+
const l = point.data;
|
|
1237
1115
|
const light = new THREE.PointLight(l.color, 1, l.range);
|
|
1238
|
-
light.position.set(
|
|
1116
|
+
light.position.set(point.position[0], point.position[1], point.position[2]);
|
|
1239
1117
|
into.add(light);
|
|
1240
1118
|
lightsOut.push(light);
|
|
1241
1119
|
}
|
|
1242
1120
|
};
|
|
1243
|
-
const lightsOut = [];
|
|
1244
1121
|
return {
|
|
1245
|
-
api,
|
|
1122
|
+
api: recorder.api,
|
|
1246
1123
|
materials,
|
|
1247
1124
|
finish(spec, finishOptions) {
|
|
1248
1125
|
const materialFor = finishOptions?.materialFor ?? options.materialFor;
|
|
1249
1126
|
if (finished) throw new Error("createMeshApi: finish() was already called");
|
|
1250
1127
|
finished = true;
|
|
1128
|
+
const buffers = generate(recorder.schema(), {
|
|
1129
|
+
colors: colorSpaceOf(THREE),
|
|
1130
|
+
materials: MATERIAL_KEYS,
|
|
1131
|
+
fallbackMaterial: "plain",
|
|
1132
|
+
materialAlias,
|
|
1133
|
+
validate: false,
|
|
1134
|
+
key: false
|
|
1135
|
+
});
|
|
1136
|
+
const batches = buffers.levels[0]?.batches ?? [];
|
|
1137
|
+
const of = (id) => ({ batches: batches.filter((b) => b.part === id), points: buffers.points.filter((p) => p.part === id) });
|
|
1251
1138
|
const group = new THREE.Group();
|
|
1252
1139
|
group.name = "interactable";
|
|
1253
1140
|
const parts = {};
|
|
1254
|
-
|
|
1255
|
-
|
|
1141
|
+
const body = of(null);
|
|
1142
|
+
build(body.batches, body.points, null, group, materialFor);
|
|
1143
|
+
for (const part of recorder.parts) {
|
|
1256
1144
|
const g = new THREE.Group();
|
|
1257
|
-
g.name = `part:${id}`;
|
|
1258
|
-
g.userData.part = id;
|
|
1259
|
-
g.position.set(
|
|
1260
|
-
g.rotation.y =
|
|
1145
|
+
g.name = `part:${part.id}`;
|
|
1146
|
+
g.userData.part = part.id;
|
|
1147
|
+
g.position.set(part.pivot[0], part.pivot[1], part.pivot[2]);
|
|
1148
|
+
g.rotation.y = part.ry;
|
|
1261
1149
|
g.updateMatrix();
|
|
1262
|
-
|
|
1150
|
+
const drawn = of(part.id);
|
|
1151
|
+
build(drawn.batches, drawn.points, part.id, g, materialFor);
|
|
1263
1152
|
rememberRest(g);
|
|
1264
1153
|
group.add(g);
|
|
1265
|
-
parts[id] = g;
|
|
1154
|
+
parts[part.id] = g;
|
|
1266
1155
|
}
|
|
1267
1156
|
if (spec && (options.mergeParts ?? quality === "game")) mergeParts(THREE, group, parts, spec, { swayAmplitude: options.swayAmplitude });
|
|
1268
1157
|
if (spec) tagDestruction(group, parts, spec);
|