11 |
|
|
12 |
/** A loader for Mipzip files: files of zipped, |
/** A loader for Mipzip files: files of zipped, |
13 |
* compressed mipmap levels. |
* compressed mipmap levels. |
14 |
|
* <p> |
15 |
|
* RESPONSIBILITIES: Manage the combination of |
16 |
|
* a single texture and a mipzip file, creating a "virtualized" texture |
17 |
|
* of which better-quality versions can be loaded and discarded at will. |
18 |
*/ |
*/ |
19 |
public class MipzipLoader { |
public class MipzipLoader { |
20 |
public static boolean dbg = false; |
public static boolean dbg = false; |
21 |
final static void pa(String s) { System.out.println(s); } |
final static void pa(String s) { System.out.println(s); } |
22 |
|
|
23 |
private int goalBaseLevel = 0; |
private int goalBaseLevel = 0; |
24 |
|
private int currentBaseLevel = 0; |
25 |
|
|
26 |
private Background bg = null; |
private Background bg = null; |
27 |
private float priority = 0; |
private float priority = 0; |
38 |
*/ |
*/ |
39 |
private final String texFormat; |
private final String texFormat; |
40 |
|
|
41 |
|
private int bitsPerTexel; |
42 |
|
|
43 |
/** The opened Zip file. At any given |
/** The opened Zip file. At any given |
44 |
* time, may be null or open. |
* time, may be null or open. |
45 |
* Use getZipFile() to get it inside these classes. |
* Use getZipFile() to get it inside these classes. |
73 |
STATE_INTEXTURE = 3; |
STATE_INTEXTURE = 3; |
74 |
|
|
75 |
/** A single mipmap level. |
/** A single mipmap level. |
76 |
|
* Responsibilities: Know if loaded, load data, teximage data, discard. |
77 |
*/ |
*/ |
78 |
private class Level implements Runnable { |
private class Level implements Runnable { |
79 |
|
|
152 |
|
|
153 |
} |
} |
154 |
|
|
155 |
|
/** Set the texture parameter "BASE_LEVEL" to the given value. |
156 |
|
*/ |
157 |
synchronized private void setBaseLevel(int level) { |
synchronized private void setBaseLevel(int level) { |
158 |
tex.setTexParameter("TEXTURE_2D", "TEXTURE_BASE_LEVEL", level); |
tex.setTexParameter("TEXTURE_2D", "TEXTURE_BASE_LEVEL", level); |
159 |
|
currentBaseLevel = level; |
160 |
} |
} |
161 |
|
|
162 |
/** Set the base level synchronously: load or discard. |
/** Set the base level synchronously: load or discard. |
163 |
|
* @param level The base level: the lowest-detail level to be loaded |
164 |
*/ |
*/ |
165 |
synchronized public void loadToBaseLevelSynch(int level) throws IOException { |
synchronized public void loadToBaseLevelSynch(int level) throws IOException { |
166 |
for(int i=0; i<level; i++) { |
for(int i=0; i<level; i++) { |
175 |
|
|
176 |
/** Set the base level goal for |
/** Set the base level goal for |
177 |
* asynchronous loading. |
* asynchronous loading. |
178 |
|
* Calling this method starts the asynchronous process of loading |
179 |
|
* or discarding |
180 |
|
* mipmap levels. |
181 |
|
* @param level The base level to move towards |
182 |
|
* @param bg The background thread in which to run the part of loading |
183 |
|
* which does not need to be in the OpenGL thread. |
184 |
|
* @param priority The priority to pass to the background thread and UpdateManager |
185 |
*/ |
*/ |
186 |
synchronized public void setGoalBaseLevel(int level, |
synchronized public void setGoalBaseLevel(int level, |
187 |
Background bg, float priority) { |
Background bg, float priority) { |
232 |
for(int i=0; i<goalBaseLevel && i < levels.length-1; |
for(int i=0; i<goalBaseLevel && i < levels.length-1; |
233 |
i++) { |
i++) { |
234 |
if(levels[i].state != STATE_NONE) { |
if(levels[i].state != STATE_NONE) { |
235 |
|
if(currentBaseLevel <= i) |
236 |
|
setBaseLevel(i+1); |
237 |
levels[i].discard(); |
levels[i].discard(); |
238 |
AbstractUpdateManager.doWhenIdle(r_runGL, |
AbstractUpdateManager.doWhenIdle(r_runGL, |
239 |
priority); |
priority); |
250 |
} |
} |
251 |
} |
} |
252 |
|
|
253 |
|
/** Get the OpenGL texture for this MipzipLoader. |
254 |
|
* The only operations on the texture should be |
255 |
|
* drawing it, or setting the following OpenGL parameters: |
256 |
|
* <ul> |
257 |
|
* <li> filtering modes |
258 |
|
* <li> LOD min, max, bias |
259 |
|
* <li> shadow parameters |
260 |
|
* </ul> |
261 |
|
* Texture base level and max level and the actual texture |
262 |
|
* images should not be touched. |
263 |
|
*/ |
264 |
|
public GL.Texture getTexture() { |
265 |
|
return tex; |
266 |
|
} |
267 |
|
|
268 |
|
/** Get the amount of memory currently used. |
269 |
|
* @return Memory, in bytes |
270 |
|
*/ |
271 |
|
public int getMemory() { |
272 |
|
return getMemory(currentBaseLevel); |
273 |
|
} |
274 |
|
|
275 |
|
/** Get the amount of memory used if given level is loaded. |
276 |
|
* @return Memory, in bytes |
277 |
|
*/ |
278 |
|
public int getMemory(int level) { |
279 |
|
int bytesForLevel = levels[level].size.width * levels[level].size.height * bitsPerTexel / 8; |
280 |
|
int totalBytes = (bytesForLevel * 4) / 3; |
281 |
|
return totalBytes; |
282 |
|
} |
283 |
|
|
284 |
|
/** Get the level that uses at most given amount of memory. |
285 |
|
* @return The level index. |
286 |
|
*/ |
287 |
|
public int getLevelBytes(int memory) { |
288 |
|
for(int i = 0; i<levels.length; i++) { |
289 |
|
if(getMemory(i) <= memory) return i; |
290 |
|
} |
291 |
|
return levels.length-1; // XXX |
292 |
|
} |
293 |
|
|
294 |
|
float LG2 = (float)Math.log(2); |
295 |
|
/** Get the level that needs to be used to obtain |
296 |
|
* the given quality. |
297 |
|
* |
298 |
|
* Quality = pixels per texcoord unit, i.e. 1 = |
299 |
|
* the whole texture is shown in 1 pixel, 100 = texture |
300 |
|
* shown in 100x100 square (or like). |
301 |
|
*/ |
302 |
|
public int getLevelForQuality(float quality) { |
303 |
|
int maxdim = Math.max(levels[0].size.height, levels[0].size.width); |
304 |
|
float ratio = maxdim / quality; |
305 |
|
int l = (int)(Math.log(ratio) / LG2); |
306 |
|
l = Math.max(0, l); |
307 |
|
l = Math.min(levels.length-1, l); |
308 |
|
return l; |
309 |
|
} |
310 |
|
|
311 |
|
public float getQuality() { |
312 |
|
return getQuality(currentBaseLevel); |
313 |
|
} |
314 |
|
|
315 |
|
public float getQuality(int level) { |
316 |
|
int maxdim = Math.max(levels[level].size.height, |
317 |
|
levels[level].size.width); |
318 |
|
return maxdim; |
319 |
|
} |
320 |
|
|
321 |
|
/** Get the number of mipmap levels in this mipzip. |
322 |
|
*/ |
323 |
|
public int getNLevels() { |
324 |
|
return levels.length; |
325 |
|
} |
326 |
|
|
327 |
|
/** Get the size, in texels, of a texture level. |
328 |
|
* The return value must not be altered! |
329 |
|
*/ |
330 |
|
public Dimension getLevelSize(int level) { |
331 |
|
return levels[level].size; |
332 |
|
} |
333 |
|
|
334 |
/** Create a new MipzipLoader for the given mipzip file. |
/** Create a new MipzipLoader for the given mipzip file. |
335 |
*/ |
*/ |
336 |
public MipzipLoader(File mipzipFile) throws IOException { |
public MipzipLoader(File mipzipFile) throws IOException { |
337 |
this.mipzipFile = mipzipFile; |
this.mipzipFile = mipzipFile; |
338 |
this.tex = GL.createTexture(); |
this.tex = GL.createTexture(); |
339 |
ZipFile f = getZipFile(); |
ZipFile f = getZipFile(); |
340 |
this.texFormat = f.getEntry("texformat").getComment(); |
if(dbg) { |
341 |
|
pa("Creating new mipzip loader: "+mipzipFile); |
342 |
|
for(Enumeration e = f.entries(); e.hasMoreElements(); ) { |
343 |
|
pa("Entry: " + e.nextElement()); |
344 |
|
} |
345 |
|
} |
346 |
|
ZipEntry e = f.getEntry("texformat"); |
347 |
|
if(e == null) throw new IOException("Invalid format: no texformat in mipzip"); |
348 |
|
this.texFormat = e.getComment(); |
349 |
ArrayList l = new ArrayList(); |
ArrayList l = new ArrayList(); |
350 |
for(int i=0; i<100; i++) { |
for(int i=0; i<100; i++) { |
351 |
ZipEntry e = f.getEntry(""+i); |
e = f.getEntry(""+i); |
352 |
if(e == null) break; |
if(e == null) break; |
353 |
l.add(new Level(i, e.getComment())); |
l.add(new Level(i, e.getComment())); |
354 |
} |
} |
357 |
levels[levels.length-1].size.height != 1 ) { |
levels[levels.length-1].size.height != 1 ) { |
358 |
throw new IOException("Not all levels there!"); |
throw new IOException("Not all levels there!"); |
359 |
} |
} |
360 |
|
bitsPerTexel = GL.bitsPerTexel(texFormat); |
361 |
|
if(bitsPerTexel < 0) { |
362 |
|
if(dbg) pa("Warning: memory consumption for "+texFormat+" not known, assuming 32bpt"); |
363 |
|
bitsPerTexel = 32; |
364 |
|
} |
365 |
|
currentBaseLevel = levels.length; |
366 |
} |
} |
367 |
|
|
368 |
} |
} |