xxxxxxxxxx
410
// todo:
// render object using another dataset. playground equipment (with objs) or trees or power lines idk. maybe roads or actors
// make only those objects pixelated hehe
// have a map in the background
// ripple effect
// optimization is sorely needed bc this shit will not run at high resolutions. look in performance sketch for fixes
// can i use the vert shader to make cool tilings or other repetitions of the model?? (or just like pass it multiple times from the draw loop ig)
// O MY GOD DITHERING!!!!! DONT DO SHADES JUST DO PURE CMY PIXELS BUT DITHER THEM TO DO OTHER COLORS!!! MAKE/GRAB COOL DITHERING PATTERNS (BLUE NOISE OR FIGURES OR WHATEVER, NOT JUST THE STANDARD ONE!!!)
let theShader;
let pass2Shader;
let pass3Shader;
let shaderBG;
let pointBG;
let afGraphics;
let pass3Graphics;
let framebuffer;
let depthShader;
let secs;
let polygons = [];
let normals = [];
let cityURL = "3dmodel93.txt";
let cityText;
const lowercorner = [0, 0, 0];
const uppercorner = [1674, 1874, 62.58163]
// const lowercorner = [79409, 450938, -5.03208];
// const uppercorner = [81083, 452812, 57.54955]
// let target = [(uppercorner[0]-lowercorner[0])*0.5,
// (uppercorner[1]-lowercorner[1])*0.5];
let target = [850,1010];
// performance metrics. numbers are for square culling not circle
// range > polygons > bootup > performance
// max 48k 17 s 19 fps
// 500 23k 8 s 34 fps
// 330 10659 4s 59 fps
// 300 8859 3.5 s 60 fps
const range = 1000;
let zoomLvl = 7.0;
let cityGeom;
let model3;
function preload(){
theShader = loadShader('shader.vert','shader.frag');
pass2Shader = loadShader('shbasic.vert','shedges.frag');
pass3Shader = loadShader('shbasic.vert','shpixelize.frag');
model3 = loadModel('cow-nonormals.obj');
loadStrings(cityURL, parseCity, uhOh);
}
function setup() {
createCanvas(600, 600);
shaderBG = createGraphics(width,height,WEBGL);
afGraphics = createGraphics(width,height);
framebuffer = shaderBG.createFramebuffer();
pass3Graphics = createGraphics(width,height,WEBGL);
pass3Graphics.noStroke();
pass3Shader.setUniform('texSize',width);
pass2Graphics = createGraphics(width,height,WEBGL);
pass2Graphics.noStroke();
pass2Shader.setUniform('texSize',width);
pointBG = createGraphics(width,height);
pointBG.background(255);
const rast=30;
for(let i=rast/2;i<width;i+=rast){
for(let j=rast/2;j<height;j+=rast){
pointBG.point(i,j);
}
}
noCursor();
shaderBG.noStroke(); //wow actually saves crazy performinz
}
function draw() {
secs = millis()*0.001;
image(pointBG,0,0,width,height);
framebuffer.begin();
shaderBG.push();
shaderBG.shader(theShader);
theShader.setUniform('time', secs);
// // funny glitch mode
// theShader.setUniform('targetX', target[0]*300);
// theShader.setUniform('targetY', target[1]*300);
// // normal mode
theShader.setUniform('targetX', target[0]*1.0);
theShader.setUniform('targetY', target[1]*1.0);
theShader.setUniform('cullRadius', 0.4 * width / (zoomLvl*1.05));
shaderBG.background(255,0);
shaderBG.rotateX(1);
shaderBG.rotateZ(secs);
// shaderBG.model(model3);
shaderBG.translate(-target[0]*zoomLvl,
-target[1]*zoomLvl,
0);
shaderBG.scale(zoomLvl);
shaderBG.model(cityGeom);
// //make a tree
// shaderBG.translate(target[0],target[1], 7.5);
// shaderBG.rotateX(1.6);
// shaderBG.cylinder(1, 15,5);
// shaderBG.translate(0.0,7.5,0.0 );
// shaderBG.sphere(5, 5, 5)
shaderBG.pop();
framebuffer.end();
//this shit dum as fuck
shaderBG.image(framebuffer.depth,-width/2,-height/2,width,height);
afGraphics.image(shaderBG,0,0,width,height);
shaderBG.background(255,0)
shaderBG.image(framebuffer,-width/2,-height/2,width,height);
pass2Shader.setUniform('colorTexture', shaderBG);
pass2Shader.setUniform('depth',afGraphics);
pass2Graphics.shader(pass2Shader);
pass2Graphics.background(255,0);
pass2Shader.setUniform('texSize',width)
pass2Shader.setUniform('bgTexture', pointBG);
pass2Graphics.rect(0,0,width,height);
pass3Graphics.shader(pass3Shader);
pass3Graphics.background(255,0);
pass3Shader.setUniform('colorTexture', pass2Graphics);
pass3Shader.setUniform('texSize',width);
pass3Shader.setUniform('depth',afGraphics);
pass3Shader.setUniform('time', secs);
pass3Shader.setUniform('mouseX', mouseX);
pass3Shader.setUniform('mouseY', mouseY);
pass3Graphics.rect(0,0,width,height);
image(pass3Graphics,0,0,width,height);
text('fps:'+frameRate(),0,10);
text('zoom_lvl:' + zoomLvl,0,20);
text('poly_count:' + polygons.length,0,30);
text('target_x:' + target[0],0,40);
text('target_y:' + target[1],0,50);
rect(mouseX-12.5*cos(secs), mouseY-12.5*sin(secs*1.2), 25*cos(secs), 25*sin(secs*1.2))
// noLoop();
}
function parseCity(city){
cityText = city;
print('loaded');
polygons = [];
for(let i=0; i<city.length; i++){
// for(let i=0; i<200; i++){
if(city[i] !='ob' && city[i] !='cb'){
// print(city[i]);
let numstring = '';
let points = [];
for(let j=0; j<city[i].length; j++){
if(city[i][j] != " "){
numstring += city[i][j];
}
else{
points.push(int(numstring) * 0.01 +random()*0.00001); // slight random offset to eliminate colinear faces. i dont think it matters but the warnings flood the console and it upsets me
numstring = ''
}
}
if(sqrt(
pow(points[0]-lowercorner[0]-target[0],2)+
pow(points[1]-lowercorner[1]-target[1],2)) < range
){
polygons.push([]);
for(let j=0;j<points.length;j+=3){
polygons[polygons.length-1].push(createVector(
points[j]-lowercorner[0],
points[j+1]-lowercorner[1],
points[j+2]-lowercorner[2]));
}
}
}
}
cityGeom = new createModel(polygons);
print('polygon count: ' + polygons.length);
}
function createModel(polygonArray) {
return new p5.Geometry(
// detailX and detailY are not used in this example
1, 1,
// The callback must be an anonymous function, not an arrow function in
// order for "this" to be bound correctly.
function createGeometry() {
let vertIndex = 0;
for(let i=0;i<polygonArray.length;i++){
for(let j=0;j<polygonArray[i].length;j++){
this.vertices.push(p5.Vector.mult(polygonArray[i][j],1.0));
}
let u = p5.Vector.sub(polygons[polygons.length-1][1],
polygons[polygons.length-1][0]);
let v = p5.Vector.sub(polygons[polygons.length-1][2],
polygons[polygons.length-1][0]);
let n = p5.Vector.cross(u,v).normalize();
let alph = -atan(n.y/n.x);
let beta = -p5.Vector.angleBetween(n,createVector(0,0,1));
let passArray = [];
for(let j=0;j<polygonArray[i].length;j++){
let v = polygonArray[i][j].copy();
let vz = createVector(
v.x*cos(alph)-v.y*sin(alph),
v.x*sin(alph)+v.y*cos(alph),
v.z
)
let vy = createVector(
vz.x*cos(beta)+vz.z*sin(beta),
vz.y
// -vz.x*sin(beta)+vz.z*cos(beta)
)
passArray.push(vy.copy())
}
let faceList = earClipper(passArray.slice());
for(let j=0;j<faceList.length;j++){
this.faces.push([vertIndex+faceList[j][0],vertIndex+faceList[j][1],vertIndex+faceList[j][2]]);
}
// for(let j=1;j<polygonArray[i].length-1;j++){
// this.faces.push([vertIndex,vertIndex+j,vertIndex+j+1]);
// }
vertIndex+=polygonArray[i].length;
}
this.computeNormals();
}
);
}
function earClipper(polygon){
// 0 -> 1 angle, 1->2 angle
let tempTriangles = [];
let indices = []
let initlength = polygon.length;
let dir = 1;
for(let i=0;i<polygon.length;i++){
indices.push(i);
}
let breaker = 0;
while(tempTriangles.length<initlength-2 && breaker<50){
let trinum = tempTriangles.length;
breaker++
for(let i=0; i<polygon.length-2;i++){
let angle = p5.Vector.angleBetween(
p5.Vector.sub(polygon[(i+1)%polygon.length],polygon[i]),
p5.Vector.sub(polygon[(i+2)%polygon.length],polygon[(i+1)%polygon.length]));
if(angle * dir < 0.0){
let found = false;
for(let j=0;j<polygon.length;j++){
if(i==j || (i+2)%polygon.length==j || (j+1)%polygon.length==i || (i+2)%polygon.length==(j+1)%polygon.length){
continue;
}
if(checkIntersect(
polygon[i].x,
polygon[i].y,
polygon[(i+2)%polygon.length].x,
polygon[(i+2)%polygon.length].y,
polygon[j].x,
polygon[j].y,
polygon[(j+1)%polygon.length].x,
polygon[(j+1)%polygon.length].y)){
found = true;
}
}
if(found){
continue;
}
//bounding box check. not very elegant.
if(polygon.length>3){
let xmin = min(polygon[i].x,polygon[(i+2)%polygon.length].x, polygon[(i+1)%polygon.length].x);
let xmax = max(polygon[i].x,polygon[(i+2)%polygon.length].x, polygon[(i+1)%polygon.length].x);
let ymin = min(polygon[i].y,polygon[(i+2)%polygon.length].y, polygon[(i+1)%polygon.length].y);
let ymax = max(polygon[i].y,polygon[(i+2)%polygon.length].y, polygon[(i+1)%polygon.length].y);
for (let j=0;j<polygon.length;j++){
if(polygon[j].x < xmin ||polygon[j].x > xmax ||polygon[j].y < ymin ||polygon[j].y > ymax){
found = true;
}
}
if(!found){
continue;
}
}
//we have an ear hehe
tempTriangles.push([indices[(i )%polygon.length],
indices[(i+1)%polygon.length],
indices[(i+2)%polygon.length]])
indices.splice((i+1)%polygon.length,1)
polygon.splice((i+1)%polygon.length,1)
// continue;
}
}
if(trinum-tempTriangles.length==0)
dir = dir*-1;
}
return tempTriangles;
}
function checkIntersect(x1,y1,x2,y2,x3,y3,x4,y4){
let t = ((x1-x3)*(y3-y4)-(y1-y3)*(x3-x4))/
((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
let u = ((x1-x3)*(y1-y2)-(y1-y3)*(x1-x2))/
((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
return( t<=1 && t>=0 && u<=1 && u>=0);
}
function uhOh(response){
print('sad');
print(response);
}
function mouseMoved(){
target = [(uppercorner[0]-lowercorner[0]) * (0.3+(mouseX/width)*0.7),
(uppercorner[1]-lowercorner[1]) * (0.3+(mouseY/height)*0.7)];
// parseCity(cityText);
}
function mouseWheel(event) {
if (event.deltaY > 0) {
zoomLvl *= 1.1;
} else {
zoomLvl *= 0.9;
}
}