“Gehryisms” by Dave Pagurek
https://openprocessing.org/sketch/1418577
License CreativeCommons Attribution NonCommercial ShareAlike
https://creativecommons.org/licenses/by-nc-sa/3.0
{{filePath}}
{{width}} x {{height}}
Report Sketch
Oh, that naughty sketch! Please let us know what the issue is below.
Apply Template
Applying this template will reset your sketch and remove all your changes. Are you sure you would like to continue?
Report Sketch
Report Comment
Please confirm that you would like to report the comment below.
We will review your submission and take any actions necessary per our Community Guidelines. In addition to reporting this comment, you can also block the user to prevent any future interactions.
Please report comments only when necessary. Unnecessary or abusive use of this tool may result in your own account being suspended.
Are you sure you want to delete your sketch?
Any files uploaded will be deleted as well.
Delete Comment?
This will also delete all the replies to this comment.
Delete this tab? Any code in it will be deleted as well.
Select a collection to submit your sketch
We Need Your Support
Since 2008, OpenProcessing has provided tools for creative coders to learn, create, and share over a million open source projects in a friendly environment.
Niche websites like ours need your continued support for future development and maintenance, while keeping it an ad-free platform that respects your data and privacy!
Please consider subscribing below to show your support with a "Plus" badge on your profile and get access to many other features!
CC Attribution NonCommercial ShareAlike
Gehryisms
Pagurek
xxxxxxxxxx
/*
Randomly generated curve shapes inspired by Frank Gehry's buildings.
Each shape is a line swept along a curve. Each piece of the "building"
randomly spawns child pieces on top.
*/
let building = []
const regenerate = () => {
if (building) {
// Clean out old meshes
for (const { mesh } of building) {
_renderer._freeBuffers(mesh.gid)
}
}
building = []
let id = 0
// Create one curve sweep, which will be offsetted/scaled relative
// to a parent piece
const genPiece = (parent, depth, bounds) => {
const transforms = []
if (parent) {
transforms.push(...parent.transforms)
}
const t = {}
for (const prop in bounds) {
t[prop] = random(...bounds[prop])
}
transforms.push(() => {
translate(t.offX, t.offY, t.offZ)
rotateZ(t.rotZ)
rotateY(t.rotY)
scale(t.sX, t.sY, t.sZ)
})
const piece = {
mesh: makeBuildingPiece(id, depth, t.sX, t.sY, t.sZ),
transforms,
}
id++
building.push(piece)
let numChildren = depth < 2 ? random([0, 1, 2]) : 0
if (depth === 0) numChildren = max(numChildren, 1)
if (numChildren > 0) {
let positions = []
for (let offX = -40; offX <= 40; offX += 20) {
for (let offZ = -40; offZ <= 40; offZ += 20) {
positions.push({ offX, offZ })
}
}
for (let i = 0; i < numChildren; i++) {
const position = random(positions)
positions = positions.filter((p) => p !== position)
const { offX, offZ } = position
genPiece(piece, depth + 1, {
offY: [-70, -80],
offX: [offX - 10, offX + 10],
offZ: [offZ - 10, offZ + 10],
rotZ: [-PI/12, PI/12],
rotY: [0, TWO_PI],
sX: [0.25, 0.75 / numChildren],
sZ: [0.25, 0.75 / numChildren],
sY: [0.5, 0.9],
})
}
}
}
// Create the base piece and recursively generate children pieces
genPiece(undefined, 0, {
offY: [100, 100],
offX: [0, 0],
offZ: [0, 0],
rotZ: [0, 0],
rotY: [0, 0],
sX: [1, 1],
sY: [1, 1],
sZ: [1, 1],
})
}
let bgColors
function setup() {
createCanvas(500, 500, WEBGL)
frameRate(30)
setupRendering()
bgColors = ['#102436', '#4f2641'].map((c) => color(c))
}
function drawBuilding() {
for (const { mesh, transforms } of building) {
push()
transforms.forEach((t) => t())
model(mesh)
pop()
}
}
let lastId = -1
function draw() {
const period = 4000
background(
lerpColor(
bgColors[0],
bgColors[1],
map(cos(millis() / period * TWO_PI), -1, 1, 0, 1)
)
)
orbitControl()
const id = floor(millis() / period)
if (id !== lastId) {
regenerate()
}
lastId = id
// The shader does a reflection using a sphere map
shader(reflection)
reflection.setUniform('sphereMap', sphereMap)
reflection.setUniform('roughSphereMap', irradianceMap)
reflection.setUniform('texture', texture)
reflection.setUniform('bumpMap', bumpMap)
reflection.setUniform('bumpHeightScale', 1.5)
reflection.setUniform('time', millis())
noStroke()
scale(1.4)
rotateX(-PI * 0.02)
rotateY(millis() * 0.0003)
// In order for the reflections to work properly, I need the surface normal
// to be pointing out of the surface. Only the front face has the normal
// pointing in the right direction, but I want the back faces' reflections
// to work too, so I have to draw the front and back faces separately so
// I can give each one properly oriented normals
// Back faces
_renderer.GL.enable(_renderer.GL.CULL_FACE)
_renderer.GL.cullFace(_renderer.GL.FRONT)
reflection.setUniform('normalScale', -1)
drawBuilding()
// Front faces
_renderer.GL.cullFace(_renderer.GL.BACK)
reflection.setUniform('normalScale', 1)
drawBuilding()
}
// Function to sweep a line about a curve and save it to a P5 Geometry
// for efficient rendering
function makeBuildingPiece(id, depth, sx, sy, sz) {
return new p5.Geometry(1, 1, function() {
this.gid = `buildingPiece|${id}`
const numPoints = 10 - depth * 2
const r = 100
// Generate points in a circle with random offsets
const points = _.times(numPoints).map((i) => {
const angle = (i / numPoints) * TWO_PI
const pt = createVector(
r * cos(angle),
0,
r * sin(angle)
).add(createVector(
random(-20, 20),
0,
random(-20, 20)
))
return { pt }
})
// Add bezier tangent control points to make it smooth
for (let i = 0; i < points.length; i++) {
const prev = points[(i - 1 + points.length) % points.length]
const curr = points[i]
const next = points[(i + 1) % points.length]
// Make most points smooth, some are randomly cusps
if (random() < 0.8) {
const tangent = next.pt.copy().sub(prev.pt).mult(1/6)
curr.left = curr.pt.copy().sub(tangent)
curr.right = curr.pt.copy().add(tangent)
}
}
// Make it repeat
points.push(points[0])
// We're going to extrude the curve up, but instead of directly up,
// we will wobble the up vector a bit using theta (basically X rotation)
// and phi (basically Z rotation)
const thetaPeriod = random([1, 2, 3]) * TWO_PI
const thetaScale = random(0.1, 0.3) * PI / (1 + depth)
const thetaOffset = random(TWO_PI)
const phiPeriod = random([1, 2, 3]) * TWO_PI
const phiScale = random(-1, 1) * PI / 2
const phiOffset = random(TWO_PI)
const theta = (t) => thetaScale * sin(t * thetaPeriod + thetaOffset)
const phi = (t) => phiScale * sin(t * phiPeriod + phiOffset)
// The rest is just making mesh vertices and normals out of those curves
const path = createBezierPath(points)
const len = path.getTotalLength()
const samples = max(4, ceil((len * min(sx, sz)) / 4))
const bottomPts = []
const bitangents = []
for (let i = 0; i < samples; i++) {
const t = i / samples
const d = t * len
const { x, y, z } = path.getPointAtLength(d)
const pt = createVector(x, y, z)
bottomPts.push(pt)
bitangents.push(p5.Vector.fromAngles(theta(t), phi(t)))
}
const facesY = max(2, ceil(20 * sz))
for (let j = 0; j <= facesY; j++) {
const frac = j / facesY
const pts = bottomPts.map((pt, i) =>
pt.copy().add(bitangents[i].copy().mult(80*frac)))
for (let i = 0; i < pts.length; i++) {
const pt = pts[i]
const tangent = pts[(i + 1) % pts.length].copy().sub(pt).normalize()
const normal = tangent.cross(bitangents[i]).normalize().mult(-1)
this.vertexNormals.push(normal)
}
this.vertices.push(...pts)
this.uvs.push(
...pts.map((_, i) => [i / (samples - 1), 1 - frac]),
)
}
for (let j = 0; j < facesY; j++) {
const offset = samples * j
for (let i = 0; i < samples; i++) {
// +--+
// \ |
// +
this.faces.push([
offset + samples + i,
offset + samples + (i + 1) % samples,
offset + (i + 1) % samples,
]);
// +
// | \
// +--+
this.faces.push([
offset + samples + i,
offset + (i + 1) % samples,
offset + i,
]);
}
}
})
}
See More Shortcuts