From e0297929b69c94a9d067ce66340137c5e6f17b9e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 17 Sep 2015 09:47:47 -0400 Subject: [PATCH] lib/gamnit: intro texture support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- lib/gamnit/display_android.nit | 28 ++++++ lib/gamnit/display_linux.nit | 24 ++++- lib/gamnit/gamnit.nit | 1 + lib/gamnit/textures.nit | 188 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 lib/gamnit/textures.nit diff --git a/lib/gamnit/display_android.nit b/lib/gamnit/display_android.nit index d0e5235..3997c4a 100644 --- a/lib/gamnit/display_android.nit +++ b/lib/gamnit/display_android.nit @@ -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 diff --git a/lib/gamnit/display_linux.nit b/lib/gamnit/display_linux.nit index 109ff51..e79d22f 100644 --- a/lib/gamnit/display_linux.nit +++ b/lib/gamnit/display_linux.nit @@ -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 diff --git a/lib/gamnit/gamnit.nit b/lib/gamnit/gamnit.nit index 079062d..615e491 100644 --- a/lib/gamnit/gamnit.nit +++ b/lib/gamnit/gamnit.nit @@ -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 index 0000000..04efeb1 --- /dev/null +++ b/lib/gamnit/textures.nit @@ -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 -- 1.7.9.5