1.2K tris5.84 × 2.05 × 1.57 m
Use it in code
Remix in the editorsatellite.js · 283 lines
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
* Satellite — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the model (it floats — pickups, planets)
* Triangles: 1,152 · Size (W × H × D): 5.84 × 2.04 × 1.57 m
*
* Usage:
* import { createSatellite } from './satellite.js';
*
* const model = createSatellite();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// Revolve a 2D profile [[radius, y], ...] around the Y axis.
function lathe(points, segments, phiStart = 0, phiLength = Math.PI * 2) {
return new THREE.LatheGeometry(points.map(([x, y]) => new THREE.Vector2(x, y)), segments, phiStart, phiLength);
}
// 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 createSatellite() {
const root = new THREE.Group();
root.name = 'Satellite';
// Materials
const foilMat = new THREE.MeshStandardMaterial({ name: 'foil', color: 0xd9a83c, roughness: 0.35, metalness: 0.7, flatShading: true });
const seamMat = new THREE.MeshStandardMaterial({ name: 'seam', color: 0x7a5a1e, roughness: 0.6, metalness: 0.4, flatShading: true });
const frameMat = new THREE.MeshStandardMaterial({ name: 'frame', color: 0xc9ced6, roughness: 0.35, metalness: 0.7, flatShading: true });
const panelBackMat = new THREE.MeshStandardMaterial({ name: 'panelBack', color: 0x1b2238, roughness: 0.7, flatShading: true });
const cellMat = new THREE.MeshStandardMaterial({ name: 'cell', color: 0x2c55b8, roughness: 0.25, metalness: 0.5, flatShading: true });
const whiteMat = new THREE.MeshStandardMaterial({ name: 'white', color: 0xeef0f3, roughness: 0.45, flatShading: true, side: THREE.DoubleSide });
const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x2a2d33, roughness: 0.5, metalness: 0.5, flatShading: true });
// Shared geometries
const boxGeo = new THREE.BoxGeometry(1.012, 0.025, 1.012);
const boxGeo2 = new THREE.BoxGeometry(0.8, 0.06, 0.8);
const cylinderGeo = new THREE.CylinderGeometry(0.035, 0.035, 0.45, 6);
const cylinderGeo2 = new THREE.CylinderGeometry(0.07, 0.07, 0.1, 8);
const boxGeo3 = new THREE.BoxGeometry(0.95, 1.05, 0.035);
const boxGeo4 = new THREE.BoxGeometry(0.97, 0.03, 0.045);
const boxGeo5 = new THREE.BoxGeometry(0.425, 0.31, 0.012);
const boxGeo6 = new THREE.BoxGeometry(0.08, 0.05, 0.03);
const boxGeo7 = new THREE.BoxGeometry(0.04, 0.05, 0.03);
// Parts
const bus = new THREE.Mesh(new THREE.BoxGeometry(1, 1.2, 1), foilMat);
bus.name = 'bus';
root.add(bus);
const seamLow = new THREE.Mesh(boxGeo, seamMat);
seamLow.name = 'seamLow';
seamLow.position.set(0, -0.3, 0);
root.add(seamLow);
const seamHigh = new THREE.Mesh(boxGeo, seamMat);
seamHigh.name = 'seamHigh';
seamHigh.position.set(0, 0.3, 0);
root.add(seamHigh);
const topDeck = new THREE.Mesh(boxGeo2, frameMat);
topDeck.name = 'topDeck';
topDeck.position.set(0, 0.63, 0);
root.add(topDeck);
const bottomDeck = new THREE.Mesh(boxGeo2, frameMat);
bottomDeck.name = 'bottomDeck';
bottomDeck.position.set(0, -0.63, 0);
root.add(bottomDeck);
const thruster = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.17, 0.22, 10), darkMat);
thruster.name = 'thruster';
thruster.position.set(0, -0.77, 0);
root.add(thruster);
const whip1 = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.55, 5), frameMat);
whip1.name = 'whip1';
whip1.position.set(-0.28, 0.9, -0.25);
whip1.rotation.set(0, 0, deg(18));
root.add(whip1);
const whip2 = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.45, 5), frameMat);
whip2.name = 'whip2';
whip2.position.set(0.3, 0.85, -0.3);
whip2.rotation.set(deg(-15), 0, deg(-12));
root.add(whip2);
const beacon = new THREE.Mesh(new THREE.SphereGeometry(0.06, 6, 4), whiteMat);
beacon.name = 'beacon';
beacon.position.set(0, 0.7, 0.2);
root.add(beacon);
const wingRight = new THREE.Group();
wingRight.name = 'wingRight';
wingRight.position.set(0.5, 0, 0);
wingRight.rotation.set(deg(20), 0, 0);
root.add(wingRight);
const arm = new THREE.Mesh(cylinderGeo, frameMat);
arm.name = 'arm';
arm.position.set(0.225, 0, 0);
arm.rotation.set(0, 0, deg(90));
wingRight.add(arm);
const drive = new THREE.Mesh(cylinderGeo2, darkMat);
drive.name = 'drive';
drive.position.set(0.05, 0, 0);
drive.rotation.set(0, 0, deg(90));
wingRight.add(drive);
const panel1 = new THREE.Mesh(boxGeo3, panelBackMat);
panel1.name = 'panel1';
panel1.position.set(0.925, 0, 0);
wingRight.add(panel1);
const box14 = new THREE.Mesh(boxGeo4, frameMat);
box14.position.set(0.925, 0.525, 0);
wingRight.add(box14);
const box15 = new THREE.Mesh(boxGeo4, frameMat);
box15.position.set(0.925, -0.525, 0);
wingRight.add(box15);
const box16 = new THREE.Mesh(boxGeo5, cellMat);
box16.position.set(0.7025, 0.33, 0.02);
wingRight.add(box16);
const box17 = new THREE.Mesh(boxGeo5, cellMat);
box17.position.set(1.1475, 0.33, 0.02);
wingRight.add(box17);
const box18 = new THREE.Mesh(boxGeo5, cellMat);
box18.position.set(0.7025, 0, 0.02);
wingRight.add(box18);
const box19 = new THREE.Mesh(boxGeo5, cellMat);
box19.position.set(1.1475, 0, 0.02);
wingRight.add(box19);
const box20 = new THREE.Mesh(boxGeo5, cellMat);
box20.position.set(0.7025, -0.33, 0.02);
wingRight.add(box20);
const box21 = new THREE.Mesh(boxGeo5, cellMat);
box21.position.set(1.1475, -0.33, 0.02);
wingRight.add(box21);
const panel2 = new THREE.Mesh(boxGeo3, panelBackMat);
panel2.name = 'panel2';
panel2.position.set(1.935, 0, 0);
wingRight.add(panel2);
const box23 = new THREE.Mesh(boxGeo4, frameMat);
box23.position.set(1.935, 0.525, 0);
wingRight.add(box23);
const box24 = new THREE.Mesh(boxGeo4, frameMat);
box24.position.set(1.935, -0.525, 0);
wingRight.add(box24);
const box25 = new THREE.Mesh(boxGeo5, cellMat);
box25.position.set(1.7125, 0.33, 0.02);
wingRight.add(box25);
const box26 = new THREE.Mesh(boxGeo5, cellMat);
box26.position.set(2.1575, 0.33, 0.02);
wingRight.add(box26);
const box27 = new THREE.Mesh(boxGeo5, cellMat);
box27.position.set(1.7125, 0, 0.02);
wingRight.add(box27);
const box28 = new THREE.Mesh(boxGeo5, cellMat);
box28.position.set(2.1575, 0, 0.02);
wingRight.add(box28);
const box29 = new THREE.Mesh(boxGeo5, cellMat);
box29.position.set(1.7125, -0.33, 0.02);
wingRight.add(box29);
const box30 = new THREE.Mesh(boxGeo5, cellMat);
box30.position.set(2.1575, -0.33, 0.02);
wingRight.add(box30);
const box31 = new THREE.Mesh(boxGeo6, frameMat);
box31.position.set(1.43, 0, 0);
wingRight.add(box31);
const box32 = new THREE.Mesh(boxGeo7, frameMat);
box32.position.set(0.46, 0, 0);
wingRight.add(box32);
const wingLeft = new THREE.Group();
wingLeft.name = 'wingLeft';
wingLeft.position.set(-0.5, 0, 0);
wingLeft.rotation.set(deg(20), 0, deg(180));
root.add(wingLeft);
const arm2 = new THREE.Mesh(cylinderGeo, frameMat);
arm2.name = 'arm';
arm2.position.set(0.225, 0, 0);
arm2.rotation.set(0, 0, deg(90));
wingLeft.add(arm2);
const drive2 = new THREE.Mesh(cylinderGeo2, darkMat);
drive2.name = 'drive';
drive2.position.set(0.05, 0, 0);
drive2.rotation.set(0, 0, deg(90));
wingLeft.add(drive2);
const panel12 = new THREE.Mesh(boxGeo3, panelBackMat);
panel12.name = 'panel1';
panel12.position.set(0.925, 0, 0);
wingLeft.add(panel12);
const box37 = new THREE.Mesh(boxGeo4, frameMat);
box37.position.set(0.925, 0.525, 0);
wingLeft.add(box37);
const box38 = new THREE.Mesh(boxGeo4, frameMat);
box38.position.set(0.925, -0.525, 0);
wingLeft.add(box38);
const box39 = new THREE.Mesh(boxGeo5, cellMat);
box39.position.set(0.7025, 0.33, 0.02);
wingLeft.add(box39);
const box40 = new THREE.Mesh(boxGeo5, cellMat);
box40.position.set(1.1475, 0.33, 0.02);
wingLeft.add(box40);
const box41 = new THREE.Mesh(boxGeo5, cellMat);
box41.position.set(0.7025, 0, 0.02);
wingLeft.add(box41);
const box42 = new THREE.Mesh(boxGeo5, cellMat);
box42.position.set(1.1475, 0, 0.02);
wingLeft.add(box42);
const box43 = new THREE.Mesh(boxGeo5, cellMat);
box43.position.set(0.7025, -0.33, 0.02);
wingLeft.add(box43);
const box44 = new THREE.Mesh(boxGeo5, cellMat);
box44.position.set(1.1475, -0.33, 0.02);
wingLeft.add(box44);
const panel22 = new THREE.Mesh(boxGeo3, panelBackMat);
panel22.name = 'panel2';
panel22.position.set(1.935, 0, 0);
wingLeft.add(panel22);
const box46 = new THREE.Mesh(boxGeo4, frameMat);
box46.position.set(1.935, 0.525, 0);
wingLeft.add(box46);
const box47 = new THREE.Mesh(boxGeo4, frameMat);
box47.position.set(1.935, -0.525, 0);
wingLeft.add(box47);
const box48 = new THREE.Mesh(boxGeo5, cellMat);
box48.position.set(1.7125, 0.33, 0.02);
wingLeft.add(box48);
const box49 = new THREE.Mesh(boxGeo5, cellMat);
box49.position.set(2.1575, 0.33, 0.02);
wingLeft.add(box49);
const box50 = new THREE.Mesh(boxGeo5, cellMat);
box50.position.set(1.7125, 0, 0.02);
wingLeft.add(box50);
const box51 = new THREE.Mesh(boxGeo5, cellMat);
box51.position.set(2.1575, 0, 0.02);
wingLeft.add(box51);
const box52 = new THREE.Mesh(boxGeo5, cellMat);
box52.position.set(1.7125, -0.33, 0.02);
wingLeft.add(box52);
const box53 = new THREE.Mesh(boxGeo5, cellMat);
box53.position.set(2.1575, -0.33, 0.02);
wingLeft.add(box53);
const box54 = new THREE.Mesh(boxGeo6, frameMat);
box54.position.set(1.43, 0, 0);
wingLeft.add(box54);
const box55 = new THREE.Mesh(boxGeo7, frameMat);
box55.position.set(0.46, 0, 0);
wingLeft.add(box55);
const dishAntenna = new THREE.Group();
dishAntenna.name = 'dishAntenna';
dishAntenna.position.set(0, 0.12, 0.6);
dishAntenna.rotation.set(deg(70), 0, 0);
root.add(dishAntenna);
const dish = new THREE.Mesh(lathe([[0, 0], [0.0917, 0.005], [0.1833, 0.02], [0.275, 0.045], [0.3667, 0.08], [0.4583, 0.125], [0.55, 0.1801]], 18), whiteMat);
dish.name = 'dish';
dishAntenna.add(dish);
const dishHub = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.07, 0.12, 8), frameMat);
dishHub.name = 'dishHub';
dishHub.position.set(0, -0.02, 0);
dishAntenna.add(dishHub);
const feed = new THREE.Mesh(new THREE.ConeGeometry(0.06, 0.1, 8), darkMat);
feed.name = 'feed';
feed.position.set(0, 0.42, 0);
feed.rotation.set(deg(180), 0, 0);
dishAntenna.add(feed);
const tube60 = new THREE.Mesh(tube([[0.4675, 0.1301, 0], [0.2338, 0.295, 0], [0, 0.44, 0]], 0.012, 4, 4), frameMat);
dishAntenna.add(tube60);
const tube61 = new THREE.Mesh(tube([[-0.2337, 0.1301, 0.4049], [-0.1169, 0.295, 0.2024], [0, 0.44, 0]], 0.012, 4, 4), frameMat);
dishAntenna.add(tube61);
const tube62 = new THREE.Mesh(tube([[-0.2338, 0.1301, -0.4049], [-0.1169, 0.295, -0.2024], [0, 0.44, 0]], 0.012, 4, 4), frameMat);
dishAntenna.add(tube62);
const dishMount = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.16, 6), frameMat);
dishMount.name = 'dishMount';
dishMount.position.set(0, 0.1, 0.55);
dishMount.rotation.set(deg(90), 0, 0);
root.add(dishMount);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createSatellite;
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









