closedCylinder ends remain untextured

Found a bug? Post here.
Post Reply
ted
Posts: 57
Joined: Wed 25. Jul 2012, 22:53

closedCylinder ends remain untextured

Post by ted » Thu 9. Aug 2012, 04:45

closedCylinder caps are not textured, even when a texture matrix is specified.

Code: Select all

package throwaway;

import static de.jreality.shader.CommonAttributes.POLYGON_SHADER;
import static java.awt.Color.white;
import static java.lang.Math.PI;

import java.io.IOException;

import de.jreality.geometry.Primitives;
import de.jreality.math.Matrix;
import de.jreality.plugin.JRViewer;
import de.jreality.scene.Appearance;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.shader.DefaultGeometryShader;
import de.jreality.shader.DefaultPolygonShader;
import de.jreality.shader.ImageData;
import de.jreality.shader.RenderingHintsShader;
import de.jreality.shader.ShaderUtility;
import de.jreality.shader.Texture2D;
import de.jreality.shader.TextureUtility;
import de.jreality.util.Input;

public class CylTexApp {

	public static void main(String[]unused) {
		Appearance ap = new Appearance();
		DefaultGeometryShader dgs = (DefaultGeometryShader) ShaderUtility.createDefaultGeometryShader(ap, true);
		dgs.setShowLines(false);
		dgs.setShowPoints(false);
		dgs.setShowFaces(true);
		RenderingHintsShader rhs = ShaderUtility.createDefaultRenderingHintsShader(ap, true);
		rhs.setSeparateSpecularColor(true);
		DefaultPolygonShader dps = (DefaultPolygonShader) dgs.createPolygonShader("default");
		dps.setTransparency(0.);
		dps.setSmoothShading(true);
		dps.setAmbientCoefficient(.2);
		dps.setAmbientColor(white);
		dps.setDiffuseCoefficient(.7);
		dps.setDiffuseColor(white);
		dps.setSpecularCoefficient(.5);
		dps.setSpecularColor(white);
		dps.setSpecularExponent(15.);
		ImageData imgDat = null;
		try {
			imgDat = ImageData.load(Input.getInput("GALVANIZED_HEAVY.png"));
		} catch (IOException ex) {
			System.err.println("ERROR: Couldn't load "+"GALVANIZED_HEAVY.png");
		}
		int n = 36;
		double r = 1;
		double zmin = 0;
		double zmax = 2*PI;
		double thetamax = 2*PI;
		SceneGraphComponent cyl = Primitives.closedCylinder(n, r, zmin, zmax, thetamax );
		Texture2D tex = TextureUtility.createTexture(ap, POLYGON_SHADER,imgDat);
		tex.setTextureMatrix(new Matrix());
		cyl.setAppearance(ap);
		JRViewer.display(cyl);
	}
}
[img]galvanized-cyl-screen.png[/img]
Attachments
galvanized-cyl-screen.png
galvanized-cyl-screen.png (50.25 KiB) Viewed 1854 times

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: closedCylinder ends remain untextured

Post by gunn » Thu 9. Aug 2012, 14:00

The caps don't have any texture coordinates. It turns out I happen to have some old code to create disks with texture coordinates, either polar or rectangular. This can be easily adapted to produce the desired effect for the caps. I show how below.
cappedCyl-02.png
cappedCyl-02.png (45.89 KiB) Viewed 1851 times
Note however that the transition from the texture on the cylinder itself to that on the caps isn't continuous. One can make the transition less obvious by judicious use of texture matrices to obtain roughly the same feature size, but this is complicated since you have to go in and set texture matrices in the scene graph components containing the caps, etc., and I haven't done that here. In any case, I hope it looks better than having no texture at all.

First replace the line 489 of Primitives.java:

Code: Select all

		IndexedFaceSet disk = regularPolygon(n,0.0);
with

Code: Select all

    IndexedFaceSet disk = texturedDisk(n+1, 2, false); 
This will produce rectangular texture coordinates; for polar coordinates, use

Code: Select all

    IndexedFaceSet disk = texturedDisk(n+1, 2, true); 
Then add the following code at the end of the file:

