@scenoco-three/create 0.0.0-stage → 0.7.0
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/LICENSE +21 -0
- package/index.js +30 -0
- package/package.json +38 -4
- package/template/.mcp.json +1 -0
- package/template/AGENTS.md +77 -0
- package/template/CLAUDE.md +1 -0
- package/template/gitignore +2 -0
- package/template/index.html +18 -0
- package/template/node_modules/.vite/deps/_metadata.json +32 -0
- package/template/node_modules/.vite/deps/package.json +3 -0
- package/template/node_modules/.vite/deps/three.js +2 -0
- package/template/node_modules/.vite/deps/three.module-Dhi4sXJn.js +51948 -0
- package/template/node_modules/.vite/deps/three.module-Dhi4sXJn.js.map +1 -0
- package/template/node_modules/.vite/deps/three_addons_loaders_GLTFLoader__js.js +2512 -0
- package/template/node_modules/.vite/deps/three_addons_loaders_GLTFLoader__js.js.map +1 -0
- package/template/node_modules/.vite/deps/three_addons_utils_SkeletonUtils__js.js +210 -0
- package/template/node_modules/.vite/deps/three_addons_utils_SkeletonUtils__js.js.map +1 -0
- package/template/package.json +21 -0
- package/template/src/Dropper.ts +11 -0
- package/template/src/Fade.ts +15 -0
- package/template/src/Follow.ts +14 -0
- package/template/src/Mover.ts +12 -0
- package/template/src/main.ts +5 -0
- package/template/src/scenes/Main.tsx +32 -0
- package/template/src/scenes/prefabs.tsx +11 -0
- package/template/tsconfig.json +16 -0
- package/template/vite.config.ts +7 -0
- package/README.md +0 -3
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Bs as Vector3, Hs as VectorKeyframeTrack, Io as SkeletonHelper, S as AnimationMixer, _a as Quaternion, b as AnimationClip, ni as Matrix4, va as QuaternionKeyframeTrack } from "./three.module-Dhi4sXJn.js";
|
|
2
|
+
//#region node_modules/three/examples/jsm/utils/SkeletonUtils.js
|
|
3
|
+
/**
|
|
4
|
+
* @module SkeletonUtils
|
|
5
|
+
* @three_import import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';
|
|
6
|
+
*/
|
|
7
|
+
function getBoneName(bone, options) {
|
|
8
|
+
if (options.getBoneName !== void 0) return options.getBoneName(bone);
|
|
9
|
+
return options.names[bone.name];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Retargets the skeleton from the given source to the target.
|
|
13
|
+
*
|
|
14
|
+
* Both `target` and `source` can be a 3D object with a skeleton property (e.g. a skinned mesh)
|
|
15
|
+
* or a {@link Skeleton} directly.
|
|
16
|
+
*
|
|
17
|
+
* @param {Object3D|Skeleton} target - The target object.
|
|
18
|
+
* @param {Object3D|Skeleton} source - The source object.
|
|
19
|
+
* @param {module:SkeletonUtils~RetargetOptions} options - The options.
|
|
20
|
+
*/
|
|
21
|
+
function retarget(target, source, options = {}) {
|
|
22
|
+
const quat = new Quaternion(), scale = new Vector3(), relativeMatrix = new Matrix4(), globalMatrix = new Matrix4();
|
|
23
|
+
options.preserveBoneMatrix = options.preserveBoneMatrix !== void 0 ? options.preserveBoneMatrix : true;
|
|
24
|
+
options.preserveBonePositions = options.preserveBonePositions !== void 0 ? options.preserveBonePositions : true;
|
|
25
|
+
options.useTargetMatrix = options.useTargetMatrix !== void 0 ? options.useTargetMatrix : false;
|
|
26
|
+
options.hip = options.hip !== void 0 ? options.hip : "hip";
|
|
27
|
+
options.hipInfluence = options.hipInfluence !== void 0 ? options.hipInfluence : new Vector3(1, 1, 1);
|
|
28
|
+
options.scale = options.scale !== void 0 ? options.scale : 1;
|
|
29
|
+
options.names = options.names || {};
|
|
30
|
+
const sourceBones = source.isObject3D ? source.skeleton.bones : getBones(source), bones = target.isObject3D ? target.skeleton.bones : getBones(target);
|
|
31
|
+
let bone, name, boneTo, bonesPosition;
|
|
32
|
+
if (target.isObject3D) target.skeleton.pose();
|
|
33
|
+
else {
|
|
34
|
+
options.useTargetMatrix = true;
|
|
35
|
+
options.preserveBoneMatrix = false;
|
|
36
|
+
}
|
|
37
|
+
if (options.preserveBonePositions) {
|
|
38
|
+
bonesPosition = [];
|
|
39
|
+
for (let i = 0; i < bones.length; i++) bonesPosition.push(bones[i].position.clone());
|
|
40
|
+
}
|
|
41
|
+
if (options.preserveBoneMatrix) {
|
|
42
|
+
target.updateMatrixWorld();
|
|
43
|
+
target.matrixWorld.identity();
|
|
44
|
+
for (let i = 0; i < target.children.length; ++i) target.children[i].updateMatrixWorld(true);
|
|
45
|
+
}
|
|
46
|
+
for (let i = 0; i < bones.length; ++i) {
|
|
47
|
+
bone = bones[i];
|
|
48
|
+
name = getBoneName(bone, options);
|
|
49
|
+
boneTo = getBoneByName(name, sourceBones);
|
|
50
|
+
globalMatrix.copy(bone.matrixWorld);
|
|
51
|
+
if (boneTo) {
|
|
52
|
+
boneTo.updateMatrixWorld();
|
|
53
|
+
if (options.useTargetMatrix) relativeMatrix.copy(boneTo.matrixWorld);
|
|
54
|
+
else {
|
|
55
|
+
relativeMatrix.copy(target.matrixWorld).invert();
|
|
56
|
+
relativeMatrix.multiply(boneTo.matrixWorld);
|
|
57
|
+
}
|
|
58
|
+
scale.setFromMatrixScale(relativeMatrix);
|
|
59
|
+
relativeMatrix.scale(scale.set(1 / scale.x, 1 / scale.y, 1 / scale.z));
|
|
60
|
+
globalMatrix.makeRotationFromQuaternion(quat.setFromRotationMatrix(relativeMatrix));
|
|
61
|
+
if (target.isObject3D) {
|
|
62
|
+
if (options.localOffsets) {
|
|
63
|
+
if (options.localOffsets[bone.name]) globalMatrix.multiply(options.localOffsets[bone.name]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
globalMatrix.copyPosition(relativeMatrix);
|
|
67
|
+
}
|
|
68
|
+
if (name === options.hip) {
|
|
69
|
+
globalMatrix.elements[12] *= options.scale * options.hipInfluence.x;
|
|
70
|
+
globalMatrix.elements[13] *= options.scale * options.hipInfluence.y;
|
|
71
|
+
globalMatrix.elements[14] *= options.scale * options.hipInfluence.z;
|
|
72
|
+
if (options.hipPosition !== void 0) {
|
|
73
|
+
globalMatrix.elements[12] += options.hipPosition.x * options.scale;
|
|
74
|
+
globalMatrix.elements[13] += options.hipPosition.y * options.scale;
|
|
75
|
+
globalMatrix.elements[14] += options.hipPosition.z * options.scale;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (bone.parent) {
|
|
79
|
+
bone.matrix.copy(bone.parent.matrixWorld).invert();
|
|
80
|
+
bone.matrix.multiply(globalMatrix);
|
|
81
|
+
} else bone.matrix.copy(globalMatrix);
|
|
82
|
+
bone.matrix.decompose(bone.position, bone.quaternion, bone.scale);
|
|
83
|
+
bone.updateMatrixWorld();
|
|
84
|
+
}
|
|
85
|
+
if (options.preserveBonePositions) for (let i = 0; i < bones.length; ++i) {
|
|
86
|
+
bone = bones[i];
|
|
87
|
+
name = getBoneName(bone, options) || bone.name;
|
|
88
|
+
if (name !== options.hip) bone.position.copy(bonesPosition[i]);
|
|
89
|
+
}
|
|
90
|
+
if (options.preserveBoneMatrix) target.updateMatrixWorld(true);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Retargets the animation clip of the source to the target 3D object.
|
|
94
|
+
*
|
|
95
|
+
* The `source` can be a 3D object with a skeleton property (e.g. a skinned mesh)
|
|
96
|
+
* or a {@link Skeleton} directly.
|
|
97
|
+
*
|
|
98
|
+
* @param {Object3D} target - The target 3D object. Must have a `skeleton` property.
|
|
99
|
+
* @param {Object3D|Skeleton} source - The source object.
|
|
100
|
+
* @param {AnimationClip} clip - The animation clip.
|
|
101
|
+
* @param {module:SkeletonUtils~RetargetOptions} options - The options.
|
|
102
|
+
* @return {AnimationClip} The retargeted animation clip.
|
|
103
|
+
*/
|
|
104
|
+
function retargetClip(target, source, clip, options = {}) {
|
|
105
|
+
options.useFirstFramePosition = options.useFirstFramePosition !== void 0 ? options.useFirstFramePosition : false;
|
|
106
|
+
options.fps = options.fps !== void 0 ? options.fps : Math.max(...clip.tracks.map((track) => track.times.length)) / clip.duration;
|
|
107
|
+
options.names = options.names || [];
|
|
108
|
+
if (!source.isObject3D) source = getHelperFromSkeleton(source);
|
|
109
|
+
const numFrames = Math.round(clip.duration * (options.fps / 1e3) * 1e3), delta = clip.duration / (numFrames - 1), convertedTracks = [], mixer = new AnimationMixer(source), bones = getBones(target.skeleton), boneDatas = [];
|
|
110
|
+
let positionOffset, bone, boneTo, boneData, name;
|
|
111
|
+
mixer.clipAction(clip).play();
|
|
112
|
+
let start = 0, end = numFrames;
|
|
113
|
+
if (options.trim !== void 0) {
|
|
114
|
+
start = Math.round(options.trim[0] * options.fps);
|
|
115
|
+
end = Math.min(Math.round(options.trim[1] * options.fps), numFrames) - start;
|
|
116
|
+
mixer.update(options.trim[0]);
|
|
117
|
+
} else mixer.update(0);
|
|
118
|
+
source.updateMatrixWorld();
|
|
119
|
+
for (let frame = 0; frame < end; ++frame) {
|
|
120
|
+
const time = frame * delta;
|
|
121
|
+
retarget(target, source, options);
|
|
122
|
+
for (let j = 0; j < bones.length; ++j) {
|
|
123
|
+
bone = bones[j];
|
|
124
|
+
name = getBoneName(bone, options) || bone.name;
|
|
125
|
+
boneTo = getBoneByName(name, source.skeleton);
|
|
126
|
+
if (boneTo) {
|
|
127
|
+
boneData = boneDatas[j] = boneDatas[j] || { bone };
|
|
128
|
+
if (options.hip === name) {
|
|
129
|
+
if (!boneData.pos) boneData.pos = {
|
|
130
|
+
times: new Float32Array(end),
|
|
131
|
+
values: new Float32Array(end * 3)
|
|
132
|
+
};
|
|
133
|
+
if (options.useFirstFramePosition) {
|
|
134
|
+
if (frame === 0) positionOffset = bone.position.clone();
|
|
135
|
+
bone.position.sub(positionOffset);
|
|
136
|
+
}
|
|
137
|
+
boneData.pos.times[frame] = time;
|
|
138
|
+
bone.position.toArray(boneData.pos.values, frame * 3);
|
|
139
|
+
}
|
|
140
|
+
if (!boneData.quat) boneData.quat = {
|
|
141
|
+
times: new Float32Array(end),
|
|
142
|
+
values: new Float32Array(end * 4)
|
|
143
|
+
};
|
|
144
|
+
boneData.quat.times[frame] = time;
|
|
145
|
+
bone.quaternion.toArray(boneData.quat.values, frame * 4);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (frame === end - 2) mixer.update(delta - 1e-7);
|
|
149
|
+
else mixer.update(delta);
|
|
150
|
+
source.updateMatrixWorld();
|
|
151
|
+
}
|
|
152
|
+
for (let i = 0; i < boneDatas.length; ++i) {
|
|
153
|
+
boneData = boneDatas[i];
|
|
154
|
+
if (boneData) {
|
|
155
|
+
if (boneData.pos) convertedTracks.push(new VectorKeyframeTrack(".bones[" + boneData.bone.name + "].position", boneData.pos.times, boneData.pos.values));
|
|
156
|
+
convertedTracks.push(new QuaternionKeyframeTrack(".bones[" + boneData.bone.name + "].quaternion", boneData.quat.times, boneData.quat.values));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
mixer.uncacheAction(clip);
|
|
160
|
+
return new AnimationClip(clip.name, -1, convertedTracks);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Clones the given 3D object and its descendants, ensuring that any `SkinnedMesh` instances are
|
|
164
|
+
* correctly associated with their bones. Bones are also cloned, and must be descendants of the
|
|
165
|
+
* object passed to this method. Other data, like geometries and materials, are reused by reference.
|
|
166
|
+
*
|
|
167
|
+
* @param {Object3D} source - The 3D object to clone.
|
|
168
|
+
* @return {Object3D} The cloned 3D object.
|
|
169
|
+
*/
|
|
170
|
+
function clone(source) {
|
|
171
|
+
const sourceLookup = /* @__PURE__ */ new Map();
|
|
172
|
+
const cloneLookup = /* @__PURE__ */ new Map();
|
|
173
|
+
const clone = source.clone();
|
|
174
|
+
parallelTraverse(source, clone, function(sourceNode, clonedNode) {
|
|
175
|
+
sourceLookup.set(clonedNode, sourceNode);
|
|
176
|
+
cloneLookup.set(sourceNode, clonedNode);
|
|
177
|
+
});
|
|
178
|
+
clone.traverse(function(node) {
|
|
179
|
+
if (!node.isSkinnedMesh) return;
|
|
180
|
+
const clonedMesh = node;
|
|
181
|
+
const sourceMesh = sourceLookup.get(node);
|
|
182
|
+
const sourceBones = sourceMesh.skeleton.bones;
|
|
183
|
+
clonedMesh.skeleton = sourceMesh.skeleton.clone();
|
|
184
|
+
clonedMesh.bindMatrix.copy(sourceMesh.bindMatrix);
|
|
185
|
+
clonedMesh.skeleton.bones = sourceBones.map(function(bone) {
|
|
186
|
+
return cloneLookup.get(bone);
|
|
187
|
+
});
|
|
188
|
+
clonedMesh.bind(clonedMesh.skeleton, clonedMesh.bindMatrix);
|
|
189
|
+
});
|
|
190
|
+
return clone;
|
|
191
|
+
}
|
|
192
|
+
function getBoneByName(name, skeleton) {
|
|
193
|
+
for (let i = 0, bones = getBones(skeleton); i < bones.length; i++) if (name === bones[i].name) return bones[i];
|
|
194
|
+
}
|
|
195
|
+
function getBones(skeleton) {
|
|
196
|
+
return Array.isArray(skeleton) ? skeleton : skeleton.bones;
|
|
197
|
+
}
|
|
198
|
+
function getHelperFromSkeleton(skeleton) {
|
|
199
|
+
const source = new SkeletonHelper(skeleton.bones[0]);
|
|
200
|
+
source.skeleton = skeleton;
|
|
201
|
+
return source;
|
|
202
|
+
}
|
|
203
|
+
function parallelTraverse(a, b, callback) {
|
|
204
|
+
callback(a, b);
|
|
205
|
+
for (let i = 0; i < a.children.length; i++) parallelTraverse(a.children[i], b.children[i], callback);
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
export { clone, retarget, retargetClip };
|
|
209
|
+
|
|
210
|
+
//# sourceMappingURL=three_addons_utils_SkeletonUtils__js.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"three_addons_utils_SkeletonUtils__js.js","names":[],"sources":["../../../../../../node_modules/three/examples/jsm/utils/SkeletonUtils.js"],"sourcesContent":["import {\n\tAnimationClip,\n\tAnimationMixer,\n\tMatrix4,\n\tQuaternion,\n\tQuaternionKeyframeTrack,\n\tSkeletonHelper,\n\tVector3,\n\tVectorKeyframeTrack\n} from 'three';\n\n/**\n * @module SkeletonUtils\n * @three_import import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';\n */\n\nfunction getBoneName( bone, options ) {\n\n\tif ( options.getBoneName !== undefined ) {\n\n\t\treturn options.getBoneName( bone );\n\n\t}\n\n\treturn options.names[ bone.name ];\n\n}\n\n/**\n * Retargets the skeleton from the given source to the target.\n *\n * Both `target` and `source` can be a 3D object with a skeleton property (e.g. a skinned mesh)\n * or a {@link Skeleton} directly.\n *\n * @param {Object3D|Skeleton} target - The target object.\n * @param {Object3D|Skeleton} source - The source object.\n * @param {module:SkeletonUtils~RetargetOptions} options - The options.\n */\nfunction retarget( target, source, options = {} ) {\n\n\tconst quat = new Quaternion(),\n\t\tscale = new Vector3(),\n\t\trelativeMatrix = new Matrix4(),\n\t\tglobalMatrix = new Matrix4();\n\n\toptions.preserveBoneMatrix = options.preserveBoneMatrix !== undefined ? options.preserveBoneMatrix : true;\n\toptions.preserveBonePositions = options.preserveBonePositions !== undefined ? options.preserveBonePositions : true;\n\toptions.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;\n\toptions.hip = options.hip !== undefined ? options.hip : 'hip';\n\toptions.hipInfluence = options.hipInfluence !== undefined ? options.hipInfluence : new Vector3( 1, 1, 1 );\n\toptions.scale = options.scale !== undefined ? options.scale : 1;\n\toptions.names = options.names || {};\n\n\tconst sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),\n\t\tbones = target.isObject3D ? target.skeleton.bones : getBones( target );\n\n\tlet bone, name, boneTo,\n\t\tbonesPosition;\n\n\t// reset bones\n\n\tif ( target.isObject3D ) {\n\n\t\ttarget.skeleton.pose();\n\n\t} else {\n\n\t\toptions.useTargetMatrix = true;\n\t\toptions.preserveBoneMatrix = false;\n\n\t}\n\n\tif ( options.preserveBonePositions ) {\n\n\t\tbonesPosition = [];\n\n\t\tfor ( let i = 0; i < bones.length; i ++ ) {\n\n\t\t\tbonesPosition.push( bones[ i ].position.clone() );\n\n\t\t}\n\n\t}\n\n\tif ( options.preserveBoneMatrix ) {\n\n\t\t// reset matrix\n\n\t\ttarget.updateMatrixWorld();\n\n\t\ttarget.matrixWorld.identity();\n\n\t\t// reset children matrix\n\n\t\tfor ( let i = 0; i < target.children.length; ++ i ) {\n\n\t\t\ttarget.children[ i ].updateMatrixWorld( true );\n\n\t\t}\n\n\t}\n\n\tfor ( let i = 0; i < bones.length; ++ i ) {\n\n\t\tbone = bones[ i ];\n\t\tname = getBoneName( bone, options );\n\n\t\tboneTo = getBoneByName( name, sourceBones );\n\n\t\tglobalMatrix.copy( bone.matrixWorld );\n\n\t\tif ( boneTo ) {\n\n\t\t\tboneTo.updateMatrixWorld();\n\n\t\t\tif ( options.useTargetMatrix ) {\n\n\t\t\t\trelativeMatrix.copy( boneTo.matrixWorld );\n\n\t\t\t} else {\n\n\t\t\t\trelativeMatrix.copy( target.matrixWorld ).invert();\n\t\t\t\trelativeMatrix.multiply( boneTo.matrixWorld );\n\n\t\t\t}\n\n\t\t\t// ignore scale to extract rotation\n\n\t\t\tscale.setFromMatrixScale( relativeMatrix );\n\t\t\trelativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );\n\n\t\t\t// apply to global matrix\n\n\t\t\tglobalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );\n\n\t\t\tif ( target.isObject3D ) {\n\n\t\t\t\tif ( options.localOffsets ) {\n\n\t\t\t\t\tif ( options.localOffsets[ bone.name ] ) {\n\n\t\t\t\t\t\tglobalMatrix.multiply( options.localOffsets[ bone.name ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tglobalMatrix.copyPosition( relativeMatrix );\n\n\t\t}\n\n\t\tif ( name === options.hip ) {\n\n\t\t\tglobalMatrix.elements[ 12 ] *= options.scale * options.hipInfluence.x;\n\t\t\tglobalMatrix.elements[ 13 ] *= options.scale * options.hipInfluence.y;\n\t\t\tglobalMatrix.elements[ 14 ] *= options.scale * options.hipInfluence.z;\n\n\t\t\tif ( options.hipPosition !== undefined ) {\n\n\t\t\t\tglobalMatrix.elements[ 12 ] += options.hipPosition.x * options.scale;\n\t\t\t\tglobalMatrix.elements[ 13 ] += options.hipPosition.y * options.scale;\n\t\t\t\tglobalMatrix.elements[ 14 ] += options.hipPosition.z * options.scale;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( bone.parent ) {\n\n\t\t\tbone.matrix.copy( bone.parent.matrixWorld ).invert();\n\t\t\tbone.matrix.multiply( globalMatrix );\n\n\t\t} else {\n\n\t\t\tbone.matrix.copy( globalMatrix );\n\n\t\t}\n\n\t\tbone.matrix.decompose( bone.position, bone.quaternion, bone.scale );\n\n\t\tbone.updateMatrixWorld();\n\n\t}\n\n\tif ( options.preserveBonePositions ) {\n\n\t\tfor ( let i = 0; i < bones.length; ++ i ) {\n\n\t\t\tbone = bones[ i ];\n\t\t\tname = getBoneName( bone, options ) || bone.name;\n\n\t\t\tif ( name !== options.hip ) {\n\n\t\t\t\tbone.position.copy( bonesPosition[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tif ( options.preserveBoneMatrix ) {\n\n\t\t// restore matrix\n\n\t\ttarget.updateMatrixWorld( true );\n\n\t}\n\n}\n\n/**\n * Retargets the animation clip of the source to the target 3D object.\n *\n * The `source` can be a 3D object with a skeleton property (e.g. a skinned mesh)\n * or a {@link Skeleton} directly.\n *\n * @param {Object3D} target - The target 3D object. Must have a `skeleton` property.\n * @param {Object3D|Skeleton} source - The source object.\n * @param {AnimationClip} clip - The animation clip.\n * @param {module:SkeletonUtils~RetargetOptions} options - The options.\n * @return {AnimationClip} The retargeted animation clip.\n */\nfunction retargetClip( target, source, clip, options = {} ) {\n\n\toptions.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;\n\n\t// Calculate the fps from the source clip based on the track with the most frames, unless fps is already provided.\n\toptions.fps = options.fps !== undefined ? options.fps : ( Math.max( ...clip.tracks.map( track => track.times.length ) ) / clip.duration );\n\toptions.names = options.names || [];\n\n\tif ( ! source.isObject3D ) {\n\n\t\tsource = getHelperFromSkeleton( source );\n\n\t}\n\n\tconst numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),\n\t\tdelta = clip.duration / ( numFrames - 1 ),\n\t\tconvertedTracks = [],\n\t\tmixer = new AnimationMixer( source ),\n\t\tbones = getBones( target.skeleton ),\n\t\tboneDatas = [];\n\n\tlet positionOffset,\n\t\tbone, boneTo, boneData,\n\t\tname;\n\n\tmixer.clipAction( clip ).play();\n\n\t// trim\n\n\tlet start = 0, end = numFrames;\n\n\tif ( options.trim !== undefined ) {\n\n\t\tstart = Math.round( options.trim[ 0 ] * options.fps );\n\t\tend = Math.min( Math.round( options.trim[ 1 ] * options.fps ), numFrames ) - start;\n\n\t\tmixer.update( options.trim[ 0 ] );\n\n\t} else {\n\n\t\tmixer.update( 0 );\n\n\t}\n\n\tsource.updateMatrixWorld();\n\n\t//\n\n\tfor ( let frame = 0; frame < end; ++ frame ) {\n\n\t\tconst time = frame * delta;\n\n\t\tretarget( target, source, options );\n\n\t\tfor ( let j = 0; j < bones.length; ++ j ) {\n\n\t\t\tbone = bones[ j ];\n\t\t\tname = getBoneName( bone, options ) || bone.name;\n\t\t\tboneTo = getBoneByName( name, source.skeleton );\n\n\t\t\tif ( boneTo ) {\n\n\t\t\t\tboneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };\n\n\t\t\t\tif ( options.hip === name ) {\n\n\t\t\t\t\tif ( ! boneData.pos ) {\n\n\t\t\t\t\t\tboneData.pos = {\n\t\t\t\t\t\t\ttimes: new Float32Array( end ),\n\t\t\t\t\t\t\tvalues: new Float32Array( end * 3 )\n\t\t\t\t\t\t};\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( options.useFirstFramePosition ) {\n\n\t\t\t\t\t\tif ( frame === 0 ) {\n\n\t\t\t\t\t\t\tpositionOffset = bone.position.clone();\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbone.position.sub( positionOffset );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tboneData.pos.times[ frame ] = time;\n\n\t\t\t\t\tbone.position.toArray( boneData.pos.values, frame * 3 );\n\n\t\t\t\t}\n\n\t\t\t\tif ( ! boneData.quat ) {\n\n\t\t\t\t\tboneData.quat = {\n\t\t\t\t\t\ttimes: new Float32Array( end ),\n\t\t\t\t\t\tvalues: new Float32Array( end * 4 )\n\t\t\t\t\t};\n\n\t\t\t\t}\n\n\t\t\t\tboneData.quat.times[ frame ] = time;\n\n\t\t\t\tbone.quaternion.toArray( boneData.quat.values, frame * 4 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( frame === end - 2 ) {\n\n\t\t\t// last mixer update before final loop iteration\n\t\t\t// make sure we do not go over or equal to clip duration\n\t\t\tmixer.update( delta - 0.0000001 );\n\n\t\t} else {\n\n\t\t\tmixer.update( delta );\n\n\t\t}\n\n\t\tsource.updateMatrixWorld();\n\n\t}\n\n\tfor ( let i = 0; i < boneDatas.length; ++ i ) {\n\n\t\tboneData = boneDatas[ i ];\n\n\t\tif ( boneData ) {\n\n\t\t\tif ( boneData.pos ) {\n\n\t\t\t\tconvertedTracks.push( new VectorKeyframeTrack(\n\t\t\t\t\t'.bones[' + boneData.bone.name + '].position',\n\t\t\t\t\tboneData.pos.times,\n\t\t\t\t\tboneData.pos.values\n\t\t\t\t) );\n\n\t\t\t}\n\n\t\t\tconvertedTracks.push( new QuaternionKeyframeTrack(\n\t\t\t\t'.bones[' + boneData.bone.name + '].quaternion',\n\t\t\t\tboneData.quat.times,\n\t\t\t\tboneData.quat.values\n\t\t\t) );\n\n\t\t}\n\n\t}\n\n\tmixer.uncacheAction( clip );\n\n\treturn new AnimationClip( clip.name, - 1, convertedTracks );\n\n}\n\n/**\n * Clones the given 3D object and its descendants, ensuring that any `SkinnedMesh` instances are\n * correctly associated with their bones. Bones are also cloned, and must be descendants of the\n * object passed to this method. Other data, like geometries and materials, are reused by reference.\n *\n * @param {Object3D} source - The 3D object to clone.\n * @return {Object3D} The cloned 3D object.\n */\nfunction clone( source ) {\n\n\tconst sourceLookup = new Map();\n\tconst cloneLookup = new Map();\n\n\tconst clone = source.clone();\n\n\tparallelTraverse( source, clone, function ( sourceNode, clonedNode ) {\n\n\t\tsourceLookup.set( clonedNode, sourceNode );\n\t\tcloneLookup.set( sourceNode, clonedNode );\n\n\t} );\n\n\tclone.traverse( function ( node ) {\n\n\t\tif ( ! node.isSkinnedMesh ) return;\n\n\t\tconst clonedMesh = node;\n\t\tconst sourceMesh = sourceLookup.get( node );\n\t\tconst sourceBones = sourceMesh.skeleton.bones;\n\n\t\tclonedMesh.skeleton = sourceMesh.skeleton.clone();\n\t\tclonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );\n\n\t\tclonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {\n\n\t\t\treturn cloneLookup.get( bone );\n\n\t\t} );\n\n\t\tclonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );\n\n\t} );\n\n\treturn clone;\n\n}\n\n// internal helper\n\nfunction getBoneByName( name, skeleton ) {\n\n\tfor ( let i = 0, bones = getBones( skeleton ); i < bones.length; i ++ ) {\n\n\t\tif ( name === bones[ i ].name )\n\n\t\t\treturn bones[ i ];\n\n\t}\n\n}\n\nfunction getBones( skeleton ) {\n\n\treturn Array.isArray( skeleton ) ? skeleton : skeleton.bones;\n\n}\n\n\nfunction getHelperFromSkeleton( skeleton ) {\n\n\tconst source = new SkeletonHelper( skeleton.bones[ 0 ] );\n\tsource.skeleton = skeleton;\n\n\treturn source;\n\n}\n\nfunction parallelTraverse( a, b, callback ) {\n\n\tcallback( a, b );\n\n\tfor ( let i = 0; i < a.children.length; i ++ ) {\n\n\t\tparallelTraverse( a.children[ i ], b.children[ i ], callback );\n\n\t}\n\n}\n\n/**\n * Retarget options of `SkeletonUtils`.\n *\n * @typedef {Object} module:SkeletonUtils~RetargetOptions\n * @property {boolean} [useFirstFramePosition=false] - Whether to use the position of the first frame or not.\n * @property {number} [fps] - The FPS of the clip.\n * @property {Object<string,string>} [names] - A dictionary for mapping target to source bone names.\n * @property {function(string):string} [getBoneName] - A function for mapping bone names. Alternative to `names`.\n * @property {Array<number>} [trim] - Whether to trim the clip or not. If set the array should hold two values for the start and end.\n * @property {boolean} [preserveBoneMatrix=true] - Whether to preserve bone matrices or not.\n * @property {boolean} [preserveBonePositions=true] - Whether to preserve bone positions or not.\n * @property {boolean} [useTargetMatrix=false] - Whether to use the target matrix or not.\n * @property {string} [hip='hip'] - The name of the source's hip bone.\n * @property {Vector3} [hipInfluence=(1,1,1)] - The hip influence.\n * @property {number} [scale=1] - The scale.\n * @property {Object<string,Matrix4>} [localOffsets] - Per-bone local offset matrices, keyed by bone name.\n * @property {Vector3} [hipPosition] - An additional position offset applied to the hip bone.\n **/\n\nexport {\n\tretarget,\n\tretargetClip,\n\tclone,\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "game",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"check": "tsc --noEmit && scenoco check src"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@scenoco-three/core": "^0.7.0",
|
|
12
|
+
"three": "^0.184.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@scenoco-three/observer": "^0.7.0",
|
|
16
|
+
"@scenoco-three/tools": "^0.7.0",
|
|
17
|
+
"@types/three": "^0.184.1",
|
|
18
|
+
"typescript": "^7.0.2",
|
|
19
|
+
"vite": "^8.3.4"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Behaviour, Input, instantiate } from '@scenoco-three/core';
|
|
2
|
+
import { Crate } from './scenes/prefabs';
|
|
3
|
+
|
|
4
|
+
/** Drops a crate where its node stands, on Space — Unity's 'Jump' button. */
|
|
5
|
+
export class Dropper extends Behaviour {
|
|
6
|
+
update() {
|
|
7
|
+
if (!Input.getButtonDown('Jump')) return;
|
|
8
|
+
const { x, z } = this.node.position;
|
|
9
|
+
instantiate(Crate, { position: [x, 0.25, z] });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Behaviour, WaitForSeconds, destroy } from '@scenoco-three/core';
|
|
2
|
+
|
|
3
|
+
/** Removes its node after a while — a coroutine, as in Unity. */
|
|
4
|
+
export class Fade extends Behaviour {
|
|
5
|
+
seconds = 2;
|
|
6
|
+
|
|
7
|
+
start() {
|
|
8
|
+
this.startCoroutine(this.later());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private *later() {
|
|
12
|
+
yield new WaitForSeconds(this.seconds);
|
|
13
|
+
destroy(this.node);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Vector3, type Object3D } from 'three';
|
|
2
|
+
import { Behaviour } from '@scenoco-three/core';
|
|
3
|
+
|
|
4
|
+
/** Keeps its node — the camera — at an offset from a target, looking at it. */
|
|
5
|
+
export class Follow extends Behaviour {
|
|
6
|
+
target: Object3D | null = null;
|
|
7
|
+
offset = new Vector3(0, 5, 8);
|
|
8
|
+
|
|
9
|
+
lateUpdate() {
|
|
10
|
+
if (!this.target) return;
|
|
11
|
+
this.node.position.copy(this.target.position).add(this.offset);
|
|
12
|
+
this.node.lookAt(this.target.position);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Mesh } from 'three';
|
|
2
|
+
import { Behaviour, Input } from '@scenoco-three/core';
|
|
3
|
+
|
|
4
|
+
/** Steers its node on the ground with the arrow keys or WASD — Unity's named axes. */
|
|
5
|
+
export class Mover extends Behaviour<Mesh> {
|
|
6
|
+
speed = 4;
|
|
7
|
+
|
|
8
|
+
update(dt: number) {
|
|
9
|
+
this.node.position.x += Input.getAxis('Horizontal') * this.speed * dt;
|
|
10
|
+
this.node.position.z -= Input.getAxis('Vertical') * this.speed * dt;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BoxGeometry, Color, DirectionalLight, HemisphereLight, Mesh, MeshStandardMaterial, PerspectiveCamera, PlaneGeometry, Scene } from 'three';
|
|
2
|
+
import { ref } from '@scenoco-three/core';
|
|
3
|
+
import { Dropper } from '../Dropper';
|
|
4
|
+
import { Follow } from '../Follow';
|
|
5
|
+
import { Mover } from '../Mover';
|
|
6
|
+
|
|
7
|
+
export const Main = () => {
|
|
8
|
+
const player = ref<Mesh>();
|
|
9
|
+
return (
|
|
10
|
+
<Scene>
|
|
11
|
+
<Color attach="background" args={['#10131c']} />
|
|
12
|
+
<HemisphereLight args={['#ffffff', '#334455', 1]} />
|
|
13
|
+
<DirectionalLight position={[3, 8, 5]} intensity={2} castShadow />
|
|
14
|
+
|
|
15
|
+
<PerspectiveCamera position={[0, 6, 8]}>
|
|
16
|
+
<Follow target={player} offset={[0, 6, 8]} />
|
|
17
|
+
</PerspectiveCamera>
|
|
18
|
+
|
|
19
|
+
<Mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
|
|
20
|
+
<PlaneGeometry args={[30, 30]} />
|
|
21
|
+
<MeshStandardMaterial color="#2a3142" />
|
|
22
|
+
</Mesh>
|
|
23
|
+
|
|
24
|
+
<Mesh ref={player} position={[0, 0.5, 0]} castShadow>
|
|
25
|
+
<BoxGeometry />
|
|
26
|
+
<MeshStandardMaterial color="#f97316" />
|
|
27
|
+
<Mover speed={5} />
|
|
28
|
+
<Dropper />
|
|
29
|
+
</Mesh>
|
|
30
|
+
</Scene>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BoxGeometry, Mesh, MeshStandardMaterial } from 'three';
|
|
2
|
+
import { Fade } from '../Fade';
|
|
3
|
+
|
|
4
|
+
/** A prefab is a function returning TSX; `instantiate(Crate)` builds a fresh one each call. */
|
|
5
|
+
export const Crate = () => (
|
|
6
|
+
<Mesh scale={0.5} castShadow>
|
|
7
|
+
<BoxGeometry />
|
|
8
|
+
<MeshStandardMaterial color="#facc15" />
|
|
9
|
+
<Fade seconds={3} />
|
|
10
|
+
</Mesh>
|
|
11
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noUncheckedIndexedAccess": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"types": ["vite/client"],
|
|
12
|
+
"jsx": "react-jsx",
|
|
13
|
+
"jsxImportSource": "@scenoco-three/core"
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|
package/README.md
DELETED