X-Git-Url: http://nitlanguage.org diff --git a/lib/gamnit/depth/depth_core.nit b/lib/gamnit/depth/depth_core.nit index 03a3970..e7e42de 100644 --- a/lib/gamnit/depth/depth_core.nit +++ b/lib/gamnit/depth/depth_core.nit @@ -15,38 +15,107 @@ # Base entities of the depth 3D game framework module depth_core -intrude import gamnit::flat +import gamnit::flat_core -# Visible entity in the game world, represented by its `model` modified by the other attributes +# Visible 3D entity in the game world +# +# Similar to `gamnit::Sprite` which is in 2D. +# +# Each actor associates a `model` to the position `center`. +# The appearance is modified by `rotation`, `scale` and `alpha`, +# as well as the attributes of `model` itself. +# +# ~~~ +# import gamnit::depth +# +# # Load model from the assets folder +# var model = new Model("path/in/assets.obj") +# +# # Create and configure an actor +# var actor = new Actor(model, new Point3d[Float](0.0, 0.0, 0.0)) +# actor.scale = 2.0 +# +# # Add to the visible game world +# app.actors.add actor +# ~~~ class Actor - # Model used to dray this actor + # Model used to draw this actor var model: Model # Position of this sprite in world coordinates var center: Point3d[Float] is writable - # Rotation on the Z axis - var rotation = 0.0 is writable + # Rotation around the X axis (+ looks up, - looks down) + # + # Positive values look up, and negative look down. + # + # All actor rotations follow the right hand rule. + # The default orientation of the model should look towards -Z. + var pitch = 0.0 is writable + + # Rotation around the Y axis (+ turns left, - turns right) + # + # Positive values turn `self` to the left, and negative values to the right. + # + # All actor rotations follow the right hand rule. + # The default orientation of the model should look towards -Z. + var yaw = 0.0 is writable + + # Rotation around the Z axis (looking to -Z: + turns counterclockwise, - clockwise) + # + # From the default camera point of view, looking down on the Z axis, + # positive values turn `self` counterclockwise, and negative values clockwise. + # + # All actor rotations follow the right hand rule. + # The default orientation of the model should look towards -Z. + var roll = 0.0 is writable - # Scale applied to this sprite + # Scale applied to the model var scale = 1.0 is writable - # Transparency applied to the texture on draw + # Transparency applied to the model on draw + # + # This value may be ignored by some materials. + # Non-opaque values may result in artifacts as there is no specialized + # support for transparent models and the depth buffer. var alpha = 1.0 is writable end -# Entire 3D model defined by its `leaves`, an association of `Mesh` to `Material` +# 3D model composed of `Mesh` and `Material`, loaded from the assets folder by default +# +# Instances can be created at any time and must be loaded after or at the end of `create_scene`. +# If loading fails, the model is replaced by `placeholder_model`. +# +# ~~~ +# import gamnit::depth +# +# var model = new Model("path/in/assets.obj") +# model.load +# ~~~ +# +# The most simple model is `LeafModel`, composed of a single `Mesh` and `Material`. +# It can be easily created programmatically to display simple geometries. +# Whereas `CompositeModel` is composed of one or many `LeafModel` and is usually +# loaded from the assets folder as a `ModelAsset`. +# Instances of `ModelAsset` must be in the format OBJ and MAT, +# and their texture in PNG or JPG. abstract class Model # Load this model in memory fun load do end + # Errors raised at loading + var errors = new Array[Error] + # All `LeafModel` composing this model # # Usually, there is one `LeafModel` per material. # At each frame, each material is asked to draw all the live `LeafModel` instaces. fun leaves: Array[LeafModel] is abstract + + # Sub-models with names, usually declared in the asset file + var named_parts = new Map[Text, Model] end # Model composed of one or many other `LeafModel` @@ -56,7 +125,7 @@ class CompositeModel redef var leaves = new Array[LeafModel] end -# Single model with a `mesh` and `material` +# Basic model with a single `mesh` and `material` # # Only leaves are actually drawn by the `material`. class LeafModel @@ -71,23 +140,55 @@ class LeafModel redef var leaves = [self] end -# Material for a model or how to draw the model +# Material for models, or how to draw the model +# +# To create a simple basic blueish material, use `new Material`. +# +# Each class of material is associated to a `GLProgram` and its GPU shaders. +# The simple material `SmoothMaterial` allows to set an ambient, diffuse and specular color. +# To which `TextureMaterial` adds three textures, for each kind of light. +# The `NormalsMaterial` may be useful for debugging, it show the orientation of +# the normal vectors as colors. +# +# ~~~ +# import gamnit::depth +# +# var blueish_material = new Material +# var redish_material = new SmoothMaterial([0.3, 0.0, 0.0], +# [0.6, 0.0, 0.0], +# [1.0, 1.0, 1.0]) +# var normals_material = new NormalsMaterial +# ~~~ abstract class Material - # Draw `actor` + # Draw a `model` from `actor` # # This method should be refined by subclasses as the default implementation is a no-op. # # This method is called on many materials for many `actor` and `model` at each frame. # It is expected to use a `GLProgram` and call an equivalent to `glDrawArrays`. # However, it should not call `glClear` nor `GamnitDisplay::flip`. - fun draw(actor: Actor, model: LeafModel) do end + fun draw(actor: Actor, model: LeafModel, camera: Camera) do end end # Mesh with all geometry data +# +# May be created via `Plane`, `Cube` or `UVSphere`, +# or loaded from the assets folder indirectly with a `Model`. +# +# ~~~ +# import gamnit::depth +# +# var plane = new Plane +# var cube = new Cube +# var sphere = new UVSphere(1.0, 32, 16) +# ~~~ class Mesh - # Vertices coordinates + # Number for vertices + fun n_vertices: Int do return vertices.length / 3 + + # Vertices relative coordinates, 3 floats per vertex var vertices = new Array[Float] is lazy, writable # Indices to draw triangles with `glDrawElements` @@ -97,151 +198,43 @@ class Mesh private var indices_c = new CUInt16Array.from(indices) is lazy, writable - # Normals on each vertex + # Normals, 3 floats per vertex var normals = new Array[Float] is lazy, writable - # Coordinates on the texture per vertex + # Coordinates on the texture, 2 floats per vertex var texture_coords = new Array[Float] is lazy, writable - # Create an UV sphere of `radius` with `n_meridians` and `n_parallels` - init uv_sphere(radius: Float, n_meridians, n_parallels: Int) - do - var w = n_meridians - var h = n_parallels - - var vertices = new Array[Float].with_capacity(w*h*3) - self.vertices = vertices - - var texture_coords = new Array[Float].with_capacity(w*h*2) - self.texture_coords = texture_coords - - var normals = new Array[Float].with_capacity(w*h*3) - self.normals = normals - - # Build vertices - for m in [0..w[ do - for p in [0..h[ do - var u = m.to_f * 2.0 * pi / (w-1).to_f - var v = p.to_f * pi / (h-1).to_f - - vertices.add radius * u.cos * v.sin - vertices.add radius * v.cos - vertices.add radius * u.sin * v.sin - - texture_coords.add (1.0 - m.to_f/(w-1).to_f) - texture_coords.add(p.to_f/(h-1).to_f) - - normals.add u.cos * v.sin - normals.add v.cos - normals.add u.sin * v.sin - end - end - - # Build faces - var indices = new Array[Int].with_capacity((w-1)*(h-1)*6) - self.indices = indices - for m in [0..w-1[ do - for p in [0..h-1[ do - var a = m*h + p - - indices.add a - indices.add a+h - indices.add a+1 - - indices.add a+h - indices.add a+h+1 - indices.add a+1 - end - end - end - - # Dimensions of this geometry using the min and max of all points on each axis - var dimensions: Point3d[Float] is lazy, writable do - assert vertices.length % 3 == 0 - - var minx = inf - var miny = inf - var minz = inf - var maxx = -inf - var maxy = -inf - var maxz = -inf - - var i = 0 - while i < vertices.length do - var x = vertices[i] - i += 1 - var y = vertices[i] - i += 1 - var z = vertices[i] - i += 1 - - minx = minx.min(x) - miny = miny.min(y) - minz = minz.min(z) - - maxx = maxx.max(x) - maxy = maxy.max(y) - maxz = maxz.max(z) - end - - return new Point3d[Float](maxx-minx, maxy-miny, maxz-minz) - end - - # Center of the geometry - var center: Point3d[Float] is lazy, writable do - assert vertices.length % 3 == 0 - - var minx = inf - var miny = inf - var minz = inf - var maxx = -inf - var maxy = -inf - var maxz = -inf - - var i = 0 - while i < vertices.length do - var x = vertices[i] - i += 1 - var y = vertices[i] - i += 1 - var z = vertices[i] - i += 1 - - minx = minx.min(x) - miny = miny.min(y) - minz = minz.min(z) - - maxx = maxx.max(x) - maxy = maxy.max(y) - maxz = maxz.max(z) - end - - var center = new Point3d[Float]( - (minx+maxx)/2.0, - (miny+maxy)/2.0, - (minz+maxz)/2.0) - return center - end + # `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES` + fun draw_mode: GLDrawMode do return gl_TRIANGLES end # Source of light -# -# Instances of this class define a light source position and type. -class Light - - # TODO point light, spotlight, directional light, etc. +abstract class Light # Center of this light source in world coordinates var position = new Point3d[Float](0.0, 1000.0, 0.0) end +# Basic light projected from a single point +class PointLight + super Light +end + +# Source of light casting shadows +abstract class LightCastingShadows + super Light + + # View from the camera, used for shadow mapping, implemented by sub-classes + fun camera: Camera is abstract +end + redef class App # Live actors to be drawn on screen var actors = new Array[Actor] # Single light of the scene - var light = new Light + var light: Light = new PointLight is writable # TODO move `actors & light` to a scene object # TODO support more than 1 light