Code: Select all

	public static IndexedFaceSet texturedDisk(int rays, int circles)	{
		return texturedDisk(rays, circles, true);
	}
	public static IndexedFaceSet texturedDisk(int rays, int circles, final boolean polar)	{
		return texturedDiskFactory(rays, circles, polar).getIndexedFaceSet();
	}
	public static ParametricSurfaceFactory texturedDiskFactory(int rays, int circles,  final boolean polar)	{
	      ParametricSurfaceFactory factory = new ParametricSurfaceFactory(new ParametricSurfaceFactory.DefaultImmersion() {
		        public void evaluate(double u, double v) {
		          x=foo*Math.cos(u);
		          y=foo*Math.sin(u);
		          z=0;
		        }
		      });
		      
		      factory.setULineCount(rays);
		      factory.setVLineCount(circles);
		      
		      factory.setClosedInUDirection(false); 
		      factory.setClosedInVDirection(false);
		      
		      factory.setUMin(0);
		      factory.setUMax(Math.PI*2);
		      factory.setVMin(0);
		      factory.setVMax(1.0);
		      
		      factory.setGenerateFaceNormals(true);
		      factory.setGenerateVertexNormals(false);
		      factory.setGenerateTextureCoordinates(polar);
		      factory.setGenerateEdgesFromFaces(true);
		      factory.setEdgeFromQuadMesh(true);
		      factory.update();
		      if (!polar) {
			      DataList verts = factory.getIndexedFaceSet().getVertexAttributes(Attribute.COORDINATES);
			      factory.getIndexedFaceSet().setVertexAttributes(Attribute.TEXTURE_COORDINATES, verts);		    	  
		      }
		      return factory;      
	}
jReality core developer

ted
Posts: 57
Joined: Wed 25. Jul 2012, 22:53

Re: closedCylinder ends remain untextured

Post by ted » Sun 12. Aug 2012, 08:58

It seems a best guess at a reasonable texture map should be made for the end caps, since an attempt is made for the sides of the closed cylinder. My suggestion is to map texture coordinates to best meet two design goals:
1. The texture should be about the same resolution as the side (1/PI x 1/PI)
2. The texture should be centered
3. The texture should be right-side-up when initially shown with the default view and an identity transform.

