Bug in Milkshape model texture loading code?

Not sure if this is a bug, but shouldn’t



private TextureState loadTexture(String file) {
    URL fileURL = null;
    fileURL = MilkshapeASCIIModel.class.getClassLoader().getResource(textureDirectory + file);

    if(!textureDirectory.endsWith("/")) {
        textureDirectory += "/";
    }



read instead in this order, so the check for having a slash in the proper place occurs before it can go wrong?


private TextureState loadTexture(String file) {
    URL fileURL = null;

    if(!textureDirectory.endsWith("/")) {
        textureDirectory += "/";
    }

    fileURL = MilkshapeASCIIModel.class.getClassLoader().getResource(textureDirectory + file);



Indeed, I think it also will fail if textureDirectory has not been set (in which case, perhaps, '.' may be a better default), adding a "/" to the end would be setting the texture directory to "/". This logic would address that


private TextureState loadTexture(String file) {
    URL fileURL = null;

    if(!textureDirectory.equals("") && !textureDirectory.endsWith("/")) {
        textureDirectory += "/";
    }

    fileURL = MilkshapeASCIIModel.class.getClassLoader().getResource(textureDirectory + file);

One other issue I note is that the latest Milkshape3D (am using 1.7.0b) writes out texture filenames in the same directory as the MSASCII file in the form ".foo.png"



To avoid hand-massaging MSASCII files to make them jME ready, perhaps loadTexture() really needs to also remove any prefix of . or ./ from the texture filename?



tone

One other observation… possibly ignorant.



I’m looking at how Milkshape models are loaded and see the code for the joint controller. Does a joint controller only help with animation? If so, could you not defer the newing up of a joint controller until you see that there are more than 0 bones?



Most things I’d model and convert for jME using Milkshape would have no animations, and it would be good to spare adding a jointcontroller to every model I import into jME.



to sum up in pseudocode:



load(URL filename)

try {
 // remove the jointController = from here
  int numFrames, curFrame;
 while (line=getNextLine()) {
    if (line.startsWith("Frames:"))
      numFrames = parseInt();
    else
    if (line.startsWith("Frame:"))
      curFrame=parseInt();
    else
    if (line.startsWith("Meshes:")) {
      int numMesh = parseInt();
      meshes = new JointMesh[numMesh];
      parseMeshes(reader);
      // comment out jointController.setMeshes()
    }
    else
    if (line.startsWith("Materials:")) { same old code; }
    else
    if (line.startsWith("Bones:")) {
      int numJoints = parseInt();
      if (numJoints > 0) { // now we NEED a jointController!
        joinController = new jointDeformationController();
       
           // sock in the values we have saved in locals
        jointController.setTotalFrames(numFrames);
        jointController.setCurrentFrame(curFrame);
        jointController.setMeshes(this.meshes);

        jointController.setJoints(new DeformationJoints[numJoints]);
        parseJoints(reader);
      }
    }
 }
} catch (foo) { }

if (jointController != null) {
  jointController.setUpAnimations();
  this.addController(joingController);
}



If I've missed the point, or the structure of the MSASCII format is more sophisticated than my pseudocode can mange.... forgive me.

tone

Good suggestions, agree with you on all three points. I’ll get those fixed soon. Unfortunately, I’m under much strain at work right now. However, next week I’m on TDY and traveling to Ft. Hood. Which means, I’ll be spending my nights in a hotel room. So, I’ve gotten jME development set up on the work laptop and will be working on it a lot next week. There I will focus on improving existing features. I’m taking a bit of a break implementing new ones until I feel more comfortable about some of the older ones.



This means… CLOD will be delayed, but I will be supporting user’s requests/complaints. So keep them coming.

Speaking of spending a week in a hotel working on jMe, what’s motivating you to build all this? Do you have a game of your own you’ll start once jMe is ‘ready’, or game library development just your thing? (Whatever the reason, keep it up!)

what's motivating you to build all this


Boredom. ;)

You do amazing work mojo. I lack the chops to understand the big picture of how 3D happens, but I can readily identify places in a fairly clear codebase where modularization can be extended or the philosophy of use can be made more apparent without resort to documentation that may not exist or may not be up to date.



I know that control of their product vision is important to many developers, but if you ever want help shaking these sorts of refinements into place, I stand ready to help. I could easily perform this particular item if you could help frame a test procedure to ensure the work had been done right.



tone

I am always willing to accept any help I can get. Particularly in the 3D model loaders. So, I appreciate any opinions/bugs/enhancements you can find and/or recommend, as well as any code you are willing to contribute (developer status is readily available to those that show they are willing to contribute).



As for test procedures… well, as you can see we don’t do any unit testing (which we probably should). We just write up a test that shows it working and then rely on people like yourself to find errors. :slight_smile: While not a valid test plan, this is all just fun for us, so we tend to skip the boring bits.



Like I said, I am going to work hard next week to fix the Milkshape errors, and enhance clone and clonenode to make it something useful. I’ll look into documenting the times in the main loop better as well.

your fun thing might turn out to be a pretty good commercially viable tool,

thats impressive for something started out of boredom.

I’m a big fan of automated tests. I might just be a lousy programmer, but I find having unit tests protects me from a lot of my own mistakes, particularly as the codebase gets large (and certainly in multi-developer scenarios, where you’re protecting yourself from the mistakes of others). The difference in quality is so satisfying that I feel the effort is almost always worthwhile, even on small, for-fun projects.



If you think it would be of use, I’d like to contribute a simple unit testing setup to the project, including a test coverage report (courtesy of Grobo Utils), and start writing some tests.



There’s a lot about jMe that’s really only testable manually - such as whether the you can hear anything when you run the sound system on a Mac - but as the codebase gets larger, more and more code is being laid down that’s easily scaffolded with automated tests (model loaders, CLOD, timing, network, scenegraph traversal, performance metrics, math classes, etc.)

We could really use a devoted tester on the team. :slight_smile:

I have a couple of models which use Milkshape materials that don’t have any texture images associated. These are rendered as bright white, for some reason.



I’m trying to track down why, but I don’t know enough about how jME renders yet. The materials are correctly assigned to the mesh, and the material properties are read from the file correctly.



I wonder if this code, from LWJGLTextureState, has something to do with it:


public void set() {
   if (isEnabled()) {
      for (int i = 0; i < getNumberOfUnits(); i++) {
         if (getTexture(i) == null) {
            continue;
         }
         GL13.glActiveTexture(GL13.GL_TEXTURE0 + i);

         GL11.glEnable(GL11.GL_TEXTURE_2D);

         Texture texture = getTexture(i);
         if (texture == null) {
            GL11.glDisable(GL11.GL_TEXTURE_2D);
            return;
         }



The code that calls glDisable for null textures is never reached, as there's a continue at the top of the method.

Incidentally, if this turns out to be the bug, this situation is one of the main reasons I was disapointed when LWJGL went to static methods for GL calls. A good way to test this sort of code would be to provide a mock object for GL11, which could verify that glDisable(..) was called for untextured texture states. Unfortunately, that's a lot harder now - I can't think of a way to mock a static method.

If jME gets a lot of bugs at this level, then it might be worth slipping a lightweight, non-static wrapper around the LWJGL APIs so that mocks can be easily substituted. Another major plus there would be that the unit tests could run without instantiating a real OpenGL display system, which would improve their speed enormously (and make it possible to run on a headless build server, for example).

P.S. There's an unused line in MilkshapeASCIIModel.java:

String texture = line.substring(1, line.length() - 1);

Hi, forgive me if I’m not understanding your issue, but if you don’t assign a texture to an object it will be white. That is, with no texture assigned to a triangle, the incoming pixel (1,1,1) is not affected by a texture unit for output, so it comes out (1,1,1). However, it should still react to lighting, etc.



Can you describe what you think should be happening?

What I’m trying to achieve is normal, non texture-mapped OpenGL materials - e.g. gouraud shading based on vertex colours, as you get if you never enable textures.



It looks like somebody tried to support this in the LWJGLTextureState.set(), because this snippet of code does exactly what I need:


Texture texture = getTexture(i);
if (texture == null) {
    GL11.glDisable(GL11.GL_TEXTURE_2D);
    return;
}



Unfortunately, this is unreachable, because this precedes it:

if (getTexture(i) == null) {
    continue;
}



I'd guess that these two bits of code were written by different people.

Ah, gotcha. I’ll fix that.