Simple 3D graphics engine.
- Phong lighting
- Fly style camera - Freely move around a 3D scene
- Text rendering - English characters and numbers.
- Out of the box Shapes - Vector, Box, Circle, Cone, Cylinder, HeightField, Plane, Point, Pyramid, Sphere, Tetrahedron, Triangle, Line & Ray
- OpenGL via LWJGL3
- User input to transform and rotate shapes.
- Basic linear algebra library -
Matrix3
,Matrix2
,Vector3
&Vector2
- Immutable API
- W, A, S, D - Move along all 3 axes
- Mouse drag - Changes camera view position
- Enter - Return to initial camera view
- Space (Hold!) - Pauses rendering/animation (camera is still active)
libraryDependencies += "com.github.simerplaha" %% "slack3d" % "0.1.0"
Allow LWJGL to run on main thread with -XstartOnFirstThread
in VM options. If you are using IntelliJ set the flag in
the "Build and run" window.
Slack3D can be thought of a collection instance where your code provide shapes to render for each frame.
Render a sphere of radius 0.5
of colour Purple
.
Slack3D("A sphere") foreach {
state => //State of current render
//shapes to render
Seq(Sphere(radius = 0.5, colour = Colour.Purple))
}
Render shapes after some time interval. The following will render a Sphere
of radius 0.5
with a new colour after
every 1.second.
Slack3D("Colour").foreach(interval = 1.second) {
_ =>
Seq(Sphere(0.5, colour = Colour.next()))
}
All OpenGL colours can be found class type Colour. To
get the next colour from a randomly sorted colour queue useColour.next()
Apply Y axis rotation to the box.
Slack3D("Rotating Box") foreach {
state =>
//same code as above but with added rotation at Y axis
val box = Box(Colour.Red).rotateY(state.getTime() * 30)
Seq(box)
}
Rotation can be applied to all axis. See APIs
Configures the Box
to be
- translatable when
Z
key is pressed and arrows keys are moved - rotatable when
X
key is pressed and arrows keys are moved
Slack3D("Custom rotation").foldLeft(Box()) {
case (_box, state) =>
val box =
_box
.translatable(state.window, GLFW.GLFW_KEY_Z) //translate box when Z key is pressed
.rotatable(state.window, GLFW.GLFW_KEY_X) //rotate box when X key is pressed
(box, Seq(box))
}
Create 2 vectors where the third vector is a cross product.
Slack3D("Lines and vectors") foreach {
state =>
//vector1
val vector1 = Vector3(0.5, -0.5, 0)
//vector2
val vector2 = Vector3(0.5, 0.5, 0)
//cross product
val cross = vector1 cross vector2
Seq(
Line(vector1, Colour.Red),
Line(vector2, Colour.Yellow),
Line(cross, Colour.Green)
)
}
All Lines
and Points
will render a text displaying the position and length of that vector.
You can change the width, height, title, camera etc. See the following example.
Slack3D(
title = "My configurations", //window title
width = 800, //window width
height = 600, //window height
backgroundColor = Colour.Orange,
enableWireframes = true, //configures OpenGL in wireframes mode.
camera = None, // disable camera or provide your custom instance. See how default works
enable2DCoordinates = false, //enables or disables coordinate drawing
perspective = None, //perspective view
light = None //configures lighting
) foreach {
state =>
//no shapes to render
Seq.empty
}
All shapes are a combination of primitives Triangle
, Line
or a Point
.
/**
* A custom shape that render text and a line
*/
case class MyCustomShape(text: Text,
line: Cylinder) extends Shape {
override type Self = MyCustomShape
//function to apply to each vector on this shape.
//this function is used for applying perspective, rotation & translation
override def map(f: Vector3[Double] => Vector3[Double]): MyCustomShape =
MyCustomShape(
text = text.map(f),
line = line.map(f)
)
//expands each shape into points, lines and triangles
override def buildMesh(mesh: Mesh[Point, LineOrRay, Triangle]): Unit = {
text buildMesh mesh
line buildMesh mesh
}
}
//Start Slack3D instance and render the above custom shape
Slack3D("My custom shape") foreach {
state =>
//create my custom shape instance
val myShape =
MyCustomShape(
text = Text("My custom shape text", Colour.White) * 2 + Vector3(-0.6, 0.4), //a scaled & translated custom text
line = Cylinder(Colour.Purple) / 2 //A scaled down cylinder
)
Seq(myShape)
}
Thank you Jetbrains for providing an open-source licence for their awesome development tools.