Loader output

Hi everybody!

I just tried to use the loader.loadModel() function providing an empty file as input. I can’t quite get a reliable error message or exception thrown. I’m not sure why. It provided the following warning once, but I can’t reproduce it.

express(warning): zlib error in inflate: Z_BUF_ERROR

Is there a way to verify the file loaded is actually valid? Or to verify the file hasn’t actually been loaded?

Manu

loader.loadModel() returns an empty NodePath on failure. To test for this, use something like this:

model = loader.loadModel('foo.egg')
if model.isEmpty():
  raise StandardError, 'model not loadable'

David

would it make more sense to raise an exception though? Like if you open a file in python its not there.

Yah. That’s one of the things on my short list this week.

David

I second the motion to throw an exception. I’m glad to hear it’s coming. You give bad food to a Panda at least it should shout at you. :slight_smile:

model = loader.loadModel('foo.egg')
if model.isEmpty():
  raise StandardError, 'model not loadable' 

Especially because this solution doesn’t seem to work. I’m pretty sure the model being loaded is not valid: it’s zero-sized. But model.isEmpty() returns False nevertheless and therefore the exception is not thrown. Are zero-sized files a special case?

Manu

A zero-sized egg file is technically valid (it just doesn’t have any polygons in it). A zero-sized bam file, on the other hand, or a zero-sized texture will be invalid.

This means it’s difficult to determine whether the egg file you loaded was zero-sized or not. However, note that a completely nonexistent egg file will indeed be considered invalid. Do you really have the danger of accidentally loading zero-sized egg files instead of real egg files?

Let me also correct my earlier claim: on an invalid file, loader.loadModel() will return None, not an empty NodePath.

David

Ah! That explains it. As I don’t have a broken egg file (and I wouldn’t know how to make one) I was trying to “simulate it” with a zero-sized file.

Was that a Murphy’s law that says if there’s the chance for something to go wrong it will? =) In this case: it’s true, it’s unlikely. But eventually it might be the game’s users that might supply models and in that context I wouldn’t be surprised of anything.

But you confirm that if the file is zero-sized it will return an empty node rather than None?

Manu

No, even if you load an empty model it will still return a non-empty perfectly valid NodePath.

Ok, I’m now detecting zero-sized files -before- I pass them to the loader and managed to simplify my code in the process.

Given that some of the documentation is not complete though, I’d like to know: if an attempt is made to load a corrupt file through the following loader methods, what happens?
Loader.loadModel(vfsPath)
Loader.loadImage(vfsPath)
Loader.loadTexture(vfsPath)
Loader.loadSfx(vfsPath)

I seem to understand the first one will return None. Can I rely on this being the case for all of them? (the reference lists these methods as undocumented and does not provide indication of return types)

I also noticed that the loader provides unloader methods. Wouldn’t python garbage-collect items such as file handles that are no longer in use?

Manu

I think they generally return None on failure. Why not try it and see?

Unloading a model means removing it from the cache. There is a cache of recently-loaded models, so that if you load a model twice it only hits the disk the first time. Garbage collection can’t automatically empty the cache, of course.

David

Good point about trying. I couldn’t think of a way to produce corrupt files but of course it’s just a matter of opening them with an hex editor and doing some random canceling in the headers area. Thanks for pushing me on that. The result of further testing, inclusive of warnings and error messages sent by Panda is below. As you will see the only Loader method, among those tested, that returns None upon failure is loadTexture(). loadModel() sends a warning but returns a seemingly valid and not empty “libpanda.NodePath”. Maybe I didn’t break the .pz file enough? loadShader() sends an error but returns a shader object rather than None. Finally loadSound() also sends errors and also returns an object, not None. Soooo… unless I’m doing something wrong I still can’t discriminate between proper models, shaders and sounds loaded up and corrupted ones. Can I?

Here’s the (edited) output of the test script:

testLoadIntoMemory_wrongFileFormat (main.TestGeometryAssetClass) …
:express(warning): zlib error in inflate: Z_DATA_ERROR = unknown compression method
Return value of loader.loadModel(): bogusGeometry.egg.pz

testLoadIntoMemory_wrongFileFormat (main.TestImageAssetClass) …
:pnmimage:png(error): Not a PNG file
:gobj(error): Texture::read() - couldn’t read: /s/Panda3D-1.5.3/projects/hm/images/bogusImage.png
:gobj(error): Texture “/s/Panda3D-1.5.3/projects/hm/images/bogusImage.png” exists but cannot be read.
Return value of loader.loadTexture(): None

testLoadIntoMemory_wrongFileFormat (main.TestShaderAssetClass) …
:gobj(error): Shader is not in a supported shader-language.
Return value of loader.loadShader(): <libpanda.Shader object at 0x00A6A608>

testLoadIntoMemory_wrongFileFormat (main.TestSoundAssetClass) …
:movies(error): Could not open /s/Panda3D-1.5.3/projects/hm/sounds/bogusSound.wav
:audio(error): Cannot open file: /s/Panda3D-1.5.3/projects/hm/sounds/bogusSound.wav
:audio(error): Could not open audio /s/Panda3D-1.5.3/projects/hm/sounds/bogusSound.wav
Return value of loader.loadSfx(): OpenALAudioSound bogusSound.wav READY

P.S. thanks for explaining the unloading functionality. Makes perfect sense.

Manu

Hmm, yeah, it does look difficult to detect a corrupt model. Panda wasn’t really designed for differentiating between corrupt models and uncorrupt ones–it makes its best effort to load whatever you give it, and it returns back a handle to whatever it found. If it wasn’t able to load enough of a file to get any meaningful data, it returns back a handle to a trivially simple model, or an empty sound file, or whatever.

In short, Panda assumes that anything you give it is not corrupt. But, frankly, isn’t that a safe assumption? If you’re worried about the user supplying you a random filename, well, so what if you display a model with no vertices, or play back an empty sound, in response to that?

I won’t argue with you that it would be more useful if Panda gave you more diagnostics, but it does seem, at best, a very minor problem.

David

Random filename? The user won’t have the chance to select anything but existing files. So, the filename is not a problem. The problem is if the user provides a corrupt file that he or she has produced. I’d like to be able to notify them right away that the file hasn’t been loaded correctly.

I don’t want to argue either, I just want to make sure I’m understanding the boundaries I have to work with. In the context of user-generated content it’d be useful to have some API-level diagnostic that the developer can use to inform the user who generated the content. I guess the errors and warnings from Panda will appear in the python window and a user could keep an eye on it in case of unexpected behavior. It might be tricky for those running the application full screen though…

Manu

You can check if it’s bogus :
shader : getErrorFlag()
sound : length()==.0
nodepath :
getErrorFlag() can’t tell if it’s bogus. When zlib failed to inflate, the node should be marked with ETFail instead of ETOk.
But you can use :
not hasVertexColumn(InternalName.make(‘vertex’))

That’s perfect! The test cases involved are now properly succeeding because the methods they are calling are correctly reporting corrupted files! Thank you!!! Much appreciated!

Manu