MineOS/lib/MeowEngine/Main.lua

520 lines
20 KiB
Lua
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-------------------------------------------------------- Libraries --------------------------------------------------------
local buffer = require("doubleBuffering")
local vector = require("vector")
local OCGL = require("OpenComputersGL/Main")
local renderer = require("OpenComputersGL/Renderer")
local materials = require("OpenComputersGL/Materials")
local meowEngine = {}
-------------------------------------------------------- Universal object methods --------------------------------------------------------
function meowEngine.newPivotPoint(vector3Position)
return {
position = vector3Position,
axis = {
vector.newVector3(1, 0, 0),
vector.newVector3(0, 1, 0),
vector.newVector3(0, 0, 1),
}
}
end
-------------------------------------------------------- Light object --------------------------------------------------------
function meowEngine.newLight(vector3Position, intensity, emissionDistance)
return {
position = vector3Position,
emissionDistance = emissionDistance,
intensity = intensity
}
end
-------------------------------------------------------- Mesh object --------------------------------------------------------
local function pushMeshToRenderQueue(mesh)
local vector3Vertex1, vector3Vertex2, vector3Vertex3
for triangleIndex = 1, #mesh.triangles do
vector3Vertex1, vector3Vertex2, vector3Vertex3 = mesh.vertices[mesh.triangles[triangleIndex][1]], mesh.vertices[mesh.triangles[triangleIndex][2]], mesh.vertices[mesh.triangles[triangleIndex][3]]
OCGL.pushTriangleToRenderQueue(
vector.newVector5(vector3Vertex1[1], vector3Vertex1[2], vector3Vertex1[3], vector3Vertex1[4], vector3Vertex1[5]),
vector.newVector5(vector3Vertex2[1], vector3Vertex2[2], vector3Vertex2[3], vector3Vertex2[4], vector3Vertex2[5]),
vector.newVector5(vector3Vertex3[1], vector3Vertex3[2], vector3Vertex3[3], vector3Vertex3[4], vector3Vertex3[5]),
mesh.triangles[triangleIndex][4] or mesh.material
)
end
end
function meowEngine.newMesh(vector3Position, vertices, triangles, material)
local mesh = {}
mesh.vertices = vertices
mesh.position = vector3Position
for vertexIndex = 1, #mesh.vertices do
mesh.vertices[vertexIndex][1], mesh.vertices[vertexIndex][2], mesh.vertices[vertexIndex][3] = mesh.vertices[vertexIndex][1] + vector3Position[1], mesh.vertices[vertexIndex][2] + vector3Position[2], mesh.vertices[vertexIndex][3] + vector3Position[3]
end
mesh.triangles = triangles
mesh.material = material
mesh.pushToRenderQueue = pushMeshToRenderQueue
return mesh
end
-------------------------------------------------------- Line object --------------------------------------------------------
local function pushLineToRenderQueue(line)
OCGL.pushLineToRenderQueue(
vector.newVector3(line.vertices[1][1], line.vertices[1][2], line.vertices[1][3]),
vector.newVector3(line.vertices[2][1], line.vertices[2][2], line.vertices[2][3]),
line.color
)
end
function meowEngine.newLine(vector3Position, vector3Vertex1, vector3Vertex2, color)
return {
vertices = { vector3Vertex1, vector3Vertex2 },
color = color,
pushToRenderQueue = pushLineToRenderQueue
}
end
-------------------------------------------------------- Floating text object --------------------------------------------------------
local function pushFloatingTextToRenderQueue(floatingText)
OCGL.pushFloatingTextToRenderQueue(
vector.newVector3(floatingText.position[1], floatingText.position[2], floatingText.position[3]),
floatingText.text,
floatingText.color
)
end
function meowEngine.newFloatingText(vector3Position, color, text)
return {
position = vector3Position,
color = color,
text = text,
pushToRenderQueue = pushFloatingTextToRenderQueue
}
end
-------------------------------------------------------- Plane object --------------------------------------------------------
function meowEngine.newPlane(vector3Position, width, height, segmentsWidth, segmentsHeight, material)
local vertices, triangles, widthCellSize, heightCellSize, vertexIndex = {}, {}, width / segmentsWidth, height / segmentsHeight, 1
segmentsWidth, segmentsHeight = segmentsWidth + 1, segmentsHeight + 1
for zSegment = 1, segmentsHeight do
for xSegment = 1, segmentsWidth do
table.insert(vertices, vector.newVector3(xSegment * widthCellSize - widthCellSize, 0, zSegment * heightCellSize - heightCellSize))
if xSegment < segmentsWidth and zSegment < segmentsHeight then
table.insert(triangles,
OCGL.newIndexedTriangle(
vertexIndex,
vertexIndex + 1,
vertexIndex + segmentsWidth
)
)
table.insert(triangles,
OCGL.newIndexedTriangle(
vertexIndex + 1,
vertexIndex + segmentsWidth + 1,
vertexIndex + segmentsWidth
)
)
end
vertexIndex = vertexIndex + 1
end
end
return meowEngine.newMesh(vector3Position, vertices, triangles, material)
end
-------------------------------------------------------- Textured plane object --------------------------------------------------------
function meowEngine.newTexturedPlane(vector3Position, width, height, texture)
width, height = width / 2, height / 2
return meowEngine.newMesh(
vector3Position,
{
vector.newVector5(-width, 0, -height, 1, texture.height),
vector.newVector5(-width, 0, height, 1, 1),
vector.newVector5(width, 0, height, texture.width, 1),
vector.newVector5(width, 0, -height, texture.width, texture.height),
},
{
OCGL.newIndexedTriangle(1, 2, 3),
OCGL.newIndexedTriangle(1, 4, 3)
},
materials.newTexturedMaterial(texture)
)
end
-------------------------------------------------------- Cube object --------------------------------------------------------
--[[
| /
| /
y z
x -----
FRONT LEFT BACK RIGHT TOP BOTTOM
2######3 3######6 6######7 7######2 7######6 8######5
######## ######## ######## ######## ######## ########
1######4 4######5 5######8 8######1 2######3 1######4
]]
function meowEngine.newCube(vector3Position, size, material)
local halfSize = size / 2
return meowEngine.newMesh(
vector3Position,
{
-- (1-2-3-4)
vector.newVector3(-halfSize, -halfSize, -halfSize),
vector.newVector3(-halfSize, halfSize, -halfSize),
vector.newVector3(halfSize, halfSize, -halfSize),
vector.newVector3(halfSize, -halfSize, -halfSize),
-- (5-6-7-8)
vector.newVector3(halfSize, -halfSize, halfSize),
vector.newVector3(halfSize, halfSize, halfSize),
vector.newVector3(-halfSize, halfSize, halfSize),
vector.newVector3(-halfSize, -halfSize, halfSize),
},
{
-- Front
OCGL.newIndexedTriangle(1, 2, 3),
OCGL.newIndexedTriangle(1, 4, 3),
-- Left
OCGL.newIndexedTriangle(4, 3, 6),
OCGL.newIndexedTriangle(4, 5, 6),
-- Back
OCGL.newIndexedTriangle(5, 6, 7),
OCGL.newIndexedTriangle(5, 8, 7),
-- Right
OCGL.newIndexedTriangle(8, 7, 2),
OCGL.newIndexedTriangle(8, 1, 2),
-- Top
OCGL.newIndexedTriangle(2, 7, 6),
OCGL.newIndexedTriangle(2, 3, 6),
-- Bottom
OCGL.newIndexedTriangle(1, 8, 5),
OCGL.newIndexedTriangle(1, 4, 5),
},
material
)
end
-------------------------------------------------------- Camera object --------------------------------------------------------
local function cameraSetRotation(camera, axisXRotation, axisYRotation, axisZRotation)
camera.rotation[1], camera.rotation[2], camera.rotation[3] = axisXRotation, axisYRotation, axisZRotation
return camera
end
local function cameraRotate(camera, axisXAdditionalRotation, axisYAdditionalRotation, axisZAdditionalRotation)
cameraSetRotation(camera, camera.rotation[1] + axisXAdditionalRotation, camera.rotation[2] + axisYAdditionalRotation, camera.rotation[3] + axisZAdditionalRotation)
return camera
end
local function cameraLookAt(camera, xLook, yLook, zLook)
local dx, dy, dz = xLook - camera.position[1], yLook - camera.position[2], zLook - camera.position[3]
local rad180 = math.rad(180)
local roty = math.atan(dx / dz)
if dz < 0 then roty = roty + rad180 end
local rotx = math.atan(math.sqrt(dx ^ 2 + dz ^ 2) / dy) - math.rad(90)
if dy < 0 then rotx = rotx + rad180 end
cameraSetRotation(camera, rotx, roty, 0)
end
local function cameraSetPosition(camera, x, y, z)
camera.position[1], camera.position[2], camera.position[3] = x, y, z
return camera
end
local function cameraTranslate(camera, xTranslation, yTranslation, zTranslation, xLookingAtTranslation, yLookingAtTranslation, zLookingAtTranslation)
cameraSetPosition(camera, camera.position[1] + xTranslation, camera.position[2] + yTranslation, camera.position[3] + zTranslation)
return camera
end
local function cameraSetFOV(camera, FOV)
if FOV > 0 and FOV < math.pi then
camera.FOV = FOV
camera.projectionSurface = camera.farClippingSurface - camera.FOV / math.rad(180) * (camera.farClippingSurface - camera.nearClippingSurface)
else
error("FOV can't be < 0 or > 180 degrees")
end
return camera
end
function meowEngine.newCamera(vector3Position, FOV, nearClippingSurface, farClippingSurface)
local camera = {}
camera.projectionEnabled = true
camera.position = vector3Position
camera.rotation = {}
camera.nearClippingSurface = nearClippingSurface
camera.farClippingSurface = farClippingSurface
camera.FOV = FOV
camera.setPosition = cameraSetPosition
camera.translate = cameraTranslate
camera.rotate = cameraRotate
camera.setRotation = cameraSetRotation
camera.setFOV = cameraSetFOV
camera.lookAt = cameraLookAt
-- Создаем точку "лука" (и матрицу поворота камеры), а также ее плоскость проекции через ФОВ
cameraSetRotation(camera, 0, 0, 0)
cameraSetFOV(camera, camera.FOV)
return camera
end
-------------------------------------------------------- Scene object --------------------------------------------------------
local function sceneAddObject(scene, object)
table.insert(scene.objects, object)
return object
end
local function sceneAddLight(scene, light)
table.insert(scene.lights, light)
return light
end
local function sceneAddObjects(scene, objects)
for objectIndex = 1, #objects do table.insert(scene.objects, objects[objectIndex]) end
return objects
end
local function sceneRender(scene)
renderer.setViewport( 1, 1, buffer.width, buffer.height * 2, scene.camera.nearClippingSurface, scene.camera.farClippingSurface, scene.camera.projectionSurface)
OCGL.clearBuffer(scene.backgroundColor)
OCGL.renderMode = scene.renderMode
OCGL.auxiliaryMode = scene.auxiliaryMode
for objectIndex = 1, #scene.objects do
scene.objects[objectIndex]:pushToRenderQueue()
end
for lightIndex = 1, #scene.lights do
OCGL.pushLightToRenderQueue(
vector.newVector3(scene.lights[lightIndex].position[1], scene.lights[lightIndex].position[2], scene.lights[lightIndex].position[3]),
scene.lights[lightIndex].intensity,
scene.lights[lightIndex].emissionDistance
)
end
OCGL.translate(-scene.camera.position[1], -scene.camera.position[2], -scene.camera.position[3])
OCGL.rotate(OCGL.rotateVectorRelativeToYAxis, -scene.camera.rotation[2])
OCGL.rotate(OCGL.rotateVectorRelativeToXAxis, -scene.camera.rotation[1])
-- OCGL.rotate(OCGL.rotateVectorRelativeToZAxis, -scene.camera.rotation[3])
if scene.renderMode == OCGL.renderModes.flatShading then
OCGL.calculateLights()
end
if scene.camera.projectionEnabled then
OCGL.createPerspectiveProjection()
end
OCGL.render()
return scene
end
function meowEngine.newScene(backgroundColor)
local scene = {}
scene.renderMode = OCGL.renderModes.constantShading
scene.auxiliaryMode = OCGL.auxiliaryModes.disabled
scene.backgroundColor = backgroundColor
scene.objects = {}
scene.lights = {}
scene.addObject = sceneAddObject
scene.addLight = sceneAddLight
scene.addObjects = sceneAddObjects
scene.render = sceneRender
scene.camera = meowEngine.newCamera(vector.newVector3(0, 0, 0), math.rad(90), 1, 100)
return scene
end
-------------------------------------------------------- Raycasting methods --------------------------------------------------------
local function vectorMultiply(a, b)
return vector.newVector3(
a[2] * b[3] - a[3] * b[2],
a[3] * b[1] - a[1] * b[3],
a[1] * b[2] - a[2] * b[1]
)
end
local function getVectorDistance(a)
return math.sqrt(a[1] ^ 2 + a[2] ^ 2 + a[3] ^ 2)
end
-- В случае попадания лучика этот метод вернет сам треугольник, а также дистанцию до его плоскости
function meowEngine.meshRaycast(mesh, vector3RayStart, vector3RayEnd)
local minimalDistance, closestTriangleIndex
for triangleIndex = 1, #mesh.triangles do
-- Это вершины треугольника
local A, B, C = mesh.vertices[mesh.triangles[triangleIndex][1]], mesh.vertices[mesh.triangles[triangleIndex][2]], mesh.vertices[mesh.triangles[triangleIndex][3]]
-- Это вектор, образованный произведением двух векторов-сторон треугольника, он образует параллелограмм
local ABC = vectorMultiply(
vector.newVector3(C[1] - A[1], C[2] - A[2], C[3] - A[3]),
vector.newVector3(B[1] - A[1], B[2] - A[2], B[3] - A[3])
)
-- Рассчитываем удаленность виртуальной плоскости треугольника от старта нашего луча
local D = -ABC[1] * A[1] - ABC[2] * A[2] - ABC[3] * A[3]
local firstPart = D + ABC[1] * vector3RayStart[1] + ABC[2] * vector3RayStart[2] + ABC[3] * vector3RayStart[3]
local secondPart = ABC[1] * vector3RayStart[1] - ABC[1] * vector3RayEnd[1] + ABC[2] * vector3RayStart[2] - ABC[2] * vector3RayEnd[2] + ABC[3] * vector3RayStart[3] - ABC[3] * vector3RayEnd[3]
-- Если наш лучик не параллелен той ебучей плоскости треугольника
if secondPart ~= 0 then
local distance = firstPart / secondPart
-- И если этот объект находится ближе к старту луча, нежели предыдущий
if (distance >= 0 and distance <= 1) and (not minimalDistance or distance < minimalDistance) then
-- То считаем точку попадания луча в данную плоскость (но ни хуя не факт, что он попадет в треугольник!)
local S = vector.newVector3(
vector3RayStart[1] + (vector3RayEnd[1] - vector3RayStart[1]) * distance,
vector3RayStart[2] + (vector3RayEnd[2] - vector3RayStart[2]) * distance,
vector3RayStart[3] + (vector3RayEnd[3] - vector3RayStart[3]) * distance
)
-- Далее считаем сумму площадей параллелограммов, образованных тремя треугольниками, образовавшихся при попадании точки в треугольник
-- Нууу тип кароч смари: точка ебанула в центр, и треугольник распидорасило на три мелких. Ну, и три мелких могут образовать параллелограммы свои
-- И, кароч, если сумма трех площадей этих мелких уебков будет сильно отличаться от площади жирного треугольника, то луч не попал
-- Ну, а площадь считается через sqrt(x^2+y^2+z^2) для каждого йоба-вектора
---- *A *B
-- * Shotxyz
-- *ABC
--- *C
local SA = vector.newVector3(A[1] - S[1], A[2] - S[2], A[3] - S[3])
local SB = vector.newVector3(B[1] - S[1], B[2] - S[2], B[3] - S[3])
local SC = vector.newVector3(C[1] - S[1], C[2] - S[2], C[3] - S[3])
local vectorDistanceSum = getVectorDistance(vectorMultiply(SA, SB)) + getVectorDistance(vectorMultiply(SB, SC)) + getVectorDistance(vectorMultiply(SC, SA))
local ABCDistance = getVectorDistance(ABC)
-- Вот тут мы чекаем погрешность расчетов. Если все заебок, то кидаем этот треугольник в "проверенные""
if math.abs(vectorDistanceSum - ABCDistance) < 1 then
closestTriangleIndex = triangleIndex
minimalDistance = distance
end
end
end
end
return closestTriangleIndex, minimalDistance
end
function meowEngine.sceneRaycast(scene, vector3RayStart, vector3RayEnd)
local closestObjectIndex, closestTriangleIndex, minimalDistance
for objectIndex = 1, #scene.objects do
if scene.objects[objectIndex].triangles then
local triangleIndex, distance = meowEngine.meshRaycast(scene.objects[objectIndex], vector3RayStart, vector3RayEnd)
if triangleIndex and (not minimalDistance or distance < minimalDistance ) then
closestObjectIndex, closestTriangleIndex, minimalDistance = objectIndex, triangleIndex, distance
end
end
end
return closestObjectIndex, closestTriangleIndex, minimalDistance
end
-------------------------------------------------------- Intro --------------------------------------------------------
function meowEngine.newPolyCatMesh(vector3Position, size)
return meowEngine.newMesh(
vector3Position,
{
vector.newVector3(-1.0 * size, 0.8 * size, 0.3 * size),
vector.newVector3(-0.5 * size, 0.5 * size, 0.3 * size),
vector.newVector3(0.0 * size, 0.5 * size, 0.3 * size),
vector.newVector3(0.5 * size, 0.5 * size, 0.3 * size),
vector.newVector3(1.0 * size, 0.8 * size, 0.3 * size),
vector.newVector3(0.8 * size, 0.2 * size, 0.3 * size),
vector.newVector3(0.7 * size, -0.3 * size, 0.3 * size),
vector.newVector3(0.0 * size, -0.8 * size, 0.3 * size),
vector.newVector3(-0.7 * size, -0.3 * size, 0.3 * size),
vector.newVector3(-0.8 * size, 0.2 * size, 0.3 * size),
vector.newVector3(-0.2 * size, -0.1 * size, 0.0 * size),
vector.newVector3(0.2 * size, -0.1 * size, 0.0 * size),
vector.newVector3(0.0 * size, -0.3 * size, 0.0 * size)
},
{
OCGL.newIndexedTriangle(1, 2, 10, materials.newSolidMaterial(0x555555)),
OCGL.newIndexedTriangle(2, 11, 10, materials.newSolidMaterial(0x6fe7fc)),
OCGL.newIndexedTriangle(2, 3, 11, materials.newSolidMaterial(0xDDDDDD)),
OCGL.newIndexedTriangle(3, 12, 11, materials.newSolidMaterial(0xDDDDDD)),
OCGL.newIndexedTriangle(3, 4, 12, materials.newSolidMaterial(0xDDDDDD)),
OCGL.newIndexedTriangle(4, 6, 12, materials.newSolidMaterial(0xa8f1fd)),
OCGL.newIndexedTriangle(4, 5, 6, materials.newSolidMaterial(0x808080)),
OCGL.newIndexedTriangle(6, 7, 8, materials.newSolidMaterial(0xCCCCCC)),
OCGL.newIndexedTriangle(12, 6, 8, materials.newSolidMaterial(0xCCCCCC)),
OCGL.newIndexedTriangle(13, 12, 8, materials.newSolidMaterial(0xCCCCCC)),
OCGL.newIndexedTriangle(11, 12, 13, materials.newSolidMaterial(0x555555)),
OCGL.newIndexedTriangle(11, 13, 8, materials.newSolidMaterial(0xBBBBBB)),
OCGL.newIndexedTriangle(10, 11, 8, materials.newSolidMaterial(0xBBBBBB)),
OCGL.newIndexedTriangle(10, 8, 9, materials.newSolidMaterial(0xBBBBBB))
},
materials.newSolidMaterial(0xFF0000)
)
end
function meowEngine.intro(vector3Position, size)
local GUI = require("GUI")
local scene = meowEngine.newScene(0xEEEEEE)
scene:addObject(meowEngine.newPolyCatMesh(vector3Position, size))
scene:addObject(meowEngine.newFloatingText(vector.newVector3(vector3Position[1] + 2, vector3Position[2] - size, vector3Position[3] + size * 0.1), 0xBBBBBB, "Powered by MeowEngine™"))
local from, to, speed = -30, 20, 4
local transparency, transparencyStep = 0, 1 / math.abs(to - from) * speed
scene.camera:setPosition(from, 0, -32)
while scene.camera.position[1] < to do
scene.camera:translate(speed, 0, 0)
scene.camera:lookAt(0, 0, 0)
scene:render()
if scene.camera.position[1] < to then buffer.clear(0x0, transparency) end
buffer.draw()
transparency = transparency + transparencyStep
-- ecs.error("POS: " .. scene.camera.position[1] .. ", " .. scene.camera.position[2] .. ", " .. scene.camera.position[3] .. ", ROT: " .. math.deg(scene.camera.rotation[1]) .. ", " .. math.deg(scene.camera.rotation[2]) .. ", " .. math.deg(scene.camera.rotation[3]))
os.sleep(0.01)
end
os.sleep(2)
for i = 1, 0, -0.2 do
scene:render()
buffer.clear(0x0, i)
buffer.draw()
end
end
-------------------------------------------------------- Zalupa --------------------------------------------------------
return meowEngine