Skip to content

Commit 67c1af0

Browse files
committed
Merge: gamnit: intro textures, programs, camera and the globe example
Intro 3 core modules for gamnit with a lot of features: * textures: Provices portable services to load textures. Textures are organized as trees where only the roots have the pixels data and subtextures relay load calls to the root. This structure should remain as is but more features need to be implemented, namely automating the use of offsets in the subtexture, auto reloading textures when invalidated and services to unload the textures. * programs: Structures to organize GL programs and shaders and to facilitate access to shader variables. The main advantages of this API are static types on shader variables and the gamnit programs that can be specialized. I'm not 100% satisfied with the structure of GamnitProgram and GamnitProgramFromSource, but I don't have a better idea on how to organize it. Also I will probably review the implementation of how `GamnitProgram::attributes` and `uniforms` are populated, but it shouldn't change the API much. * cameras: Add an abstract Camera class and a simple EulerCamera based on euler angles, perfect for FPS type games or top-down strategy games. These camera main feature is to produce the MVP matrix. More camera should be implemented, they should also be considered objects attachable to a scene when we have scenes. The EulerCamera lacks some feature, like the usual `look_at`. These services are independent, it's clean but maybe it's not the best idea. We can't pass a camera to a mat4 shader variable, we have to manually extract the MVP matrix from the camera and pass it to the shader. Also to assign a texture to a sampler2d shader variable, we have to load the texture, assign its GL id to a texture unit and assign the unit's index as the shader variable value. This last issue could be simplified by more code in gamnit itself, but it will add coupling, it will be for a next PR. The globe examples should replace the triangle example as the "minimal" example for gamnit. However I plan on updating it as I integrate more code into gamnit services allowing me to remove custom calls to OpenGL ES 2.0 from the example. Pull-Request: #1890 Reviewed-by: Jean Privat <jean@pryen.org>
2 parents c61c1fe + 9ad426b commit 67c1af0

24 files changed

+1566
-6
lines changed

lib/android/load_image.nit

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ redef extern class NativeBitmap
3131
# Copy the pixel data into a new `CByteArray`
3232
#
3333
# If `pad_to_pow2` the new buffer contains artificial pixels used to make
34-
# the width and the height powers of 2 for compatibility with OpenGL.
35-
fun copy_pixels(pad_to_pow2: nullable Bool): CByteArray
34+
# the width and the height powers of 2 for compatibility with older OpenGL.
35+
#
36+
# If `unmultiply`, extra work is done to revert the multiplication of color
37+
# values per the alpha channel applied by the Android system.
38+
fun copy_pixels(pad_to_pow2, unmultiply: nullable Bool): CByteArray
3639
do
3740
var height = height
3841
var row_bytes = row_bytes
@@ -49,7 +52,7 @@ redef extern class NativeBitmap
4952
var java_buf = buf.to_java_nio_buffer
5053
copy_pixels_to_buffer java_buf
5154

52-
if has_alpha then buf.native_array.unmultiply(width, height)
55+
if has_alpha and unmultiply == true then buf.native_array.unmultiply(width, height)
5356

5457
if pad_to_pow2 == true then
5558
for r in [height-1..0[.step(-1) do

lib/c.nit

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class CIntArray
9393
super size
9494
end
9595

96-
# Create from an `SequenceRead[Int]`
96+
# Create from a `SequenceRead[Int]`
9797
new from(array: SequenceRead[Int])
9898
do
9999
var carray = new CIntArray(array.length)
@@ -118,6 +118,42 @@ extern class NativeCIntArray `{ int* `}
118118
redef fun +(offset) `{ return self + offset; `}
119119
end
120120

121+
# Wrapper of a C array of type `uint16_t*` with length and destroy state
122+
class CUInt16Array
123+
super CArray[Int]
124+
redef type NATIVE: NativeCUInt16Array
125+
126+
# Initialize a new CIntArray of `size` elements.
127+
init(size: Int) is old_style_init do
128+
native_array = new NativeCUInt16Array(size)
129+
super size
130+
end
131+
132+
# Create from a `SequenceRead[Int]`
133+
new from(array: SequenceRead[Int])
134+
do
135+
var carray = new CUInt16Array(array.length)
136+
for i in array.length.times do
137+
carray[i] = array[i]
138+
end
139+
return carray
140+
end
141+
end
142+
143+
# An array of `uint16_t` in C
144+
extern class NativeCUInt16Array `{ uint16_t* `}
145+
super NativeCArray
146+
redef type E: Int
147+
148+
# Initialize a new NativeCUInt16Array of `size` elements.
149+
new(size: Int) `{ return calloc(size, sizeof(uint16_t)); `}
150+
151+
redef fun [](index) `{ return self[index]; `}
152+
redef fun []=(index, val) `{ self[index] = val; `}
153+
154+
redef fun +(offset) `{ return self + offset; `}
155+
end
156+
121157
# Wrapper around an array of `unsigned char` in C (`unsigned char*`) with length and destroy state
122158
class CByteArray
123159
super CArray[Byte]

