Skip to content

Instantly share code, notes, and snippets.

@LingDong-
Created March 4, 2021 03:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LingDong-/e67ca86e5fc93b2c628ffeb3210fff66 to your computer and use it in GitHub Desktop.
Save LingDong-/e67ca86e5fc93b2c628ffeb3210fff66 to your computer and use it in GitHub Desktop.
A procedurally generated landscape experiment using OpenGL and C only
//
// gllandscape000.c
// Copyright © 2020 lingdonghuang
//
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
#include <GL/gl.h>
#include <GL/glut.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int W = 1280;
int H = 720;
int initialWindowReshape = 3;
int frameCount = 0;
clock_t fpsTimer;
float fps;
/* =============================
Perlin noise
ADAPTED FROM:
https://github.com/processing/p5.js/blob/master/src/math/noise.js
WHICH WAS ADAPTED FROM:
https://www.kuehlbox.wtf/download/demos/farbrausch/fr010src.zip
============================= */
#define PERLIN_YWRAPB 4
#define PERLIN_YWRAP (1<<PERLIN_YWRAPB)
#define PERLIN_ZWRAPB 8
#define PERLIN_ZWRAP (1<<PERLIN_ZWRAPB)
#define PERLIN_SIZE 4095
int perlin_octaves = 4;
float perlin_amp_falloff = 0.5;
float* p_perlin;
int perlin_initialized = 0;
float scaled_cosine(float i){
return 0.5*(1.0-cos(i*M_PI));
}
void noiseSeed(unsigned s){
int i;
srand(s);
if (!perlin_initialized){
p_perlin = malloc(sizeof(float)*(PERLIN_SIZE + 1));
perlin_initialized = 1;
}
for (i = 0; i < PERLIN_SIZE + 1; i++) {
p_perlin[i] = ((float)rand())/(float)RAND_MAX;
}
}
float noise(float x, float y, float z){
int xi, yi, zi, o, of;
float xf, yf, zf, rxf, ryf, r, ampl, n1, n2, n3;
if (!perlin_initialized){
noiseSeed(0);
}
if (x<0) { x=-x; } if (y<0) { y=-y; } if (z<0) { z=-z; }
xi=(int)x; yi=(int)y; zi=(int)z;
xf = x - xi; yf = y - yi; zf = z - zi;
r=0; ampl=0.5;
for (o=0; o<perlin_octaves; o++) {
of=xi+(yi<<PERLIN_YWRAPB)+(zi<<PERLIN_ZWRAPB);
rxf = scaled_cosine(xf); ryf = scaled_cosine(yf);
n1 = p_perlin[of&PERLIN_SIZE];
n1 += rxf*(p_perlin[(of+1)&PERLIN_SIZE]-n1);
n2 = p_perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
n2 += rxf*(p_perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n2);
n1 += ryf*(n2-n1);
of += PERLIN_ZWRAP;
n2 = p_perlin[of&PERLIN_SIZE];
n2 += rxf*(p_perlin[(of+1)&PERLIN_SIZE]-n2);
n3 = p_perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
n3 += rxf*(p_perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n3);
n2 += ryf*(n3-n2);
n1 += scaled_cosine(zf)*(n2-n1);
r += n1*ampl;
ampl *= perlin_amp_falloff;
xi<<=1; xf*=2; yi<<=1; yf*=2; zi<<=1; zf*=2;
if (xf>=1.0) { xi++; xf--; }
if (yf>=1.0) { yi++; yf--; }
if (zf>=1.0) { zi++; zf--; }
}
return r;
}
void noiseDetail(int lod, float falloff){
if (lod>0){perlin_octaves=lod;}
if (falloff>0){perlin_amp_falloff=falloff;}
}
// ========================
// MATH
// ========================
#define V3_CROSS(a1,a2,a3,b1,b2,b3) {(a2)*(b3)-(a3)*(b2),(a3)*(b1)-(a1)*(b3),(a1)*(b2)-(a2)*(b1)}
#define V3_DOT(a1,a2,a3,b1,b2,b3) ((a1)*(b1)+(a2)*(b2)+(a3)*(b3))
#define V3_MAG(a1,a2,a3) (sqrt((a1)*(a1)+(a2)*(a2)+(a3)*(a3)))
#define MAT_IDEN {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}
#define MAT_ROTX(a) {1,0,0,0, 0,cos(a),sin(a),0, 0,-sin(a),cos(a),0, 0,0,0,1}
#define MAT_ROTY(a) {cos(a),0,-sin(a),0, 0,1,0,0, sin(a),0,cos(a),0, 0,0,0,1}
#define MAT_ROTZ(a) {cos(a),sin(a),0,0,-sin(a),cos(a),0,0, 0,0,1,0, 0,0,0,1}
#define MAT_TRSL(x,y,z) {1,0,0,0, 0,1,0,0, 0,0,1,0, x,y,z,1}
#define MAT_SCAL(x,y,z) {x,0,0,0, 0,y,0,0, 0,0,z,0, 0,0,0,1}
#define MAT_MULT(A,B) {(A)[0]*(B)[0]+(A)[1]*(B)[4]+(A)[2]*(B)[8]+(A)[3]*(B)[12],(A)[0]*(B)[1]+(A)[1]*(B)[5]+(A)[2]*(B)[9]+(A)[3]*(B)[13],(A)[0]*(B)[2]+(A)[1]*(B)[6]+(A)[2]*(B)[10]+(A)[3]*(B)[14],(A)[0]*(B)[3]+(A)[1]*(B)[7]+(A)[2]*(B)[11]+(A)[3]*(B)[15],(A)[4]*(B)[0]+(A)[5]*(B)[4]+(A)[6]*(B)[8]+(A)[7]*(B)[12],(A)[4]*(B)[1]+(A)[5]*(B)[5]+(A)[6]*(B)[9]+(A)[7]*(B)[13],(A)[4]*(B)[2]+(A)[5]*(B)[6]+(A)[6]*(B)[10]+(A)[7]*(B)[14],(A)[4]*(B)[3]+(A)[5]*(B)[7]+(A)[6]*(B)[11]+(A)[7]*(B)[15],(A)[8]*(B)[0]+(A)[9]*(B)[4]+(A)[10]*(B)[8]+(A)[11]*(B)[12],(A)[8]*(B)[1]+(A)[9]*(B)[5]+(A)[10]*(B)[9]+(A)[11]*(B)[13],(A)[8]*(B)[2]+(A)[9]*(B)[6]+(A)[10]*(B)[10]+(A)[11]*(B)[14],(A)[8]*(B)[3]+(A)[9]*(B)[7]+(A)[10]*(B)[11]+(A)[11]*(B)[15],(A)[12]*(B)[0]+(A)[13]*(B)[4]+(A)[14]*(B)[8]+(A)[15]*(B)[12],(A)[12]*(B)[1]+(A)[13]*(B)[5]+(A)[14]*(B)[9]+(A)[15]*(B)[13],(A)[12]*(B)[2]+(A)[13]*(B)[6]+(A)[14]*(B)[10]+(A)[15]*(B)[14],(A)[12]*(B)[3]+(A)[13]*(B)[7]+(A)[14]*(B)[11]+(A)[15]*(B)[15]}
float sigmoid (float x, float k){
return 1/(1+exp(-k*(x-0.5)));
}
// ========================
// MAIN CODE
// ========================
float* heightmap;
float* heightmapnorm;
char* heightmaptree;
float* cloud;
int hmw = 1200;
int hmh = 1500;
float hmr = 0.32;
float light[3] = {0.6,0.0,0.0};
float* birds;
int nbirds = 50;
void setup(void){
heightmap = (float*)malloc(sizeof(float)*hmw*hmh);
heightmapnorm = (float*)malloc(sizeof(float)*hmw*hmh);
heightmaptree = (char* )malloc(sizeof(char )*hmw*hmh);
cloud = (float*)malloc(sizeof(float)*hmw*hmh);
birds = (float*)malloc(sizeof(float)*nbirds*4);
for (int i = 0; i < hmh; i++){
for (int j = 0; j < hmw; j++){
float ns0 = noise((float)i*0.005,(float)j*0.005,0);
float ns1 = noise((float)i*0.02,(float)j*0.02,1);
float ns2 = noise((float)i*0.005,(float)j*0.005,2);
float g0 = sigmoid(ns0*0.5 + ns1*0.5,20);
float g = g0*g0*ns2;
heightmap[i*hmw+j] = g*120;
}
}
// c
// |
// a -- o -- b
// |
// d
for (int i = 1; i < hmh-1; i++){
for (int j = 1; j < hmw-1; j++){
float o = heightmap[i*hmw+j];
float a[] = {-hmr,(-o+heightmap[i*hmw+j-1] ), 0};
float b[] = { hmr,(-o+heightmap[i*hmw+j+1] ), 0};
float c[] = { 0, (-o+heightmap[(i-1)*hmw+j]),-hmr};
float d[] = { 0, (-o+heightmap[(i+1)*hmw+j]), hmr};
float bc[] = V3_CROSS(b[0],b[1],b[2], c[0],c[1],c[2]);
float ca[] = V3_CROSS(c[0],c[1],c[2], a[0],a[1],a[2]);
float ad[] = V3_CROSS(a[0],a[1],a[2], d[0],d[1],d[2]);
float db[] = V3_CROSS(d[0],d[1],d[2], b[0],b[1],b[2]);
float lbc = V3_MAG(bc[0],bc[1],bc[2]);
float lca = V3_MAG(ca[0],ca[1],ca[2]);
float lad = V3_MAG(ad[0],ad[1],ad[2]);
float ldb = V3_MAG(db[0],db[1],db[2]);
float nml[] = {(bc[0]/lbc+ca[0]/lca+ad[0]/lad+db[0]/ldb)*0.25,(bc[1]/lbc+ca[1]/lca+ad[1]/lad+db[1]/ldb)*0.25,(bc[2]/lbc+ca[2]/lca+ad[2]/lad+db[2]/ldb)*0.25};
if (nml[2] < 0.4 && (i % 2 == 0 && j % 2 == 0) && heightmap[i*hmw+j] > rand()%30 + noise((float)i*0.1,(float)j*0.1,13)*80 && rand()%100 > 10 && i > 50){
if (noise((float)i*0.1,(float)j*0.1,42) > 0.5 || i < 100 || heightmap[i*hmw+j]<40){
heightmaptree[i*hmw+j]=1;
}else{
heightmaptree[i*hmw+j]=2;
}
}else{
heightmaptree[i*hmw+j]=0;
}
heightmapnorm[i*hmw+j] = /*fmax(80-o,0)*/o*0.002-0.02+0.05*fmax(0,fmin(1,V3_DOT(nml[0],nml[1],nml[2],light[0],light[1],light[2])))-0.02*(float)rand()/(float)RAND_MAX;
}
}
for (int i = 0; i < hmh; i++){
for (int j = 0; j < hmw; j++){
cloud[i*hmw+j] = noise((float)i*0.01,(float)j*0.01,99)*fmin(1,sin((float)j/(float)hmw*M_PI)*3);
}
}
for (int i = 0; i < nbirds; i++){
birds[i*4] = rand()%100-50;
birds[i*4+1] = rand()%60+40;
birds[i*4+2] =-(rand()%500);
birds[i+4+3] = ((float)rand()/(float)RAND_MAX);
}
}
void drawTree(float x, float y, float z, float a, float aa, float d, int n){
if (d < 0.2 || n <= 0) {
return;
}
float x0=x+cos( a-aa)*d;
float y0=y+sin( a-aa)*d;
float x1=x+cos( a+aa)*d;
float y1=y+sin( a+aa)*d;
float col =0.1*(float)rand()/(float)RAND_MAX;
glColor3f(col,col,col);
glVertex3f(x,y,z);
glVertex3f(x0,y0,z);
glVertex3f(x,y,z);
glVertex3f(x1,y1,z);
drawTree(x0,y0,z,a-aa,aa,d*(0.3+0.7*(float)rand()/(float)RAND_MAX),n-1);
drawTree(x1,y1,z,a+aa,aa,d*(0.3+0.7*(float)rand()/(float)RAND_MAX),n-1);
}
void drawTree2(float x, float y, float z){
glLineWidth(3);
glBegin(GL_LINES);
glColor3f(0,0,0);
glVertex3f(x,y,z);
glVertex3f(x,y+5,z);
glEnd();
glLineWidth(2);
for (int i = 0; i < 12; i++){
float h = 6;
float dx = 0;
if (i != 0){
h = ((float)rand()/(float)RAND_MAX)*5+1;
dx = ((float)rand()/(float)RAND_MAX)*5-2.5;
}
float w0 = ((float)rand()/(float)RAND_MAX)*2.5;
float w1 = ((float)rand()/(float)RAND_MAX)*2.5;
glBegin(GL_LINES);
glVertex3f(x,y+h-2,z);
glVertex3f(x+dx,y+h-0.8,z);
glEnd();
float g = ((float)rand()/(float)RAND_MAX)*0.05;
glColor3f(g,g,g);
glBegin(GL_TRIANGLES);
glVertex3f(x+dx,y+h,z);
glVertex3f(x+dx+w0,y+h-0.8,z);
glVertex3f(x+dx-w1,y+h-0.8,z);
glEnd();
}
}
void draw(){
srand(0);
//select clearing (background) color
glClearColor(0.65, 0.65, 0.65, 0.0);
//initialize viewing values
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, (float)W/(float)H, 1, 512);
glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable (GL_FOG);
glFogi(GL_FOG_MODE, GL_LINEAR);
GLfloat fogColor[4] = {0.65, 0.65, 0.65,1.0f};
glFogfv(GL_FOG_COLOR, fogColor);
glHint(GL_FOG_HINT, GL_DONT_CARE);
glFogf(GL_FOG_DENSITY, 1.0f);
glFogf(GL_FOG_START,100);
glFogf(GL_FOG_END,400);
float mat0[] = MAT_ROTX(0.4);
float mat1[] = MAT_TRSL(-sin(frameCount*0.01)*10,-80,-5);
float mat[] = MAT_MULT(mat1,mat0);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(mat);
glBegin( GL_QUADS );
for (int i = 0; i < hmh-1; i++){
for (int j = 0; j < hmw-1; j++){
glColor3f(heightmapnorm[(i*hmw+j)],heightmapnorm[(i*hmw+j)],heightmapnorm[(i*hmw+j)]);
glVertex3f( (-hmw/2+j)*hmr, heightmap[i*hmw+j],-(i )*hmr);
glColor3f(heightmapnorm[(i*hmw+j+1)],heightmapnorm[(i*hmw+j+1)],heightmapnorm[(i*hmw+j+1)]);
glVertex3f( (-hmw/2+j+1)*hmr, heightmap[i*hmw+j+1],-(i )*hmr);
glColor3f(heightmapnorm[((i+1)*hmw+j+1)],heightmapnorm[((i+1)*hmw+j+1)],heightmapnorm[((i+1)*hmw+j+1)]);
glVertex3f( (-hmw/2+j+1)*hmr, heightmap[(i+1)*hmw+j+1],-(i+1)*hmr);
glColor3f(heightmapnorm[((i+1)*hmw+j)],heightmapnorm[((i+1)*hmw+j)],heightmapnorm[((i+1)*hmw+j)]);
glVertex3f( (-hmw/2+j)*hmr, heightmap[(i+1)*hmw+j],-(i+1)*hmr);
}
}
glEnd();
glLineWidth(1);
glBegin(GL_LINES);
for (int i = 0; i < hmh-1; i++){
for (int j = 0; j < hmw-1; j++){
if (heightmaptree[i*hmw+j]==1 && i < 500){
drawTree((-hmw/2+j)*hmr, heightmap[i*hmw+j], -(i )*hmr,M_PI/2,0.28+0.5*(float)rand()/(float)RAND_MAX,fmax(1,4-heightmap[i*hmw+j]*0.05),6);
}
}
}
glEnd();
for (int i = 0; i < hmh-1; i++){
for (int j = 0; j < hmw-1; j++){
if (heightmaptree[i*hmw+j]==2 && i < 500){
drawTree2((-hmw/2+j)*hmr, heightmap[i*hmw+j], -(i )*hmr);
}
}
}
glBegin(GL_TRIANGLES);
for (int i = 0; i < hmh-1; i++){
for (int j = 0; j < hmw-1; j++){
if (heightmaptree[i*hmw+j]){
glColor3f(0,0,0);
if (i >= 500){
glVertex3f((-hmw/2+j)*hmr, heightmap[i*hmw+j], -(i )*hmr);
glVertex3f((-hmw/2+j)*hmr-1.5, heightmap[i*hmw+j]+1.5, -(i )*hmr);
glVertex3f((-hmw/2+j)*hmr+1.5, heightmap[i*hmw+j]+1.5, -(i )*hmr);
}
}
}
}
glEnd();
glBegin( GL_QUADS );
int dx = 8;
for (int i = 0; i < 800; i+=dx){
for (int j = 0; j < hmw-1; j+=dx){
int jj = (j + frameCount)%(hmw-dx);
float col0 = 0.8*(-0.3+((-heightmap[i*hmw+j]) )*0.05+cloud[i*hmw+jj]);
float col1 = 0.8*(-0.3+((-heightmap[i*hmw+j+dx]) )*0.05+cloud[i*hmw+jj+dx]);
float col2 = 0.8*(-0.3+((-heightmap[(i+dx)*hmw+j+dx]))*0.05+cloud[(i+dx)*hmw+jj+dx]);
float col3 = 0.8*(-0.3+((-heightmap[(i+dx)*hmw+j]) )*0.05+cloud[(i+dx)*hmw+jj]);
int ii = (i + 4 )%(hmh-dx);
float h0 = 5+cloud[ii*hmw+jj]*20;
float h1 = 5+cloud[ii*hmw+jj+dx]*20;
float h2 = 5+cloud[(ii+dx)*hmw+jj+dx]*20;
float h3 = 5+cloud[(ii+dx)*hmw+jj]*20;
glColor4f(1.0,1.0,1.0,col0);
glVertex3f( (-hmw/2+j)*hmr, h0,-(i )*hmr);
glColor4f(1.0,1.0,1.0,col1);
glVertex3f( (-hmw/2+j+dx)*hmr, h1,-(i )*hmr);
glColor4f(1.0,1.0,1.0,col2);
glVertex3f( (-hmw/2+j+dx)*hmr, h2,-(i+dx)*hmr);
glColor4f(1.0,1.0,1.0,col3);
glVertex3f( (-hmw/2+j)*hmr, h3,-(i+dx)*hmr);
}
}
for (int i = 0; i < hmh-1; i+=dx){
for (int j = 0; j < hmw-1; j+=dx){
int jj = (j + frameCount + 19)%(hmw-dx);
float col0 = -0.4+((-heightmap[i*hmw+j]) )*0.05+cloud[i*hmw+jj];
float col1 = -0.4+((-heightmap[i*hmw+j+dx]) )*0.05+cloud[i*hmw+jj+dx];
float col2 = -0.4+((-heightmap[(i+dx)*hmw+j+dx]))*0.05+cloud[(i+dx)*hmw+jj+dx];
float col3 = -0.4+((-heightmap[(i+dx)*hmw+j]) )*0.05+cloud[(i+dx)*hmw+jj];
int ii = (i + 49 )%(hmh-dx);
float h0 = 50+cloud[ii*hmw+jj]*10;
float h1 = 50+cloud[ii*hmw+jj+dx]*10;
float h2 = 50+cloud[(ii+dx)*hmw+jj+dx]*10;
float h3 = 50+cloud[(ii+dx)*hmw+jj]*10;
glColor4f(1.0,1.0,1.0,col0);
glVertex3f( (-hmw/2+j)*hmr, h0,-(i )*hmr);
glColor4f(1.0,1.0,1.0,col1);
glVertex3f( (-hmw/2+j+dx)*hmr, h1,-(i )*hmr);
glColor4f(1.0,1.0,1.0,col2);
glVertex3f( (-hmw/2+j+dx)*hmr, h2,-(i+dx)*hmr);
glColor4f(1.0,1.0,1.0,col3);
glVertex3f( (-hmw/2+j)*hmr, h3,-(i+dx)*hmr);
}
}
glEnd();
float mat10[] = MAT_ROTY(-0.8);
float mat11[] = MAT_TRSL(-60,-30,-60);
float matb[] = MAT_MULT(mat11,mat10);
glMatrixMode(GL_MODELVIEW);
glMultMatrixf(matb);
glLineWidth(1);
glBegin(GL_LINES);
glColor3f(0,0,0);
for (int i = 0; i < nbirds; i++){
float rx =birds[i*4];
float ry= birds[i*4+1];
float rz= birds[i*4+2];
float l = sin((float)(frameCount)*0.5+birds[i*4+3]*M_PI*2);
glVertex3f(rx,ry-l,rz);
glVertex3f(rx-1,ry,rz);
glVertex3f(rx,ry-l,rz);
glVertex3f(rx+1,ry,rz);
glVertex3f(rx,ry-l,rz-0.3);
glVertex3f(rx,ry-l,rz+0.3);
birds[i*4+2]+=2;
if (birds[i*4+2]>hmh*hmr/2){
birds[i*4+2]=-hmh*hmr/2;
}
}
glEnd();
}
// ========================
// OPENGL APP BOILERPLATE
// ========================
void display(void){
if (initialWindowReshape>0){
glutReshapeWindow(W+initialWindowReshape-1,H);
initialWindowReshape--;
}
draw();
// Don't wait start processing buffered OpenGL routines
glFlush();
frameCount ++;
}
void animationFrame(){
glutPostRedisplay();
float spf = (float)(clock() - fpsTimer)/(float)CLOCKS_PER_SEC;
fps = 1.0/spf;
//printf("%f\n",fps);
fpsTimer = clock();
glutTimerFunc( 10, animationFrame, 1);
}
void onreshape(){
if (initialWindowReshape<=0){
W = glutGet(GLUT_WINDOW_WIDTH);
H = glutGet(GLUT_WINDOW_HEIGHT);
printf("%d %d\n",W,H);
}
}
void init(void){
setup();
fpsTimer = clock();
}
int main(int argc, char** argv) {
//Initialise GLUT with command-line parameters.
glutInit(&argc, argv);
//Set Display Mode
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
//Set the window size
glutInitWindowSize(W,H);
//Set the window position
glutInitWindowPosition(0,0);
//Create the window
glutCreateWindow(" ");
//Call init (initialise GLUT
init();
//Call "display" function
glutDisplayFunc(display);
glutReshapeFunc(onreshape);
glutTimerFunc( 10, animationFrame, 1);
//Enter the GLUT event loop
glutMainLoop();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment