496 lines
15 KiB
Lua

-------------------------------------------------------- Libraries --------------------------------------------------------
local matrix = require("matrix")
local OCGL = {}
-------------------------------------------------------- Constants --------------------------------------------------------
OCGL.axis = {
x = 1,
y = 2,
z = 3,
}
OCGL.colors = {
axis = {
x = 0xFF0000,
y = 0x00FF00,
z = 0x0000FF,
},
pivotPoint = 0xFFFFFF,
wireframe = 0x00FFFF,
}
OCGL.renderModes = {
material = 1,
wireframe = 2,
vertices = 3,
}
OCGL.materialTypes = {
textured = 1,
solid = 2,
}
-------------------------------------------------------- Materials --------------------------------------------------------
function OCGL.newSolidMaterial(color)
return {
type = OCGL.materialTypes.solid,
color = color
}
end
function OCGL.newTexturedMaterial(texture)
return {
type = OCGL.materialTypes.textured,
texture = texture
}
end
-------------------------------------------------------- Vertices manipulation --------------------------------------------------------
function OCGL.newVector2(x, y)
checkArg(1, x, "number")
checkArg(2, y, "number")
return { x, y }
end
function OCGL.newVector3(x, y, z)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, z, "number")
return { x, y, z }
end
function OCGL.newVector4(x, y, z, w)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, z, "number")
checkArg(4, w, "number")
return { x, y, z, w }
end
function OCGL.convertVector3ArrayToVerticesMatrix(vector3Position, vector3Vertices)
checkArg(1, vector3Vertices, "table")
local verticesMatrix = {}
for i = 1, #vector3Vertices do
verticesMatrix[i] = OCGL.newVector3(
vector3Position[1] + vector3Vertices[i][1],
vector3Position[2] + vector3Vertices[i][2],
vector3Position[3] + vector3Vertices[i][3]
)
end
-- Костыль, добавляющий нулевой вектор в матрицу в том случае, если количество вертексов меньше 3, чисто для заполнения массива
-- if #vector3Vertices < 3 then
-- for i = 1, 3 - #vector3Vertices do
-- table.insert(verticesMatrix, OCGL.newVector3(0, 0, 0))
-- end
-- end
return verticesMatrix
end
-------------------------------------------------------- Matrix objects --------------------------------------------------------
function OCGL.newRotationMatrix(axis, angle)
checkArg(1, axis, "number")
checkArg(1, angle, "number")
local rotationMatrix, sin, cos = {}, math.sin(angle), math.cos(angle)
if axis == OCGL.axis.x then
rotationMatrix = {
{ 1, 0, 0 },
{ 0, cos, -sin },
{ 0, sin, cos }
}
elseif axis == OCGL.axis.y then
rotationMatrix = {
{ cos, 0, sin },
{ 0, 1, 0 },
{ -sin, 0, cos }
}
elseif axis == OCGL.axis.z then
rotationMatrix = {
{ cos, -sin, 0 },
{ sin, cos, 0 },
{ 0, 0, 1 }
}
else
error("Axis enum " .. tostring(axis) .. " doesn't exists")
end
return rotationMatrix
end
function OCGL.newPivotPoint(vector3Position)
return {
position = vector3Position,
axis = {
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }
}
}
end
function OCGL.newScaleMatrix(vector3Scale)
return {
{ vector3Scale[1], 0, 0 },
{ 0, vector3Scale[2], 0 },
{ 0, 0, vector3Scale[3] }
}
end
-------------------------------------------------------- Object translation methods --------------------------------------------------------
local function objectTranslate(object, vector3Translation)
for vertexIndex = 1, #object.verticesMatrix do
object.verticesMatrix[vertexIndex][1] = object.verticesMatrix[vertexIndex][1] + vector3Translation[1]
object.verticesMatrix[vertexIndex][2] = object.verticesMatrix[vertexIndex][2] + vector3Translation[2]
object.verticesMatrix[vertexIndex][3] = object.verticesMatrix[vertexIndex][3] + vector3Translation[3]
end
object.pivotPoint.position[1] = object.pivotPoint.position[1] + vector3Translation[1]
object.pivotPoint.position[2] = object.pivotPoint.position[2] + vector3Translation[2]
object.pivotPoint.position[3] = object.pivotPoint.position[3] + vector3Translation[3]
return object
end
local function objectSetPosition(object, vector3Position)
object:translate(OCGL.newVector3(
vector3Position[1] - object.pivotPoint.position[1],
vector3Position[2] - object.pivotPoint.position[2],
vector3Position[3] - object.pivotPoint.position[3]
))
return object
end
-------------------------------------------------------- Object rotation methods --------------------------------------------------------
local function objectRotateRelativeToWorldAxisPosition(object, rotationMatrix)
object.verticesMatrix = matrix.multiply(object.verticesMatrix, rotationMatrix)
object.pivotPoint.axis = matrix.multiply(object.pivotPoint.axis, rotationMatrix)
object.pivotPoint.position = matrix.multiply({object.pivotPoint.position}, rotationMatrix)[1]
return object
end
local function objectRotateRelativeToSpecifiedAxisPosition(object, vector3Position, rotationMatrix)
local oldPosition = OCGL.newVector3(vector3Position[1], vector3Position[2], vector3Position[3])
object:translate(OCGL.newVector3(-oldPosition[1], -oldPosition[2], -oldPosition[3]))
object:rotateRelativeToWorldAxisPosition(rotationMatrix)
object:translate(oldPosition)
return object
end
local function objectRotateRelativeToSpecifiedPivotPoint(object, specifiedPivotPoint, rotationMatrix)
local oldPosition = OCGL.newVector3(specifiedPivotPoint.position[1], specifiedPivotPoint.position[2], specifiedPivotPoint.position[3])
object:translate(OCGL.newVector3(-oldPosition[1], -oldPosition[2], -oldPosition[3]))
local transitionMatrix = matrix.transpose(object.pivotPoint.axis)
local invertedTransitionMatrix = matrix.invert(transitionMatrix)
object.verticesMatrix = matrix.multiply(object.verticesMatrix, transitionMatrix)
object.pivotPoint.axis = matrix.multiply(object.pivotPoint.axis, transitionMatrix)
object:rotateRelativeToWorldAxisPosition(rotationMatrix)
object.verticesMatrix = matrix.multiply(object.verticesMatrix, invertedTransitionMatrix)
object.pivotPoint.axis = matrix.multiply(object.pivotPoint.axis, invertedTransitionMatrix)
object:translate(oldPosition)
end
local function objectRotateRelativeToLocalPivotPoint(object, rotationMatrix)
object:rotateRelativeToSpecifiedPivotPoint(object.pivotPoint, rotationMatrix)
return object
end
-------------------------------------------------------- Object Scale methods --------------------------------------------------------
local function objectScaleRelativeToWorldAxisPosition(object, scaleMatrix)
object.verticesMatrix = matrix.multiply(object.verticesMatrix, scaleMatrix)
object.pivotPoint.axis = matrix.multiply(object.pivotPoint.axis, scaleMatrix)
return object
end
local function objectScaleRelativeToSpecifiedAxisPosition(object, vector3Position, scaleMatrix)
local oldPosition = OCGL.newVector3(vector3Position[1], vector3Position[2], vector3Position[3])
object:translate(OCGL.newVector3(-oldPosition[1], -oldPosition[2], -oldPosition[3]))
object:scaleRelativeToWorldAxisPosition(scaleMatrix)
object:translate(oldPosition)
return object
end
local function objectScaleRelativeToLocalPivotPoint(object, scaleMatrix)
local oldPosition = OCGL.newVector3(object.pivotPoint.position[1], object.pivotPoint.position[2], object.pivotPoint.position[3])
object:translate(OCGL.newVector3(-oldPosition[1], -oldPosition[2], -oldPosition[3]))
object:scaleRelativeToWorldAxisPosition(scaleMatrix)
object:translate(oldPosition)
return object
end
-------------------------------------------------------- Object creation --------------------------------------------------------
function OCGL.newObject(vector3Position, vector3Vertices)
local object = {}
object.verticesMatrix = OCGL.convertVector3ArrayToVerticesMatrix(vector3Position, vector3Vertices)
object.pivotPoint = OCGL.newPivotPoint(vector3Position)
object.translate = objectTranslate
object.setPosition = objectSetPosition
object.rotateRelativeToWorldAxisPosition = objectRotateRelativeToWorldAxisPosition
object.rotateRelativeToSpecifiedAxisPosition = objectRotateRelativeToSpecifiedAxisPosition
object.rotateRelativeToSpecifiedPivotPoint = objectRotateRelativeToSpecifiedPivotPoint
object.rotate = objectRotateRelativeToLocalPivotPoint
object.scaleRelativeToWorldAxisPosition = objectScaleRelativeToWorldAxisPosition
object.scaleRelativeToSpecifiedAxisPosition = objectScaleRelativeToSpecifiedAxisPosition
object.scale = objectScaleRelativeToLocalPivotPoint
return object
end
-------------------------------------------------------- Mesh object --------------------------------------------------------
function OCGL.newIndexedTriangle(indexOfVertex1, indexOfVertex2, indexOfVertex3, material)
local triangle = { indexOfVertex1, indexOfVertex2, indexOfVertex3 }
if material then triangle.material = material end
return triangle
end
function OCGL.newMesh(vector3Position, vector3Vertices, triangles, material)
local mesh = OCGL.newObject(vector3Position, vector3Vertices)
mesh.material = material
mesh.triangles = triangles
mesh.render = renderer.renderMesh
return mesh
end
-------------------------------------------------------- Line object --------------------------------------------------------
function OCGL.newLine(vector3Position, vector3Vertex1, vector3Vertex2, color)
local line = OCGL.newObject(vector3Position, { vector3Vertex1, vector3Vertex2 })
line.color = color
line.render = renderer.renderLineObject
return line
end
-------------------------------------------------------- Plane object --------------------------------------------------------
function OCGL.newPlane(vector3Position, width, height, material)
local halfWidth, halfHeight = width / 2, height / 2
return OCGL.newMesh(
vector3Position,
{
OCGL.newVector3(-halfWidth, 0, -halfHeight),
OCGL.newVector3(-halfWidth, 0, halfHeight),
OCGL.newVector3(halfWidth, 0, halfHeight),
OCGL.newVector3(halfWidth, 0, -halfHeight),
},
{
OCGL.newIndexedTriangle(1, 2, 3),
OCGL.newIndexedTriangle(1, 4, 3)
},
material
)
end
-------------------------------------------------------- Cyka helper --------------------------------------------------------
--[[
| /
| /
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
]]
-------------------------------------------------------- Cube object --------------------------------------------------------
-- Start point is a bottom left nearest corner of cube
function OCGL.newCube(vector3Position, size, material)
local halfSize = size / 2
return OCGL.newMesh(
vector3Position,
{
-- (1-2-3-4)
OCGL.newVector3(-halfSize, -halfSize, -halfSize),
OCGL.newVector3(-halfSize, halfSize, -halfSize),
OCGL.newVector3(halfSize, halfSize, -halfSize),
OCGL.newVector3(halfSize, -halfSize, -halfSize),
-- (5-6-7-8)
OCGL.newVector3(halfSize, -halfSize, halfSize),
OCGL.newVector3(halfSize, halfSize, halfSize),
OCGL.newVector3(-halfSize, halfSize, halfSize),
OCGL.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
-------------------------------------------------------- Grid lines --------------------------------------------------------
function OCGL.newGridLines(vector3Position, axisRange, gridRange, gridRangeStep)
local objects = {}
-- Grid
for x = -gridRange, gridRange, gridRangeStep do
table.insert(objects, 1, OCGL.newLine(
OCGL.newVector3(vector3Position[1] + x, vector3Position[2], vector3Position[3]),
OCGL.newVector3(0, 0, -gridRange),
OCGL.newVector3(0, 0, gridRange),
0x444444
))
end
for z = -gridRange, gridRange, gridRangeStep do
table.insert(objects, 1, OCGL.newLine(
OCGL.newVector3(vector3Position[1], vector3Position[2], vector3Position[3] + z),
OCGL.newVector3(-gridRange, 0, 0),
OCGL.newVector3(gridRange, 0, 0),
0x444444
))
end
-- Axis
table.insert(objects, OCGL.newLine(
vector3Position,
OCGL.newVector3(-axisRange, -1, 0),
OCGL.newVector3(axisRange, -1, 0),
OCGL.colors.axis.x
))
table.insert(objects, OCGL.newLine(
vector3Position,
OCGL.newVector3(0, -axisRange, 0),
OCGL.newVector3(0, axisRange, 0),
OCGL.colors.axis.y
))
table.insert(objects, OCGL.newLine(
vector3Position,
OCGL.newVector3(0, -1, -axisRange),
OCGL.newVector3(0, -1, axisRange),
OCGL.colors.axis.z
))
return objects
end
-------------------------------------------------------- ObjectGroup object --------------------------------------------------------
local function objectGroupAddObject(objectGroup, object)
table.insert(objectGroup.objects, object)
return object
end
local function objectGroupAddObjects(objectGroup, objects)
for objectIndex = 1, #objects do
table.insert(objectGroup.objects, objects[objectIndex])
end
return objects
end
local function objectGroupRotate(objectGroup, rotationMatrix)
for objectIndex = 1, #objectGroup.objects do
objectGroup.objects[objectIndex]:rotateRelativeToSpecifiedAxisPosition(objectGroup.pivotPoint.position, rotationMatrix)
end
return objectGroup
end
local function objectGroupTranslate(objectGroup, vector3Translation)
for objectIndex = 1, #objectGroup.objects do
objectGroup.objects[objectIndex]:translate(vector3Translation)
end
objectGroup.pivotPoint.position = OCGL.newVector3(
objectGroup.pivotPoint.position[1] + vector3Translation[1],
objectGroup.pivotPoint.position[2] + vector3Translation[2],
objectGroup.pivotPoint.position[3] + vector3Translation[3]
)
return objectGroup
end
local function objectGroupScale(objectGroup, scaleMatrix)
for objectIndex = 1, #objectGroup.objects do
objectGroup.objects[objectIndex]:scaleRelativeToSpecifiedAxisPosition(objectGroup.pivotPoint.position, scaleMatrix)
end
return objectGroup
end
local function objectGroupRender(objectGroup, renderMode)
for objectIndex = 1, #objectGroup.objects do
objectGroup.objects[objectIndex]:render(renderMode)
end
return objectGroup
end
function OCGL.newObjectGroup(vector3Position, ...)
local objectGroup = {}
objectGroup.pivotPoint = OCGL.newPivotPoint(vector3Position)
objectGroup.objects = {}
objectGroup.rotate = objectGroupRotate
objectGroup.translate = objectGroupTranslate
objectGroup.scale = objectGroupScale
objectGroup.addObject = objectGroupAddObject
objectGroup.addObjects = objectGroupAddObjects
objectGroup.render = objectGroupRender
return objectGroup
end
-------------------------------------------------------- Playground --------------------------------------------------------
-------------------------------------------------------- Constants --------------------------------------------------------
return OCGL