Codels
720 tris0.97 × 1.79 × 0.10 m

Use it in code

Remix in the editor
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
 * Wall Banner — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: wall mount point (back of the model on the wall plane z = 0)
 * Triangles: 720 · Size (W × H × D): 0.97 × 1.79 × 0.10 m
 *
 * Usage:
 *   import { createWallBanner } from './wall-banner.js';
 *   
 *   const model = createWallBanner();
 *   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 createWallBanner() {
  const root = new THREE.Group();
  root.name = 'WallBanner';

  // Materials
  const fieldMat = new THREE.MeshStandardMaterial({ name: 'field', color: 0x8e1f27, roughness: 0.95, flatShading: true });
  const sleeveMat = new THREE.MeshStandardMaterial({ name: 'sleeve', color: 0x6b171d, roughness: 0.95, flatShading: true });
  const trimMat = new THREE.MeshStandardMaterial({ name: 'trim', color: 0xe2b64c, roughness: 0.75, flatShading: true });
  const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0x5e3b22, roughness: 0.85, flatShading: true });
  const ironMat = new THREE.MeshStandardMaterial({ name: 'iron', color: 0x3d3f45, roughness: 0.5, metalness: 0.55, flatShading: true });
  const goldMat = new THREE.MeshStandardMaterial({ name: 'gold', color: 0xc99b3c, roughness: 0.35, metalness: 0.7, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.05, 0.12, 0.012);
  const cylinderGeo = new THREE.CylinderGeometry(0.007, 0.007, 0.006, 6);
  const boxGeo2 = new THREE.BoxGeometry(0.018, 0.018, 0.063);
  const torusGeo = new THREE.TorusGeometry(0.021, 0.006, 4, 8);
  const sphereGeo = new THREE.SphereGeometry(0.026, 7, 5);
  const coneGeo = new THREE.ConeGeometry(0.016, 0.05, 6);
  const sphereGeo2 = new THREE.SphereGeometry(0.0192, 6, 4);

  // Parts
  const wallPlate = new THREE.Mesh(boxGeo, ironMat);
  wallPlate.name = 'wallPlate';
  wallPlate.position.set(0.35, 0.02, 0.006);
  root.add(wallPlate);
  const rivet = new THREE.Mesh(cylinderGeo, ironMat);
  rivet.name = 'rivet';
  rivet.position.set(0.35, 0.06, 0.014);
  rivet.rotation.set(deg(90), 0, 0);
  root.add(rivet);
  const rivet2 = new THREE.Mesh(cylinderGeo, ironMat);
  rivet2.name = 'rivet';
  rivet2.position.set(0.35, -0.02, 0.014);
  rivet2.rotation.set(deg(90), 0, 0);
  root.add(rivet2);
  const bracketArm = new THREE.Mesh(boxGeo2, ironMat);
  bracketArm.name = 'bracketArm';
  bracketArm.position.set(0.35, 0, 0.0435);
  root.add(bracketArm);
  const bracketRing = new THREE.Mesh(torusGeo, ironMat);
  bracketRing.name = 'bracketRing';
  bracketRing.position.set(0.35, 0, 0.075);
  bracketRing.rotation.set(0, deg(90), 0);
  root.add(bracketRing);
  const finialBall = new THREE.Mesh(sphereGeo, goldMat);
  finialBall.name = 'finialBall';
  finialBall.position.set(0.415, 0, 0.075);
  root.add(finialBall);
  const finialTip = new THREE.Mesh(coneGeo, goldMat);
  finialTip.name = 'finialTip';
  finialTip.position.set(0.46, 0, 0.075);
  finialTip.rotation.set(0, 0, deg(-90));
  root.add(finialTip);
  const wallPlate2 = new THREE.Mesh(boxGeo, ironMat);
  wallPlate2.name = 'wallPlate';
  wallPlate2.position.set(-0.35, 0.02, 0.006);
  root.add(wallPlate2);
  const rivet3 = new THREE.Mesh(cylinderGeo, ironMat);
  rivet3.name = 'rivet';
  rivet3.position.set(-0.35, 0.06, 0.014);
  rivet3.rotation.set(deg(90), 0, 0);
  root.add(rivet3);
  const rivet4 = new THREE.Mesh(cylinderGeo, ironMat);
  rivet4.name = 'rivet';
  rivet4.position.set(-0.35, -0.02, 0.014);
  rivet4.rotation.set(deg(90), 0, 0);
  root.add(rivet4);
  const bracketArm2 = new THREE.Mesh(boxGeo2, ironMat);
  bracketArm2.name = 'bracketArm';
  bracketArm2.position.set(-0.35, 0, 0.0435);
  root.add(bracketArm2);
  const bracketRing2 = new THREE.Mesh(torusGeo, ironMat);
  bracketRing2.name = 'bracketRing';
  bracketRing2.position.set(-0.35, 0, 0.075);
  bracketRing2.rotation.set(0, deg(90), 0);
  root.add(bracketRing2);
  const finialBall2 = new THREE.Mesh(sphereGeo, goldMat);
  finialBall2.name = 'finialBall';
  finialBall2.position.set(-0.415, 0, 0.075);
  root.add(finialBall2);
  const finialTip2 = new THREE.Mesh(coneGeo, goldMat);
  finialTip2.name = 'finialTip';
  finialTip2.position.set(-0.46, 0, 0.075);
  finialTip2.rotation.set(0, 0, deg(90));
  root.add(finialTip2);
  const rod = new THREE.Mesh(new THREE.CylinderGeometry(0.014, 0.014, 0.82, 8), woodMat);
  rod.name = 'rod';
  rod.position.set(0, 0, 0.075);
  rod.rotation.set(0, 0, deg(90));
  root.add(rod);
  const sleeve = new THREE.Mesh(new THREE.CylinderGeometry(0.026, 0.026, 0.61, 8), sleeveMat);
  sleeve.name = 'sleeve';
  sleeve.position.set(0, 0, 0.075);
  sleeve.rotation.set(0, 0, deg(90));
  root.add(sleeve);
  const banner = new THREE.Mesh(extrude([[-0.3, -0.02], [-0.3, -1.432], [0, -1.6], [0.3, -1.432], [0.3, -0.02]], 0.012), fieldMat);
  banner.name = 'banner';
  banner.position.set(0, 0, 0.075);
  root.add(banner);
  const border = new THREE.Mesh(extrude([[-0.3, -0.02], [-0.3, -1.432], [0, -1.6], [0.3, -1.432], [0.3, -0.02]], 0.004, [[[-0.265, -0.055], [-0.265, -1.4044], [0, -1.565], [0.265, -1.4044], [0.265, -0.055]]]), trimMat);
  border.name = 'border';
  border.position.set(0, 0, 0.083);
  root.add(border);
  const crown = new THREE.Mesh(extrude([[-0.126, -0.078], [0.126, -0.078], [0.15, 0.09], [0.072, 0.018], [0, 0.114], [-0.072, 0.018], [-0.15, 0.09]], 0.006), trimMat);
  crown.name = 'crown';
  crown.position.set(0, -0.5355, 0.084);
  root.add(crown);
  const crownBand = new THREE.Mesh(new THREE.BoxGeometry(0.258, 0.03, 0.004), sleeveMat);
  crownBand.name = 'crownBand';
  crownBand.position.set(0, -0.5805, 0.088);
  root.add(crownBand);
  const crownPearl = new THREE.Mesh(sphereGeo2, trimMat);
  crownPearl.name = 'crownPearl';
  crownPearl.position.set(-0.15, -0.4255, 0.085);
  root.add(crownPearl);
  const crownPearl2 = new THREE.Mesh(sphereGeo2, trimMat);
  crownPearl2.name = 'crownPearl';
  crownPearl2.position.set(0, -0.4015, 0.085);
  root.add(crownPearl2);
  const crownPearl3 = new THREE.Mesh(sphereGeo2, trimMat);
  crownPearl3.name = 'crownPearl';
  crownPearl3.position.set(0.15, -0.4255, 0.085);
  root.add(crownPearl3);
  const tasselKnot = new THREE.Mesh(new THREE.SphereGeometry(0.022, 6, 4), goldMat);
  tasselKnot.name = 'tasselKnot';
  tasselKnot.position.set(0, -1.612, 0.075);
  root.add(tasselKnot);
  const tassel = new THREE.Mesh(new THREE.ConeGeometry(0.024, 0.09, 6), goldMat);
  tassel.name = 'tassel';
  tassel.position.set(0, -1.665, 0.075);
  tassel.rotation.set(deg(180), 0, 0);
  root.add(tassel);

  root.traverse((o) => {
    if (o.isMesh) {
      o.castShadow = true;
      o.receiveShadow = true;
    }
  });
  return root;
}

export default createWallBanner;

Ratings & comments

—
No ratings yet — be the first.
5
0
4
0
3
0
2
0
1
0

Sign in to rate and comment.

0 comments

No comments yet. Share how you used this model or what could be better.

Related models

Same category or similar tags