mirror of
https://github.com/tanema/light_world.lua.git
synced 2024-12-24 20:24:19 +00:00
530 lines
13 KiB
Lua
530 lines
13 KiB
Lua
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
|
|
local class = require(_PACKAGE.."/class")
|
|
|
|
local body = class(function(o, world, id, type, ...)
|
|
local args = {...}
|
|
o.id = id
|
|
o.type = type
|
|
o.normal = nil
|
|
o.material = nil
|
|
o.glow = nil
|
|
o.world = world
|
|
if o.type == "circle" then
|
|
o.x = args[1] or 0
|
|
o.y = args[2] or 0
|
|
o.radius = args[3] or 16
|
|
o.ox = args[4] or 0
|
|
o.oy = args[5] or 0
|
|
o.shadowType = "circle"
|
|
o.reflection = false
|
|
o.reflective = false
|
|
o.refraction = false
|
|
o.refractive = false
|
|
world.isShadows = true
|
|
elseif o.type == "rectangle" then
|
|
o.x = args[1] or 0
|
|
o.y = args[2] or 0
|
|
o.width = args[3] or 64
|
|
o.height = args[4] or 64
|
|
o.ox = o.width * 0.5
|
|
o.oy = o.height * 0.5
|
|
o.shadowType = "rectangle"
|
|
o.data = {
|
|
o.x - o.ox,
|
|
o.y - o.oy,
|
|
o.x - o.ox + o.width,
|
|
o.y - o.oy,
|
|
o.x - o.ox + o.width,
|
|
o.y - o.oy + o.height,
|
|
o.x - o.ox,
|
|
o.y - o.oy + o.height
|
|
}
|
|
o.reflection = false
|
|
o.reflective = false
|
|
o.refraction = false
|
|
o.refractive = false
|
|
world.isShadows = true
|
|
elseif o.type == "polygon" then
|
|
o.shadowType = "polygon"
|
|
o.data = args or {0, 0, 0, 0, 0, 0}
|
|
o.reflection = false
|
|
o.reflective = false
|
|
o.refraction = false
|
|
o.refractive = false
|
|
world.isShadows = true
|
|
elseif o.type == "image" then
|
|
o.img = args[1]
|
|
o.x = args[2] or 0
|
|
o.y = args[3] or 0
|
|
if o.img then
|
|
o.imgWidth = o.img:getWidth()
|
|
o.imgHeight = o.img:getHeight()
|
|
o.width = args[4] or o.imgWidth
|
|
o.height = args[5] or o.imgHeight
|
|
o.ix = o.imgWidth * 0.5
|
|
o.iy = o.imgHeight * 0.5
|
|
o.vert = {
|
|
{ 0.0, 0.0, 0.0, 0.0 },
|
|
{ o.width, 0.0, 1.0, 0.0 },
|
|
{ o.width, o.height, 1.0, 1.0 },
|
|
{ 0.0, o.height, 0.0, 1.0 },
|
|
}
|
|
o.msh = love.graphics.newMesh(o.vert, o.img, "fan")
|
|
else
|
|
o.width = args[4] or 64
|
|
o.height = args[5] or 64
|
|
end
|
|
o.ox = args[6] or o.width * 0.5
|
|
o.oy = args[7] or o.height * 0.5
|
|
o.shadowType = "rectangle"
|
|
o.data = {
|
|
o.x - o.ox,
|
|
o.y - o.oy,
|
|
o.x - o.ox + o.width,
|
|
o.y - o.oy,
|
|
o.x - o.ox + o.width,
|
|
o.y - o.oy + o.height,
|
|
o.x - o.ox,
|
|
o.y - o.oy + o.height
|
|
}
|
|
o.reflection = false
|
|
o.reflective = true
|
|
o.refraction = false
|
|
o.refractive = false
|
|
world.isShadows = true
|
|
elseif o.type == "refraction" then
|
|
o.normal = args[1]
|
|
o.x = args[2] or 0
|
|
o.y = args[3] or 0
|
|
if o.normal then
|
|
o.normalWidth = o.normal:getWidth()
|
|
o.normalHeight = o.normal:getHeight()
|
|
o.width = args[4] or o.normalWidth
|
|
o.height = args[5] or o.normalHeight
|
|
o.nx = o.normalWidth * 0.5
|
|
o.ny = o.normalHeight * 0.5
|
|
o.normal:setWrap("repeat", "repeat")
|
|
o.normalVert = {
|
|
{0.0, 0.0, 0.0, 0.0},
|
|
{o.width, 0.0, 1.0, 0.0},
|
|
{o.width, o.height, 1.0, 1.0},
|
|
{0.0, o.height, 0.0, 1.0}
|
|
}
|
|
o.normalMesh = love.graphics.newMesh(o.normalVert, o.normal, "fan")
|
|
else
|
|
o.width = args[4] or 64
|
|
o.height = args[5] or 64
|
|
end
|
|
o.ox = o.width * 0.5
|
|
o.oy = o.height * 0.5
|
|
o.reflection = false
|
|
o.reflective = false
|
|
o.refraction = true
|
|
o.refractive = false
|
|
world.isRefraction = true
|
|
elseif o.type == "reflection" then
|
|
o.normal = args[1]
|
|
o.x = args[2] or 0
|
|
o.y = args[3] or 0
|
|
if o.normal then
|
|
o.normalWidth = o.normal:getWidth()
|
|
o.normalHeight = o.normal:getHeight()
|
|
o.width = args[4] or o.normalWidth
|
|
o.height = args[5] or o.normalHeight
|
|
o.nx = o.normalWidth * 0.5
|
|
o.ny = o.normalHeight * 0.5
|
|
o.normal:setWrap("repeat", "repeat")
|
|
o.normalVert = {
|
|
{0.0, 0.0, 0.0, 0.0},
|
|
{o.width, 0.0, 1.0, 0.0},
|
|
{o.width, o.height, 1.0, 1.0},
|
|
{0.0, o.height, 0.0, 1.0}
|
|
}
|
|
o.normalMesh = love.graphics.newMesh(o.normalVert, o.normal, "fan")
|
|
else
|
|
o.width = args[4] or 64
|
|
o.height = args[5] or 64
|
|
end
|
|
o.ox = o.width * 0.5
|
|
o.oy = o.height * 0.5
|
|
o.reflection = true
|
|
o.reflective = false
|
|
o.refraction = false
|
|
o.refractive = false
|
|
world.isReflection = true
|
|
end
|
|
o.shine = true
|
|
o.red = 0
|
|
o.green = 0
|
|
o.blue = 0
|
|
o.alpha = 1.0
|
|
o.glowRed = 255
|
|
o.glowGreen = 255
|
|
o.glowBlue = 255
|
|
o.glowStrength = 0.0
|
|
o.tileX = 0
|
|
o.tileY = 0
|
|
end)
|
|
|
|
-- refresh
|
|
function body:refresh()
|
|
if self.data then
|
|
self.data[1] = self.x - self.ox
|
|
self.data[2] = self.y - self.oy
|
|
self.data[3] = self.x - self.ox + self.width
|
|
self.data[4] = self.y - self.oy
|
|
self.data[5] = self.x - self.ox + self.width
|
|
self.data[6] = self.y - self.oy + self.height
|
|
self.data[7] = self.x - self.ox
|
|
self.data[8] = self.y - self.oy + self.height
|
|
end
|
|
end
|
|
-- set position
|
|
function body:setPosition(x, y)
|
|
if x ~= self.x or y ~= self.y then
|
|
self.x = x
|
|
self.y = y
|
|
self:refresh()
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- set x position
|
|
function body:setX(x)
|
|
if x ~= self.x then
|
|
self.x = x
|
|
self:refresh()
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- set y position
|
|
function body:setY(y)
|
|
if y ~= self.y then
|
|
self.y = y
|
|
self:refresh()
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- get x position
|
|
function body:getX()
|
|
return self.x
|
|
end
|
|
-- get y position
|
|
function body:getY(y)
|
|
return self.y
|
|
end
|
|
-- get width
|
|
function body:getWidth()
|
|
return self.width
|
|
end
|
|
-- get height
|
|
function body:getHeight()
|
|
return self.height
|
|
end
|
|
-- get image width
|
|
function body:getImageWidth()
|
|
return self.imgWidth
|
|
end
|
|
-- get image height
|
|
function body:getImageHeight()
|
|
return self.imgHeight
|
|
end
|
|
-- set dimension
|
|
function body:setDimension(width, height)
|
|
self.width = width
|
|
self.height = height
|
|
self:refresh()
|
|
self.world.changed = true
|
|
end
|
|
-- set offset
|
|
function body:setOffset(ox, oy)
|
|
if ox ~= self.ox or oy ~= self.oy then
|
|
self.ox = ox
|
|
self.oy = oy
|
|
if self.shadowType == "rectangle" then
|
|
self:refresh()
|
|
end
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- set offset
|
|
function body:setImageOffset(ix, iy)
|
|
if ix ~= self.ix or iy ~= self.iy then
|
|
self.ix = ix
|
|
self.iy = iy
|
|
self:refresh()
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- set offset
|
|
function body:setNormalOffset(nx, ny)
|
|
if nx ~= self.nx or ny ~= self.ny then
|
|
self.nx = nx
|
|
self.ny = ny
|
|
self:refresh()
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- set glow color
|
|
function body:setGlowColor(red, green, blue)
|
|
self.glowRed = red
|
|
self.glowGreen = green
|
|
self.glowBlue = blue
|
|
self.world.changed = true
|
|
end
|
|
-- set glow alpha
|
|
function body:setGlowStrength(strength)
|
|
self.glowStrength = strength
|
|
self.world.changed = true
|
|
end
|
|
-- get radius
|
|
function body:getRadius()
|
|
return self.radius
|
|
end
|
|
-- set radius
|
|
function body:setRadius(radius)
|
|
if radius ~= self.radius then
|
|
self.radius = radius
|
|
self.world.changed = true
|
|
end
|
|
end
|
|
-- set polygon data
|
|
function body:setPoints(...)
|
|
self.data = {...}
|
|
self.world.changed = true
|
|
end
|
|
-- get polygon data
|
|
function body:getPoints()
|
|
return unpack(self.data)
|
|
end
|
|
-- set shadow on/off
|
|
function body:setShadowType(type)
|
|
self.shadowType = type
|
|
self.world.changed = true
|
|
end
|
|
-- set shadow on/off
|
|
function body:setShadow(b)
|
|
self.castsNoShadow = not b
|
|
self.world.changed = true
|
|
end
|
|
-- set shine on/off
|
|
function body:setShine(b)
|
|
self.shine = b
|
|
self.world.changed = true
|
|
end
|
|
-- set glass color
|
|
function body:setColor(red, green, blue)
|
|
self.red = red
|
|
self.green = green
|
|
self.blue = blue
|
|
self.world.changed = true
|
|
end
|
|
-- set glass alpha
|
|
function body:setAlpha(alpha)
|
|
self.alpha = alpha
|
|
self.world.changed = true
|
|
end
|
|
-- set reflection on/off
|
|
function body:setReflection(reflection)
|
|
self.reflection = reflection
|
|
end
|
|
-- set refraction on/off
|
|
function body:setRefraction(refraction)
|
|
self.refraction = refraction
|
|
end
|
|
-- set reflective on other objects on/off
|
|
function body:setReflective(reflective)
|
|
self.reflective = reflective
|
|
end
|
|
-- set refractive on other objects on/off
|
|
function body:setRefractive(refractive)
|
|
self.refractive = refractive
|
|
end
|
|
-- set image
|
|
function body:setImage(img)
|
|
if img then
|
|
self.img = img
|
|
self.imgWidth = self.img:getWidth()
|
|
self.imgHeight = self.img:getHeight()
|
|
self.ix = self.imgWidth * 0.5
|
|
self.iy = self.imgHeight * 0.5
|
|
end
|
|
end
|
|
-- set normal
|
|
function body:setNormalMap(normal, width, height, nx, ny)
|
|
if normal then
|
|
self.normal = normal
|
|
self.normal:setWrap("repeat", "repeat")
|
|
self.normalWidth = width or self.normal:getWidth()
|
|
self.normalHeight = height or self.normal:getHeight()
|
|
self.nx = nx or self.normalWidth * 0.5
|
|
self.ny = ny or self.normalHeight * 0.5
|
|
self.normalVert = {
|
|
{0.0, 0.0, 0.0, 0.0},
|
|
{self.normalWidth, 0.0, self.normalWidth / self.normal:getWidth(), 0.0},
|
|
{self.normalWidth, self.normalHeight, self.normalWidth / self.normal:getWidth(), self.normalHeight / self.normal:getHeight()},
|
|
{0.0, self.normalHeight, 0.0, self.normalHeight / self.normal:getHeight()}
|
|
}
|
|
self.normalMesh = love.graphics.newMesh(self.normalVert, self.normal, "fan")
|
|
|
|
self.world.isPixelShadows = true
|
|
else
|
|
self.normalMesh = nil
|
|
end
|
|
end
|
|
-- set height map
|
|
function body:setHeightMap(heightMap, strength)
|
|
self:setNormalMap(HeightMapToNormalMap(heightMap, strength))
|
|
end
|
|
-- generate flat normal map
|
|
function body:generateNormalMapFlat(mode)
|
|
local imgData = self.img:getData()
|
|
local imgNormalData = love.image.newImageData(self.imgWidth, self.imgHeight)
|
|
local color
|
|
|
|
if mode == "top" then
|
|
color = {127, 127, 255}
|
|
elseif mode == "front" then
|
|
color = {127, 0, 127}
|
|
elseif mode == "back" then
|
|
color = {127, 255, 127}
|
|
elseif mode == "left" then
|
|
color = {31, 0, 223}
|
|
elseif mode == "right" then
|
|
color = {223, 0, 127}
|
|
end
|
|
|
|
for i = 0, self.imgHeight - 1 do
|
|
for k = 0, self.imgWidth - 1 do
|
|
local r, g, b, a = imgData:getPixel(k, i)
|
|
if a > 0 then
|
|
imgNormalData:setPixel(k, i, color[1], color[2], color[3], 255)
|
|
end
|
|
end
|
|
end
|
|
|
|
self:setNormalMap(love.graphics.newImage(imgNormalData))
|
|
end
|
|
-- generate faded normal map
|
|
function body:generateNormalMapGradient(horizontalGradient, verticalGradient)
|
|
local imgData = self.img:getData()
|
|
local imgNormalData = love.image.newImageData(self.imgWidth, self.imgHeight)
|
|
local dx = 255.0 / self.imgWidth
|
|
local dy = 255.0 / self.imgHeight
|
|
local nx
|
|
local ny
|
|
local nz
|
|
|
|
for i = 0, self.imgWidth - 1 do
|
|
for k = 0, self.imgHeight - 1 do
|
|
local r, g, b, a = imgData:getPixel(i, k)
|
|
if a > 0 then
|
|
if horizontalGradient == "gradient" then
|
|
nx = i * dx
|
|
elseif horizontalGradient == "inverse" then
|
|
nx = 255 - i * dx
|
|
else
|
|
nx = 127
|
|
end
|
|
|
|
if verticalGradient == "gradient" then
|
|
ny = 127 - k * dy * 0.5
|
|
nz = 255 - k * dy * 0.5
|
|
elseif verticalGradient == "inverse" then
|
|
ny = 127 + k * dy * 0.5
|
|
nz = 127 - k * dy * 0.25
|
|
else
|
|
ny = 255
|
|
nz = 127
|
|
end
|
|
|
|
imgNormalData:setPixel(i, k, nx, ny, nz, 255)
|
|
end
|
|
end
|
|
end
|
|
|
|
self:setNormalMap(love.graphics.newImage(imgNormalData))
|
|
end
|
|
-- generate normal map
|
|
function body:generateNormalMap(strength)
|
|
self:setNormalMap(HeightMapToNormalMap(self.img, strength))
|
|
end
|
|
-- set material
|
|
function body:setMaterial(material)
|
|
if material then
|
|
self.material = material
|
|
end
|
|
end
|
|
-- set normal
|
|
function body:setGlowMap(glow)
|
|
self.glow = glow
|
|
self.glowStrength = 1.0
|
|
|
|
self.world.isGlow = true
|
|
end
|
|
-- set tile offset
|
|
function body:setNormalTileOffset(tx, ty)
|
|
self.tileX = tx / self.normalWidth
|
|
self.tileY = ty / self.normalHeight
|
|
self.normalVert = {
|
|
{0.0, 0.0, self.tileX, self.tileY},
|
|
{self.normalWidth, 0.0, self.tileX + 1.0, self.tileY},
|
|
{self.normalWidth, self.normalHeight, self.tileX + 1.0, self.tileY + 1.0},
|
|
{0.0, self.normalHeight, self.tileX, self.tileY + 1.0}
|
|
}
|
|
self.world.changed = true
|
|
end
|
|
-- get type
|
|
function body:getType()
|
|
return self.type
|
|
end
|
|
-- get type
|
|
function body:setShadowType(type, ...)
|
|
self.shadowType = type
|
|
local args = {...}
|
|
if self.shadowType == "circle" then
|
|
self.radius = args[1] or 16
|
|
self.ox = args[2] or 0
|
|
self.oy = args[3] or 0
|
|
elseif self.shadowType == "rectangle" then
|
|
self.width = args[1] or 64
|
|
self.height = args[2] or 64
|
|
self.ox = args[3] or self.width * 0.5
|
|
self.oy = args[4] or self.height * 0.5
|
|
self.data = {
|
|
self.x - self.ox,
|
|
self.y - self.oy,
|
|
self.x - self.ox + self.width,
|
|
self.y - self.oy,
|
|
self.x - self.ox + self.width,
|
|
self.y - self.oy + self.height,
|
|
self.x - self.ox,
|
|
self.y - self.oy + self.height
|
|
}
|
|
elseif self.shadowType == "polygon" then
|
|
self.data = args or {0, 0, 0, 0, 0, 0}
|
|
elseif self.shadowType == "image" then
|
|
if self.img then
|
|
self.width = self.imgWidth
|
|
self.height = self.imgHeight
|
|
self.shadowVert = {
|
|
{0.0, 0.0, 0.0, 0.0},
|
|
{self.width, 0.0, 1.0, 0.0},
|
|
{self.width, self.height, 1.0, 1.0},
|
|
{0.0, self.height, 0.0, 1.0}
|
|
}
|
|
if not self.shadowMesh then
|
|
self.shadowMesh = love.graphics.newMesh(self.shadowVert, self.img, "fan")
|
|
self.shadowMesh:setVertexColors(true)
|
|
end
|
|
else
|
|
self.width = 64
|
|
self.height = 64
|
|
end
|
|
self.shadowX = args[1] or 0
|
|
self.shadowY = args[2] or 0
|
|
self.fadeStrength = args[3] or 0.0
|
|
end
|
|
end
|
|
|
|
return body
|