diff --git a/Bird.pde b/Bird.pde new file mode 100644 index 0000000..d0e2a39 --- /dev/null +++ b/Bird.pde @@ -0,0 +1,94 @@ +class Bird { + float x; + float y; + float z; + float radius; + float angle; + float angleSpeed = 0.01 + random(0.0005 - 0.00001); + int directionX = (random(0, 1) < 0.5) ? 1 : -1; + int directionY = (random(0, 1) < 0.5) ? 1 : -1; + float wingAngle1 = PI; + float wingAngle2 = PI; + int wingDirection1 = 1; + int wingDirection2 = 1; + float wingFlapSpeed = 0.05; + float wingFlapRange = QUARTER_PI; + float perlinOffset; + + Bird(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + this.radius = 500; + this.angle = 0; + this.perlinOffset = random(1000); + } + + void drawBird() { + pushMatrix(); + + float angleX = HALF_PI; + float angleY = 0; + + rotateX(angleX); + rotateY(angleY); + + float birdZ = map(noise(perlinOffset, millis() * 0.0002), 0, 1, 800, 1000); + + translate(x, y, birdZ); + float birdX = cos(angle) * radius * directionX; + float birdY = sin(angle) * radius * directionY; + + translate(birdX, birdY, 0); + + fill(100, 100, 100); + box(10, 10, 10); + + translate(-6, 3, 5); + box(6, 6, 6); + + translate(6, -3, -5); + translate(0, -5, 5); + + float wingFlap1 = sin(wingAngle1) * wingFlapRange; + rotateX(wingFlap1); + + if (wingAngle1 >= PI + wingFlapRange || wingAngle1 <= PI - wingFlapRange) { + wingDirection1 *= -1; + } + + wingAngle1 += wingDirection1 * wingFlapSpeed; + + fill(128, 128, 128); + drawWing(10, 30, 5); + + rotateX(-wingFlap1); + translate(5, 5); + rotateZ(HALF_PI); + + float wingFlap2 = sin(wingAngle2) * wingFlapRange; + rotateX(wingFlap2); + + if (wingAngle2 >= PI + wingFlapRange || wingAngle2 <= PI - wingFlapRange) { + wingDirection2 *= -1; + } + + wingAngle2 += wingDirection2 * wingFlapSpeed; + + fill(128, 128, 128); + drawWing(10, 30, 5); + + angle += angleSpeed; + + popMatrix(); + } + + void drawWing(float width, float length, float thickness) { + beginShape(); + vertex(-width / 2, 0); + vertex(width / 2, 0); + vertex(width / 4, -length); + vertex(-width / 4, -length); + endShape(CLOSE); + } +} diff --git a/Block.pde b/Block.pde new file mode 100644 index 0000000..986007f --- /dev/null +++ b/Block.pde @@ -0,0 +1,16 @@ +class Block { + int x; + int y; + float h; + boolean isTree; + boolean isFlower; + int treeSize; + color treeColor; + Block(int x, int y, float h, color treeColor, int treeSize) { + this.x = x; + this.y = y; + this.h = h; + this.treeColor = treeColor; + this.treeSize = treeSize; + } +} diff --git a/Colors.pde b/Colors.pde new file mode 100644 index 0000000..ac56210 --- /dev/null +++ b/Colors.pde @@ -0,0 +1,12 @@ +color yellow = color(255, 255, 0); +color green = color(0, 255, 0); +color blue = color(0, 0, 255); +color red = color(255, 0, 0); +color darkgreen = color(0, 100, 0); +color autumn1 = color(183, 65, 14); +color autumn2 = color(204, 85, 0); +color autumn3 = color(255, 215, 0); +color autumn4 = color(255, 204, 0); +color autumn5 = color(139, 69, 19); +color autumn6 = color(101, 67, 33); +color autumn7 = color(107, 142, 35); diff --git a/Main.pde b/Main.pde new file mode 100644 index 0000000..e4021e6 --- /dev/null +++ b/Main.pde @@ -0,0 +1,83 @@ +PVector camPosition; +PVector lookAt; +float camSpeed = 20; +float sensitivity = 0.01; +float pitch = 0; +float yaw = 0; +int farPlaneDistance = 3000; + +float cubeSize = 100; +float cubeWidth = 20; +float noiseScale = 5; + +long randomSeedValue = 42; +long noiseSeedValue = 24; +int noiseOctaves = 4; +float noiseFalloff = 0.5; + +Terrain terra = new Terrain(); + +void setup() { + fullScreen(P3D); + frameRate(144); + + randomSeed(randomSeedValue); + noiseSeed(noiseSeedValue); + noiseDetail(noiseOctaves, noiseFalloff); + + camPosition = new PVector(0, 0, 0); + lookAt = new PVector(0, 0, -1); + terra.generateTerrain(); + terra.reloadTrees(); +} + +void keyPressed() { + if (keyCode == UP) { + noiseScale += 0.25; + terra.reloadTrees(); + } else if (keyCode == DOWN) { + noiseScale -= 0.25; + terra.reloadTrees(); + } +} + +void draw() { + background(173, 216, 230); + lights(); + + yaw += -1 * sensitivity * (mouseX - pmouseX); + pitch += sensitivity * (mouseY - pmouseY); + + pitch = constrain(pitch, -HALF_PI + 0.01, HALF_PI - 0.01); + + float cosPitch = cos(pitch); + float sinPitch = sin(pitch); + float cosYaw = cos(yaw); + float sinYaw = sin(yaw); + + lookAt.x = sinYaw * cosPitch; + lookAt.y = sinPitch; + lookAt.z = cosYaw * cosPitch; + + if (keyPressed) { + if (key == 'w') { + camPosition.add(PVector.mult(lookAt, camSpeed)); + } + if (key == 's') { + camPosition.sub(PVector.mult(lookAt, camSpeed)); + } + if (key == 'a') { + PVector left = lookAt.cross(new PVector(0, -1, 0)); + camPosition.add(PVector.mult(left, camSpeed)); + } + if (key == 'd') { + PVector right = lookAt.cross(new PVector(0, -1, 0)); + camPosition.sub(PVector.mult(right, camSpeed)); + } + } + + camera(camPosition.x, camPosition.y, camPosition.z, camPosition.x + lookAt.x, camPosition.y + lookAt.y, camPosition.z + lookAt.z, 0, 1, 0); + perspective(PI / 3.0, float(width) / float(height), 1, farPlaneDistance); + + terra.drawTerrain(); +} diff --git a/Terrain.pde b/Terrain.pde new file mode 100644 index 0000000..9da563e --- /dev/null +++ b/Terrain.pde @@ -0,0 +1,113 @@ +class Terrain { + ArrayList < Block > blocks = new ArrayList<>(); + ArrayList < Bird > birds = new ArrayList<>(); + int terrainLength = 100; + int terrainWidth = 100; + int minTreeHeight = 20; + int maxTreeHeight = 70; + float noiseStep = 0.02; + int cubeWidth = 20; + int cubeLength = 20; + + void generateTerrain() { + for (int i = 0; i < terrainWidth; i++) { + for (int j = 0; j < terrainLength; j++) { + Block block = new Block(i, j, noise(noiseStep * i, noiseStep * j), getRandomTreeColor(), int(random(minTreeHeight, maxTreeHeight))); + blocks.add(block); + if (random(0, 1) < 0.001) birds.add(new Bird(i * cubeWidth, j * cubeWidth, 1000)); + } + } + } + + void reloadTrees() { + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + if ((noiseScale * block.h * cubeSize) > 170) { + if (int(random(0, 101)) < 1) { + block.isTree = true; + block.treeColor = getRandomTreeColor(); + } + else block.isTree = false; + } else block.isTree = false; + + if (!block.isTree && (noiseScale * block.h * cubeSize) > 170) { + if (int(random(0, 101)) < 1) { + block.isFlower = true; + block.treeColor = getRandomFlowerColor(); + } + else block.isFlower = false; + } + else block.isFlower = false; + } + } + + void drawTerrain() { + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + float blockHeight = noiseScale * block.h * cubeSize; + + if (blockHeight < 150) { + fill(0, 0, 255); + blockHeight = 150; + } else if (blockHeight > 150 && blockHeight < 170) { + fill(yellow); + } else if (blockHeight > 350) { + blockHeight *= 1.5; + fill(255, 255, 255); + } else { + fill(green); + } + + float posX = block.x * cubeWidth; + float posY = block.y * cubeLength; + float posZ = blockHeight / 2.0; + + float angleX = HALF_PI; + float angleY = 0; + + pushMatrix(); + + rotateX(angleX); + rotateY(angleY); + translate(posX, posY, posZ); + box(cubeWidth, cubeLength, blockHeight); + + if (block.isTree && blockHeight < 350) { + fill(222, 184, 135); + translate(0, 0, posZ + 50); + box(cubeWidth, cubeLength, 100); + translate(0, 0, 50); + fill(block.treeColor); + noStroke(); + sphere(block.treeSize); + stroke(1); + } else if (block.isTree) { + fill(222, 184, 135); + translate(0, 0, posZ + 50); + box(cubeWidth, cubeLength, 100); + translate(0, 0, 50); + fill(0, 100, 0); + noStroke(); + cylinder(50, 1, 100, 50); + stroke(1); + } + + if (block.isFlower && blockHeight < 350) { + fill(0, 200, 0); + translate(0, 0, posZ + 10); + box(5, 5 , 20); + translate(0, 0, 10); + fill(block.treeColor); + noStroke(); + sphere(10); + stroke(1); + } + popMatrix(); + } + for (int i = 0; i < birds.size(); i++) { + Bird bird = birds.get(i); + bird.drawBird(); + } + + } +} diff --git a/Utils.pde b/Utils.pde new file mode 100644 index 0000000..f5dff90 --- /dev/null +++ b/Utils.pde @@ -0,0 +1,84 @@ +void cylinder(float bottom, float top, float h, int sides) { + pushMatrix(); + float angleX = HALF_PI; + float angleY = 0; + rotateX(angleX); + rotateY(angleY); + translate(0, h / 2, 0); + + float angle; + float[] x = new float[sides + 1]; + float[] z = new float[sides + 1]; + + float[] x2 = new float[sides + 1]; + float[] z2 = new float[sides + 1]; + + for (int i = 0; i < x.length; i++) { + angle = TWO_PI / (sides) * i; + x[i] = sin(angle) * bottom; + z[i] = cos(angle) * bottom; + } + + for (int i = 0; i < x.length; i++) { + angle = TWO_PI / (sides) * i; + x2[i] = sin(angle) * top; + z2[i] = cos(angle) * top; + } + + beginShape(TRIANGLE_FAN); + + vertex(0, -h / 2, 0); + + for (int i = 0; i < x.length; i++) { + vertex(x[i], -h / 2, z[i]); + } + + endShape(); + + beginShape(QUAD_STRIP); + + for (int i = 0; i < x.length; i++) { + vertex(x[i], -h / 2, z[i]); + vertex(x2[i], h / 2, z2[i]); + } + + endShape(); + + beginShape(TRIANGLE_FAN); + + vertex(0, h / 2, 0); + + for (int i = 0; i < x.length; i++) { + vertex(x2[i], h / 2, z2[i]); + } + + endShape(); + + popMatrix(); +} + +color getRandomTreeColor() { + int rand = int(random(0, 101)); + if (rand < 10) return color(255, 69, 0); + else if (rand < 20) return autumn1; + else if (rand < 30) return autumn2; + else if (rand < 40) return autumn3; + else if (rand < 50) return autumn4; + else if (rand < 60) return autumn5; + else if (rand < 70) return autumn6; + else if (rand < 80) return autumn7; + return darkgreen; +} + +color getRandomFlowerColor() { + float yellowBrightness = 150; + float rand1 = yellowBrightness + randomGaussian() * 25; + float rand2 = yellowBrightness + randomGaussian() * 25; + float rand3 = yellowBrightness + randomGaussian() * 25; + + rand1 = constrain(rand1, 0, 255); + rand2 = constrain(rand2, 0, 255); + rand3 = constrain(rand3, 0, 255); + + return color(rand1, rand2, rand3); +}