Trouble with texture

Hello everyone,

I am trying to make a texture for a building. To describe my situation in better detail, imagine using a default cube like the one you find in Blender 3D, and then scaling it to create variable-sized buildings. Then, I need in 4 of the 6 faces to apply a texture of windows such that the windows repeat instead of stretching.

I know that this could be in the manual, however all of my attempts to achieve the desired effect have failed me in one way or another, such that I am not being able to apply the texture to all the desired faces, or doing so but having the texture stretch instead of repeat, and sometimes centered awkwardly.

While this is my main question, I do have a second question (but not as important) on how easy it is to implement a normal map in Panda3D… I am not experienced in normal mapping by any means nor do I understand well how it works, but from what I have seen (mostly outside of Panda3D) it seems to be that after you generate the “normal” image off the texture you then apply it to the surface with the texture specified as being a normal map. Could such procedure be used in Panda3D and if so, how?

Thanks in advance,
Okal

As to applying your textures and having them repeat, you might apply them as you did when they ended up stretched, then set a texture-scale such that they repeat horizontally. Something like this:

myModel.setTexture(myTexture)
myModel.setTexScale(TextureStage.getDefault(), 4, 1)

(Note that the above is untested, so it’s possible that I’ve made mistakes in it!)

For more information, see this page.

As to normal-maps, you can do that indeed, I do believe! See this page for more information.

Note that having normal maps take visible effect calls for adding at least one (non-ambient-)light to the scene, and starting the shader generator–or, of course, writing your own shaders.

Thanks a lot for the idea and the sources, this is very useful to me. I will attempt to implement it tomorrow although I fear this might not work as the texture does stretch by different amounts depending on the side (as I can make my 1x1x1 cube have any size by stretching on rescaling), which means I will need at minimum 2 different textures (as in the 4 sides of the cube, in my case, left, right, front, back, a single texture can cover the front and back and another can cover the left side and right side).
This is what I have as of yet:

  • Let “Box.egg” be the default cube one may find by simply starting up Blender, and “Texture.png” any 256 by 256 sized image

        self.box = self.loader.loadModel("Box.egg")
        self.box.reparentTo(self.render)
        # This is just an example, could be any size. 
        self.box.setScale(10, 10, 20)
        self.box.setPos(0, 0, 0)

        textr = loader.loadTexture('Texture.png')
        # For some reason the following line doesn't work so I am using the getDefault() method.
        #ts = TextureStage('ts')
        self.box.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition)
        self.box.setTexture(textr, 1)
        self.box.setTexTransform(TextureStage.getDefault(), TransformState.makeHpr(VBase3(0, 90, 0)))
        # Could be more or less depending on the image and desired effect.
        self.box.setTexTransform(TextureStage.getDefault(), TransformState.makeScale2d(VBase2(1,1)))
        textr.setWrapU(Texture.WM_repeat)
        textr.setWrapV(Texture.WM_repeat)

If I could apply two different Texture Stages in my cube then by rotating them differently I will get my desired effect, however doing:

 ts = TextureStage('ts')

doesn’t seem to work thus I am left using the default texture stage and since it is just one I cannot have it be in two different orientations.

Hmm… I presume that the proportion between one pair of opposite sides and the other might vary from building to building, and thus that you can’t just alter the UVs of the box in Blender.

So, given that, you could potentially–in no particular order–use the geometry-editing classes that Panda provides to edit the UVs; or model your box as two pairs of opposing sides, then fetch them from the loaded model via the “find” method and apply separate scales via separate texture-stages; or use a custom shader to apply your textures, perhaps using the model’s scale to determine UV-coordinates. (That being off the top of my head, and untested–there may be other options, and I don’t know that all of the above will work!)

These are all great ideas and I will give them a try. I tried something similar earlier but I could not get it to work (I assigned each face to a vertex group but I had no way of getting the desired surface from the vertex group in Panda). At the end of the egg file however there is the following information specifying each surface:

<Polygon> {
      <MRef> { Material }
      <Normal> {0.000000 0.000000 -1.000000}
      <VertexRef> { 0 1 2 3 <Ref> { Cube }} 
    }
    <Polygon> {
      <MRef> { Material }
      <Normal> {0.000000 0.000000 1.000000}
      <VertexRef> { 4 5 6 7 <Ref> { Cube }} 
    }
    <Polygon> {
      <MRef> { Material }
      <Normal> {1.000000 -0.000000 0.000000}
      <VertexRef> { 8 9 10 11 <Ref> { Cube }} 
    }
    <Polygon> {
      <MRef> { Material }
      <Normal> {-0.000000 -1.000000 -0.000000}
      <VertexRef> { 12 13 14 15 <Ref> { Cube }} 
    }
    <Polygon> {
      <MRef> { Material }
      <Normal> {-1.000000 0.000000 -0.000000}
      <VertexRef> { 16 17 18 19 <Ref> { Cube }} 
    }
    <Polygon> {
      <MRef> { Material }
      <Normal> {0.000000 1.000000 0.000000}
      <VertexRef> { 20 21 22 23 <Ref> { Cube }} 
    }

