928 三角面0.77 × 1.32 × 0.08 m
在代码中使用
在编辑器中改编bow.js · 136 行
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
* Recurve Bow — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 928 · Size (W × H × D): 0.77 × 1.32 × 0.08 m
*
* Usage:
* import { createBow } from './bow.js';
*
* const model = createBow();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// Extrude a 2D outline along Z (centered). bevel = [thickness, size, segments].
function extrude(outline, depth, holes = [], bevel = null) {
const shape = new THREE.Shape(outline.map(([x, y]) => new THREE.Vector2(x, y)));
for (const hole of holes) shape.holes.push(new THREE.Path(hole.map(([x, y]) => new THREE.Vector2(x, y))));
const options = bevel
? { depth, bevelEnabled: true, bevelThickness: bevel[0], bevelSize: bevel[1], bevelSegments: bevel[2] }
: { depth, bevelEnabled: false };
return new THREE.ExtrudeGeometry(shape, options).translate(0, 0, -depth / 2);
}
// Tube along a smooth curve through the given points.
function tube(path, radius, tubularSegments, radialSegments, closed = false) {
const curve = new THREE.CatmullRomCurve3(path.map(([x, y, z]) => new THREE.Vector3(x, y, z)), closed);
return new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
}
export function createBow() {
const root = new THREE.Group();
root.name = 'Bow';
// Materials
const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0x8b5a32, roughness: 0.75, flatShading: true });
const leatherMat = new THREE.MeshStandardMaterial({ name: 'leather', color: 0x4a2c1a, roughness: 0.9, flatShading: true });
const hornMat = new THREE.MeshStandardMaterial({ name: 'horn', color: 0xe6d3a8, roughness: 0.6, flatShading: true });
const stringMat = new THREE.MeshStandardMaterial({ name: 'string', color: 0xefe8d6, roughness: 0.8, flatShading: true });
const shaftMat = new THREE.MeshStandardMaterial({ name: 'shaft', color: 0xc79c5e, roughness: 0.85, flatShading: true });
const steelMat = new THREE.MeshStandardMaterial({ name: 'steel', color: 0xa7b0ba, roughness: 0.35, metalness: 0.7, flatShading: true });
const featherMat = new THREE.MeshStandardMaterial({ name: 'feather', color: 0xd6493a, roughness: 0.9, flatShading: true, side: THREE.DoubleSide });
// Shared geometries
const sphereGeo = new THREE.SphereGeometry(0.017, 6, 4);
const extrudeGeo = extrude([[0.003, 0], [0.028, 0.012], [0.032, 0.08], [0.02, 0.12], [0.003, 0.14]], 0.0025);
// Parts
const bow = new THREE.Group();
bow.name = 'bow';
bow.position.set(-0.36, 0, 0);
root.add(bow);
const limbs = new THREE.Mesh(tube([[0.0204, 0.016, 0], [0, 0.0604, 0], [0.0238, 0.1428, 0], [0.0799, 0.2696, 0], [0.1462, 0.4598, 0], [0.17, 0.65, 0], [0.1462, 0.8402, 0], [0.0799, 1.0304, 0], [0.0238, 1.1572, 0], [0, 1.2396, 0], [0.0204, 1.284, 0]], 0.016, 44, 6), woodMat);
limbs.name = 'limbs';
bow.add(limbs);
const riser = new THREE.Mesh(tube([[0.1224, 0.3837, 0], [0.1581, 0.5232, 0], [0.17, 0.65, 0], [0.1581, 0.7768, 0], [0.1224, 0.9163, 0]], 0.0216, 12, 6), woodMat);
riser.name = 'riser';
bow.add(riser);
const grip = new THREE.Mesh(new THREE.CylinderGeometry(0.024, 0.024, 0.15, 7), leatherMat);
grip.name = 'grip';
grip.position.set(0.17, 0.65, 0);
bow.add(grip);
const tipTop = new THREE.Mesh(sphereGeo, hornMat);
tipTop.name = 'tipTop';
tipTop.position.set(0.0204, 1.284, 0);
tipTop.scale.set(1, 1.4, 1);
bow.add(tipTop);
const tipBottom = new THREE.Mesh(sphereGeo, hornMat);
tipBottom.name = 'tipBottom';
tipBottom.position.set(0.0204, 0.016, 0);
tipBottom.scale.set(1, 1.4, 1);
bow.add(tipBottom);
const string = new THREE.Mesh(new THREE.CylinderGeometry(0.0045, 0.0045, 1.1792, 4), stringMat);
string.name = 'string';
string.position.set(-0.004, 0.65, 0);
bow.add(string);
const arrow = new THREE.Group();
arrow.name = 'arrow';
arrow.position.set(-0.012, 0.74, 0.028);
bow.add(arrow);
const arrowShaft = new THREE.Mesh(new THREE.CylinderGeometry(0.0065, 0.0065, 0.68, 6), shaftMat);
arrowShaft.name = 'arrowShaft';
arrowShaft.position.set(0.34, 0, 0);
arrowShaft.rotation.set(0, 0, deg(-90));
arrow.add(arrowShaft);
const arrowSocket = new THREE.Mesh(new THREE.CylinderGeometry(0.009, 0.0075, 0.035, 6), steelMat);
arrowSocket.name = 'arrowSocket';
arrowSocket.position.set(0.67, 0, 0);
arrowSocket.rotation.set(0, 0, deg(-90));
arrow.add(arrowSocket);
const arrowHead = new THREE.Mesh(extrude([[0, 0], [0.025, 0.03], [0.026, 0.05], [0, 0.1], [-0.026, 0.05], [-0.025, 0.03]], 0.002, [], [0.004, 0.003, 1]), steelMat);
arrowHead.name = 'arrowHead';
arrowHead.position.set(0.665, 0, 0);
arrowHead.rotation.set(0, 0, deg(-90));
arrow.add(arrowHead);
const vane1 = new THREE.Group();
vane1.name = 'vane1';
vane1.position.set(0.03, 0, 0);
vane1.rotation.set(deg(30), 0, deg(-90));
arrow.add(vane1);
const arrowVane1 = new THREE.Mesh(extrudeGeo, featherMat);
arrowVane1.name = 'arrowVane1';
arrowVane1.rotation.set(0, deg(90), 0);
vane1.add(arrowVane1);
const vane2 = new THREE.Group();
vane2.name = 'vane2';
vane2.position.set(0.03, 0, 0);
vane2.rotation.set(deg(150), 0, deg(-90));
arrow.add(vane2);
const arrowVane2 = new THREE.Mesh(extrudeGeo, featherMat);
arrowVane2.name = 'arrowVane2';
arrowVane2.rotation.set(0, deg(90), 0);
vane2.add(arrowVane2);
const vane3 = new THREE.Group();
vane3.name = 'vane3';
vane3.position.set(0.03, 0, 0);
vane3.rotation.set(deg(270), 0, deg(-90));
arrow.add(vane3);
const arrowVane3 = new THREE.Mesh(extrudeGeo, featherMat);
arrowVane3.name = 'arrowVane3';
arrowVane3.rotation.set(0, deg(90), 0);
vane3.add(arrowVane3);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createBow;
评分与评论
0 条评论
暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。
相关模型
同一分类或相似标签









