Codels
648 треуг.1,12 × 1,94 × 1,03 m

Использовать в коде

Ремикс в редакторе
Самодостаточный ES-модуль — зависит только от three. Вызовите фабрику и добавьте группу в сцену.
/**
 * 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;

Оценки и комментарии

—
Оценок пока нет — будьте первым.
5
0
4
0
3
0
2
0
1
0

Войдите, чтобы ставить оценки и оставлять комментарии.

0 комментариев

Комментариев пока нет. Расскажите, как вы использовали эту модель или что можно улучшить.

Похожие модели

Та же категория или похожие теги