Is there any method of using this information to assign the texture to a specific surface?

Hmm… That I don’t know offhand, I’m afraid–I’ll leave that for someone else to perhaps answer. Sorry! :/

As to those ideas that I gave, I strongly recommend the “modelling the box as two pairs of sides” approach–it should be by far the simplest. The custom shader should be fairly straightforward, depending on your requirements–but may also be overkill. The geometry-editing classes are likely better reserved for more advanced or complicated purposes.

How to overlap the texture determines the unwrap pattern on the model. Which is set by the 3D editor, you did not specify which model you using 3D editor or program generated.

While the idea of modelling the box as three pairs of sides is by far the simplest way to do this, it does mean that our final adjacent surfaces won’t share the same vertices which introduces the problem of T-junctions in lower anti-aliasing settings.

I have managed to get what I wanted but the way I did it is much more complicated than it has any right of being. Nonetheless I will describe what I did here in case anyone finds this useful in the future.

First things first, I unwrapped the Blender3D cube such that each face takes up the entire size of the UV map. Then after exporting the egg file, I added 6 groups to the 6 polygons, like this:

<Group> Top {
	<Polygon> {
	  <MRef> { Material }
	  <Normal> {0.000000 0.000000 -1.000000}
	  <VertexRef> { 0 1 2 3 <Ref> { Cube }} 
	}
}

<Group> Bottom {
	<Polygon> {
	  <MRef> { Material }
	  <Normal> {0.000000 0.000000 1.000000}
	  <VertexRef> { 4 5 6 7 <Ref> { Cube }} 
	}
}
<Group> Right {
	<Polygon> {
	  <MRef> { Material }
	  <Normal> {1.000000 -0.000000 0.000000}
	  <VertexRef> { 8 9 10 11 <Ref> { Cube }} 
	}
}
<Group> Back {
	<Polygon> {
	  <MRef> { Material }
	  <Normal> {-0.000000 -1.000000 -0.000000}
	  <VertexRef> { 12 13 14 15 <Ref> { Cube }} 
	}
}
<Group> Left {
	<Polygon> {
	  <MRef> { Material }
	  <Normal> {-1.000000 0.000000 -0.000000}
	  <VertexRef> { 16 17 18 19 <Ref> { Cube }} 
	}
}
<Group> Front {
	<Polygon> {
	  <MRef> { Material }
	  <Normal> {0.000000 1.000000 0.000000}
	  <VertexRef> { 20 21 22 23 <Ref> { Cube }} 
	}
}

And then I implement the textures in python and individually resize them as follows:

        textr = loader.loadTexture('Window.png')
        
        self.box.find("**/Right").setTexture(textr, 1)
        self.box.find("**/Right").setTexTransform(TextureStage.getDefault(),
                                                  TransformState.makeScale2d(VBase2(self.box.getScale().getZ(),
                                                                                    self.box.getScale().getY())))

        self.box.find("**/Left").setTexture(textr, 1)
        self.box.find("**/Left").setTexTransform(TextureStage.getDefault(),
                                                  TransformState.makeScale2d(VBase2(self.box.getScale().getZ(),
                                                                                    self.box.getScale().getY())))

        self.box.find("**/Front").setTexture(textr, 1)
        self.box.find("**/Front").setTexTransform(TextureStage.getDefault(),
                                                  TransformState.makeScale2d(VBase2(self.box.getScale().getZ(),
                                                                                    self.box.getScale().getX())))

        self.box.find("**/Back").setTexture(textr, 1)
        self.box.find("**/Back").setTexTransform(TextureStage.getDefault(),
                                                  TransformState.makeScale2d(VBase2(self.box.getScale().getZ(),
                                                                                    self.box.getScale().getX())))

Did you actually associate the texture with that texture stage for the model, like so:

        self.box.setTexture(ts, textr, 1)

If the texture is associated with 2 different texture stages for the model, then it could indeed work if you apply a different texture transform for each stage.

Oh, I think I only used:

self.box.setTexture(ts, textr)

instead of:

self.box.setTexture(ts, textr, 1)

so this may be the reason. However even if this works there is still a small (not so relevant) problem (or at least was with the way I had it implemented) and that was if the surface size was not an exact multiple of the texture size then the texture would stretch to the adjacent sides connecting the two opposite sides with the applied image texture.

Nonetheless this is useful to know and essential if I am to use normal maps as the default texture stage can’t be both.