I may end up implementing this, so if you want afterward I could upload a patch. Or if you want to implement it that would be even better. I haven't looked at the code yet, but it's gotta be simply a bunch of acute triangles sharing a common point, so it should be pretty easy to do. [ my higher priority is to get the MATLAB interface working - that's a critical show-stopper for me and the cylinder cap is not ]

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: closedCylinder ends remain untextured

Post by gunn » Sun 12. Aug 2012, 10:11

If you get around to doing more on this, please post a patch. I don't anticipate having time for further work on this.
jReality core developer

ted
Posts: 57
Joined: Wed 25. Jul 2012, 22:53

Re: closedCylinder ends remain untextured

Post by ted » Mon 13. Aug 2012, 17:49

OK - when I implement I'll do it as a patch to existing libraries then. Aloha

ted
Posts: 57
Joined: Wed 25. Jul 2012, 22:53

Re: closedCylinder ends remain untextured

Post by ted » Mon 13. Aug 2012, 22:54

I made a patch for the ends of the cylinder, but noticed the sides orientation is not quite what I would expect. It is inside-out and upside-down, rotated 90 degrees.

Should I straighten all that out, or would it be an unacceptable risk to possible existing code that depends on the current cylinder wall implementation?

I could make mine inside-out and backwards and rotated to match if that's the case.

Code: Select all

Index: src-core/de/jreality/geometry/Primitives.java
===================================================================
--- src-core/de/jreality/geometry/Primitives.java	(revision 5496)
+++ src-core/de/jreality/geometry/Primitives.java	(working copy)
@@ -486,7 +486,7 @@
 		SceneGraphComponent d1 = new SceneGraphComponent("disk1"), d2 = new SceneGraphComponent("disk2");
 		IndexedFaceSet cyl = cylinder(n, r, R, zmin, zmax, thetamax);
 		result.setGeometry(cyl);
-		IndexedFaceSet disk = regularPolygon(n,0.0);
+		IndexedFaceSet disk = texturedRegularPolygon(n,0.0,1./(2*Math.PI));
 		d1.setGeometry(disk);
 		d2.setGeometry(disk);
 		result.addChild(d1);
@@ -612,11 +612,27 @@
 	public static IndexedFaceSet regularPolygon(int order, double offset) {
 		return regularPolygonFactory(order, offset).getIndexedFaceSet();
 	}
+	public static IndexedFaceSet texturedRegularPolygon(int order, double offset, double stretch) {
+		return texturedRegularPolygonFactory(order, offset, stretch).getIndexedFaceSet();
+	}	
+	public static IndexedFaceSet texturedRegularPolygon(int order, double offset) {
+		return texturedRegularPolygonFactory(order, offset, 1.).getIndexedFaceSet();
+	}
 
 	public static IndexedFaceSetFactory regularPolygonFactory(int order, double offset) {
 		double[][] verts = regularPolygonVertices(order, offset);
 		return IndexedFaceSetUtility.constructPolygonFactory(null, verts, Pn.EUCLIDEAN);
 	}
+	
+	public static IndexedFaceSetFactory texturedRegularPolygonFactory(int order, double offset, double stretch) {
+		double[][] verts = regularPolygonVertices(order, offset);
+		double[][] texCoords = new double[verts.length][2];
+		for (int i=0;i<verts.length;i++) {
+			texCoords[i][0] = .5+verts[i][0]*stretch;
+			texCoords[i][1] = .5+verts[i][1]*stretch;
+		}
+		return IndexedFaceSetUtility.constructPolygonFactory(null, verts, Pn.EUCLIDEAN, texCoords);
+	}
 
 	public static double[][] regularPolygonVertices(int order, double offset) {
 		double[][] verts = new double[order][3];
Index: src-core/de/jreality/geometry/IndexedFaceSetUtility.java
===================================================================
--- src-core/de/jreality/geometry/IndexedFaceSetUtility.java	(revision 5496)
+++ src-core/de/jreality/geometry/IndexedFaceSetUtility.java	(working copy)
@@ -221,6 +221,16 @@
 	}
 	
 	public static IndexedFaceSetFactory constructPolygonFactory(IndexedFaceSetFactory ifsf, double[][] points, int sig)	{
+		return constructPolygonFactory( ifsf, points,  sig, null);
+	}
+	/**
+	 * @param ifsf
+	 * @param points vertices
+	 * @param sig
+	 * @param texCoords texture coordinates corresponding to vertices or null if not textured
+	 * @return
+	 */
+	public static IndexedFaceSetFactory constructPolygonFactory(IndexedFaceSetFactory ifsf, double[][] points, int sig,double[][] texCoords)	{
 		int[][] ind = new int[1][points.length];
 		for (int i = 0; i<points.length; ++i)	ind[0][i] = i;
 		// TODO replace this code when it's fixed to initialize the factory with the existing ifs.
@@ -232,6 +242,7 @@
 		ifsf.setVertexCoordinates(points);
 		ifsf.setFaceIndices(new int[][]{{}});
 		ifsf.setFaceIndices(ind);
+		if (texCoords != null) ifsf.setVertexTextureCoordinates(texCoords);
 		ifsf.setEdgeCount(1);
 		ind = new int[1][points.length+1];
 		for (int i = 0; i<=points.length; ++i)	ind[0][i] = (i%points.length);
Attachments
cyltex.png
Cylinder texture used for closedCylinder test
cyltex.png (69.73 KiB) Viewed 1837 times
cyl.png
Rendered closedCylinder
cyl.png (103.07 KiB) Viewed 1837 times

ted
Posts: 57
Joined: Wed 25. Jul 2012, 22:53

Re: closedCylinder ends remain untextured

Post by ted » Thu 30. Aug 2012, 23:21

I've created a new patch against the the repository 5518 version.

Code: Select all

Index: src-core/de/jreality/geometry/Primitives.java
===================================================================
--- src-core/de/jreality/geometry/Primitives.java	(revision 5518)
+++ src-core/de/jreality/geometry/Primitives.java	(working copy)
@@ -486,7 +486,7 @@
 		SceneGraphComponent d1 = new SceneGraphComponent("disk1"), d2 = new SceneGraphComponent("disk2");
 		IndexedFaceSet cyl = cylinder(n, r, R, zmin, zmax, thetamax);
 		result.setGeometry(cyl);
-		IndexedFaceSet disk = regularPolygon(n,0.0);
+		IndexedFaceSet disk = texturedRegularPolygon(n,0.0,1./(2*Math.PI));
 		d1.setGeometry(disk);
 		d2.setGeometry(disk);
 		result.addChild(d1);
@@ -612,6 +612,21 @@
 	public static IndexedFaceSet regularPolygon(int order, double offset) {
 		return regularPolygonFactory(order, offset).getIndexedFaceSet();
 	}
+	public static IndexedFaceSet texturedRegularPolygon(int order, double offset, double stretch) {
+		return texturedRegularPolygonFactory(order, offset, stretch).getIndexedFaceSet();
+	}   
+	public static IndexedFaceSet texturedRegularPolygon(int order, double offset) {
+		return texturedRegularPolygonFactory(order, offset, 1.).getIndexedFaceSet();
+	}
+	public static IndexedFaceSetFactory texturedRegularPolygonFactory(int order, double offset, double stretch) {
+		double[][] verts = regularPolygonVertices(order, offset);
+		double[][] texCoords = new double[verts.length][2];
+		for (int i=0;i<verts.length;i++) {
+			texCoords[i][0] = .5+verts[i][0]*stretch;
+			texCoords[i][1] = .5+verts[i][1]*stretch;
+		}
+		return IndexedFaceSetUtility.constructPolygonFactory(null, verts, Pn.EUCLIDEAN, texCoords);
+	}
 
 	public static IndexedFaceSetFactory regularPolygonFactory(int order, double offset) {
 		double[][] verts = regularPolygonVertices(order, offset);
Index: src-core/de/jreality/geometry/IndexedFaceSetUtility.java
===================================================================
--- src-core/de/jreality/geometry/IndexedFaceSetUtility.java	(revision 5518)
+++ src-core/de/jreality/geometry/IndexedFaceSetUtility.java	(working copy)
@@ -221,6 +221,9 @@
 	}
 	
 	public static IndexedFaceSetFactory constructPolygonFactory(IndexedFaceSetFactory ifsf, double[][] points, int sig)	{
+		return constructPolygonFactory( ifsf, points,  sig, null);
+	}
+	public static IndexedFaceSetFactory constructPolygonFactory(IndexedFaceSetFactory ifsf, double[][] points, int sig,double[][] texCoords)	{
 		int[][] ind = new int[1][points.length];
 		for (int i = 0; i<points.length; ++i)	ind[0][i] = i;
 		// TODO replace this code when it's fixed to initialize the factory with the existing ifs.
@@ -232,6 +235,7 @@
 		ifsf.setVertexCoordinates(points);
 		ifsf.setFaceIndices(new int[][]{{}});
 		ifsf.setFaceIndices(ind);
+		if (texCoords != null) ifsf.setVertexTextureCoordinates(texCoords);
 		ifsf.setEdgeCount(1);
 		ind = new int[1][points.length+1];
 		for (int i = 0; i<=points.length; ++i)	ind[0][i] = (i%points.length);
What do you want to do with this?

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: closedCylinder ends remain untextured

Post by gunn » Fri 31. Aug 2012, 08:18

Hi Ted,

Sorry not to have replied earlier regarding this patch.

I'm not satisfied with the solution as it stands. The danger is that we create too many variations of the static method regularPolygon(). We introduced the method regularPolygonFactory() to prevent such profusion. The idea is that if the standard method doesn't do what you want (in this case, it doesn't by default provide texture coordinates) then you can get the factory (instead of just the geometry) and use it to customize the geometry as you wish (in this case, add texture coordinates of the form you desire). In this case, that would mean the changes would be restricted to the static method

Code: Select all

	public static SceneGraphComponent closedCylinder(int n,   double r, double R, double zmin, double zmax, double thetamax) {
Instead of

Code: Select all

		IndexedFaceSet disk = regularPolygon(n,0.0);
use

Code: Select all

		IndexedFaceSetFactory diskFactory = regularPolygonFactory(n,0.0);
Then work with this factory to set the texture coordinates as you wish. (Note that my original "solution" involving the method texturedDisk() doesn't conform with this advice either. :oops: )

If you wish to propose another patch along these lines I will gladly consider applying it to the respository.
jReality core developer

ted
Posts: 57
Joined: Wed 25. Jul 2012, 22:53

Re: closedCylinder ends remain untextured

Post by ted » Fri 31. Aug 2012, 21:56

Thanks for the response.

You could declare it private till we get back to it with the factory approach, if that would be a reasonable compromise...
However, would you want to preserve the current mirrored behavior of the cylinder walls and change the caps to be mirrored as well? Or fix the cylinder walls with the assumption that it won't break existing code depending on that bug? (or deprecate the current method in favor of a new one at the cost of some proliferation of bloat?)

_-T

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: closedCylinder ends remain untextured

Post by gunn » Sat 1. Sep 2012, 10:09

I think the best solution for now is to leave the textures on the cylinders as they are now, and introduce the textures on the caps in the way you think is most reasonable. If you want to put hooks in for switching the orientation of the texture on the cylinders, too, feel free to do so. We can then discuss your proposed changes in our developer meeting and decide how to go from there.
jReality core developer

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: closedCylinder ends remain untextured

Post by gunn » Mon 3. Sep 2012, 11:04

After posting, I realized that mirroring the texture and stretching it both can (and should) be handled in the texture matrix attached to the texture, not in the code which attaches the texture coordinates. That means, I think, that the addition to closedCylinder() should be limited to setting the texture coordinates only.
jReality core developer

Post Reply