Created
November 23, 2015 06:14
-
-
Save dermotbalson/adc5004948da9caaa392 to your computer and use it in GitHub Desktop.
Tank with target width
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Main | |
-- Tiger4 | |
displayMode(FULLSCREEN) | |
function setup() | |
backup("WoT","ver 106","culling 2") | |
Settings() | |
end | |
function Settings() | |
backgroundColor=color(152, 180, 208, 255) | |
mapWidth,mapHeight=400,400 | |
fogRange=5000 | |
LoadImages() | |
MakeTerrain(mapWidth,mapHeight) | |
SetupTanks() | |
camAngle=math.pi/2 | |
speed=0.5 | |
FPS=60 | |
joy=JoyStick() | |
joyAiming=JoyStick{centre=vec2(310,105)} | |
firePos,fireRadius=vec2(WIDTH-100,105),100 | |
zoomPos,zoomRadius=vec2(WIDTH-105,600),50 | |
zoomLevels,currZoom,zoomDefault={90,60,45,30,10},3,3 | |
SetZoom(zoomDefault) | |
end | |
function SetupTanks() | |
player=Tank(true) | |
player.pos=vec3(350,0.5,-50) | |
player.turretAngle=0 | |
player:rotate(0) | |
player.gunAngle=0 | |
player.viewAngle=180 | |
T={} | |
for j=1,1 do | |
local t=Tank() | |
local keepGoing=true | |
while keepGoing do | |
keepGoing=false | |
t.pos=vec3((0.05+.9*math.random())*mapWidth,0.5,-(0.05+0.9*math.random())*mapHeight) | |
for _,tt in pairs(T) do | |
if t.pos:dist(tt.pos)<Tank.collisionRange then keepGoing=true break end | |
end | |
end | |
t:rotate(math.random(0,360),true) | |
t.turretAngle=0 | |
t.gunAngle=0 | |
t.turretAngleChange=0.25 | |
t.gunAngleChange=0.1 | |
T[t]=t | |
end | |
end | |
function draw() | |
background(backgroundColor) | |
FPS=FPS*0.8+0.2/DeltaTime | |
perspective(zoomLevels[currZoom]) | |
local d=joy:update() | |
local aim=joyAiming:update() | |
player:update(d.x, d.y, aim.x, aim.y) | |
if joy:isTouched() or joyAiming:isTouched() then player.viewAngle=-90+player.angle+player.turretAngle end | |
SetCamera() | |
qq=0 | |
for i=1,#Terrain.trees do Terrain.trees[i].occ=1 end | |
for _,t in pairs(T) do | |
t:update(0,0.5,0,0) | |
t:draw(camPos,camLook,camDir) | |
end | |
player:draw(camPos,camLook,camDir) | |
DrawShots(T) | |
DrawTerrain(camPos) | |
joy:draw() | |
joyAiming:draw() | |
DrawHUD() | |
end | |
function DrawHUD() | |
ortho() | |
viewMatrix(matrix()) | |
pushStyle() | |
if player:ready() then fill(109, 150, 205, 100) else fill(255,0,0,100) end | |
ellipse(firePos.x,firePos.y,fireRadius) | |
fill(0) | |
fontSize(18) | |
text("FPS: "..math.floor(FPS),50,HEIGHT-25) | |
text("Speed: "..math.floor(player.speed),50,HEIGHT-50) | |
text("Tanks:"..qq,50,HEIGHT-75) | |
if hitText then | |
fill(255) | |
text(hitText,WIDTH-100,HEIGHT-200) | |
end | |
fill(255,255,0,100) | |
if drawTouch then ellipse(drawTouch.x,drawTouch.y,25) drawTouch=nil end | |
if aiming then | |
pushStyle() | |
stroke(0) | |
strokeWidth(2) | |
line(WIDTH/2,0,WIDTH/2,HEIGHT) | |
line(0,HEIGHT/2,WIDTH,HEIGHT/2) | |
noFill() | |
ellipse(WIDTH/2,HEIGHT/2,200) | |
popStyle() | |
--draw zoom button | |
fill(196, 118, 193, 255) | |
ellipse(zoomPos.x,zoomPos.y,zoomRadius*2) | |
end | |
popStyle() | |
end | |
function SetCamera() | |
if aiming then | |
local b1,b2=player:SetAim() | |
local d=(b2-b1):normalize() | |
camPos=b2+d*2.3 | |
camLook=camPos+d | |
else | |
camPos=player.pos+RotateVector(vec3(0,20,-20),player.viewAngle,0) | |
camLook=player.pos+vec3(0,17,0) | |
end | |
camDir=(camLook-camPos):normalize() | |
camera(camPos.x,camPos.y,camPos.z,camLook.x,camLook.y,camLook.z) | |
PrepareVisibilityTests(camPos,camDir) | |
end | |
function SetLighting(m,s) | |
s=s or TankShader | |
m.shader=shader(s.v,s.f) | |
m.shader.directDirection=vec4(-2,2,1,0):normalize() | |
m.shader.ambientColor=color(255)*0.4 | |
m.shader.directColor=color(255)*0.5 | |
m.shader.fog=fogRange | |
m.shader.mistColor=backgroundColor | |
m.shader.offset=0 | |
m.shader.mModel=modelMatrix() | |
return m | |
end | |
function touched(t) | |
drawTouch=vec2(t.x,t.y) | |
if joy:touched(t) or joyAiming:touched(t) or FireButtonTouched(t) or ZoomButtonTouched(t) then return | |
elseif t.state==MOVING then | |
player.viewAngle=player.viewAngle+2*t.deltaX/WIDTH*zoomLevels[currZoom] | |
aiming=nil | |
SetZoom(zoomDefault) | |
end | |
end | |
function FireButtonTouched(t) | |
if t.state==ENDED and vec2(t.x,t.y):dist(firePos)<fireRadius then | |
if not aiming then | |
aiming=true | |
else | |
player:Shoot() | |
aiming=nil | |
SetZoom(zoomDefault) | |
end | |
return true | |
end | |
end | |
function ZoomButtonTouched(t) | |
if t.state==ENDED and vec2(t.x,t.y):dist(zoomPos)<zoomRadius then | |
local n=currZoom+1 | |
if n>#zoomLevels then n=1 end | |
SetZoom(n) | |
return true | |
end | |
end | |
function SetZoom(n) | |
currZoom=n | |
cosFOV=math.cos(math.rad(zoomLevels[n]*1.34/2)) | |
cosFOV=math.cos(math.rad(zoomLevels[n]/2)) | |
cosFOV2=cosFOV*cosFOV | |
end | |
--# Model | |
--Model | |
local sin,cos,atan2=math.sin,math.cos,math.atan2 | |
Tank=class() | |
Tank.turretMaxAngle=40 | |
Tank.barrelMaxAngles=vec2(-5,20) | |
Tank.maxSpeed=vec2(5,16) --pixels/sec | |
Tank.maxSpeedChange=0.1 | |
Tank.reloadTime=5 | |
Tank.width=19 | |
Tank.count=0 | |
Tank.shotRadius=15 | |
Tank.bounceDot=0.35 | |
Tank.damageReductionWithDistance=1/10000 | |
Tank.shotForce=50 | |
Tank.turnRate=0.5 | |
Tank.turretTurnRate=1 | |
Tank.collisionRange=30 | |
--Tank.avoidStrength=50 | |
Tank.colors={color(158, 171, 48, 255),color(74, 81, 45, 255)} | |
function Tank:init(p) | |
if not Tank.model then | |
--Tank.LoadTextures() | |
Tank.Shot=MakeShell() | |
Tank.CreateModel() | |
end | |
self.trackOffset=0 | |
self.turretAngle=0 | |
self.gunAngle=0 | |
self.startOfBarrel=vec3(-3.8,5.75,0) | |
self.endOfBarrel=vec3(-10,0,0) --position of end of barrel relative to its base | |
self.muzzleSpeed=2000 --px/s | |
self.health=100 | |
self.speed=0.25 | |
self.angle=-90 | |
self.angleTarget=self.angle | |
self.rad=math.rad(self.angle) | |
self.pos=vec3(0,0,0) | |
self.offset2=math.random() | |
self.wobble=math.random() | |
self.lastShot=-99 | |
Tank.count=Tank.count+1 | |
self.player=p | |
self.id=self | |
self.ammoStatus,self.speedStatus,self.aimStatus,self.visStatus,self.rotationStatus=1,1,1,1,1 | |
self.health=1 | |
end | |
function Tank:draw(camPos,camLook,camDir) | |
self.trackOffset=self.trackOffset-self.speed/200 | |
self:Drive() | |
if not self.player and IsVisible(self.pos,camPos,camDir,Tank.width/2)==false then return end | |
qq=qq+1 | |
local m=Tank.model | |
m.track.shader.offset=self.trackOffset | |
pushMatrix() | |
translate(self.pos:unpack()) | |
rotate(self.angle,0,1,0) | |
if not self.player then self:Wobble(self.speed) end | |
m.wheels.shader.mModel=modelMatrix() | |
m.wheels.shader.camPos=camPos | |
m.wheels:draw() | |
m.track.shader.mModel=modelMatrix() | |
m.track.shader.camPos=camPos | |
m.track:draw() | |
m.body.shader.mModel=modelMatrix() | |
m.body.shader.camPos=camPos | |
m.body.shader.offset2=self.offset2 | |
m.body:draw() | |
rotate(self.turretAngle,0,1,0) | |
m.turret.shader.mModel=modelMatrix() | |
m.turret.shader.camPos=camPos | |
m.turret.shader.offset2=self.offset2 | |
m.turret:draw() | |
translate(self.startOfBarrel:unpack()) | |
rotate(-self.gunAngle,0,0,1) | |
m.barrel.shader.mModel=modelMatrix() | |
m.barrel.shader.camPos=camPos | |
m.barrel.shader.offset2=self.offset2 | |
m.barrel:draw() | |
--calculate position and angle of end of barrel | |
local mm=modelMatrix() | |
local a=vec3(mm[13],mm[14],mm[15]) | |
mm=mm:translate(self.endOfBarrel:unpack()) | |
self.muzzlePos=vec3(mm[13],mm[14],mm[15]) | |
self.muzzleAngle=(self.muzzlePos-a):normalize() | |
popMatrix() | |
end | |
function Tank:Drive() | |
local dir=vec3(-math.cos(self.rad),0,math.sin(self.rad)) | |
local pos=self.pos+dir*self.speed*self.speedStatus*DeltaTime | |
if self.angleTarget==self.angle then | |
if self.player then self.pos=pos return end | |
if pos.x<20 or pos.x>mapWidth-20 or pos.z>-20 or pos.z<20-mapHeight then | |
local a=math.random(75,105) | |
if math.random()<0.5 then self:rotate(-a) else self:rotate(a) end | |
else | |
if not self:CheckForCollisions(dir) then self.pos=pos end | |
end | |
end | |
if not self.player then self:GetOcclusion(player) end | |
local minEdge,maxEdge | |
if not self.player then minEdge,maxEdge=self:GetTankEdges(player) end | |
end | |
function Tank:CheckForCollisions(dir) | |
local minDist,minP,minT=Tank.collisionRange | |
local r=Tank.shotRadius*2 | |
for _,t in pairs(T) do | |
if t~=self then | |
local d=self.pos:dist(t.pos) | |
if d<minDist then | |
local p=self.pos+d*dir | |
if p:dist(t.pos)<r then | |
minDist=d | |
minP=p | |
minT=t.pos | |
end | |
end | |
end | |
end | |
if minT then | |
if (minP.x-self.pos.x)*(minT.z-self.pos.z)-(minP.z-self.pos.z)*(minT.x-self.pos.x)>0 then | |
self:rotate(0.5) else self:rotate(-0.5) end | |
return true | |
end | |
end | |
function Tank:update(a1,s,a2,a3) | |
self:rotate(-a1/4) | |
local ss | |
if s<0 then ss=s*Tank.maxSpeed[1] else ss=s*Tank.maxSpeed[2] end | |
self.speed=self.speed+math.max(-Tank.maxSpeedChange,math.min(Tank.maxSpeedChange,ss-self.speed)) | |
self.turretAngle=math.max(-Tank.turretMaxAngle,math.min(Tank.turretMaxAngle,self.turretAngle-a2/2)) | |
self.gunAngle=math.max(Tank.barrelMaxAngles[1],math.min(Tank.barrelMaxAngles[2],self.gunAngle+a3/5)) | |
end | |
--local testing=true | |
function Tank:GetOcclusion(t) | |
local tiles=GetTiles(self.pos,t.pos,tileSize,treeTiles) --print(self.pos) print(t.pos) | |
local trees={} | |
--if testing then print("------------") end | |
for i,tt in pairs(tiles) do | |
for j=1,#tt do | |
local jj=tt[j] | |
if not trees[jj] then | |
--local trees=Terrain.trees | |
--if testing then print(jj,trees[jj].pos) end | |
treesID[jj].occ=0.75 | |
trees[jj]=jj | |
end | |
end | |
end | |
--testing=false | |
return trees --ID list of trees needing checking | |
end | |
function Tank:GetTankEdges(t) | |
local s,c=sin(t.rad),cos(t.rad) | |
local min,max=999,-999 | |
local corners=Tank.Test.corners | |
for i=1,4 do | |
local x,z=corners[i].x,corners[i].z | |
local xx,zz=c*x-s*z,s*x+c*z | |
local dx,dz=xx-self.pos.x,zz-self.pos.z | |
local a=atan2(dz,dx) | |
if a<min then min=a end | |
if a>max then max=a end | |
end | |
return min,max | |
end | |
function Tank:rotate(a,NoLimit) | |
self.angleTarget=self.angleTarget+a | |
if NoLimit then | |
self.angle=self.angleTarget | |
else | |
self.angle=self.angle+Limit(self.angleTarget-self.angle,Tank.turnRate) | |
end | |
self.rad=math.rad(self.angle) | |
end | |
function Tank:rotateTurret(a) | |
self.turretAngle=self.turretAngle+a*self.rotationStatus | |
end | |
function Tank:rotateBarrel(a) | |
self.barrelAngle=self.barrelAngle+a | |
end | |
function Tank:SetAim() | |
local m=matrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1) | |
m=m:translate(self.pos:unpack()) | |
m=m:rotate(self.angle,0,1,0) | |
m=m:rotate(self.turretAngle,0,1,0) | |
m=m:translate(self.startOfBarrel:unpack()) | |
local b1=vec3(m[13],m[14],m[15]) | |
m=m:rotate(-self.gunAngle,0,0,1) | |
--calculate position and angle of end of barrel | |
m=m:translate(self.endOfBarrel:unpack()) | |
local b2=vec3(m[13],m[14],m[15]) | |
local a=(b2-b1):normalize() | |
self.muzzlePos,self.muzzleAngle=b2,a | |
return b1,b2,a | |
end | |
function Tank:Shoot() | |
if not self:ready() then return end | |
AddShot(self.muzzlePos,self.muzzleAngle,self.muzzleSpeed) | |
self.lastShot=ElapsedTime | |
end | |
function Tank:ready() | |
return ElapsedTime>self.lastShot+Tank.reloadTime | |
end | |
function Tank:Wobble(speed) | |
if speed==0 then return end | |
local f,e=math.abs(1.5*speed/60),ElapsedTime+self.wobble | |
local a,b=e,e*math.pi | |
rotate(10*noise(a,b)*f,1,0,0) | |
rotate(3*noise(b,a)*f,0,0,1) | |
end | |
--[[ | |
function Tank.LoadTextures() | |
Tank.Shot=MakeShell() | |
Tank.texBody=readImage("Dropbox:Tank_Body") | |
if not Tank.texBody then | |
local w,h,f=400,400,5 | |
local img1=image(w,h) | |
for i=1,w do | |
for j=1,h do | |
local m=(noise(i/w*f,j/h*f)+1)/2 | |
img1:set(i,j,Tank.colors[1]:mix(Tank.colors[2],m)) | |
end | |
end | |
saveImage("Dropbox:Tank_Body",img1) | |
Tank.texBody=img1 | |
end | |
--Track------- | |
Tank.texTrack=readImage("Dropbox:Tank_Track") | |
if not Tank.texTrack then | |
local img=image(50,5) | |
setContext(img) | |
pushStyle() | |
noSmooth() | |
background(123, 122, 122, 255) | |
stroke(25) | |
strokeWidth(0.5) | |
for i=1,img.width do | |
line(i,0,i,5) | |
end | |
popStyle() | |
setContext() | |
saveImage("Dropbox:Tank_Track",img) | |
Tank.texTrack=img | |
end | |
--Wheel------- | |
Tank.texWheel=readImage("Dropbox:Tank_Wheel") | |
if not Tank.texWheel then | |
img=image(100,10) | |
local col=Tank.colors[1]*2/3+Tank.colors[2]/3 col.a=255 | |
print(col) | |
setContext(img) | |
background(0) | |
pushStyle() | |
noSmooth() | |
fill(col) | |
stroke(Tank.colors[2]) | |
strokeWidth(2) | |
for i=1,4 do | |
ellipse(i*26-11,10,20) | |
end | |
fill(Tank.colors[2]) | |
for i=1,4 do | |
ellipse(i*26-11,10,4) | |
end | |
fill(col) | |
for i=1,3 do | |
ellipse(2+i*26,9,20) | |
end | |
fill(Tank.colors[2]) | |
for i=1,4 do | |
ellipse(2+i*26,9,4) | |
end | |
popStyle() | |
setContext() | |
saveImage("Dropbox:Tank_Wheel",img) | |
Tank.texWheel=img | |
end | |
end | |
--]] | |
function Tank.CreateModel() | |
local x1,x2,x3,y1,y2,y3,z1,z2,z3,z4,f1,f2 | |
--body of tank | |
local body=mesh() | |
local v,t={},{} | |
x1,x2,y,z1,z2=-8.25,8.25,4.5,3.5,-3.5 --deck | |
v[#v+1]=vec3(x1,y,z1) v[#v+1]=vec3(x2,y,z1) v[#v+1]=vec3(x2,y,z2) | |
v[#v+1]=vec3(x2,y,z2) v[#v+1]=vec3(x1,y,z2) v[#v+1]=vec3(x1,y,z1) | |
x,y1,y2,z1,z2=8.25,2,4.5,3.5,-3.5 --back | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x,y1,z2) v[#v+1]=vec3(x,y2,z2) | |
v[#v+1]=vec3(x,y2,z2) v[#v+1]=vec3(x,y2,z1) v[#v+1]=vec3(x,y1,z1) | |
x1,x2,y1,y2,z1,z2,z3,z4=-11,-8.25,3,4.5,-4.5,4.5,-3.5,3.5 --front #2 angle | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z4) | |
v[#v+1]=vec3(x2,y2,z4) v[#v+1]=vec3(x2,y2,z3) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,y2,y3,z1,z2=-8.25,8.25,2,2,3.5,4.5,-4.5 --sides | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y3,z1) | |
v[#v+1]=vec3(x2,y3,z1) v[#v+1]=vec3(x1,y3,z1) v[#v+1]=vec3(x1,y1,z1) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y3,z2) | |
v[#v+1]=vec3(x1,y3,z2) v[#v+1]=vec3(x2,y3,z2) v[#v+1]=vec3(x2,y2,z2) | |
x1,x2,y1,y2,z1,z2=-8.25,8.25,3.5,4.5,4.5,3.5 --sloping sides | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x1,y2,z2) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,y2,z1,z2=8.25,-8.25,3.5,4.5,-4.5,-3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x1,y2,z2) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,z1,z2=-10,7.25,1,-2.5,2.5 --underneath | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y1,z2) | |
v[#v+1]=vec3(x2,y1,z2) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y1,z1) | |
x,y1,y2,z1,z2=-11,2,3,-4.5,4.5 --front bottom | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x,y1,z2) v[#v+1]=vec3(x,y2,z2) | |
v[#v+1]=vec3(x,y2,z2) v[#v+1]=vec3(x,y2,z1) v[#v+1]=vec3(x,y1,z1) | |
x1,x2,y1,y2,z1,z2=-10,-11,1,2,-2.5,2.5 --front under slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,y2,z1,z2=7.25,8.25,1,2,2.5,-2.5 --back under slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,x3,y1,y2,y3,z1,z2=-11,-8.25,-8.25,3,2,3.5,4.5,3.5 --fill the cracks | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x3,y3,z1) | |
z1=-4.5 | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x3,y3,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,x3,y1,y2,y3,z1=-11,-11,-8.25,3,2,2,4.5 | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x3,y3,z1) | |
z1=-4.5 | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x3,y3,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,y1,y2,y3,z1,z2=-11,-8.25,3,3.5,4.5,4.5,3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y3,z2) | |
v[#v+1]=vec3(x1,y1,-z1) v[#v+1]=vec3(x2,y3,-z2) v[#v+1]=vec3(x2,y2,-z1) | |
x1,y1,y2,z1,z2=8.25,5.5,6.5,3.5,-3.5 | |
x1,y1,y2,z1,z2=8.25,2,3.5,4.5,3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y2,z2) | |
v[#v+1]=vec3(x1,y2,z2) v[#v+1]=vec3(x1,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
v[#v+1]=vec3(x1,y1,-z1) v[#v+1]=vec3(x1,y2,-z2) v[#v+1]=vec3(x1,y1,-z2) | |
v[#v+1]=vec3(x1,y2,-z2) v[#v+1]=vec3(x1,y1,-z1) v[#v+1]=vec3(x1,y2,-z1) | |
x1,y1,y2,z1,z2=8.25,3.5,4.5,4.5,3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y2,z2) | |
z1,z2=-3.5,-4.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y2,z1) | |
body.vertices=v | |
body.texture=Tank.texBody | |
body.name="body" | |
local testBody={v} | |
--tracks | |
local track,v,t=mesh(),{},{} | |
local f=.05 | |
x1,x2,y1,y2,z1,z2,f1,f2=8.25,8.25,1,2,4.5,2.5,0,f*2 --back right vert | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
z1,z2=-2.5,-4.5 --back left vert | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
x1,x2,y1,y2,z1,z2,f1,f2=7.25,8.25,0,1,4.5,2.5,f*2,f*4 --back right slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
z1,z2=-2.5,-4.5 --back left slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
x1,x2,y1,y2,z1,z2,f1,f2=-9,-11,0,2,2.5,4.5,0,f*2 --front right slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f1,1) | |
t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f2,0) | |
z1,z2=-4.5,-2.5 --front left slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f1,1) | |
t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f2,0) | |
x1,x2,y1,y2,z1,z2,f1,f2=-9,7.25,0,0,4.5,2.5,4*f,20.25*f --bottom right track | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y1,z2) | |
v[#v+1]=vec3(x2,y1,z2) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
z1,z2=-2.5,-4.5 --bottom left track | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y1,z2) | |
v[#v+1]=vec3(x2,y1,z2) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
track.vertices=v | |
track.texCoords=t | |
track.texture=Tank.texTrack | |
track.name="track" | |
wheels,v,t=mesh(),{},{} | |
x1,x2,y1,y2,z1=-11,-9,2,0,4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y1,z1) | |
x1,x2,y1,y2,z1=-9,7.25,2,0,4 | |
v[#v+1]=vec3(x1,y2,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y1,z1) | |
v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y2,z1) | |
x1,x2,y1,y2,y3,z1=7.25,8.25,0,1,2,4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y3,z1) | |
v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y3,z1) v[#v+1]=vec3(x1,y3,z1) | |
x1,x2,y1,y2,z1=-11,-9,2,0,-4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,y1,y2,z1=-9,7.25,2,0,-4 | |
v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,y1,y2,y3,z1=7.25,8.25,0,1,2,-4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y3,z1) v[#v+1]=vec3(x2,y2,z1) | |
v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y3,z1) v[#v+1]=vec3(x2,y3,z1) | |
for i=1,#v/2 do t[#t+1]=vec2((v[i].x+11)/19,v[i].y/2) end | |
for i=#v/2+1,#v do t[#t+1]=vec2(-(8.25-v[i].x)/19,v[i].y/2) end | |
wheels.vertices=v | |
wheels.texCoords=t | |
wheels.texture=Tank.texWheel | |
wheels.name="wheels" | |
local turret,v=mesh(),{} | |
local h,y,slope=2.5,4.5,0.5 | |
local p={vec3(-4,0,1.5),vec3(0,0,3.5),vec3(2,0,3),vec3(4,0,1),vec3(4,0,-1), | |
vec3(2,0,-3),vec3(0,0,-3.5),vec3(-4,0,-1.5)} | |
for i=1,#p do p[i].y=y+(p[i].x/8+0.5)*slope end | |
for i=1,#p do | |
local a,b=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(a.x,y,a.z) | |
v[#v+1]=vec3(b.x,y,b.z) | |
v[#v+1]=vec3(b.x,b.y+h,b.z) | |
v[#v+1]=vec3(b.x,b.y+h,b.z) | |
v[#v+1]=vec3(a.x,a.y+h,a.z) | |
v[#v+1]=vec3(a.x,y,a.z) | |
end | |
local norm={} | |
for i=1,#v do norm[i]=v[i]:normalize() end | |
--roof | |
for i=1,#p do | |
local a,b=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(a.x,a.y+h,a.z) | |
v[#v+1]=vec3(b.x,b.y+h,b.z) | |
v[#v+1]=vec3(0,y+h+slope/2,0) | |
for j=1,3 do norm[#norm+1]=vec3(0,1,0) end | |
end | |
turret.vertices=v | |
--turret.normals=norm | |
turret.texture=Tank.texBody | |
turret.name="turret" | |
local testTurret={v} | |
barrel,p,v,norm=mesh(),{},{},{} | |
local x1,x2,r1,r2,y=-4,-7,0.75,0.4,7.75 | |
for i=1,8 do p[i]=vec3(0,math.cos(i*math.pi/4),math.sin(i*math.pi/4)) end | |
for i=1,8 do | |
local p1,p2=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
v[#v+1]=vec3(x2,y+p1.y*r2,p1.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x1,y+p2.y*r1,p2.z*r1) | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
end | |
local x1,x2,r1,r2,y=-7,-17,0.4,0.3,7.75 | |
for i=1,8 do p[i]=vec3(0,math.cos(i*math.pi/4),math.sin(i*math.pi/4)) end | |
for i=1,8 do | |
local p1,p2=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
v[#v+1]=vec3(x2,y+p1.y*r2,p1.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x1,y+p2.y*r1,p2.z*r1) | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
end | |
for i=1,#v do norm[i]=(v[i]-vec3(v[i].x,y,0)):normalize() end | |
for i=1,#v do v[i].x,v[i].y=v[i].x+4,v[i].y-7.75 end | |
barrel.vertices=v | |
barrel.normals=norm | |
barrel.texture=Tank.texBody | |
barrel.name="barrel" | |
local tank={body=body,track=track,turret=turret,barrel=barrel,wheels=wheels} | |
for _,m in pairs(tank) do | |
local norm={} | |
local nn=m:buffer("normal") | |
if nn[1]==nil then | |
local v=m:buffer("position") | |
for i=1,m.size,3 do | |
local v1,v2=v[i]-v[i+2],v[i+1]-v[i+2] | |
local n=(v1:cross(v2)):normalize() | |
norm[i],norm[i+1],norm[i+2]=n,n,n | |
end | |
m.normals=norm | |
end | |
if m.texture==Tank.texBody then | |
local t={} | |
local v=m:buffer("position") | |
for i=1,m.size do | |
t[i]=vec2(v[i].x/8+v[i].y/5,v[i].z/6+v[i].y/9)/2 | |
end | |
m.texCoords=t | |
end | |
m=SetLighting(m) | |
m.shader.offset2=0 | |
end | |
--make tables for testing of penetration and damage | |
Tank.Damage={Speed=1,Rotation=2,Ammo=3,Accuracy=4,Visibility=5} | |
Tank.DamageList={"Speed","Rotation","Ammo","Accuracy","Visibility"} | |
Tank.model=tank | |
local vert,norm,armor,damage={},{},{},{} | |
local v,n=body:buffer("position"),body:buffer("normal") | |
for i=1,body.size do | |
vert[i]=v[i] | |
norm[i]=n[i] | |
if norm[i]:dot(vec3(-1,0,0))>0 then | |
armor[i]=40 | |
damage[i]=Tank.Damage.Visibility | |
else | |
armor[i]=20 | |
if norm[i]:dot(vec3(1,0,0))>0 then damage[i]=Tank.Damage.Speed | |
elseif norm[i]:dot(vec3(0,0,-1))>0 then damage[i]=Tank.Damage.Ammo | |
else damage[i]=Tank.Damage.Accuracy end | |
end | |
end | |
local testBody={vert,norm,armor,damage} | |
local vert,norm,armor,damage={},{},{},{} | |
local v,n=turret:buffer("position"),turret:buffer("normal") | |
for i=1,turret.size do vert[i]=v[i] norm[i]=n[i] armor[i]=40 end | |
for i=1,turret.size do damage[i]=Tank.Damage.Rotation end | |
local testTurret={vert,norm,armor,damage} | |
--create bounding box for tracks for penetration testing | |
local b=CreateBlock(19,2,2) | |
local v=b:buffer("position") | |
local vert,norm,armor,damage={},{},{},{} | |
for i=1,b.size do vert[#vert+1]=v[i]+vec3(0,1,-3.5) norm[#norm+1]=vec3(0,0,-1) end | |
for i=1,b.size do vert[#vert+1]=v[i]+vec3(0,1, 3.5) norm[#norm+1]=vec3(0,0, 1) end | |
for i=1,#vert do armor[i]=20 end | |
for i=1,#vert do damage[i]=Tank.Damage.Speed end | |
local testTrack={vert,norm,armor,damage} | |
local corners={vec3(-11,0,-4.5),vec3(-11,0,4.5),vec3(8.25,0,-4.5),vec3(-8.25,0,4.5)} | |
Tank.Test={track=testTrack,turret=testTurret,body=testBody,corners=corners} | |
end | |
function Tank:PenetrationTest(pos,dir) | |
hitText="" | |
local min,h,a,ang,dam,nam=9999 | |
local v=Tank.Test.body | |
local name="body" | |
for i=1,#v[1],3 do | |
local d,p=LineIntersectsTriangle(pos,dir,v[1][i],v[1][i+1],v[1][i+2],v[2][i]) | |
if d and d<min then | |
ang=(-dir):dot(v[2][i]) | |
min,h,a,dam,nam=d,p,v[3][i],v[4][i],name | |
end | |
end | |
local v=Tank.Test.turret | |
local name="turret" | |
for i=1,#v[1],3 do | |
local d,p=LineIntersectsTriangle(pos,dir,v[1][i],v[1][i+1],v[1][i+2],v[2][i]) | |
if d and d<min then | |
ang=(-dir):dot(v[2][i]) | |
min,h,a,dam,nam=d,p,v[3][i],v[4][i],name | |
end | |
end | |
local v=Tank.Test.track | |
local name="track" | |
for i=1,#v[1],3 do | |
local d,p=LineIntersectsTriangle(pos,dir,v[1][i],v[1][i+1],v[1][i+2],v[2][i]) | |
if d and d<min then | |
ang=(-dir):dot(v[2][i]) | |
min,h,a,dam,nam=d,p,v[3][i],v[4][i],name | |
end | |
end | |
if h then self:CalcDamage(min,h,a,ang,dam,nam) end | |
return min,h,a,ang,dam,nam | |
end | |
function Tank:CalcDamage(min,h,a,ang,dam,nam) | |
if ang<Tank.bounceDot then hitText="Bounce" return end | |
local f=(1-Tank.damageReductionWithDistance)*ang | |
if Tank.shotForce*f<a then hitText="No penetration" return end | |
local d=0.25+0.75*math.random() | |
self.health=self.health-d/2 | |
if self.health<0 then self:Die() return | |
elseif dam==Tank.Damage.Speed then self.speedStatus=math.max(0,self.speedStatus-d) | |
elseif dam==Tank.Damage.Rotation then self.rotationStatus=math.max(0,self.rotationStatus-d) | |
elseif dam==Tank.Damage.Ammo then self.ammoStatus=math.max(0,self.ammoStatus-d) | |
elseif dam==Tank.Damage.Accuracy then self.aimStatus=math.max(0,self.aimStatus-d) | |
elseif dam==Tank.Damage.Visibility then self.visStatus=math.max(0,self.visStatus-d) | |
end | |
local f=function(a) return string.format("%.f%%", 100*a) end | |
hitText="health="..f(self.health).."\nspeed="..f(self.speedStatus).."\nrotate="..f(self.rotationStatus).. | |
"\nammo="..f(self.ammoStatus).."\naim="..f(self.aimStatus).."\nvis="..f(self.visStatus) | |
end | |
function Tank:Die() | |
T[self]=nil | |
hitText="Tank died" | |
end | |
--# Terrain | |
--Terrain2 | |
local atan,rand,min,max,cos=math.atan,math.random,math.min,math.max,math.cos | |
local rad=math.pi/180 | |
Terrain={} | |
function MakeTerrain(w,d,pic,f,t) | |
local w,d,img,f=w or 1000,d or 1000,pic or grassImage,f or .02 | |
local floor=mesh() | |
--local img=readImage(pic) | |
floor.texture=img | |
local wn,dn=w/img.width/f,d/img.height/f | |
floor.vertices={vec3(0,0,0),vec3(w,0,0),vec3(w,0,-d),vec3(w,0,-d),vec3(0,0,-d),vec3(0,0,0)} | |
floor.texCoords={vec2(0,0),vec2(wn,0),vec2(wn,dn),vec2(wn,dn),vec2(0,dn),vec2(0,0)} | |
floor=SetLighting(floor,TileShader) | |
tileSize=t or 40 | |
local trees={} | |
treesID={} | |
for i=1,100 do | |
local ii=(i//10) | |
local p=vec3(ii+1/2,0,-(i-ii*10+1/2))*tileSize | |
trees[i]=AddBillboard(i,0,0,w,-d,treeImages,0.05,p) | |
treesID[i]=trees[i] | |
end | |
treeTiles=AllocateTrees(trees,tileSize) | |
Terrain={floor=floor,trees=trees} | |
end | |
--[[ | |
function AllocateObjects(objects,tileSize) --objects need a vec3 pos property and r(adius) value | |
local L={} | |
for p=1,#objects do | |
local b=objects[p] | |
local a={} | |
for i=-1,1 do | |
for j=-1,1 do | |
local x,z=(b.pos.x+i*b.r)//tileSize+1,(-b.pos.z+j*b.r)//tileSize+1 | |
a[x]=a[x] or {} | |
a[x][z]=p | |
end | |
end | |
for x,ax in pairs(a) do | |
for z,az in pairs(ax) do | |
L[x]=L[x] or {} | |
L[x][z]=L[x][z] or {} | |
table.insert(L[x][z],az) | |
end | |
end | |
end | |
--[ | |
for x,ax in pairs(L) do | |
for z,az in pairs(ax) do | |
for w,aw in pairs(az) do | |
print(objects[aw].pos,x,z,aw) | |
end | |
end | |
end | |
--] | |
return L | |
end | |
--]] | |
local allocSeq={{-1,0},{0,0},{1,0},{0,-1},{0,1}} | |
function AllocateTrees(t,tileSize) --objects need a vec3 pos property and r(adius) value | |
local L={} | |
for p=1,#t do | |
local b=t[p] --print("---------\n",b.pos,b.r) | |
local a={} --prevents dups | |
for u=1,#allocSeq do | |
local i,j=allocSeq[u][1],allocSeq[u][2] | |
local x,z=(b.pos.x+i*b.r)//tileSize,(-b.pos.z+j*b.r)//tileSize | |
L[x]=L[x] or {} | |
L[x][z]=L[x][z] or {} --zzz | |
local tt=L[x][z] | |
if not a[tt] then | |
a[tt]=tt | |
tt[#tt+1]=b.id | |
--print(x,z) | |
end | |
end | |
end | |
--[ | |
for i=0,10 do | |
for j=0,10 do | |
if L[i] and L[i][j] then | |
--print("------------\n",i,j) | |
for k=1,#L[i][j] do | |
--print(L[i][j][k]) | |
end | |
end | |
end | |
end | |
--]] | |
return L | |
end | |
function LoadImages() | |
--treeImages={readImage("Dropbox:3D-tree"),readImage("Dropbox:3D-tree3"),readImage("Dropbox:3D-tree7"), | |
-- readImage("Dropbox:3D-tree8")} | |
local img=readImage("Dropbox:TankSheet") | |
treeImages={img:copy(409,287,211,141),img:copy(432,169,139,104),img:copy(427,54,140,107), | |
img:copy(641,219,188,175),img:copy(587,57,141,104)} | |
Tank.texBody=img:copy(1,35,400,398) | |
--Tank.texBody2=img:copy(1,35,400,398) | |
setContext(Tank.texBody) | |
fill(198, 85, 108, 100) | |
rect(0,0,400,400) | |
setContext() | |
Tank.texWheel=img:copy(1,9,200,19) | |
setContext(Tank.texWheel) | |
fill(198, 85, 108, 75) | |
rect(0,0,200,19) | |
setContext() | |
Tank.texTrack=img:copy(210,9,100,9) | |
grassImage=img:copy(849,68,342,341) | |
end | |
function AddBillboard(id,x1,z1,x2,z2,imgList,f,pos) | |
local i=rand(1,#imgList) | |
local h=0.5+rand() | |
local w=imgList[i].width*h*f --width | |
local pos=vec3(pos.x or rand(x1,x2),imgList[i].height*f/2*h,pos.z or rand(min(z1,z2),max(z1,z2))) | |
local m=mesh() | |
m:addRect(0,0,w,imgList[i].height*h*f) | |
m.texture=imgList[i] | |
m.shader=shader(TreeShader.v,TreeShader.f) | |
m.shader.mistColor=color(255,0,0) --backgroundColor --zzz | |
return {m=m,pos=pos,r=w/2,id=id} | |
end | |
function DrawTerrain() | |
Terrain.floor.shader.camPos=camPos | |
Terrain.floor:draw() | |
DrawBillboards(Terrain.trees,camPos) | |
end | |
function LookAt(source,target) | |
local z=(source-target):normalize() | |
local x=(vec3(0,1,0):cross(z)):normalize() | |
local y=(z:cross(x)):normalize() | |
return matrix(x.x,x.y,x.z,0, y.x,y.y,y.z,0, z.x,z.y,z.z,0, source.x,source.y,source.z,1) | |
end | |
function GetScreenRect(t1,t2,s) --t1 is camera, t2 is target, s is vec3 size of object | |
--get direction and distance | |
local v=(t2.pos-t1.pos) | |
local d=v:len()-u/2 --position of t2 is assumed to be its centre, so adjust by 1\2 depth | |
v=v/d | |
--get drawing rectangle | |
local w = u*WIDTH*projectionMatrix()[1]/d/2 | |
local h = s.y*HEIGHT*projectionMatrix()[6]/d/2 | |
--draw our tank | |
local m = modelMatrix()*viewMatrix()*projectionMatrix() | |
local X,Y=(m[13]/m[16]+1)*WIDTH/2, (m[14]/m[16]+1)*HEIGHT/2 | |
return X-w/2,Y-h/2,w,h | |
end | |
--local testing1=true | |
function DrawBillboards(billboards) | |
local PM1=projectionMatrix()[1] | |
for _,b in pairs(billboards) do | |
b.pov=IsVisible(b.pos,camPos,camDir,b.r) | |
b.visibility=max(0,1-b.pos:dist(camPos)/fogRange) | |
end | |
--calc distance to camera and sort from furthest to nearest, every two seconds | |
if ElapsedTime>(lastSort or -9) +2 then | |
table.sort(billboards,function(a,b) return a.visibility<b.visibility end) | |
lastSort=ElapsedTime | |
end | |
for i=1,#billboards do | |
local b=billboards[i] | |
if b.pov and b.visibility>0 then | |
pushMatrix() | |
--move to the centre of our image | |
local p=b.pos | |
translate(p:unpack()) | |
--calculate difference in x and z | |
local dx,dz=p.x-camPos.x,-p.z+camPos.z | |
--calculate angle we need to turn | |
local ang=atan(dx/-dz)/rad | |
if b.lastAng and math.abs(ang-b.lastAng)>90 and ElapsedTime<b.lastDraw+1 then ang=ang+180 end | |
b.lastAng=ang | |
b.lastDraw=ElapsedTime | |
--turn and draw | |
rotate(ang,0,1,0) | |
b.m.shader.visibility=(b.occ or 1)*b.visibility --zzz | |
--if b.id>50 then b.m.shader.visibility=0.6 else b.m.shader.visibility=1 end | |
--if testing1 and b.occ<1 then print(i) end | |
b.m:draw() | |
popMatrix() | |
end | |
end | |
--testing1=false | |
end | |
--[[ | |
function IsVisible(pos,camPos,camLook,radius) | |
look=camPos+(camLook-camPos)*5000 | |
local xDist=(pos-camPos):dist(math.max(math.min((pos-camPos):dot(look-camPos)/ | |
(look-camPos):lenSqr(),1),0)*(look-camPos)) | |
return xDist-radius<camPos:dist(pos)/projectionMatrix()[1] | |
end | |
--]] | |
--[[ | |
function IsVisible(pos,camPos,camLook,radius) | |
local xDist=DistancePointToLine(pos,camPos,camLook) | |
return xDist-radius<camPos:dist(pos)/projectionMatrix()[1]*1.1 | |
end | |
function DistancePointToLine(p,a,b) | |
b=p+(b-a)*5000 | |
return (p-a):dist(math.max(math.min((p-a):dot(b-a)/(b-a):lenSqr(),1),0)*(b-a)) | |
end | |
--]] | |
--# Controls | |
--joystick | |
JoyStick = class() | |
local tick=60 | |
--Note all the options you can set below. Pass them through in a named table | |
function JoyStick:init(t) | |
t = t or {} | |
self.radius = t.radius or 100 --size of joystick on screen | |
self.stick = t.stick or 30 --size of inner circle | |
self.centre = t.centre or self.radius * vec2(1,1) + vec2(5,5) | |
self.damp=vec2(0.2,0.2) | |
self.position = vec2(0,0) --initial position of inner circle | |
self.target = vec2(0,0) --current position of inner circle (used when we interpolate movement) | |
self.value = vec2(0,0) | |
self.delta = vec2(0,0) | |
self.mspeed = 30 | |
self.moving = 0 | |
end | |
function JoyStick:draw() | |
ortho() | |
viewMatrix(matrix()) | |
pushStyle() | |
fill(160, 182, 191, 1) | |
stroke(0,0,0,50) | |
strokeWidth(3) | |
ellipse(self.centre.x,self.centre.y,2*self.radius) | |
fill(78, 131, 153, 1) | |
ellipse(self.centre.x+self.position.x, self.centre.y+self.position.y, self.stick*2) | |
popStyle() | |
end | |
function JoyStick:touched(t) | |
if t.state == BEGAN then | |
local v = vec2(t.x,t.y) | |
if v:dist(self.centre)<self.radius-self.stick then | |
self.touch = true | |
--else return false | |
end | |
end | |
if self.touch then | |
if t.state~=ENDED then | |
local v = vec2(t.x,t.y) | |
if v:dist(self.centre)>self.radius-self.stick then | |
v = (v - self.centre):normalize()*(self.radius - self.stick) + self.centre | |
end --set x,y values for joy based on touch | |
self.target=v - self.centre | |
else --reset joystick to centre when touch ends | |
self.target=vec2(0,0) | |
self.touch = false | |
end | |
else return false | |
end | |
return true | |
end | |
function JoyStick:update() | |
local p = self.target - self.position | |
if p:len() < tick * self.mspeed then | |
self.position = self.target | |
if not self.touch then | |
if self.moving ~= 0 then | |
self.moving = self.moving - 1 | |
end | |
else | |
self.moving = 2 | |
end | |
else | |
self.position = self.position + p:normalize() * tick * self.mspeed | |
self.moving = 2 | |
end | |
local v=self.position/(self.radius - self.stick) | |
return self:Dampen(v) | |
end | |
function JoyStick:Dampen(v) | |
if not self.damp then return v end | |
if v.x>0 then v.x=math.max(0,(v.x-self.damp.x)/(1-self.damp.x)) | |
else v.x=math.min(0,(v.x+self.damp.x)/(1-self.damp.x)) end | |
if v.y>0 then v.y=math.max(0,(v.y-self.damp.y)/(1-self.damp.y)) | |
else v.y=math.min(0,(v.y+self.damp.y)/(1-self.damp.y)) end | |
return v | |
end | |
function JoyStick:isMoving() | |
return self.moving | |
end | |
function JoyStick:isTouched() | |
return self.touch | |
end | |
--# Utility | |
--Utility | |
function CreateBlock(w,h,d,col,pos,tex,ms) --width,height,depth,colour,position,texture | |
pos=pos or vec3(0,0,0) | |
local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2 | |
local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)} | |
local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7], | |
v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]} | |
local texCoords | |
if tex then | |
local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)} | |
texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3], | |
t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]} | |
end | |
local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0),vec3(0,-1,0)} | |
local norm={} | |
for i=1,6 do for j=1,6 do norm[#norm+1]=n[i] end end | |
if not ms then ms=mesh() end | |
if ms.size==0 then | |
ms.vertices=vert | |
ms.normals=norm | |
if tex then ms.texture,ms.texCoords=tex,texCoords end | |
else | |
for i=1,#vert do | |
table.insert(ms.vertices,vert[i]) | |
table.insert(ms.normals,norm[i]) | |
if tex then table.insert(ms.texCoords,texCoords[i]) end | |
end | |
end | |
ms:setColors(col or color(255)) | |
return ms | |
end | |
function AddBlock(w,h,d,col,pos,rot,texture,vertices,texCoords,normals,colors) --width,height,depth,colour,position,texture | |
local x,X,y,Y,z,Z=-w/2,w/2,-h/2,h/2,-d/2,d/2 | |
local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)} | |
local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7], | |
v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]} | |
if rot then | |
m=modelMatrix() | |
m=m:translate(pos:unpack()) | |
m=m:rotate(rot.x,1,0,0) m=m:rotate(r.y,0,1,0) m=m:rotate(r.z,0,0,1) | |
for i=1,#vert do vertices[#vertices+1]=m*vert[i] end | |
else | |
for i=1,#vert do vertices[#vertices+1]=pos+vert[i] end | |
end | |
local tex | |
if texture then | |
local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)} | |
tex={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3], | |
t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]} | |
for i=1,#tex do texCoords[#texCoords+1]=tex[i] end | |
end | |
local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0),vec3(0,-1,0)} | |
for i=1,6 do | |
for j=1,6 do | |
normals[#normals+1]=n[i] | |
colors[#colors+1]=col | |
end | |
end | |
return vertices,texCoords,normals,colors | |
end | |
--o is start of line, d is normalised line direction | |
--v0, v1, v2 are triangle vertices | |
function LineIntersectsTriangle(o,d,v1,v2,v3,n) | |
if d:dot(n)>0 then return end | |
local e1,e2=v2-v1,v3-v1 | |
local p=d:cross(e2) | |
local det=e1:dot(p) | |
if math.abs(det)<0.001 then return end | |
local inv_det=1/det | |
local t=o-v1 | |
local u=t:dot(p)*inv_det | |
if u<0 or u>1 then return end | |
local q=t:cross(e1) | |
local v=d:dot(q)*inv_det | |
if v<0 or u+v>1 then return end | |
local a=e2:dot(q)*inv_det | |
if a>0 then return a,o+d*a end | |
end | |
function LookAtMatrix(source,target,up) | |
local Z=(source-target):normalize() | |
up=up or vec3(0,1,0) | |
local X=(up:cross(Z)):normalize() | |
local Y=(Z:cross(X)):normalize() | |
return matrix(X.x,X.y,X.z,0,Y.x,Y.y,Y.z,0,Z.x,Z.y,Z.z,0,source.x,source.y,source.z,1) | |
end | |
function RotateVector(v,y,z) | |
local m=matrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1) | |
if y then m=m:rotate(y,0,1,0) end | |
if z then m=m:rotate(z,0,0,1) end | |
return m*v,m | |
end | |
function Limit(a,L) | |
if a>0 then return math.min(a,L) else return -math.min(-a,L) end | |
end | |
function lineDistB(p,a,b) | |
return (p-a):dist(math.max(math.min((p-a):dot(b-a)/(b-a):lenSqr(),1),0)*(b-a)) | |
end | |
function DistancePointToLine(p,a,b) | |
b=p+(b-a)*5000 | |
return (p-a):dist(math.max(math.min((p-a):dot(b-a)/(b-a):lenSqr(),1),0)*(b-a)) | |
end | |
--# Shaders | |
--Shaders | |
TankFogShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
uniform vec4 directColor; | |
uniform vec4 directDirection; | |
uniform vec4 ambientColor; | |
uniform float offset; | |
attribute vec4 position; | |
attribute vec3 normal; | |
attribute vec2 texCoord; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vTexCoord = vec2(texCoord.x+offset,texCoord.y); | |
vec4 norm = normalize(mModel * vec4( normal, 0.0 )); | |
float diffuse = max( 0.0, dot( norm, directDirection )); | |
vPosition = mModel*position; | |
vColor = diffuse * directColor + ambientColor; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform float offset2; | |
uniform vec4 mistColor; | |
uniform float fog; | |
uniform vec3 camPos; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vec4 col = texture2D(texture, vec2(mod(vTexCoord.x+offset2,1.0),mod(vTexCoord.y,1.0)))*vColor; | |
float f = clamp(distance(vPosition.xyz,camPos)/fog,0.0,1.0); | |
col = mix( col, mistColor, f); | |
col.a=1.0; | |
gl_FragColor=col; | |
} | |
]]} | |
TankShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
uniform vec4 directColor; | |
uniform vec4 directDirection; | |
uniform vec4 ambientColor; | |
uniform float offset; | |
attribute vec4 position; | |
attribute vec3 normal; | |
attribute vec2 texCoord; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vTexCoord = vec2(texCoord.x+offset,texCoord.y); | |
vec4 norm = normalize(mModel * vec4( normal, 0.0 )); | |
float diffuse = max( 0.0, dot( norm, directDirection )); | |
vPosition = mModel*position; | |
vColor = diffuse * directColor + ambientColor; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform float offset2; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vec4 col = texture2D(texture, vec2(mod(vTexCoord.x+offset2,1.0),mod(vTexCoord.y,1.0)))*vColor; | |
col.a=1.0; | |
gl_FragColor=col; | |
} | |
]]} | |
TreeShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
attribute vec4 position; | |
attribute vec2 texCoord; | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vTexCoord = texCoord; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform vec4 mistColor; | |
uniform float visibility; | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
vec4 col = texture2D(texture,vTexCoord); | |
if (col.a<0.05) discard; | |
else { | |
col=mix(mistColor,col,visibility); | |
gl_FragColor=col; | |
} | |
} | |
]]} | |
LightingShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
uniform vec4 directColor; | |
uniform vec4 directDirection; | |
uniform vec4 ambientColor; | |
uniform float fog; | |
uniform vec4 mistColor; | |
uniform vec3 camPos; | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec3 normal; | |
varying lowp vec4 vColor; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vec4 norm = normalize(mModel * vec4( normal, 0.0 )); | |
float diffuse = max( 0.0, dot( norm, directDirection )); | |
vec4 p=mModel*position; | |
float f = clamp(distance(p.xyz,camPos)/fog,0.0,1.0); | |
vColor = mix(color * ( diffuse * directColor + ambientColor ),mistColor,f); | |
vColor.a=1.0; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
varying lowp vec4 vColor; | |
void main() | |
{ | |
gl_FragColor=vColor; | |
} | |
]]} | |
--Tile | |
TileShader = { | |
v = [[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
attribute vec4 position; | |
attribute vec2 texCoord; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vTexCoord = texCoord; | |
vPosition=mModel*position; | |
gl_Position = modelViewProjection * position; | |
} | |
]], | |
f = [[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform float fog; | |
uniform vec4 mistColor; | |
uniform vec3 camPos; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
lowp vec4 col = texture2D(texture, vec2(mod(vTexCoord.x,1.0),mod(vTexCoord.y,1.0))); | |
float f = clamp(distance(vPosition.xyz,camPos)/fog,0.0,1.0); | |
gl_FragColor = mix(col,mistColor,f); | |
} | |
]]} | |
--# Shot | |
--shot | |
ShotList={} | |
function AddShot(p,d,s) | |
table.insert(ShotList,{start=p,pos=p,dir=d,speed=s}) | |
end | |
function DrawShots(Tanks) | |
for i,s in pairs(ShotList) do | |
local m=LookAtMatrix(s.pos,s.pos+s.dir) | |
pushMatrix() | |
modelMatrix(m) | |
Tank.Shot:draw() | |
popMatrix() | |
local newPos=s.pos+s.dir*(s.speed*DeltaTime) | |
for _,t in pairs(Tanks) do | |
--if t.pos:dist(newPos)<Tank.shotRadius or s.dir:dot(t.pos-newPos)<0 then | |
if DistancePointToLine(t.pos,s.pos,s.dir)<Tank.shotRadius and s.dir:dot(t.pos-newPos)<0 then | |
ShotList[i]=nil | |
local objPos,m=RotateVector((s.start-t.pos),-t.angle,0) | |
local objDir=m*s.dir | |
local d,h,a,ang,dam,nam=t:PenetrationTest(objPos,objDir) | |
if not ang then hitText="Shot missed!" end | |
--[[ | |
if not ang then hitText="Shot missed!" | |
elseif ang<Tank.bounceDot then hitText="Shot bounced!" | |
else | |
local f=(1-d*Tank.damageReductionWithDistance)*ang | |
hitText=nam .. " hit!\n" .. | |
"distance: "..math.floor(d) .."\n" .. | |
"angle: "..math.floor(math.deg(math.acos(ang))) .. "\n".. | |
"adjusted power: "..string.format("%4.2f",f) .."\n" .. | |
"armour: ".. a .. "\n" .. | |
"damage to: "..Tank.DamageList[dam] | |
end | |
--]] | |
else | |
s.pos=newPos | |
end | |
end | |
end | |
end | |
--# Effects | |
--Effects | |
function MakeExplosion(s) | |
local sin,cos,rand,pi2=math.sin,math.cos,math.random,math.pi*2 | |
local ss=s*1.5 | |
local img=image(ss,ss) | |
pushMatrix() | |
setContext(img) | |
translate(ss/2,ss/2) | |
for i=1,1000 do | |
local f=i/2000 | |
local m=0.4-0.2*f | |
local a,d,r=rand()*pi2,rand()*s*.5,rand()*s*m | |
local col=150+100*f | |
fill(color(col,col,col,50)) | |
rotate(a) | |
ellipse(d*sin(a),d*cos(a),r) | |
end | |
setContext() | |
popMatrix() | |
local m=mesh() | |
local x1,y1,z,x2,y2=-s/2,-s/2,0,s/2,s/2 | |
m.vertices={vec3(x1,y1,z),vec3(x2,y1,z),vec3(x2,y2,z),vec3(x2,y2,z),vec3(x1,y2,z),vec3(x1,y1,z)} | |
m.texture=img | |
m.texCoords={vec2(0,0),vec2(1,0),vec2(1,1),vec2(1,1),vec2(0,1),vec2(0,0)} | |
m:setColors(color(255)) | |
m.shader=shader(TransparentFadeShader.v,TransparentFadeShader.f) | |
m.shader.a=1 | |
return m,img | |
end | |
function MakeShell() | |
return CreateBlock(0.15,0.15,50,color(255,255,0,100),vec3(0,0,-5)) | |
end | |
--# Backup | |
--backup | |
function backup(projectName,ver,comments,load) | |
local backupName=projectName..": "..ver..".txt" -- name of the backup file | |
local dds="/Documents/Dropbox.assetpack/" | |
local path = os.getenv("HOME").."/Documents/" | |
local file = os.getenv("HOME")..dds..backupName | |
local t=io.open(file,"r") | |
if t then | |
if load then | |
local txt=t:read('*a') | |
saveProjectTab("Recovered",txt) | |
end | |
io.close() | |
return | |
end | |
local wFd = io.open(file,"w") | |
local tabList=listProjectTabs() | |
data="--"..comments.."\n\n" | |
local txt | |
if #tabList>0 then | |
for _,tab in pairs(tabList) do | |
data=data..string.format("\n--# %s\n",tab) | |
local tt=readProjectTab(tab) | |
data=data..tt | |
if tab=="Versions" then txt=tt end | |
end | |
wFd:write(data) | |
end | |
wFd:close() | |
print(ver.." backup made") | |
--update version history | |
if not txt then txt="--[[VERSION HISTORY\n\n--]]" end | |
local i=txt:find("--]]") | |
txt=txt:sub(1,i-1)..""..ver.." ("..os.date("%c")..") - "..comments.."\n--]]" | |
saveProjectTab("Versions",txt) | |
end | |
--# Test | |
--[[ | |
displayMode(NORMAL) | |
function setup() | |
FOV=10 | |
cosFOV=math.cos(math.rad(FOV/2*1.3)) --/1.06 | |
pm1=projectionMatrix()[1] | |
T,A={},{} | |
n=10000 | |
w=20 | |
for i=1,n do | |
T[i]={} | |
T[i].pos=vec3(math.random(-100,100),0,math.random(-100,100)) | |
T[i].dir=vec3(math.random(-100,100),0,math.random(-100,100)):normalize() | |
T[i].pos=vec3(0,0,0) T[i].dir=vec3(0,0,-1) | |
m=modelMatrix() | |
local a=math.random(-45,45) | |
--a=23 | |
--T[i].pos=vec3(0,0,0) | |
--T[i].dir=vec3(0,0,-1) | |
m=m:rotate(a,0,1,0) | |
A[i]={} | |
A[i].pos=T[i].pos+(m*T[i].dir)*math.random(500,500)+vec3(0,math.random(-300,300),0) | |
--print(A[i].pos) | |
end | |
print("iterations",n) | |
dotA,lineA={},{} | |
tt={} tt[1]=0 tt[2]=0 tt[3]=0 | |
t=os.time() | |
for i=1,n do | |
local a,b=IsVisible(A[i].pos,T[i].pos,T[i].dir,w/2) | |
--if a then tt[b]=tt[b]+1 end | |
end | |
print("time=",os.time()-t) | |
print("rate/frame=",n/60/(os.time()-t)) | |
for i=1, 3 do print(i,tt[i]/n) end | |
print("Total=",(tt[1]+tt[2]+tt[3])/n) | |
end | |
function draw() | |
background(0) | |
perspective(FOV) | |
camera(T[1].pos.x,T[1].pos.y,T[1].pos.z,T[1].dir.x,T[1].dir.y,T[1].dir.z) | |
stroke(255) | |
strokeWidth(0) | |
fill(255,255,0) | |
for i=1,n do | |
pushMatrix() | |
translate(A[i].pos:unpack()) | |
if IsVisible(A[i].pos,T[i].pos,T[i].dir,0) then | |
fill(0,0,255) | |
ellipse(0,0,w/2) | |
else | |
fill(255,0,0) | |
ellipse(0,0,w/2) | |
end | |
popMatrix() | |
end | |
end | |
--[[ | |
function IsDotVisible(pos,camPos,camLook,radius) | |
--local d=pos:dist(camPos) | |
if camLook:dot((pos-camPos):normalize())>cosFOV then return true,1 end | |
local dr=radius*vec3(-camLook.z,0,camLook.x) | |
local p1=pos+dr | |
--d=p1:dist(camPos) | |
if camLook:dot((p1-camPos):normalize())>cosFOV then return true,2 end | |
local p2=pos-dr | |
--d=p2:dist(camPos) | |
if camLook:dot((p2-camPos):normalize())>cosFOV then return true,3 else return false end | |
end | |
function IsVisible(pos,camPos,camLook,radius) | |
local xDist=DistancePointToLine(pos,camPos,camLook) | |
if xDist-radius<camPos:dist(pos)/pm1 then return true else return false end | |
end | |
--]] | |
--[[ | |
displayMode(NORMAL) | |
function setup() | |
n=1000000 | |
p1,p2,p3=vec3(-100,0,-100),vec3(50,0,200),vec3(70,0,-400) | |
t=os.time() | |
for i=1,n do | |
a=DistancePointToLine(p1,p2,p3) | |
end | |
print(os.time()-t,n/(os.time()-t)/60) | |
end | |
function setup() | |
n=10000000 | |
FOV=math.rad(45) | |
dd=1000 | |
T=vec3(-100,0,-100) | |
A=vec3(50,0,200) | |
tree=vec3(0,0,100) | |
TA=(T-A):normalize() | |
diam=20 | |
t=os.time() | |
f=FOV/WIDTH/projectionMatrix()[1]/2 | |
for i=1,n do | |
if T:dist(tree)<1000 then | |
local dot=TA:dot((tree-T):normalize()) | |
if dot>0 then b=dot>math.cos(T:dist(A)*diam*f) end | |
end | |
end | |
print(os.time()-t,n/(os.time()-t)/60) | |
end | |
function draw() | |
end | |
function setup() | |
tileSize=50 | |
radius=10 | |
pos={vec2(30,30),vec2(80,30),vec2(45,30),vec2(45,45)} | |
local L={} | |
for p=1,#pos do | |
local a={} | |
for i=-1,1 do | |
for j=-1,1 do | |
local x,y=(pos[p].x+i*radius)//tileSize+1,(pos[p].y+j*radius)//tileSize+1 | |
a[x]=a[x] or {} | |
a[x][y]=p | |
end | |
end | |
for x,ax in pairs(a) do | |
for y,ay in pairs(ax) do | |
--print(x,y,ay) | |
L[x]=L[x] or {} | |
L[x][y]=L[x][y] or {} | |
table.insert(L[x][y],ay) | |
end | |
end | |
end | |
for x,ax in pairs(L) do | |
for y,ay in pairs(ax) do | |
for z,az in pairs(ay) do | |
print(x,y,az) | |
end | |
end | |
end | |
end | |
function GetTile(x,y) | |
return vec2(x//tileSize+1,p.y//tileSize+1) | |
end | |
--]] | |
--# Versions | |
--[[VERSION HISTORY | |
ver 101c (Sun Oct 25 17:18:22 2015) - Full controls working ver2 | |
ver 102 (Mon Oct 26 07:37:20 2015) - Aiming v1 | |
ver 102b (Mon Oct 26 08:15:19 2015) - Aiming v3 | |
ver 102c (Mon Oct 26 08:35:48 2015) - Aiming blue/red v4 | |
ver 103 (Mon Oct 26 14:23:31 2015) - Shooting seems to work v1 | |
ver 103a (Mon Oct 26 21:47:14 2015) - Shooting with angles v2 | |
ver 104 (Tue Oct 27 14:55:57 2015) - Shooting damage | |
ver 104a (Tue Oct 27 20:08:11 2015) - Shooting damage v2 | |
ver 104b (Sat Oct 31 14:39:31 2015) - Shooting damage v3 | |
ver 105 (Thu Nov 5 13:40:13 2015) - Collision | |
ver 106 (Mon Nov 16 23:00:11 2015) - culling 2 | |
--]] | |
--# Visibility | |
--visibility | |
local dx,dz,f1x,f1z,f2x,f2z,PM1,far | |
function PrepareVisibilityTests(camPos,camDir,f) | |
far=f or 2000 | |
PM1=1/projectionMatrix()[1] | |
dx,dz=camPos.x+camDir.x*far,camPos.z+camDir.z*far | |
local wx,wz=-far*PM1*camDir.z,far*PM1*camDir.x | |
f1x,f1z=camPos.x+camDir.x*far+wx,camPos.z+camDir.z*far+wz --far left of frustum | |
f2x,f2z=camPos.x+camDir.x*far-wx,camPos.z+camDir.z*far-wz --far right of frustum | |
end | |
function IsVisible(pos,camPos,camDir,radius) | |
local px,pz,pcx,pcz,pdx,pdz=pos.x,pos.z,camPos.x,camPos.z,camDir.x,camDir.z | |
--get tangent point | |
local u=radius*2.6 | |
local tx,tz=px+pdx*u,pz+pdz*u --tangent point | |
--which side of LOS is our circle centre | |
local IsOnLeft=(px-pcx)*(dz-pcz)-(pz-pcz)*(dx-pcx)<0 | |
local wx,wz=-far*PM1*pdz,far*PM1*pdx | |
if IsOnLeft then return (tx-pcx)*(f1z-pcz)-(tz-pcz)*(f1x-pcx)>0 | |
else return (tx-pcx)*(f2z-pcz)-(tz-pcz)*(f2x-pcx)<0 end | |
end | |
function GetTangents(centre,radius,camPos) | |
local cx,cy,cpx,cpy=centre.x,centre.y,camPos.x,camPos.y | |
--calculate offset from centre | |
local ax,ay=cx-cpx,cy-cpy | |
local d=radius*(ax*ax+ay*ay)^-0.5 | |
local dx,dy=ax*d,ay*d | |
return vec2(cx-dy,cy+dx),vec2(cx+dy,cy-dx) | |
end | |
--given a 2D table "tiles" with a cell for each tile on the ground, and each cell containing a list of objects | |
--in that cell,find all cells which are in line of sight between points A and T | |
--s is cellsize (square) | |
local floor,abs=math.floor,math.abs | |
--local testing=true | |
function GetTiles(T,A,s,tiles) | |
local x0,y0,x1,y1=T.x/s,-T.z/s,A.x/s,-A.z/s --tile positions of the two points | |
if testing then print("==================") print(T) print(x0,y0) print(A) print(x1,y1) end | |
local dx = abs(x1-x0) | |
local dy = abs(y1-y0) | |
local huge=99999 | |
local tt={} | |
local x = floor(x0) | |
local y = floor(y0) | |
local n = 1 | |
local x_inc, y_inc, error | |
if dx == 0 then | |
x_inc = 0 | |
error = huge | |
elseif x1 > x0 then | |
x_inc = 1 | |
n = n + floor(x1) - x | |
error = (floor(x0) + 1 - x0) * dy | |
else | |
x_inc = -1 | |
n = n + x - floor(x1) | |
error = (x0 - floor(x0)) * dy | |
end | |
if dy == 0 then | |
y_inc = 0 | |
error =error - huge | |
elseif (y1 > y0) then | |
y_inc = 1 | |
n = n + floor(y1) - y | |
error = error - (floor(y0) + 1 - y0) * dx | |
else | |
y_inc = -1; | |
n = n + y - floor(y1) | |
error = error - (y0 - floor(y0)) * dx | |
end | |
for i = n,1,-1 do | |
--if testing then print("t=",x,y) end | |
if tiles[x] and tiles[x][y] then | |
tt[#tt+1]=tiles[x][y] | |
--if testing then print("a=",x,y,tiles[x][y][1]) end | |
end | |
if error > 0 then | |
y = y + y_inc | |
error = error - dx | |
else | |
x = x + x_inc | |
error = error + dy | |
end | |
end | |
testing=nil | |
return tt | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment