lib/gamnit: intro texture support
authorAlexis Laferrière <alexis.laf@xymus.net>
Thu, 17 Sep 2015 13:47:47 +0000 (09:47 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Thu, 10 Dec 2015 20:32:48 +0000 (15:32 -0500)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

lib/gamnit/display_android.nit
lib/gamnit/display_linux.nit
lib/gamnit/gamnit.nit
lib/gamnit/textures.nit [new file with mode: 0644]

index d0e5235..3997c4a 100644 (file)
@@ -22,8 +22,10 @@ module display_android is
 end
 
 import ::android
+intrude import android::load_image
 
 private import gamnit::egl
+intrude import textures
 
 redef class GamnitDisplay
 
@@ -45,3 +47,29 @@ redef class GamnitDisplay
 
        redef fun close do close_egl
 end
+
+redef class GamnitAssetTexture
+
+       redef fun load_from_platform
+       do
+               jni_env.push_local_frame 4
+
+               var asset_manager = app.asset_manager
+               var bmp = asset_manager.bitmap(path)
+               if bmp.is_java_null then
+                       error = new Error("Failed to load texture at '{path}'")
+                       jni_env.pop_local_frame
+                       return
+               end
+
+               var buf = bmp.copy_pixels
+               loaded = true
+               width = bmp.width.to_f
+               height = bmp.height.to_f
+               var pixels = buf.native_array
+
+               load_from_pixels(pixels, bmp.width, bmp.height, gl_RGBA)
+
+               jni_env.pop_local_frame
+       end
+end
index 109ff51..e79d22f 100644 (file)
@@ -19,7 +19,8 @@ import sdl
 import x11
 
 import egl # local to gamnit
-import display
+intrude import display
+intrude import textures
 
 redef class GamnitDisplay
 
@@ -89,3 +90,24 @@ redef class GamnitDisplay
                return x11_display
        end
 end
+
+redef class GamnitAssetTexture
+
+       redef fun load_from_platform
+       do
+               var path = "assets" / path # TODO use app.assets_dir
+               var sdl_tex = new SDLImage.from_file(path)
+
+               if sdl_tex.address_is_null then
+                       error = new Error("Failed to load texture at '{path}'")
+                       return
+               end
+
+               self.width = sdl_tex.width.to_f
+               self.height = sdl_tex.height.to_f
+               var format = if sdl_tex.amask > 0 then gl_RGBA else gl_RGB
+               var pixels = sdl_tex.pixels
+
+               load_from_pixels(pixels, sdl_tex.width, sdl_tex.height, format)
+       end
+end
index 079062d..615e491 100644 (file)
@@ -18,6 +18,7 @@ module gamnit
 import app
 
 import display
+import textures
 
 import gamnit_android is conditional(android)
 
diff --git a/lib/gamnit/textures.nit b/lib/gamnit/textures.nit
new file mode 100644 (file)
index 0000000..04efeb1
--- /dev/null
@@ -0,0 +1,188 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to load textures, create subtextures and manage their life-cycle
+module textures
+
+import display
+
+# Abstract gamnit texture
+abstract class Texture
+
+       # Prepare a texture located at `path` within the `assets` folder
+       new (path: Text) do return new GamnitAssetTexture(path.to_s)
+
+       # Prepare and load a colorful small texture of 2x2 pixels
+       new checker
+       do
+               var pixels = [0xFFu8, 0x00u8, 0x00u8,
+                             0x00u8, 0xFFu8, 0x00u8,
+                             0x00u8, 0x00u8, 0xFFu8,
+                             0xFFu8, 0xFFu8, 0xFFu8]
+               var cpixels = new CByteArray.from(pixels)
+
+               var tex = new GamnitRootTexture
+               tex.width = 2.0
+               tex.height = 2.0
+               tex.load_from_pixels(cpixels.native_array, 2, 2, gl_RGB)
+
+               cpixels.destroy
+               return tex
+       end
+
+       # Root texture of which `self` is derived
+       fun root: GamnitRootTexture is abstract
+
+       # Width in pixels of this texture
+       var width = 0.0
+
+       # Height in pixels of this texture
+       var height = 0.0
+
+       # Load this texture, force reloading it if `force`
+       fun load(force: nullable Bool) do end
+
+       # Last error on this texture
+       var error: nullable Error = null
+
+       # OpenGL handle to this texture
+       var gl_texture: Int is noinit
+
+       # Prepare a subtexture from this texture
+       fun subtexture(left, top, width, height: Numeric): GamnitSubtexture
+       do
+               # We want only floats
+               left = left.to_f
+               top = top.to_f
+               width = width.to_f
+               height = height.to_f
+
+               # Setup the subtexture
+               var subtex = new GamnitSubtexture(root)
+               subtex.width = width
+               subtex.height = height
+
+               subtex.offset_left = offset_left + left / root.width
+               subtex.offset_top = offset_top + top / root.height
+               subtex.offset_right = offset_left + width / root.width
+               subtex.offset_bottom = offset_top + height / root.height
+
+               return subtex
+       end
+
+       private fun offset_left: Float do return 0.0
+       private fun offset_top: Float do return 0.0
+       private fun offset_right: Float do return 1.0
+       private fun offset_bottom: Float do return 1.0
+end
+
+# Texture with its own pixels
+class GamnitRootTexture
+       super Texture
+
+       redef fun root do return self
+
+       # Has this texture been loaded yet?
+       var loaded = false
+
+       init do all_root_textures.add self
+
+       private fun load_from_pixels(pixels: Pointer, width, height: Int, format: GLPixelFormat)
+       do
+               var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE)
+               if width > max_texture_size or height > max_texture_size then
+                       error = new Error("Texture {self} width or height is over the GL_MAX_TEXTURE_SIZE of {max_texture_size}")
+                       return
+               end
+
+               glPixelStorei(gl_UNPACK_ALIGNEMENT, 1)
+               var tex = glGenTextures(1)[0]
+               gl_texture = tex
+
+               glBindTexture(gl_TEXTURE_2D, tex)
+               glTexImage2D(gl_TEXTURE_2D, 0, format, width, height, 0, format, gl_UNSIGNED_BYTE, pixels)
+
+               # TODO make these settings attributes of the class
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_NEAREST)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_REPEAT)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_REPEAT)
+
+               glHint(gl_GENERATE_MIPMAP_HINT, gl_NICEST)
+               glGenerateMipmap(gl_TEXTURE_2D)
+       end
+
+       # Has this resource been deleted?
+       var deleted = false
+
+       # Delete this texture and free all its resources
+       #
+       # Use caution with this service as the subtextures may rely on the deleted data.
+       fun delete
+       do
+               if deleted or not loaded then return
+
+               deleted = true
+       end
+end
+
+# Texture loaded from the assets folder
+class GamnitAssetTexture
+       super GamnitRootTexture
+
+       # Path to this texture within the `assets` folder
+       var path: String
+
+       redef fun load(force)
+       do
+               if loaded and force != true then return
+
+               load_from_platform
+
+               loaded = true
+       end
+
+       # Partially load this texture from platform-specific features
+       #
+       # This method should fill `width`, `height` and `pixels`.
+       private fun load_from_platform is abstract
+
+       redef fun to_s do return "<{class_name} path:{path}>"
+end
+
+# Texture derived from another texture, does not own its pixels
+class GamnitSubtexture
+       super Texture
+
+       redef var root
+
+       redef fun load(force) do root.load(force)
+
+       redef var offset_left = 0.0
+       redef var offset_top = 0.0
+       redef var offset_right = 1.0
+       redef var offset_bottom = 1.0
+end
+
+redef class Sys
+       private var all_root_textures = new TextureSet
+end
+
+# Group of `Texture`
+class TextureSet
+       super HashSet[Texture]
+
+       # Load all texture of this set
+       fun load_all do for t in self do t.load
+end