
-- edited by Gnilk/Noice - thanks for the help :D // hyperunknown

local bouncingball={}
local sphere={}
local rotation={x=0,y=0,z=0}
local rotation_speed={x=2.5,y=1.9,z=1.6}
local SCREEN_W,SCREEN_H=1920,865
local radius=150
local segments=24
local scale=1.4
local distance=500
local ballx,bally=1280,720
local ballspeedx,ballspeedy=0,3
local ballradius=150
local boundarymargin=1
local ballvisible=false
local ballspawned=false
local balldisappeartime=24.6
local floory=SCREEN_H
local group_size_theta=math.pi/6
local group_size_phi=math.pi/6
local ballbounce_time=0
local function effective_radius()
return ballradius*scale
end
local function createsphere()
sphere.points={}
sphere.faces={}
for i=0,segments do
local theta=i*math.pi/segments
for j=0,segments*2 do
local phi=j*2*math.pi/(segments*2)
local x=radius*math.sin(theta)*math.cos(phi)
local y=radius*math.sin(theta)*math.sin(phi)
local z=radius*math.cos(theta)
table.insert(sphere.points,{x=x,y=y,z=z})
end end
local points_per_ring=segments*2+1
for i=1,segments do
for j=1,segments*2 do
local current=(i-1)*points_per_ring+j
local next=current+points_per_ring
local face1={a=current,b=current+1,c=next}
local face2={a=current+1,b=next+1,c=next}
table.insert(sphere.faces,face1)
table.insert(sphere.faces,face2)
end end end
function bouncingball.load()
createsphere()
ballspeedx=400
ballspeedy=math.pi/0.932
end
local function rotatex(point,angle)
local cos=math.cos(angle)
local sin=math.sin(angle)
local y=point.y*cos-point.z*sin
local z=point.y*sin+point.z*cos
return {x=point.x,y=y,z=z}
end
local function rotatey(point,angle)
local cos=math.cos(angle)
local sin=math.sin(angle)
local x=point.x*cos+point.z*sin
local z=-point.x*sin+point.z*cos
return {x=x,y=point.y,z=z}
end
local function rotatez(point,angle)
local cos=math.cos(angle)
local sin=math.sin(angle)
local x=point.x*cos-point.y*sin
local y=point.x*sin+point.y*cos
return {x=x,y=y,z=point.z}
end
local function project(point)
local factor=scale*distance/(distance+point.z)
return {x=point.x*factor+ballx,y=point.y*factor+bally,z=point.z}
end
local function cartesiantospherical(x,y,z)
local r=math.sqrt(x^2+y^2+z^2)
local theta=math.acos(z/r)
local phi=math.atan2(y,x)
if phi<0 then
phi=phi+2*math.pi
end
return theta,phi
end
local function normalize(v)
local one_over_l=1/math.sqrt(v.x*v.x+v.y*v.y+v.z*v.z)
return {x=one_over_l*v.x,y=one_over_l*v.y,z=one_over_l*v.z,}
end
local function vcross(va,vb)
return {x=va.y*vb.z-va.z*vb.y,y=va.z*vb.x-va.x*vb.z,z=va.x*vb.y-va.y*vb.x,}
end
local function facenormal(transformed,face)
local va={x=transformed[face.a].x-transformed[face.b].x,y=transformed[face.a].y-transformed[face.b].y,z=transformed[face.a].z-transformed[face.b].z}
local vb={x=transformed[face.c].x-transformed[face.b].x,y=transformed[face.c].y-transformed[face.b].y,z=transformed[face.c].z-transformed[face.b].z}
return vcross(va,vb)
end
function bouncingball.update(dt,timepassed)
if not ballspawned and timepassed>=9.6 then
ballvisible=true
ballspawned=true
local r=effective_radius()
ballx=r+boundarymargin
ballbounce_time=0
ballspeedx=math.abs(ballspeedx)
end
if ballvisible and timepassed>=balldisappeartime then
ballvisible=false
end
if ballvisible then
rotation.x=rotation.x+rotation_speed.x*dt
rotation.y=rotation.y+rotation_speed.y*dt
rotation.z=rotation.z+rotation_speed.z*dt
local r=effective_radius()
local bounce_y=(1.0-math.abs(math.cos(ballbounce_time*ballspeedy)))
bally=r+(floory-2*r)*bounce_y
ballbounce_time=ballbounce_time+dt
ballx=ballx+ballspeedx*dt
if ballx-r<boundarymargin then
ballspeedx=math.abs(ballspeedx)
ballx=r+boundarymargin
elseif ballx+r>(SCREEN_W-boundarymargin) then
ballspeedx=-math.abs(ballspeedx)
ballx=SCREEN_W-r-boundarymargin
end end end
function bouncingball.draw()
if not ballvisible then
return
end
local transformed={}
local transformed_z={}
for i,point in ipairs(sphere.points) do
local rotated=rotatex(point,rotation.x)
rotated=rotatey(rotated,rotation.y)
rotated=rotatez(rotated,rotation.z)
transformed[i]=project(rotated)
transformed_z[i]=rotated.z
end
local sorted_faces={}
for i,face in ipairs(sphere.faces) do
local a_z=transformed_z[face.a]
local b_z=transformed_z[face.b]
local c_z=transformed_z[face.c]
local avg_z=(a_z+b_z+c_z)/3
table.insert(sorted_faces,{face=face,avg_z=avg_z})
end
for _,sorted_face in ipairs(sorted_faces) do
local face=sorted_face.face
local a=sphere.points[face.a]
local b=sphere.points[face.b]
local c=sphere.points[face.c]
local avg_x=(a.x+b.x+c.x)/3
local avg_y=(a.y+b.y+c.y)/3
local avg_z=(a.z+b.z+c.z)/3
local theta,phi=cartesiantospherical(avg_x,avg_y,avg_z)
local grouped_theta=math.floor(theta/group_size_theta)
local grouped_phi=math.floor(phi/group_size_phi)
if (grouped_theta+grouped_phi)%2==0 then
love.graphics.setColor(1,0,0)
else
love.graphics.setColor(1,1,1)
end
local a_2d=transformed[face.a]
local b_2d=transformed[face.b]
local c_2d=transformed[face.c]
local fn=facenormal(transformed,face)
if fn.z<0 then
love.graphics.polygon("fill",a_2d.x,a_2d.y,b_2d.x,b_2d.y,c_2d.x,c_2d.y)
end end
love.graphics.setColor(1,1,1)
end
return bouncingball
