aboutsummaryrefslogblamecommitdiff
path: root/xp/cgxp/main1.js
blob: bd52e13093a154c525c3616fc23c8f43b723d595 (plain) (tree)































































































































































































































































































































                                                                                                        
//Copyright Chris Xiong 2018
//License: Expat (MIT)
var cvs,WGL,vpw,vph,hdiv;
var fsh=`
varying lowp vec4 vC;
void main(void)
{
	gl_FragColor=vC;
}`;
var vsh=`
attribute vec3 vp;
attribute vec4 vc;
uniform mat4 mmodelview;
uniform mat4 mprojection;
varying lowp vec4 vC;
void main(void)
{
	gl_Position=mprojection*mmodelview*vec4(vp,1.);
	vC=vc;
}`;
var shp;
var vbufsize=4096;
var vbo,ibo;
var vb=new Float32Array(vbufsize*7);
var ib=new Uint16Array(vbufsize*6/4);
var Mprojection,Mmodelview;
var primc;
var kst=[];
var dbc,mbtn,mup,mdn,mx,my,mox,moy;
var shapes=[];
var curshape=null;
var dragging=false,drg;

var Shape=function(t)
{
	this.type=t;
	this.vert=[];
}
function copyshape(s)
{
	var r=new Shape(s.type);
	for(var i=0;i<s.vert.length;++i)
	r.vert.push(new v3d(s.vert[i].x,s.vert[i].y,s.vert[i].z));
	return r;
}
Shape.prototype.draw=function(a,r,g,b)
{
	var drawseg=function(a,b,c,d,aa,rr,gg,bb)
	{
		var pa=new v3d(a,b,0);
		var pb=new v3d(c,d,0);
		var d=pb.subtract(pa);
		var pd1=new v3d(-d.y,d.x,0),pd2=new v3d(d.y,-d.x,0);
		pd1=pd1.n();pd2=pd2.n();
		var p1=pa.add(pd1),p2=pa.add(pd2);
		var p3=pb.add(pd2),p4=pb.add(pd1);
		var q=[];
		q.push(p1.x);q.push(vph-p1.y);q.push(p1.z);
		q.push(rr);q.push(gg);q.push(bb);q.push(aa);
		q.push(p2.x);q.push(vph-p2.y);q.push(p2.z);
		q.push(rr);q.push(gg);q.push(bb);q.push(aa);
		q.push(p3.x);q.push(vph-p3.y);q.push(p3.z);
		q.push(rr);q.push(gg);q.push(bb);q.push(aa);
		q.push(p4.x);q.push(vph-p4.y);q.push(p4.z);
		q.push(rr);q.push(gg);q.push(bb);q.push(aa);
		pushQuad(q);
	}
	if(this.type=='segment')
	drawseg(this.vert[0].x,this.vert[0].y,this.vert[1].x,this.vert[1].y,
	a,r,g,b);
	if(this.type=='segstrip'||this.type=='segloop')
	{
		for(var i=0;i<this.vert.length-1;++i)
		drawseg(this.vert[i].x,this.vert[i].y,this.vert[i+1].x,this.vert[i+1].y,
		a,r,g,b);
		if(this.type=='segloop')
		{
			var i=this.vert.length-1;
			drawseg(this.vert[i].x,this.vert[i].y,this.vert[0].x,this.vert[0].y,
			a,r,g,b);
		}
	}
}
Shape.prototype.coltest=function(x,y)
{
	var dp2s=function(x,y,_a,_b,_c,_d)
	{
		var a=new v3d(_a,_b,0);
		var b=new v3d(_c,_d,0);
		var p=new v3d(x,y,0);
		var ab=b.subtract(a);
		var ap=p.subtract(a);
		var pl=ab.dot(ap)/ab.l();
		var pp=a.add(ab.n().ms(pl));
		if(pl<0||pl>ab.l())pp=new v3d(1e9,1e9,1e9);
		return Math.min(p.subtract(pp).l(),p.subtract(a).l(),p.subtract(b).l());
	}
	if(this.type=='segment')
	{
		var d=dp2s(x,y,this.vert[0].x,this.vert[0].y,
						this.vert[1].x,this.vert[1].y);
		if(d<5)return true;
	}
	if(this.type=='segstrip'||this.type=="segloop")
	{
		for(var i=0;i<this.vert.length-1;++i)
		{
			var d=dp2s(x,y,this.vert[i].x,this.vert[i].y,
							this.vert[i+1].x,this.vert[i+1].y);
			if(d<5)return true;
		}
		if(this.type=="segloop")
		{
			var i=this.vert.length-1;
			var d=dp2s(x,y,this.vert[i].x,this.vert[i].y,
							this.vert[0].x,this.vert[0].y);
			if(d<5)return true;
		}
	}
	return false;
}

