let globalScaleVar = 200; class RotatingOnion { constructor(angles, scale) { this.angles = { ...angles }; // Destructure this.scale = scale; this.orbitingObjects = []; // Array to hold orbiting objects // Onion oscillation this.oscillationOffset = random(1000); // Tracks the time or phase for oscillation this.xOscillationAmplitude = 10; // Amplitude for angle.x oscillation this.zOscillationAmplitude = 10; // Amplitude for angle.z oscillation this.xBase = angles.x; // Base value for angle.y oscillation this.zBase = angles.z; // Base value for angle.z oscillation } // Add an orbiting object to the onion addOrbitingObject(model, orbitRadius, speed, scale, tiltAngleX, tiltAngleY) { const tiltAngle = { x: tiltAngleX, y: tiltAngleY }; // Pass both X and Y tilt angles this.orbitingObjects.push(new OrbitingObject(this, model, orbitRadius, speed, scale, tiltAngle)); } update() { // Increment the angles as needed per frame this.angles.y += 0.1; this.oscillationOffset += 1; // Increment the oscillation phase this.angles.x = this.xBase + this.xOscillationAmplitude * sin(this.oscillationOffset*0.7); this.angles.z = this.zBase + this.zOscillationAmplitude * cos(this.oscillationOffset*0.4); // Update each orbiting object for (let obj of this.orbitingObjects) { obj.update(); } } display() { push(); rotateX(this.angles.x); rotateY(this.angles.y); rotateZ(this.angles.z); scale(this.scale); model(onion_stroke); push() emissiveMaterial(0,0,0) model(onion_fill); pop() // Display all orbiting objects for (let obj of this.orbitingObjects) { obj.display(); this.displayTorus(obj.orbitRadius, obj.tiltAngle); } pop(); } // Function to display a torus representing the orbit path displayTorus(orbitRadius, tiltAngle) { push(); stroke(150, 150, 150); // Set the color of the orbit path strokeWeight(0.5); noFill(); // Apply the tilt before rendering the torus rotateY(-tiltAngle.y); // Apply Y-axis tilt for the torus rotateX(90-tiltAngle.x); // Apply the tilt angle to rotate the orbit plane torus(orbitRadius, 0.001); // Radius and tube radius of the torus pop(); } } class OrbitingObject { constructor(parent, model, orbitRadius, speed, scale, tiltAngle) { this.parent = parent; // The parent object (Onion) this.model = model this.orbitRadius = orbitRadius; this.speed = speed; // Orbiting speed (angular speed) this.scale = scale; this.rotationAngle_x = random(360); this.rotationAngle_y = random(360); this.rotationAngle_z = random(360); this.rotationSpeed_x = random(0.5, 2); this.rotationSpeed_y = random(0.5, 2); this.rotationSpeed_z = random(0.5, 2); this.angle = random(360); // Start at a random position in the orbit this.tiltAngle = tiltAngle; } update() { // Update the angle to simulate rotation around the parent this.angle += this.speed; // Rotate in place this.rotationAngle_x += this.rotationSpeed_x; this.rotationAngle_y += this.rotationSpeed_y; this.rotationAngle_z += this.rotationSpeed_z; // Calculate the position of the orbiting object in the un-tilted plane const x = this.orbitRadius * cos(this.angle); const z = this.orbitRadius * sin(this.angle); const y = 0; // Or any base y value if it's not 0 // Apply the tilt transformation around both the X and Y axes const tiltXCos = cos(this.tiltAngle.x); const tiltXSin = sin(this.tiltAngle.x); const tiltYCos = cos(this.tiltAngle.y); const tiltYSin = sin(this.tiltAngle.y); // Tilt transformation around the X-axis (first) let newX = x; let newY = y * tiltXCos + z * tiltXSin; let newZ = z * tiltXCos - y * tiltXSin; // Tilt transformation around the Y-axis (second) this.x = newX * tiltYCos - newZ * tiltYSin; this.y = newY; this.z = newZ * tiltYCos + newX * tiltYSin; } display() { push(); translate(this.x, this.y, this.z); // Move to the correct orbit position // Apply in-place rotation rotateX(this.rotationAngle_x) rotateY(this.rotationAngle_y); rotateZ(this.rotationAngle_z) scale(this.scale); model(this.model); pop(); } } class Text3D { constructor(fromColor, toColor, textString, position_x, position_y) { this.position_x = position_x this.position_y = position_y this.fromColor = fromColor; this.toColor = toColor; this.textString = textString; this.depth = 150; // Number of layers for 3D effect this.depthSpacing = 0.15; // Depth spacing between layers } display() { push(); for (let i = 0; i < this.depth; i++) { let r = map(i, 0, this.depth - 1, this.fromColor[0], this.toColor[0]); let g = map(i, 0, this.depth - 1, this.fromColor[1], this.toColor[1]); let b = map(i, 0, this.depth - 1, this.fromColor[2], this.toColor[2]); fill(r, g, b); translate(0, 0, this.depthSpacing); text(this.textString, this.position_x, this.position_y); } pop(); } } function preload() { onion_stroke = loadModel('./assets/onion_stroke.obj'); onion_fill = loadModel('./assets/onion_fill.obj'); font = loadFont('./assets/jgs5.ttf'); } function setup() { createCanvas(windowWidth, windowHeight, WEBGL); angleMode(DEGREES); noStroke(); textFont(font, 60); textAlign(CENTER, CENTER); randomSeed() // Initialize 3D text startGradient = [210, 0, 255]; // Start color endGradient = [0, 255, 180]; // End color text3D = new Text3D(startGradient, endGradient, "PLACEHOLDER", 0, 350); // Initialize the onion object with radius, angles, and scale onionObj = new RotatingOnion({ x: 0, y: random(360), z: 180 }, globalScaleVar); // Add orbiting objects to the onion for (let i = 1.8; i <= 4.8; i += 0.6) { onionObj.addOrbitingObject( onion_fill, i, random(0.1, 0.8), globalScaleVar * 0.0001 * random(1, 6), random(0, 30), random(0, 30) ); } } function draw() { background(0); orbitControl(); ambientLight(100); shininess(0); specularMaterial(255, 255, 255); directionalLight( startGradient[0], startGradient[1], startGradient[2], // color -180, 180, 0 // direction ); directionalLight( endGradient[0], endGradient[1], endGradient[2], // color 180, -180, 0 // direction ); // Update the onion object onionObj.update(); // Display the onion and its orbiting objects onionObj.display(); // Display 3D text text3D.display(); } function windowResized() { resizeCanvas(windowWidth, windowHeight); }