04efeb114d793512b49a1707b09a01b37e879167
[nit.git] / lib / gamnit / textures.nit
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 # Services to load textures, create subtextures and manage their life-cycle
16 module textures
17
18 import display
19
20 # Abstract gamnit texture
21 abstract class Texture
22
23 # Prepare a texture located at `path` within the `assets` folder
24 new (path: Text) do return new GamnitAssetTexture(path.to_s)
25
26 # Prepare and load a colorful small texture of 2x2 pixels
27 new checker
28 do
29 var pixels = [0xFFu8, 0x00u8, 0x00u8,
30 0x00u8, 0xFFu8, 0x00u8,
31 0x00u8, 0x00u8, 0xFFu8,
32 0xFFu8, 0xFFu8, 0xFFu8]
33 var cpixels = new CByteArray.from(pixels)
34
35 var tex = new GamnitRootTexture
36 tex.width = 2.0
37 tex.height = 2.0
38 tex.load_from_pixels(cpixels.native_array, 2, 2, gl_RGB)
39
40 cpixels.destroy
41 return tex
42 end
43
44 # Root texture of which `self` is derived
45 fun root: GamnitRootTexture is abstract
46
47 # Width in pixels of this texture
48 var width = 0.0
49
50 # Height in pixels of this texture
51 var height = 0.0
52
53 # Load this texture, force reloading it if `force`
54 fun load(force: nullable Bool) do end
55
56 # Last error on this texture
57 var error: nullable Error = null
58
59 # OpenGL handle to this texture
60 var gl_texture: Int is noinit
61
62 # Prepare a subtexture from this texture
63 fun subtexture(left, top, width, height: Numeric): GamnitSubtexture
64 do
65 # We want only floats
66 left = left.to_f
67 top = top.to_f
68 width = width.to_f
69 height = height.to_f
70
71 # Setup the subtexture
72 var subtex = new GamnitSubtexture(root)
73 subtex.width = width
74 subtex.height = height
75
76 subtex.offset_left = offset_left + left / root.width
77 subtex.offset_top = offset_top + top / root.height
78 subtex.offset_right = offset_left + width / root.width
79 subtex.offset_bottom = offset_top + height / root.height
80
81 return subtex
82 end
83
84 private fun offset_left: Float do return 0.0
85 private fun offset_top: Float do return 0.0
86 private fun offset_right: Float do return 1.0
87 private fun offset_bottom: Float do return 1.0
88 end
89
90 # Texture with its own pixels
91 class GamnitRootTexture
92 super Texture
93
94 redef fun root do return self
95
96 # Has this texture been loaded yet?
97 var loaded = false
98
99 init do all_root_textures.add self
100
101 private fun load_from_pixels(pixels: Pointer, width, height: Int, format: GLPixelFormat)
102 do
103 var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE)
104 if width > max_texture_size or height > max_texture_size then
105 error = new Error("Texture {self} width or height is over the GL_MAX_TEXTURE_SIZE of {max_texture_size}")
106 return
107 end
108
109 glPixelStorei(gl_UNPACK_ALIGNEMENT, 1)
110 var tex = glGenTextures(1)[0]
111 gl_texture = tex
112
113 glBindTexture(gl_TEXTURE_2D, tex)
114 glTexImage2D(gl_TEXTURE_2D, 0, format, width, height, 0, format, gl_UNSIGNED_BYTE, pixels)
115
116 # TODO make these settings attributes of the class
117 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_NEAREST)
118 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST)
119 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_REPEAT)
120 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_REPEAT)
121
122 glHint(gl_GENERATE_MIPMAP_HINT, gl_NICEST)
123 glGenerateMipmap(gl_TEXTURE_2D)
124 end
125
126 # Has this resource been deleted?
127 var deleted = false
128
129 # Delete this texture and free all its resources
130 #
131 # Use caution with this service as the subtextures may rely on the deleted data.
132 fun delete
133 do
134 if deleted or not loaded then return
135
136 deleted = true
137 end
138 end
139
140 # Texture loaded from the assets folder
141 class GamnitAssetTexture
142 super GamnitRootTexture
143
144 # Path to this texture within the `assets` folder
145 var path: String
146
147 redef fun load(force)
148 do
149 if loaded and force != true then return
150
151 load_from_platform
152
153 loaded = true
154 end
155
156 # Partially load this texture from platform-specific features
157 #
158 # This method should fill `width`, `height` and `pixels`.
159 private fun load_from_platform is abstract
160
161 redef fun to_s do return "<{class_name} path:{path}>"
162 end
163
164 # Texture derived from another texture, does not own its pixels
165 class GamnitSubtexture
166 super Texture
167
168 redef var root
169
170 redef fun load(force) do root.load(force)
171
172 redef var offset_left = 0.0
173 redef var offset_top = 0.0
174 redef var offset_right = 1.0
175 redef var offset_bottom = 1.0
176 end
177
178 redef class Sys
179 private var all_root_textures = new TextureSet
180 end
181
182 # Group of `Texture`
183 class TextureSet
184 super HashSet[Texture]
185
186 # Load all texture of this set
187 fun load_all do for t in self do t.load
188 end