function createShader(sh,t)
{
	var shader=WGL.createShader(t);
	WGL.shaderSource(shader,sh);
	WGL.compileShader(shader);
	if(!WGL.getShaderParameter(shader,WGL.COMPILE_STATUS))
	{
		alert("shader error..."+WGL.getShaderInfoLog(shader));
		return null;
	}
	return shader;
}
function pushQuad(a)
{
	if(primc>=vbufsize/4)batchGL();
	for(var i=0;i<4;++i)
	for(var j=0;j<7;++j)vb[primc*7*4+i*7+j]=a[i*7+j];
	++primc;
}

function init()
{
	cvs=document.getElementById("glCvs");
	if(!window.devicePixelRatio)window.devicePixelRatio=1;
	cvs.width=window.innerWidth;
	cvs.height=window.innerHeight;
	cvs.style.width=cvs.width+"px";
	cvs.style.height=cvs.height+"px";
	cvs.width=cvs.width*window.devicePixelRatio;
	cvs.height=cvs.height*window.devicePixelRatio;
	vpw=cvs.width;vph=cvs.height;
	try
	{WGL=cvs.getContext("webgl")||cvs.getContext("experimental-webgl");}
	catch(e){WGL=null;}
	if(!WGL){alert("WebGL is not supported by your browser...");return;}
	
	WGL.clearColor(0.5,0.5,0.5,1.0);
	WGL.clearDepth(1);
	WGL.enable(WGL.DEPTH_TEST);
	WGL.depthFunc(WGL.LESS);
	WGL.enable(WGL.BLEND);
	WGL.blendFunc(WGL.SRC_ALPHA,WGL.ONE_MINUS_SRC_ALPHA);
	WGL.clear(WGL.COLOR_BUFFER_BIT|WGL.DEPTH_BUFFER_BIT);
	WGL.viewport(0,0,cvs.width,cvs.height);
	
	shp=WGL.createProgram();
	var fshs=createShader(fsh,WGL.FRAGMENT_SHADER);
	var vshs=createShader(vsh,WGL.VERTEX_SHADER);
	WGL.attachShader(shp,vshs);WGL.attachShader(shp,fshs);
	WGL.linkProgram(shp);
	if(!WGL.getProgramParameter(shp,WGL.LINK_STATUS))
	{
		alert("shader link error...");
	}
	WGL.useProgram(shp);
	
	vbo=WGL.createBuffer();
	WGL.bindBuffer(WGL.ARRAY_BUFFER,vbo);
	WGL.bufferData(WGL.ARRAY_BUFFER,vb,WGL.STATIC_DRAW);
	var vpp=WGL.getAttribLocation(shp,"vp");
	var vcp=WGL.getAttribLocation(shp,"vc");
	WGL.enableVertexAttribArray(vpp);
	WGL.enableVertexAttribArray(vcp);
	WGL.bindBuffer(WGL.ARRAY_BUFFER,vbo);
	WGL.vertexAttribPointer(vpp,3,WGL.FLOAT,false,7*4,0);
	WGL.vertexAttribPointer(vcp,4,WGL.FLOAT,false,7*4,3*4);
	
	for(var i=0,n=0,p=0;i<vbufsize/4;++i)
	{
		ib[p++]=n;ib[p++]=n+1;
		ib[p++]=n+2;ib[p++]=n+2;
		ib[p++]=n+3;ib[p++]=n;
		n+=4;
	}
	ibo=WGL.createBuffer();
	WGL.bindBuffer(WGL.ELEMENT_ARRAY_BUFFER,ibo);
	WGL.bufferData(WGL.ELEMENT_ARRAY_BUFFER,ib,WGL.STATIC_DRAW);
	
	document.addEventListener('keydown',function(e){kst[e.code]=1;});
	document.addEventListener('keyup',function(e){kst[e.code]=0;});
	document.addEventListener('mousedown',function(e){mdn=true;mbtn=e.button;});
	document.addEventListener('contextmenu',function(e){e.preventDefault();});
	document.addEventListener('mouseup',function(e){mup=true;});
	document.addEventListener('dblclick',function(e){dbc=true;});
	document.addEventListener('mousemove',function(e)
	{mx=e.clientX/window.innerWidth*vpw;my=e.clientY/window.innerHeight*vph;});

	requestAnimationFrame(mainloop);
}
function getpick()
{
	for(var i=0;i<shapes.length;++i)
	if(shapes[i].coltest(mx,my))return i;
	return -1;
}

