Posted by
philjord on
Feb 01, 2020; 5:38am
URL: https://forum.jogamp.org/Multi-Texturing-tp4040312p4040314.html
Hi,
Thanks very much for submitting a easily understood problem.
The root of your problem is to do with the intent and usage of textures and TextureUnitStates, and generally the basic use of multi-texturing.
Multi-texturing is the term used for layering textures one on top of another with some sort of interesting operation happening between them. So it is a way to for example, make a brick wall have a lovely translucent "dirt" texture over it to make it look a bit down at heel.
So a TextureUnitState can have a TextureAttribute object attached, so that the dirt layer on top of the brick layer can be made a bit interesting by having a combine function applied (like inverting the colors or whatever).
We would also want layers where we have decals, so the brick wall could have bullet marks all over it. Decals tend to have lots of transparent texture to show the texture underneath.
Dot3Demo shows layers interacting
In the case where we have different textures applied to different areas of a big geometry the concept is one of grouping each chunk of geometry with an appearance, and the appearance simply has the texture we want attached to it.
3D design tools and rendering engines all use different names for this same concept, so an Obj file would call these Groups, Java3D calls them Shape3D.
So to do the two sides with a different texture we make 2 Shape3D objects, give each one a chunk of the single geometry data (but telling them where to start and stop in the geometry data array), and assign the texture coordinates separately and then assign the texture.
So below I've slightly modified your code to use 2 Shape3D in a single BranchGroup, but used your data exactly as it was. Because I've chopped out the 2 texture units the mapping is gone, so the constructors are slightly simpler (apart from the slightly odd array used in setTextureUnitState method call).
Because we now aren't layering textures on top of each other (with a operation to define how they merge) we actually don't need texture unit states at all, so all the calls below could go directly into the Appearance object, which supports a single texture nicely.
Finally, if you actually do want to do a layer texture with some crazy color operations I do suggest strongly that you use the gl2es2 pipeline, which supports programmable shaders, simply put
System.setProperty("j3d.rend", "jogl2es2");at the start of your code.
That way you could call the same code as below with the Appearance class swapped to SimpleShaderAppearance and then call getVertexShaderSource() and getFragmentShaderSource() you can see what a programmable equivilent to the fixed function code you are calling is.
EnvironmentMappingGLSL.javaShows a use of TextureUnitState with shaders
package test;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.io.IOException;
import java.net.URL;
import javax.swing.JFrame;
import org.jogamp.java3d.Alpha;
import org.jogamp.java3d.Appearance;
import org.jogamp.java3d.BoundingSphere;
import org.jogamp.java3d.BranchGroup;
import org.jogamp.java3d.Canvas3D;
import org.jogamp.java3d.GeometryArray;
import org.jogamp.java3d.RotationInterpolator;
import org.jogamp.java3d.Shape3D;
import org.jogamp.java3d.Texture;
import org.jogamp.java3d.TextureAttributes;
import org.jogamp.java3d.TextureUnitState;
import org.jogamp.java3d.Transform3D;
import org.jogamp.java3d.TransformGroup;
import org.jogamp.java3d.TriangleArray;
import org.jogamp.java3d.utils.image.TextureLoader;
import org.jogamp.java3d.utils.universe.SimpleUniverse;
import org.jogamp.vecmath.Point3d;
import org.jogamp.vecmath.Point3f;
import org.jogamp.vecmath.TexCoord2f;
import org.jogamp.vecmath.Vector3f;
/**
* Uses JDK 12.0.1 and Java3D version 1.7
*/
public class MultiTexTest2 extends JFrame {
static final long serialVersionUID = 1;
public static void main( String[] args ) { new MultiTexTest2(); }
public MultiTexTest2() {
setPreferredSize( new Dimension( 600, 400 ) );
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
Canvas3D canvas3D = new Canvas3D( config );
canvas3D.setBounds( 0, 0, 1000, 1000 );
add( canvas3D );
MultiTexRectangle multiTexRectangle = new MultiTexRectangle( canvas3D );
TransformGroup rectangleTrans = new TransformGroup();
rectangleTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
rectangleTrans.addChild( multiTexRectangle );
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, 8000);
RotationInterpolator rotator = new RotationInterpolator(
rotationAlpha, rectangleTrans, yAxis, 0f, (float) Math.PI*2f );
BoundingSphere bounds = new BoundingSphere( new Point3d(0, 0, 0), 600 );
rotator.setSchedulingBounds( bounds );
BranchGroup branchGroupRoot = new BranchGroup();
branchGroupRoot.addChild( rectangleTrans );
branchGroupRoot.addChild( rotator );
branchGroupRoot.compile();
SimpleUniverse simpleU = new SimpleUniverse( canvas3D );
simpleU.getViewingPlatform().setNominalViewingTransform();
simpleU.addBranchGraph( branchGroupRoot );
pack();
setVisible( true );
}
class MultiTexRectangle extends BranchGroup {
Shape3D shape0;
Shape3D shape1;
Point3f[] vertexArray = new Point3f[12]; // Vertex Coords for 2 sided rectangle
Vector3f[] normalArray = new Vector3f[12];
TexCoord2f[] textureArray0 = new TexCoord2f[6]; // Texture Coords for front of rectangle
TexCoord2f[] textureArray1 = new TexCoord2f[6]; // Texture Coords for back of rectangle
public MultiTexRectangle( Canvas3D canvas3D ) {
// data for first shape and geometry
vertexArray[0] = new Point3f(.6f, .4f, 0);
normalArray[0] = new Vector3f(0, 0, 1f);
textureArray0[0] = new TexCoord2f(1f, 1f);
vertexArray[1] = new Point3f(0, .4f, 0);
normalArray[1] = new Vector3f(0, 0, 1f);
textureArray0[1] = new TexCoord2f(0f, 1f);
vertexArray[2] = new Point3f(0, 0, 0);
normalArray[2] = new Vector3f(0, 0, 1f);
textureArray0[2] = new TexCoord2f(0f, 0f);
vertexArray[3] = new Point3f(0, 0, 0);
normalArray[3] = new Vector3f(0, 0, 1f);
textureArray0[3] = new TexCoord2f(0f, 0f);
vertexArray[4] = new Point3f(.6f, 0, 0);
normalArray[4] = new Vector3f(0, 0, 1f);
textureArray0[4] = new TexCoord2f(1f, 0f);
vertexArray[5] = new Point3f(.6f, .4f, 0);
normalArray[5] = new Vector3f(0, 0, 1f);
textureArray0[5] = new TexCoord2f(1f, 1f);
// data for second shape and geometry
vertexArray[6] = new Point3f(0, .4f, 0);
normalArray[6] = new Vector3f(0, 0, -1f);
textureArray1[0] = new TexCoord2f(0f, 1f);
vertexArray[7] = new Point3f(.6f, .4f, 0);
normalArray[7] = new Vector3f(0, 0, -1f);
textureArray1[1] = new TexCoord2f(1f, 1f);
vertexArray[8] = new Point3f(.6f, 0, 0);
normalArray[8] = new Vector3f(0, 0, -1f);
textureArray1[2] = new TexCoord2f(1f, 0f);
vertexArray[9] = new Point3f(.6f, 0, 0);
normalArray[9] = new Vector3f(0, 0, -1f);
textureArray1[3] = new TexCoord2f(1f, 0f);
vertexArray[10] = new Point3f(0, 0, 0);
normalArray[10] = new Vector3f(0, 0, -1f);
textureArray1[4] = new TexCoord2f(0f, 0f);
vertexArray[11] = new Point3f(0, .4f, 0);
normalArray[11] = new Vector3f(0, 0, -1f);
textureArray1[5] = new TexCoord2f(0f, 1f);
URL image0, image1;
try {
image0 = new URL( "
http://www.relativitysimulation.com/Images/Soup-Label.jpg" );
image1 = new URL( "
http://www.relativitysimulation.com/Images/Sky-Ground.jpg" );
}
catch ( IOException e ) {
System.out.println("FAILURE trying to load image files from URLs. Can't apply multi-texture.");
System.out.println( e );
return;
}
TextureLoader texLoader0 = new TextureLoader( image0, canvas3D );
Texture texture0 = texLoader0.getTexture();
TextureAttributes texAttrib0 = new TextureAttributes();
TextureUnitState unitState0 = new TextureUnitState( texture0, texAttrib0, null );
TextureLoader texLoader1 = new TextureLoader( image1, canvas3D );
Texture texture1 = texLoader1.getTexture();
TextureAttributes texAttrib1 = new TextureAttributes();
TextureUnitState unitState1 = new TextureUnitState( texture1, texAttrib1, null );
Appearance appearance0 = new Appearance();
Shape3D shape0 = new Shape3D();
GeometryArray geometryArray0 = new TriangleArray( 6, TriangleArray.COORDINATES | TriangleArray.NORMALS
| TriangleArray.TEXTURE_COORDINATE_2 );
geometryArray0.setCoordinates( 0, vertexArray, 0, 6 );
geometryArray0.setNormals( 0, normalArray, 0, 6 );
geometryArray0.setTextureCoordinates( 0, 0, textureArray0 ); // Soup-Label goes on rectangle front
shape0.setGeometry( geometryArray0 );
appearance0.setTextureUnitState( new TextureUnitState[] { unitState0 } );
shape0.setAppearance( appearance0 );
addChild(shape0);
Appearance appearance1 = new Appearance();
Shape3D shape1 = new Shape3D();
GeometryArray geometryArray1 = new TriangleArray( 6, TriangleArray.COORDINATES | TriangleArray.NORMALS
| TriangleArray.TEXTURE_COORDINATE_2 );
geometryArray1.setCoordinates( 0, vertexArray, 6, 6 );
geometryArray1.setNormals( 0, normalArray, 6, 6 );
geometryArray1.setTextureCoordinates(0, 0, textureArray1 ); // Sky-Ground goes on rectangle back
shape1.setGeometry( geometryArray1 );
appearance1.setTextureUnitState(new TextureUnitState[] { unitState1 });
shape1.setAppearance( appearance1 );
addChild(shape1);
}
}
}