lib/gamnit/cameras.nit

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# This file is part of NIT (http://www.nitlanguage.org).
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Camera services producing Model-View-Projection matrices
16+
module cameras
17+
18+
import geometry
19+
import matrix::projection
20+
21+
import display
22+
23+
# A camera with a point of view on the world
24+
abstract class Camera
25+
26+
# TODO make this a physical object in the world
27+
28+
# The host `GamnitDisplay`
29+
var display: GamnitDisplay
30+
31+
# Position of this camera in world space
32+
var position = new Point3d[Float](0.0, 0.0, 0.0) is writable
33+
34+
# The Model-View-Projection matrix created by this camera
35+
#
36+
# This method should only be called by the display at the moment
37+
# of drawing to the screen.
38+
fun mvp_matrix: Matrix is abstract
39+
end
40+
41+
# Simple camera with perspective oriented with Euler angles (`pitch`, `yaw`, `roll`)
42+
class EulerCamera
43+
super Camera
44+
45+
# Rotation around the X axis (looking down or up)
46+
var pitch = 0.0 is writable
47+
48+
# Rotation around the Y axis (looking left or right)
49+
var yaw = 0.0 is writable
50+
51+
# Rotation around the Z axis
52+
var roll = 0.0 is writable
53+
54+
# Field of view in radians on the vertical axis of the screen
55+
#
56+
# Default at `0.8`
57+
var field_of_view_y = 0.8 is writable
58+
59+
# Clipping wall near the camera, in world dimensions
60+
#
61+
# Default at `0.01`.
62+
var near = 0.01 is writable
63+
64+
# Clipping wall the farthest of the camera, in world dimensions
65+
#
66+
# Default at `100.0` but this one should be adapted to each context.
67+
var far = 100.0 is writable
68+
69+
# Look around sensitivity, used by `turn`
70+
var sensitivity = 0.005 is writable
71+
72+
# Apply a mouse movement (or similar) to the camera
73+
#
74+
# `dx` and `dy` are relative mouse movements in pixels.
75+
fun turn(dx, dy: Float)
76+
do
77+
# Moving on x, turn around the y axis
78+
yaw -= dx*sensitivity
79+
pitch -= dy*sensitivity
80+
81+
# Protect rotation around then x axis for not falling on your back
82+
pitch = pitch.min(pi/2.0)
83+
pitch = pitch.max(-pi/2.0)
84+
end
85+
86+
# Move the camera considering the current orientation
87+
fun move(dx, dy, dz: Float)
88+
do
89+
# +dz move forward
90+
position.x -= yaw.sin*dz
91+
position.z -= yaw.cos*dz
92+
93+
# +dx strafe to the right
94+
position.x += yaw.cos*dx
95+
position.z -= yaw.sin*dx
96+
97+
# +dz move towards the sky
98+
position.y += dy
99+
end
100+
101+
# Rotation matrix produced by the current rotation of the camera
102+
protected fun rotation_matrix: Matrix
103+
do
104+
var view = new Matrix.identity(4)
105+
106+
# Rotate the camera, first by looking left or right, then up or down
107+
view.rotate(yaw, 0.0, 1.0, 0.0)
108+
view.rotate(pitch, 1.0, 0.0, 0.0)
109+
view.rotate(roll, 0.0, 0.0, 1.0)
110+
111+
return view
112+
end
113+
114+
redef fun mvp_matrix
115+
do
116+
var view = new Matrix.identity(4)
117+
118+
# Translate the world away from the camera
119+
view.translate(-position.x/2.0, -position.y/2.0, -position.z/2.0)
120+
121+
# Rotate the camera, first by looking left or right, then up or down
122+
view = view * rotation_matrix
123+
124+
# Use a projection matrix with a depth
125+
var projection = new Matrix.perspective(pi*field_of_view_y/2.0,
126+
display.aspect_ratio, near, far)
127+
128+
return view * projection
129+
end
130+
end

lib/gamnit/display.nit

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class GamnitDisplay
3232
# Height of the display, in pixels
3333
fun height: Int is abstract
3434

35+
# Aspect ratio of the screen, `width / height`
36+
var aspect_ratio: Float is lazy do return width.to_f / height.to_f
37+
3538
# Prepare this display
3639
#
3740
# The implementation varies per platform.

lib/gamnit/display_android.nit

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ module display_android is
2222
end
2323

2424
import ::android
25+
intrude import android::load_image
2526

2627
private import gamnit::egl
28+
intrude import textures
2729

2830
redef class GamnitDisplay
2931

@@ -45,3 +47,29 @@ redef class GamnitDisplay
4547

