648 tris1.12 × 1.94 × 1.03 m
Use it in code
Remix in the editorknight.js · 305 lines
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
* Knight — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 648 · Size (W × H × D): 1.12 × 1.94 × 1.02 m
*
* Usage:
* import { createKnight } from './knight.js';
*
* const model = createKnight();
* 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);
}
export function createKnight() {
const root = new THREE.Group();
root.name = 'Knight';
// Materials
const armorMat = new THREE.MeshStandardMaterial({ name: 'armor', color: 0xb9c2cc, roughness: 0.35, metalness: 0.6, flatShading: true });
const armorDarkMat = new THREE.MeshStandardMaterial({ name: 'armorDark', color: 0x73787e, roughness: 0.45, metalness: 0.55, flatShading: true });
const teamMat = new THREE.MeshStandardMaterial({ name: 'team', color: 0x2f5fb3, roughness: 0.85, flatShading: true });
const trimMat = new THREE.MeshStandardMaterial({ name: 'trim', color: 0xe8b53f, roughness: 0.35, metalness: 0.6, flatShading: true });
const leatherMat = new THREE.MeshStandardMaterial({ name: 'leather', color: 0x5b3b29, roughness: 0.8, flatShading: true });
const slitMat = new THREE.MeshStandardMaterial({ name: 'slit', color: 0x15131a, roughness: 0.9, flatShading: true });
const bladeMat = new THREE.MeshStandardMaterial({ name: 'blade', color: 0xe3e8ee, roughness: 0.2, metalness: 0.8, flatShading: true });
// Shared geometries
const boxGeo = new THREE.BoxGeometry(0.19, 0.26, 0.2);
const boxGeo2 = new THREE.BoxGeometry(0.15, 0.08, 0.06);
const boxGeo3 = new THREE.BoxGeometry(0.2, 0.26, 0.21);
const boxGeo4 = new THREE.BoxGeometry(0.22, 0.09, 0.29);
const boxGeo5 = new THREE.BoxGeometry(0.34, 0.5, 0.02);
const boxGeo6 = new THREE.BoxGeometry(0.22, 0.12, 0.28);
const boxGeo7 = new THREE.BoxGeometry(0.23, 0.035, 0.29);
const boxGeo8 = new THREE.BoxGeometry(0.15, 0.22, 0.17);
const boxGeo9 = new THREE.BoxGeometry(0.16, 0.2, 0.18);
const boxGeo10 = new THREE.BoxGeometry(0.15, 0.1, 0.15);
const boxGeo11 = new THREE.BoxGeometry(0.03, 0.03, 0.02);
// Parts
const legL = new THREE.Group();
legL.name = 'legL';
legL.position.set(0.12, 0.6, 0);
root.add(legL);
const thighL = new THREE.Mesh(boxGeo, armorDarkMat);
thighL.name = 'thighL';
thighL.position.set(0, -0.13, 0);
legL.add(thighL);
const kneeCapL = new THREE.Mesh(boxGeo2, armorMat);
kneeCapL.name = 'kneeCapL';
kneeCapL.position.set(0, -0.29, 0.09);
legL.add(kneeCapL);
const greaveL = new THREE.Mesh(boxGeo3, armorMat);
greaveL.name = 'greaveL';
greaveL.position.set(0, -0.4, 0);
legL.add(greaveL);
const bootL = new THREE.Mesh(boxGeo4, leatherMat);
bootL.name = 'bootL';
bootL.position.set(0, -0.555, 0.03);
legL.add(bootL);
const legR = new THREE.Group();
legR.name = 'legR';
legR.position.set(-0.12, 0.6, 0);
root.add(legR);
const thighR = new THREE.Mesh(boxGeo, armorDarkMat);
thighR.name = 'thighR';
thighR.position.set(0, -0.13, 0);
legR.add(thighR);
const kneeCapR = new THREE.Mesh(boxGeo2, armorMat);
kneeCapR.name = 'kneeCapR';
kneeCapR.position.set(0, -0.29, 0.09);
legR.add(kneeCapR);
const greaveR = new THREE.Mesh(boxGeo3, armorMat);
greaveR.name = 'greaveR';
greaveR.position.set(0, -0.4, 0);
legR.add(greaveR);
const bootR = new THREE.Mesh(boxGeo4, leatherMat);
bootR.name = 'bootR';
bootR.position.set(0, -0.555, 0.03);
legR.add(bootR);
const body = new THREE.Group();
body.name = 'body';
root.add(body);
const breastplate = new THREE.Mesh(new THREE.BoxGeometry(0.52, 0.56, 0.32), armorMat);
breastplate.name = 'breastplate';
breastplate.position.set(0, 0.88, 0);
body.add(breastplate);
const tabardFront = new THREE.Mesh(boxGeo5, teamMat);
tabardFront.name = 'tabardFront';
tabardFront.position.set(0, 0.83, 0.163);
body.add(tabardFront);
const tabardBack = new THREE.Mesh(boxGeo5, teamMat);
tabardBack.name = 'tabardBack';
tabardBack.position.set(0, 0.83, -0.163);
body.add(tabardBack);
const crestV = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.24, 0.01), trimMat);
crestV.name = 'crestV';
crestV.position.set(0, 0.91, 0.172);
body.add(crestV);
const crestH = new THREE.Mesh(new THREE.BoxGeometry(0.18, 0.06, 0.01), trimMat);
crestH.name = 'crestH';
crestH.position.set(0, 0.95, 0.172);
body.add(crestH);
const belt = new THREE.Mesh(new THREE.BoxGeometry(0.55, 0.07, 0.35), leatherMat);
belt.name = 'belt';
belt.position.set(0, 0.65, 0);
body.add(belt);
const buckle = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.07, 0.02), trimMat);
buckle.name = 'buckle';
buckle.position.set(0, 0.65, 0.18);
body.add(buckle);
const tassets = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.14, 0.3), armorDarkMat);
tassets.name = 'tassets';
tassets.position.set(0, 0.56, 0);
body.add(tassets);
const tabardSkirt = new THREE.Mesh(new THREE.BoxGeometry(0.28, 0.2, 0.02), teamMat);
tabardSkirt.name = 'tabardSkirt';
tabardSkirt.position.set(0, 0.54, 0.166);
body.add(tabardSkirt);
const gorget = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.07, 0.24), armorDarkMat);
gorget.name = 'gorget';
gorget.position.set(0, 1.18, 0);
body.add(gorget);
const armL = new THREE.Group();
armL.name = 'armL';
armL.position.set(0.34, 1.08, 0);
armL.rotation.set(deg(4), 0, deg(6));
root.add(armL);
const pauldronL = new THREE.Mesh(boxGeo6, armorMat);
pauldronL.name = 'pauldronL';
pauldronL.position.set(0.03, 0.03, 0);
armL.add(pauldronL);
const pauldronTrimL = new THREE.Mesh(boxGeo7, trimMat);
pauldronTrimL.name = 'pauldronTrimL';
pauldronTrimL.position.set(0.03, -0.04, 0);
armL.add(pauldronTrimL);
const upperArmL = new THREE.Mesh(boxGeo8, armorDarkMat);
upperArmL.name = 'upperArmL';
upperArmL.position.set(0, -0.15, 0);
armL.add(upperArmL);
const forearmL = new THREE.Group();
forearmL.name = 'forearmL';
forearmL.position.set(0, -0.26, 0);
forearmL.rotation.set(deg(-50), 0, 0);
armL.add(forearmL);
const vambraceL = new THREE.Mesh(boxGeo9, armorMat);
vambraceL.name = 'vambraceL';
vambraceL.position.set(0, -0.1, 0);
forearmL.add(vambraceL);
const gauntletL = new THREE.Mesh(boxGeo10, leatherMat);
gauntletL.name = 'gauntletL';
gauntletL.position.set(0, -0.24, 0);
forearmL.add(gauntletL);
const shield = new THREE.Group();
shield.name = 'shield';
shield.position.set(0.13, -0.12, 0.02);
shield.rotation.set(deg(50), deg(70), 0);
forearmL.add(shield);
const shieldRim = new THREE.Mesh(extrude([[-0.21, 0.24], [0.21, 0.24], [0.21, 0.02], [0.16, -0.14], [0.08, -0.25], [0, -0.3], [-0.08, -0.25], [-0.16, -0.14], [-0.21, 0.02]], 0.05), armorMat);
shieldRim.name = 'shieldRim';
shield.add(shieldRim);
const shieldFace = new THREE.Mesh(extrude([[-0.1806, 0.2162], [0.1806, 0.2162], [0.1806, 0.0226], [0.1376, -0.1182], [0.0688, -0.215], [0, -0.259], [-0.0688, -0.215], [-0.1376, -0.1182], [-0.1806, 0.0226]], 0.02), teamMat);
shieldFace.name = 'shieldFace';
shieldFace.position.set(0, 0, 0.029);
shield.add(shieldFace);
const shieldCrossV = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.38, 0.012), trimMat);
shieldCrossV.name = 'shieldCrossV';
shieldCrossV.position.set(0, -0.03, 0.04);
shield.add(shieldCrossV);
const shieldCrossH = new THREE.Mesh(new THREE.BoxGeometry(0.28, 0.07, 0.012), trimMat);
shieldCrossH.name = 'shieldCrossH';
shieldCrossH.position.set(0, 0.07, 0.04);
shield.add(shieldCrossH);
const armR = new THREE.Group();
armR.name = 'armR';
armR.position.set(-0.34, 1.08, 0);
armR.rotation.set(deg(8), 0, deg(-6));
root.add(armR);
const pauldronR = new THREE.Mesh(boxGeo6, armorMat);
pauldronR.name = 'pauldronR';
pauldronR.position.set(-0.03, 0.03, 0);
armR.add(pauldronR);
const pauldronTrimR = new THREE.Mesh(boxGeo7, trimMat);
pauldronTrimR.name = 'pauldronTrimR';
pauldronTrimR.position.set(-0.03, -0.04, 0);
armR.add(pauldronTrimR);
const upperArmR = new THREE.Mesh(boxGeo8, armorDarkMat);
upperArmR.name = 'upperArmR';
upperArmR.position.set(0, -0.15, 0);
armR.add(upperArmR);
const forearmR = new THREE.Group();
forearmR.name = 'forearmR';
forearmR.position.set(0, -0.26, 0);
forearmR.rotation.set(deg(-75), 0, 0);
armR.add(forearmR);
const vambraceR = new THREE.Mesh(boxGeo9, armorMat);
vambraceR.name = 'vambraceR';
vambraceR.position.set(0, -0.1, 0);
forearmR.add(vambraceR);
const gauntletR = new THREE.Mesh(boxGeo10, leatherMat);
gauntletR.name = 'gauntletR';
gauntletR.position.set(0, -0.24, 0);
forearmR.add(gauntletR);
const sword = new THREE.Group();
sword.name = 'sword';
sword.position.set(0, -0.24, 0);
sword.rotation.set(deg(100), 0, 0);
forearmR.add(sword);
const grip = new THREE.Mesh(new THREE.BoxGeometry(0.045, 0.16, 0.045), leatherMat);
grip.name = 'grip';
sword.add(grip);
const pommel = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.06, 0.07), trimMat);
pommel.name = 'pommel';
pommel.position.set(0, -0.11, 0);
sword.add(pommel);
const crossguard = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.05, 0.07), trimMat);
crossguard.name = 'crossguard';
crossguard.position.set(0, 0.1, 0);
sword.add(crossguard);
const blade = new THREE.Mesh(extrude([[-0.045, 0], [0.045, 0], [0.04, 0.62], [0, 0.72], [-0.04, 0.62]], 0.022), bladeMat);
blade.name = 'blade';
blade.position.set(0, 0.125, 0);
sword.add(blade);
const fuller = new THREE.Mesh(new THREE.BoxGeometry(0.014, 0.52, 0.028), armorDarkMat);
fuller.name = 'fuller';
fuller.position.set(0, 0.42, 0);
sword.add(fuller);
const head = new THREE.Group();
head.name = 'head';
head.position.set(0, 1.18, 0);
root.add(head);
const helm = new THREE.Mesh(new THREE.BoxGeometry(0.48, 0.5, 0.46), armorMat);
helm.name = 'helm';
helm.position.set(0, 0.29, 0);
head.add(helm);
const helmBand = new THREE.Mesh(new THREE.BoxGeometry(0.492, 0.05, 0.472), trimMat);
helmBand.name = 'helmBand';
helmBand.position.set(0, 0.19, 0);
head.add(helmBand);
const visorSlit = new THREE.Mesh(new THREE.BoxGeometry(0.36, 0.06, 0.02), slitMat);
visorSlit.name = 'visorSlit';
visorSlit.position.set(0, 0.33, 0.232);
head.add(visorSlit);
const visorNoseSlit = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.2, 0.02), slitMat);
visorNoseSlit.name = 'visorNoseSlit';
visorNoseSlit.position.set(0, 0.23, 0.232);
head.add(visorNoseSlit);
const helmRidge = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.12, 0.03), armorDarkMat);
helmRidge.name = 'helmRidge';
helmRidge.position.set(0, 0.46, 0.236);
head.add(helmRidge);
const breath = new THREE.Mesh(boxGeo11, slitMat);
breath.name = 'breath';
breath.position.set(0.12, 0.16, 0.232);
head.add(breath);
const breath2 = new THREE.Mesh(boxGeo11, slitMat);
breath2.name = 'breath';
breath2.position.set(0.17, 0.16, 0.232);
head.add(breath2);
const breath3 = new THREE.Mesh(boxGeo11, slitMat);
breath3.name = 'breath';
breath3.position.set(-0.12, 0.16, 0.232);
head.add(breath3);
const breath4 = new THREE.Mesh(boxGeo11, slitMat);
breath4.name = 'breath';
breath4.position.set(-0.17, 0.16, 0.232);
head.add(breath4);
const plume = new THREE.Group();
plume.name = 'plume';
plume.position.set(0, 0.54, -0.02);
head.add(plume);
const plumeHolder = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.06, 0.08), trimMat);
plumeHolder.name = 'plumeHolder';
plumeHolder.position.set(0, 0.03, 0);
plume.add(plumeHolder);
const plumeCrest = new THREE.Mesh(extrude([[-0.19, -0.02], [-0.17, 0.07], [-0.1, 0.14], [0, 0.17], [0.1, 0.16], [0.19, 0.1], [0.25, 0], [0.27, -0.12], [0.235, -0.2], [0.21, -0.1], [0.19, -0.02]], 0.075), teamMat);
plumeCrest.name = 'plumeCrest';
plumeCrest.rotation.set(0, deg(90), 0);
plumeCrest.scale.set(1.3, 1.3, 1);
plume.add(plumeCrest);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createKnight;
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









