1.4K tris1.47 × 0.82 × 0.81 m
Use it in code
Remix in the editorfishing-rod.js · 301 lines
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
* Fishing Rod — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 1,378 · Size (W × H × D): 1.47 × 0.82 × 0.81 m
*
* Usage:
* import { createFishingRod } from './fishing-rod.js';
*
* const model = createFishingRod();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// Deterministic vertex displacement (organic low-poly look).
function jitter(geometry, amount, seed) {
const pos = geometry.attributes.position;
const h = (a, b, c, s) => {
let x = Math.imul(a, 73856093) ^ Math.imul(b, 19349663) ^ Math.imul(c, 83492791) ^ Math.imul(s, 2654435761);
x = Math.imul(x ^ (x >>> 16), 2246822507);
x = Math.imul(x ^ (x >>> 13), 3266489909);
return ((x ^ (x >>> 16)) >>> 0) / 4294967295 - 0.5;
};
for (let i = 0; i < pos.count; i++) {
const x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);
const qx = Math.round(x * 1000), qy = Math.round(y * 1000), qz = Math.round(z * 1000);
pos.setXYZ(i, x + h(qx, qy, qz, seed) * 2 * amount, y + h(qx, qy, qz, seed + 1) * 2 * amount, z + h(qx, qy, qz, seed + 2) * 2 * amount);
}
pos.needsUpdate = true;
geometry.computeVertexNormals();
return geometry;
}
// 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 createFishingRod() {
const root = new THREE.Group();
root.name = 'FishingRod';
// Materials
const blankMat = new THREE.MeshStandardMaterial({ name: 'blank', color: 0x2f5f8f, roughness: 0.35, metalness: 0.1, flatShading: true });
const corkMat = new THREE.MeshStandardMaterial({ name: 'cork', color: 0xc99b63, roughness: 0.95, flatShading: true });
const metalMat = new THREE.MeshStandardMaterial({ name: 'metal', color: 0xb4bbc4, roughness: 0.3, metalness: 0.6, flatShading: true });
const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x2b2c31, roughness: 0.6, flatShading: true });
const whiteMat = new THREE.MeshStandardMaterial({ name: 'white', color: 0xf2f0ea, roughness: 0.6, flatShading: true });
const bobberMat = new THREE.MeshStandardMaterial({ name: 'bobber', color: 0xd8352b, roughness: 0.4, flatShading: true });
const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0x7d5634, roughness: 0.9, flatShading: true, side: THREE.DoubleSide });
const waterMat = new THREE.MeshStandardMaterial({ name: 'water', color: 0x3f7fa8, roughness: 0.15, metalness: 0.1, flatShading: true });
// Shared geometries
const boxGeo = new THREE.BoxGeometry(0.012, 0.012, 0.0025);
const sphereGeo = new THREE.SphereGeometry(0.032, 8, 3, 0, Math.PI * 2, 0, Math.PI / 2);
const cylinderGeo = new THREE.CylinderGeometry(0.008, 0.012, 0.1435, 5);
// Parts
const setup = new THREE.Group();
setup.name = 'setup';
setup.rotation.set(0, deg(-28), 0);
root.add(setup);
const rod = new THREE.Group();
rod.name = 'rod';
rod.position.set(-0.78, 0.018, 0);
rod.rotation.set(0, 0, deg(26));
setup.add(rod);
const buttCap = new THREE.Mesh(new THREE.CylinderGeometry(0.018, 0.018, 0.02, 7), darkMat);
buttCap.name = 'buttCap';
buttCap.position.set(0.01, 0, 0);
buttCap.rotation.set(0, 0, deg(-90));
rod.add(buttCap);
const grip = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.0175, 0.28, 7), corkMat);
grip.name = 'grip';
grip.position.set(0.16, 0, 0);
grip.rotation.set(0, 0, deg(-90));
rod.add(grip);
const reelSeat = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.08, 7), metalMat);
reelSeat.name = 'reelSeat';
reelSeat.position.set(0.34, 0, 0);
reelSeat.rotation.set(0, 0, deg(-90));
rod.add(reelSeat);
const foreGrip = new THREE.Mesh(new THREE.CylinderGeometry(0.0115, 0.0145, 0.09, 7), corkMat);
foreGrip.name = 'foreGrip';
foreGrip.position.set(0.425, 0, 0);
foreGrip.rotation.set(0, 0, deg(-90));
rod.add(foreGrip);
const windingCheck = new THREE.Mesh(new THREE.CylinderGeometry(0.0105, 0.011, 0.015, 7), darkMat);
windingCheck.name = 'windingCheck';
windingCheck.position.set(0.4775, 0, 0);
windingCheck.rotation.set(0, 0, deg(-90));
rod.add(windingCheck);
const blank = new THREE.Mesh(new THREE.CylinderGeometry(0.0028, 0.0095, 1.32, 6), blankMat);
blank.name = 'blank';
blank.position.set(1.14, 0, 0);
blank.rotation.set(0, 0, deg(-90));
rod.add(blank);
const tipTop = new THREE.Mesh(new THREE.TorusGeometry(0.004, 0.0013, 3, 6), metalMat);
tipTop.name = 'tipTop';
tipTop.position.set(1.803, 0, 0);
tipTop.rotation.set(0, deg(90), 0);
rod.add(tipTop);
const guideFoot = new THREE.Mesh(boxGeo, metalMat);
guideFoot.name = 'guideFoot';
guideFoot.position.set(0.72, -0.0123, 0);
rod.add(guideFoot);
const guide = new THREE.Mesh(new THREE.TorusGeometry(0.013, 0.0018, 3, 7), metalMat);
guide.name = 'guide';
guide.position.set(0.72, -0.0293, 0);
guide.rotation.set(0, deg(90), 0);
rod.add(guide);
const guideFoot2 = new THREE.Mesh(boxGeo, metalMat);
guideFoot2.name = 'guideFoot';
guideFoot2.position.set(0.99, -0.0109, 0);
rod.add(guideFoot2);
const guide2 = new THREE.Mesh(new THREE.TorusGeometry(0.0105, 0.0018, 3, 7), metalMat);
guide2.name = 'guide';
guide2.position.set(0.99, -0.0254, 0);
guide2.rotation.set(0, deg(90), 0);
rod.add(guide2);
const guideFoot3 = new THREE.Mesh(boxGeo, metalMat);
guideFoot3.name = 'guideFoot';
guideFoot3.position.set(1.25, -0.0096, 0);
rod.add(guideFoot3);
const guide3 = new THREE.Mesh(new THREE.TorusGeometry(0.0085, 0.0018, 3, 7), metalMat);
guide3.name = 'guide';
guide3.position.set(1.25, -0.0221, 0);
guide3.rotation.set(0, deg(90), 0);
rod.add(guide3);
const guideFoot4 = new THREE.Mesh(boxGeo, metalMat);
guideFoot4.name = 'guideFoot';
guideFoot4.position.set(1.5, -0.0083, 0);
rod.add(guideFoot4);
const guide4 = new THREE.Mesh(new THREE.TorusGeometry(0.0068, 0.0018, 3, 7), metalMat);
guide4.name = 'guide';
guide4.position.set(1.5, -0.0191, 0);
guide4.rotation.set(0, deg(90), 0);
rod.add(guide4);
const reelFoot = new THREE.Mesh(new THREE.BoxGeometry(0.055, 0.008, 0.016), metalMat);
reelFoot.name = 'reelFoot';
reelFoot.position.set(0.34, -0.014, 0);
rod.add(reelFoot);
const reelStem = new THREE.Mesh(new THREE.BoxGeometry(0.014, 0.034, 0.012), metalMat);
reelStem.name = 'reelStem';
reelStem.position.set(0.34, -0.034, 0);
rod.add(reelStem);
const reel = new THREE.Mesh(new THREE.CylinderGeometry(0.044, 0.044, 0.03, 10), metalMat);
reel.name = 'reel';
reel.position.set(0.34, -0.088, 0);
reel.rotation.set(deg(90), 0, 0);
rod.add(reel);
const reelPlate = new THREE.Mesh(new THREE.CylinderGeometry(0.036, 0.036, 0.034, 10), darkMat);
reelPlate.name = 'reelPlate';
reelPlate.position.set(0.34, -0.088, 0);
reelPlate.rotation.set(deg(90), 0, 0);
rod.add(reelPlate);
const reelHub = new THREE.Mesh(new THREE.CylinderGeometry(0.009, 0.009, 0.04, 6), metalMat);
reelHub.name = 'reelHub';
reelHub.position.set(0.34, -0.088, 0);
reelHub.rotation.set(deg(90), 0, 0);
rod.add(reelHub);
const crankArm = new THREE.Mesh(new THREE.BoxGeometry(0.052, 0.009, 0.006), metalMat);
crankArm.name = 'crankArm';
crankArm.position.set(0.36, -0.096, 0.021);
crankArm.rotation.set(0, 0, deg(-20));
rod.add(crankArm);
const crankKnob = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 0.022, 6), darkMat);
crankKnob.name = 'crankKnob';
crankKnob.position.set(0.382, -0.104, 0.032);
crankKnob.rotation.set(deg(90), 0, 0);
rod.add(crankKnob);
const line = new THREE.Mesh(tube([[-0.4466, 0.1328, 0], [-0.12, 0.3073, 0], [0.1209, 0.4291, 0], [0.3532, 0.5461, 0], [0.5766, 0.6584, 0], [0.8419, 0.8079, 0]], 0.0016, 28, 3), whiteMat);
line.name = 'line';
setup.add(line);
const lineDrop = new THREE.Mesh(new THREE.CylinderGeometry(0.0016, 0.0016, 0.34, 3), whiteMat);
lineDrop.name = 'lineDrop';
lineDrop.position.set(0.8423, 0.637, 0);
lineDrop.rotation.set(0, 0, deg(180));
setup.add(lineDrop);
const bobberTop = new THREE.Mesh(sphereGeo, bobberMat);
bobberTop.name = 'bobberTop';
bobberTop.position.set(0.8423, 0.435, 0);
setup.add(bobberTop);
const bobberBottom = new THREE.Mesh(sphereGeo, whiteMat);
bobberBottom.name = 'bobberBottom';
bobberBottom.position.set(0.8423, 0.435, 0);
bobberBottom.rotation.set(deg(180), 0, 0);
setup.add(bobberBottom);
const bobberStick = new THREE.Mesh(new THREE.CylinderGeometry(0.004, 0.004, 0.04, 5), bobberMat);
bobberStick.name = 'bobberStick';
bobberStick.position.set(0.8423, 0.48, 0);
setup.add(bobberStick);
const leader = new THREE.Mesh(new THREE.CylinderGeometry(0.0013, 0.0013, 0.07, 3), whiteMat);
leader.name = 'leader';
leader.position.set(0.8423, 0.37, 0);
leader.rotation.set(0, 0, deg(180));
setup.add(leader);
const sinker = new THREE.Mesh(new THREE.SphereGeometry(0.008, 5, 3), darkMat);
sinker.name = 'sinker';
sinker.position.set(0.8423, 0.335, 0);
setup.add(sinker);
const hook = new THREE.Mesh(new THREE.TorusGeometry(0.011, 0.0018, 3, 8, 4.3982), metalMat);
hook.name = 'hook';
hook.position.set(0.8423, 0.315, 0);
hook.rotation.set(0, 0, deg(200));
setup.add(hook);
const standStick = new THREE.Mesh(new THREE.CylinderGeometry(0.013, 0.017, 0.3693, 6), woodMat);
standStick.name = 'standStick';
standStick.position.set(0.0209, 0.1844, 0);
standStick.rotation.set(0, 0, deg(3.1044));
setup.add(standStick);
const standProng = new THREE.Mesh(cylinderGeo, woodMat);
standProng.name = 'standProng';
standProng.position.set(0.0059, 0.4238, -0.03);
standProng.rotation.set(deg(-24.714), deg(-0.8757), deg(3.9957));
setup.add(standProng);
const standProng2 = new THREE.Mesh(cylinderGeo, woodMat);
standProng2.name = 'standProng';
standProng2.position.set(0.0059, 0.4238, 0.03);
standProng2.rotation.set(deg(24.714), deg(0.8757), deg(3.9957));
setup.add(standProng2);
const stone = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.05, 0), 0.008, 2), darkMat);
stone.name = 'stone';
stone.position.set(0.0709, 0.02, 0.04);
stone.scale.set(1, 0.55, 0.9);
setup.add(stone);
const bucket = new THREE.Group();
bucket.name = 'bucket';
bucket.position.set(-0.4, 0, 0.2);
bucket.rotation.set(0, deg(15), 0);
root.add(bucket);
const bucketShell = new THREE.Mesh(new THREE.CylinderGeometry(0.12, 0.1, 0.22, 9, 1, true), woodMat);
bucketShell.name = 'bucketShell';
bucketShell.position.set(0, 0.11, 0);
bucket.add(bucketShell);
const bucketBottom = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, 0.012, 9), woodMat);
bucketBottom.name = 'bucketBottom';
bucketBottom.position.set(0, 0.006, 0);
bucket.add(bucketBottom);
const water = new THREE.Mesh(new THREE.CylinderGeometry(0.113, 0.113, 0.01, 9), waterMat);
water.name = 'water';
water.position.set(0, 0.175, 0);
bucket.add(water);
const hoop = new THREE.Mesh(new THREE.TorusGeometry(0.1061, 0.006, 3, 9), metalMat);
hoop.name = 'hoop';
hoop.position.set(0, 0.04, 0);
hoop.rotation.set(deg(90), 0, 0);
bucket.add(hoop);
const hoop2 = new THREE.Mesh(new THREE.TorusGeometry(0.1169, 0.006, 3, 9), metalMat);
hoop2.name = 'hoop';
hoop2.position.set(0, 0.18, 0);
hoop2.rotation.set(deg(90), 0, 0);
bucket.add(hoop2);
const bail = new THREE.Mesh(new THREE.TorusGeometry(0.12, 0.004, 3, 10, Math.PI), metalMat);
bail.name = 'bail';
bail.position.set(0, 0.22, 0);
bail.rotation.set(0, deg(30), 0);
bucket.add(bail);
const fish = new THREE.Group();
fish.name = 'fish';
fish.position.set(0.02, 0.2, 0.01);
fish.rotation.set(deg(14), deg(20), deg(-12));
bucket.add(fish);
const fishBody = new THREE.Mesh(new THREE.SphereGeometry(0.045, 7, 5), metalMat);
fishBody.name = 'fishBody';
fishBody.scale.set(0.55, 1.6, 1);
fish.add(fishBody);
const fishTail = new THREE.Mesh(extrude([[0, 0], [0.045, 0.06], [0, 0.045], [-0.045, 0.06]], 0.01), metalMat);
fishTail.name = 'fishTail';
fishTail.position.set(0, 0.065, 0);
fishTail.rotation.set(0, deg(90), 0);
fish.add(fishTail);
const fishFin = new THREE.Mesh(new THREE.BoxGeometry(0.012, 0.03, 0.05), blankMat);
fishFin.name = 'fishFin';
fishFin.position.set(0, 0, 0.02);
fish.add(fishFin);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createFishingRod;
Ratings & comments
0 comments
No comments yet. Share how you used this model or what could be better.
Related models
Same category or similar tags