4648
redef fun close do close_egl
4749
end
50+
51+
redef class GamnitAssetTexture
52+
53+
redef fun load_from_platform
54+
do
55+
jni_env.push_local_frame 4
56+
57+
var asset_manager = app.asset_manager
58+
var bmp = asset_manager.bitmap(path)
59+
if bmp.is_java_null then
60+
error = new Error("Failed to load texture at '{path}'")
61+
jni_env.pop_local_frame
62+
return
63+
end
64+
65+
var buf = bmp.copy_pixels
66+
loaded = true
67+
width = bmp.width.to_f
68+
height = bmp.height.to_f
69+
var pixels = buf.native_array
70+
71+
load_from_pixels(pixels, bmp.width, bmp.height, gl_RGBA)
72+
73+
jni_env.pop_local_frame
74+
end
75+
end

lib/gamnit/display_linux.nit

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import sdl
1919
import x11
2020

2121
import egl # local to gamnit
22-
import display
22+
intrude import display
23+
intrude import textures
2324

2425
redef class GamnitDisplay
2526

@@ -89,3 +90,24 @@ redef class GamnitDisplay
8990
return x11_display
9091
end
9192
end
93+
94+
redef class GamnitAssetTexture
95+
96+
redef fun load_from_platform
97+
do
98+
var path = "assets" / path # TODO use app.assets_dir
99+
var sdl_tex = new SDLImage.from_file(path)
100+
101+
if sdl_tex.address_is_null then
102+
error = new Error("Failed to load texture at '{path}'")
103+
return
104+
end
105+
106+
self.width = sdl_tex.width.to_f
107+
self.height = sdl_tex.height.to_f
108+
var format = if sdl_tex.amask > 0 then gl_RGBA else gl_RGB
109+
var pixels = sdl_tex.pixels
110+
111+
load_from_pixels(pixels, sdl_tex.width, sdl_tex.height, format)
112+
end
113+
end

lib/gamnit/examples/globe/Makefile

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
NITC=../../../../bin/nitc
2+
NITLS=../../../../bin/nitls
3+
4+
all: bin/globe
5+
6+
bin/globe: $(shell ${NITLS} -M src/globe.nit linux) ${NITC}
7+
${NITC} src/globe.nit -m linux -o $@ -D n_parallels=100
8+
9+
check: bin/globe
10+
bin/globe
11+
12+
# ---
13+
# Android
14+
15+
android: bin/globe.apk
16+
bin/globe.apk: $(shell ${NITLS} -M src/globe.nit android) ${NITC} res/drawable-hdpi/icon.png assets/ld/earth.jpg
17+
${NITC} src/globe.nit -m android -o $@
18+
19+
android-release: $(shell ${NITLS} -M src/globe.nit android) ${NITC} res/drawable-hdpi/icon.png assets/ld/earth.jpg
20+
${NITC} src/globe.nit -m android -o bin/globe.apk --release
21+
22+
android-wear: $(shell ${NITLS} -M src/globe.nit android) ${NITC} res/drawable-hdpi/icon.png assets/ld/earth.jpg
23+
${NITC} src/globe.nit -m android -o bin/planet_wear.apk -D n_parallels=10
24+
25+
res/drawable-hdpi/icon.png: art/icon.png
26+
mkdir -p res/drawable-ldpi/ res/drawable-mdpi/ res/drawable-hdpi/ \
27+
res/drawable-xhdpi/ res/drawable-xxhdpi/ res/drawable-xxxhdpi/
28+
convert -resize 36x36 art/icon.png res/drawable-ldpi/icon.png
29+
convert -resize 48x48 art/icon.png res/drawable-mdpi/icon.png
30+
convert -resize 72x72 art/icon.png res/drawable-hdpi/icon.png
31+
convert -resize 96x96 art/icon.png res/drawable-xhdpi/icon.png
32+
convert -resize 144x144 art/icon.png res/drawable-xxhdpi/icon.png
33+
convert -resize 192x192 art/icon.png res/drawable-xxxhdpi/icon.png
34+
35+
assets/ld/earth.jpg:
36+
mkdir -p assets/ld
37+
convert -resize 2048x1024 assets/hd/earth.jpg assets/ld/earth.jpg
38+
convert -resize 2048x1024 assets/hd/seas.jpg assets/ld/seas.jpg
39+
convert -resize 2048x1024 assets/hd/clouds.png assets/ld/clouds.png
40+
convert -resize 2048x1024 assets/hd/elevation.jpg assets/ld/elevation.jpg
41+
convert -resize 2048x1024 assets/hd/lights.jpg assets/ld/lights.jpg

lib/gamnit/examples/globe/README

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Sample gamnit app rendering the earth with elevated surface, cloud layer, city lights and Phong lighting.
2+
3+
# Possible variations
4+
5+
It would have been possible to achieve the same result with different approaches.
6+
They should be explored as they could provide better performances or be easier to understand.
7+
8+
* Use a single set of vertices, normals and indices because we only need spheres.
9+
* Alternatively, use one program per sphere.
10+
11+
# Art
12+
13+
Images credit: NASA, Visible Earth
856 KB
Loading
1.1 MB
Binary file not shown.

0 commit comments

Comments
 (0)