function mainloop()
{
	WGL.clearColor(0.5,0.5,0.5,1.0);
	WGL.clearDepth(1);
	WGL.clear(WGL.COLOR_BUFFER_BIT|WGL.DEPTH_BUFFER_BIT);
	primc=0;
	Mmodelview=buildIdentityMatrix();
	var picked=getpick();
	
	//interaction shit
	if(dragging)
	{
		if(mup)
		{dragging=false;curshape=null;mdn=mup=false;}
		if(!mdn&&mbtn==0&&dragging)
		for(var i=0;i<shapes[drg].vert.length;++i)
		shapes[drg].vert[i]=curshape.vert[i].add(new v3d(mx,my,0).subtract(new v3d(mox,moy,0)));
		if(!mdn&&mbtn==2&&dragging)
		{
			var newscale=new v3d(mox,moy,0).subtract(new v3d(mx,my,0)).l()/20;
			if(mx<mox||my<moy)newscale=-newscale;
			if(newscale<-95)newscale=-95;
			var sc=(100+newscale)/100.;
			var mmx=curshape.vert[0].x,mmy=curshape.vert[0].y;
			for(var i=1;i<curshape.vert.length;++i)
			mmx=Math.min(mmx,curshape.vert[i].x),
			mmy=Math.min(mmy,curshape.vert[i].y);
			var c=new v3d(mmx,mmy,0);
			for(var i=0;i<shapes[drg].vert.length;++i)
				shapes[drg].vert[i]=curshape.vert[i].subtract(c).n().ms
				(curshape.vert[i].subtract(c).l()*sc).add(c);
		}
		
	}
	if(dbc)
	{
		if(dragging)
		{dragging=false;curshape=null;}
		else
		if(curshape)
		{
			if(kst['ControlLeft']==1&&curshape.type=='segstrip')
				curshape.type='segloop';
			shapes.push(curshape);
			curshape=null;
		}
	}
	else
	{
		if(mup&&kst['ControlLeft']==1&&!curshape)
		{
			if(picked!=-1)
			{
				dragging=true;
				mox=mx,moy=my;drg=picked;
				curshape=copyshape(shapes[picked]);
			}
		}
		if(mup&&!dragging&&!kst['ControlLeft'])
		{
			if(!curshape)
			{
				curshape=new Shape('segment');
				curshape.vert.push(new v3d(mx,my,0));
				curshape.vert.push(new v3d(mx,my,0));
			}
			else
			{
				if(curshape.type=='segment')curshape.type='segstrip';
				curshape.vert.push(new v3d(mx,my,0));
			}
		}
	}
	
	if(curshape&&!dragging)
		curshape.vert[curshape.vert.length-1]=new v3d(mx,my,0);

	for(var i=0;i<shapes.length;++i)
	if(!curshape&&!dragging&&i==picked)shapes[i].draw(1,0.8,0,0);
	else shapes[i].draw(1,0.8,0.8,0.8);
	
	if(curshape&&!dragging)curshape.draw(1,0.8,0.8,0);

	batchGL();

	mup=mdn=dbc=false;
	requestAnimationFrame(mainloop);
}

function batchGL()
{
	Mprojection=buildProjectionMatrix2D(vpw,vph);
	var mpp=WGL.getUniformLocation(shp,"mprojection");
	WGL.uniformMatrix4fv(mpp,false,Mprojection);
	var mvpp=WGL.getUniformLocation(shp,"mmodelview");
	WGL.uniformMatrix4fv(mvpp,false,Mmodelview);
	WGL.bindBuffer(WGL.ARRAY_BUFFER,vbo);
	WGL.bufferData(WGL.ARRAY_BUFFER,vb,WGL.STATIC_DRAW);
	WGL.drawElements(WGL.TRIANGLES,6*primc,WGL.UNSIGNED_SHORT,0);
	primc=0;
}