# TriangleStripArray nur ein Teil sichtbar



## Florian Kalisch (15. Dez 2007)

Hallo,

ich weiß, dass es bereits ein paar Beiträge zu dem Thema gibt, aber ich komm damit nicht weiter.
Ich sitze an dem Problem schon vergeblich seit ein paar Tagen (die sun-doku hab ich mir auch angeschaut).
Nun wollt ich einfach mal Euch um Hilfe bitten!

Die ganze Aufgabe soll zur Generierung von Terrain dienen. Aber der Einfachheit halber, lasse ich im folgenden den Z-Wert einfach auf 0, womit ich ja eigentlich eine art teppich ohne Höhen bekommen sollte.

Hier mal ein Teil des Codes:


```
int width = 8;
int height = 8;
		
int vertexCount = width * height;

int stripes = height - 1;

// Anzahl aller Vertices im Array (auch doppelte)
int verticesNum = vertexCount + (stripes-1) *  width;

Point3d[] vertices = new Point3d[verticesNum];

// Generierung der Vertices von oben nach unten: (0,0,0),(0,1,0),(1,0,0),(1,1,0),(2,0,0),(2,1,0).....
int vertarray_counter = 0;
for(int y=0; y<(height-1); y++)
{
	for(int x=0; x<width; x++)
	{
		vertices[vertarray_counter++] = scaleValue(x,y);
		vertices[vertarray_counter++] = scaleValue(x,y+1);
	}
}

int[] strip_counts = new int[stripes];
Color3f[] colors = new Color3f[stripes];

for (int i=0; i<stripes; i++)
{
	strip_counts[i] = width*2;
	colors[i] = new Color3f(1f,1f,1f);
}

TriangleStripArray tris = new TriangleStripArray(verticesNum, format, strip_counts);
tris.setCoordinates(0, vertices);
tris.setColors(0, colors);

// unwichtig für Betrachtung des Problems
Appearance trisApp = new Appearance();
PolygonAttributes pattrib = new PolygonAttributes();
pattrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
trisApp.setPolygonAttributes(pattrib);
sp.setGeometry(tris);
sp.setAppearance(trisApp);
```


Solltet Ihr noch mehr Infos brauchen, bitte noch mal nachfragen.
Ich komm echt nicht drauf, wo das Problem ist.
Über einen Lösungshinweis wäre ich dankbar.

Grüße Florian


----------



## Illuvatar (15. Dez 2007)

Ich blick grad nicht so schnell durch den Code durch, aber ich denke, das ist ein Culling-Problem, d.h., es werden nur Dreiecke gemalt, deren Eckpunkte so wie du draufschaust gegen den (im?) Uhrzeigersinn liegen. Probiel mal bei deinen PolygonAttributes das Culling auszuschalten:


```
pattrib.setCullFace (PolygonAttributes.CULL_NONE);
```

http://download.java.net/media/java3d/javadoc/1.5.1/javax/media/j3d/PolygonAttributes.html#setCullFace(int)


----------



## Florian Kalisch (15. Dez 2007)

Hi Illuvatar,

danke Dir für Deine schnelle Antwort. Ich hab noch ein paar Infos vergessen.
Als erstes mal habe ich Deinen Vorschlag ausprobiert, das bringt keine Besserung.
Generell: In meinem Programm lass ich das TriangleStripArray rotieren (um die y-achse), so dass ich es von beiden Seiten betrachten kann.

Ich habe mal nen Screen-Shot gemacht, wie die Ausgabe aussieht.








Da Du meintest, Du blickst meinen Code gerade nicht so schnell durch. Ist nicht kompliziert.
Ich erkläre es eben mal:


```
int width = 8; 
int height = 8; 
       
int vertexCount = width * height; 

int stripes = height - 1; 

// Anzahl aller Vertices im Array (auch doppelte) 
int verticesNum = vertexCount + (stripes-1) *  width;
```

In diesem Teil berechne ich die benötigten größen, für die dynamische Erzeugung.
width und height definierte Größe des TriangleStripArray-Geblides.

vertexCount (width * height) Anzahl der Vertices (ohne die nötigen Doppel-Vertices)

verticesNum bezeichnet die Anzahl der Vertices im TriangleStripArray (die Anzahl hier berücksichtigt auch die doppelt vorhandenen Vertices, welche aufgrund der Konstruktionsvorgehensweise anfallen).
Die Zuweisung benutzt die bereits berechneten Vertices (vertexCount) und fügt diesen einfach noch die Anzahl der doppelt definierten Vertices hinzu.



```
// Generierung der Vertices von oben nach unten: (0,0,0),(0,1,0),(1,0,0),(1,1,0),(2,0,0),(2,1,0)..... 
int vertarray_counter = 0; 
for(int y=0; y<(height-1); y++) 
{ 
   for(int x=0; x<width; x++) 
   { 
      vertices[vertarray_counter++] = scaleValue(x,y); 
      vertices[vertarray_counter++] = scaleValue(x,y+1); 
   } 
}
```

An dieser Stelle ein Bild, wie ich das Array aufbaue:






Die Zahlen geben die Reihenfolge der erzeugten Punkte an. Genau so werden diese in das Array geschrieben (bis darauf, dass Punkt 1 dann Vertice 0, Punkt 2 Vertice 1, etc. ist).

Die gelbe Linie ist die fehlende Linie, welche der Computer setz und womit das Ganze dann zum Dreieck wird.




```
int[] strip_counts = new int[stripes]; 
Color3f[] colors = new Color3f[stripes]; 

for (int i=0; i<stripes; i++) 
{ 
   strip_counts[i] = width*2; 
   colors[i] = new Color3f(1f,1f,1f); 
} 

TriangleStripArray tris = new TriangleStripArray(verticesNum, format, strip_counts); 
tris.setCoordinates(0, vertices); 
tris.setColors(0, colors);
```

In diesem Teil erzeuge ich das Stripes-Array, in welchem ich angebe, wieviele Stripes es gibt und wieviel Vertices in einem Stripe sind. Eine Farbe wird auch noch definiert pro Stripe (ist jedoch hier immer Weiß). Der Schluss sollte dann klar sein.

Ich hoffe, die Erklärungen waren soweit verständlich.


Grüße Florian


----------



## Florianer (17. Dez 2007)

Hi,
ich hab mir das mal grad angeschaut, aber mir ist noch nicht so ganz klar, wo dein Problem liegt. Du beschreibst ja schon mal recht gut, was du wie und warum machst. Das Einzige, was mich jetzt ein wenig stutzig macht, ist folgendes:


			
				Florian Kalisch hat gesagt.:
			
		

> Generell: In meinem Programm lass ich das TriangleStripArray rotieren (um die y-achse), so dass ich es von beiden Seiten betrachten kann.


Ist dein Problem also, dass du 'den Teppich' nur von einer Seite siehst? Falls du das meinst, mit "nur ein Teil sichtbar", liegt das wohl daran, dass du keine Unterseite hast. Du kannst von einer Seite auf die Oberfläche schauen und siehst deine Dreiecke, schaust du allerdings von hinten, ist das Ding wie ein Semi-Spiegel. Du kannst durchschauen. In dem Fall müsstest du glaub die Normalen rum drehen und ein zweites Gitter machen (mag sein, dass man das auch noch anders lösen kann.) War es das, was du meintest?!

Gruß
Florian


----------



## Florian Kalisch (17. Dez 2007)

Hallo Florian ;-)

Ok, Du hast Recht, ich sollte genauer beschreiben, wo das Problem genau liegt.
Also zuerst mal:



> Generell: In meinem Programm lass ich das TriangleStripArray rotieren (um die y-achse), so dass ich es von beiden Seiten betrachten kann.



Damit meinte ich nur, dass ich das Ganze rotieren lasse um sicher zu gehen, dass ich überhaupt etwas sehe!
Darin liegt es aber glaub ich nicht.

Das Problem, was ich habe ist, dass das TriangleStripArray nicht komplett angezeigt wird.
Meine definierte Höhe ist in dem Beispiel 7, also müssten 6 Stripes angezeigt werden, aber es wird nur ein Stripe angezeigt und das verstehe ich nicht ganz. Gut, der Stripe wird sogar gar nicht ganz komplett angezeigt.

Hoffe, das ist einigermaßen verständlich, muss los in die Vorlesung.
Bei Fragen, ruhig nach mal nachhaken!


Grüße Florian


----------



## Florianer (17. Dez 2007)

OK, nur auf die Schnelle, weil ich jetzt weg muss... 
Ich hab dein Code mal in mein Projekt eingefügt - ließ sich leider net starten. Funktion fehlt, Variable unbekannt. Jetzt hab ich den Code modifiziert und so zum Laufen gebracht. 

```
int width = 8;
			int height = 18;
			      
			int vertexCount = width * height;

			int stripes = height - 1;

//			 Anzahl aller Vertices im Array (auch doppelte)
			int verticesNum = vertexCount + (stripes-1) *  width;

			Point3d[] vertices = new Point3d[verticesNum];

//			 Generierung der Vertices von oben nach unten: (0,0,0),(0,1,0),(1,0,0),(1,1,0),(2,0,0),(2,1,0).....
			int vertarray_counter = 0;
			for(int y=0; y<(height-1); y++)
			{
			   for(int x=0; x<width; x++)
			   {
			      vertices[vertarray_counter++] = new Point3d( x, y, 0);// scaleValue(x,y);
			      vertices[vertarray_counter++] = new Point3d( x, y+1, 0);//scaleValue(x,y+1);
			   }
			}

			int[] strip_counts = new int[stripes];
			Color3f[] colors = new Color3f[stripes];

			for (int i=0; i<stripes; i++)
			{
			   strip_counts[i] = width*2;
			   colors[i] = new Color3f(1f,1f,1f);
			}

			TriangleStripArray tris = new TriangleStripArray(verticesNum, TriangleStripArray.COORDINATES/*format*/, strip_counts);
			tris.setCoordinates(0, vertices);
//			tris.setColor(0, test);

//			 unwichtig für Betrachtung des Problems
			Appearance trisApp = new Appearance();
			PolygonAttributes pattrib = new PolygonAttributes();
			pattrib.setPolygonMode(PolygonAttributes.POLYGON_POINT);
			trisApp.setPolygonAttributes(new PolygonAttributes(PolygonAttributes.POLYGON_LINE, PolygonAttributes.CULL_NONE, 0f));
			//trisApp.setPolygonAttributes(pattrib);
			
			this.setGeometry(tris);
			this.setAppearance(trisApp);
```

Du siehst, es is net wirklich was anders. Aber: Ich bekomm nun eine weiße Fläche gezeichnet. Ahhh... ich musste deine tris.setColor auskommentieren. Mit der, kam immer ne Exception have no color. Warum die Fläche nun weiß ist und net PolyLine hab ich noch net ergründen können. Laufen tut es jedenfalls und wenn ich deine Höhe/Breite abändere, wird die Fläche auch größer. Ich kann mich allerdings schon in der Szene drehen und ich muss von meinem Startpunkt erst einmal nach 'hinten' laufen und seh die Fläche dann auf der positiven X/Y-Achse wachsen. Das wird dir vllt net viel helfen. Aber kann es sein, dass der Fehler net in der Funktion hier steckt, sondern wo anders? Vielleicht in der fehlenden scaleValue-Funktion oder sonst wo außerhalb?


----------



## Florian Kalisch (17. Dez 2007)

Ich hab den Fehler nun gefunden,

war ganz gut, dass Dir beim Ausführen die Variable gefehlt hat.
Diese (format) war bei mir nämlich auf folgendes gesetzt:


```
int format = GeometryArray.COORDINATES | GeometryArray.COLOR_3;
```

Dadurch habe ich gemerkt, dass die Kolorierung das Problem ist. Diese ist falsch aufgebaut bei mir.
Ich werde meinen Code nachher mal posten....

Auf jeden Fall vielen Dank an Euch.


Grüße
Florian


----------



## Florian Kalisch (17. Dez 2007)

Hier kommt der Quellcode, habe ihn noch ein wenig kommentiert!
Ich hoffe, damit ist alles klar. Zuerst dachte ich, es wäre besser nur die einzelne Methode zu posten,
aber ich finde, dass es anschaulicher ist, direkt ein fertig ausführbares Stück Quellcode zu haben!

Have fun!



```
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;

import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TriangleStripArray;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.universe.SimpleUniverse;


/**
 * Klasse zur Darstellung einer virtuell erzeugten Landschaft mittels
 * mathematischer Funktion.
 *  
 * @author Florian Kalisch */ 
public class Terrain {
	
	static GraphicsConfiguration gc;
	static Canvas3D film;
	static SimpleUniverse universe;
	static JFrame theWindow;
	
	public static void main(String[] args)
	{
		gc = SimpleUniverse.getPreferredConfiguration();
		film = new Canvas3D(gc);
		universe = new SimpleUniverse(film);
		
		// Content Branch mit Inhalt füllen
		BranchGroup scene = createLandscape(false, 20, 20);
		scene.compile();
		
		// Die ViewPlatform um 15 Einheiten zurücksetzen
		Transform3D viewTransform = new Transform3D(translation(0f, 0, 15f));
		universe.getViewingPlatform().getViewPlatformTransform().setTransform(viewTransform);
		
		// Content Branch live machen
		universe.addBranchGraph(scene);
		
		
		// Code um das Fenster aufzubauen
		theWindow = new JFrame();
		setWindowStyle(theWindow, 1024, 768);
		theWindow.add(film,"Center");
		theWindow.setVisible(true); // muss letzter Befehl sein !!
	}
	
	/**
	 * 
	 * @param theWindow		Repräsentiert das Fenster
	 * @param screenWidth	Breite des Fensters
	 * @param screenHeight	Höhe des Fensters
	 */
	public static void setWindowStyle(JFrame theWindow, int screenWidth, int screenHeight)
	{
		// Default ist HIDE_ON_CLOSE, daher hier extra setzen
		theWindow.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		theWindow.setResizable(false);
		theWindow.setSize(screenWidth,screenHeight);
		theWindow.setLayout(new BorderLayout());
	}
	
	/**
	 * Erstellt die Landschaft mit Hügeln und Tälern
	 * 
	 * @param wireFrame			Gibt an, ob ein Drahtgittermodell gezeichnet werden soll! True:Drahtgittermodell, False: gefüllt
	 * @param landscapeWidth	Breite der Landschaft
	 * @param landscapeHeight	Höhe der Landschaft
	 * @return					Branchgroup, die beispielsweise an (universe.addBranchGraph(scene)) gehangen werden kann
	 */
	public static BranchGroup createLandscape(boolean wireFrame, int landscapeWidth, int landscapeHeight)
	{
		BranchGroup root = new BranchGroup();
		
		TransformGroup tg = new TransformGroup();
		
		Transform3D newTransform3D = translation(0f,0f,0f);
		tg.setTransform(newTransform3D);
		
		root.addChild(tg);
		
		TransformGroup tRotate = new TransformGroup();
		
		// Erzeugen des Rotation-Interpolators
		tRotate.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		tg.addChild(tRotate);
		Alpha rotationAlpha = new Alpha(-1, 15000);
		RotationInterpolator iRotation = new RotationInterpolator(rotationAlpha,tRotate);
		BoundingSphere bounds = new BoundingSphere();
		iRotation.setSchedulingBounds(bounds);
		tRotate.addChild(iRotation);
		
		int format = 0;
		if(wireFrame == true)
			format = GeometryArray.COORDINATES;
		else
			format = GeometryArray.COORDINATES | GeometryArray.COLOR_3;
		
		
		// Anzahl Vertices (ohne doppelte Zählungen)
		int vertexCount = landscapeWidth * landscapeHeight;
		
		// Anzahl Streifen
		int stripes = landscapeHeight - 1;
		
		// Anzahl an Vertices (mit den - TriangleStripArray bedingten - doppelten Vertices)
		int verticesNum = vertexCount + (stripes-1) *  landscapeWidth;
		
		// Array für Vertices
		Point3d[] vertices = new Point3d[verticesNum];
		
		
		int vertarray_counter = 0;
		
		
		// Generierung der Dreiecke für die Ebene
		// Zuerst im Uhrzeigersinn, dann gegen den Uhrzeigersinn
		for(int y=0; y<(landscapeHeight-1); y++)
		{
			for(int x=0; x<landscapeWidth; x++)
			{
				vertices[vertarray_counter++] = scaleValue(x,y+1);
				vertices[vertarray_counter++] = scaleValue(x,y);
			}
		}
		
		// Definieren der Stripes
		int[] strip_counts = new int[stripes];
		for (int i=0; i<stripes; i++)
		{
			// Streifen sind immer doppelt so breit wie die Breite der Landschaft
			strip_counts[i] = landscapeWidth*2;
		}
		
		// Die Farbe muss für alle Vertices (auch doppelte) definiert werden!
		// Alle Vertices, welche man hier nicht angibt, werden schwarz gezeichnet!
		Color3f[] colors = new Color3f[verticesNum];
		for(int i=0; i<verticesNum; i++)
		{
			// Der Color-Wert ist experimental.
			colors[i] = new Color3f((float)( 0.9f-vertices[i].getZ() ), 0.9f-(float)( vertices[i].getZ() ), 0.9f-(float)( vertices[i].getZ())*1.5f );
		}
		
		// Definieren der Geometrie der Landschaft
		TriangleStripArray tsa = new TriangleStripArray(verticesNum, format, strip_counts);
		tsa.setCoordinates(0, vertices);
		
		// Die Farben nur dann setzten, wenn kein Drahtgittermodell angezeigt werden soll
		if(wireFrame == false)
			tsa.setColors(0, colors);
		
		Shape3D landScape = createLandscapeShape(wireFrame, tsa);
		
		tRotate.addChild(landScape);
		
		return root;
	}
	
	/**
	 * 
	 * @param wireFrame		Angabe ob nur Drahtgitter (true) oder gefüllt (false)
	 * @param tsa			Erzeugtes TriangleStripArray (Geometrie) der Landschaft
	 * @return				Fertige Landschaft als Shape3D
	 */
	static Shape3D createLandscapeShape(boolean wireFrame, TriangleStripArray tsa)
	{
		Shape3D landscapeShape = new Shape3D();
		
		Appearance tsaAppearance = new Appearance();
		
		PolygonAttributes pattrib = new PolygonAttributes();
		tsaAppearance.setPolygonAttributes(pattrib);
		
		// Darstellung: 
		// PolygonAttributes.POLYGON_LINE: nur Drahtgitter
		// PolygonAttributes.POLYGON_FILL: gefüllt
		if(wireFrame == true)
			pattrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
		else
			pattrib.setPolygonMode(PolygonAttributes.POLYGON_FILL);
		
		tsaAppearance.setPolygonAttributes(pattrib);
		
		landscapeShape.setGeometry(tsa);
		landscapeShape.setAppearance(tsaAppearance);
		
		return landscapeShape;
	}
	
	/**
	 * Diese Funktion erzeugt ein Point3d-Object
	 * sValue : Skaliert die Landschaft (Werte > 1: Stauchung, Werte < 1: Streckung)
	 * tLeft : Bewirkt eine Verschiebung nach Links
	 * tUp : Bewirkt eine Verschiebung nach oben
	 * tFront : Bewirkt eine Verschiebung in Richtung View-Platform
	 * 
	 * @param x		X-Wert
	 * @param y		Y-Wert
	 */
	static Point3d scaleValue(int x, int y)
	{
		float sValue = 3f;
		int tLeft = 3;
		float tUp = -3;
		float tFront = 0;
		
		return new Point3d((x/sValue)-tLeft,(y/sValue)+tUp, (getZValue(x,y)/sValue)+tFront);
	}
	
	/**
	 * Diese Funktion berechnet den Z-Wert, abhängig von den Werten x und y
	 * 
	 * @param x		X-Wert
	 * @param y		Y-Wert
	 * @return		Z-Wert (Tiefenwert)
	 */
	static float getZValue(int x, int y)
	{
		float nMaxWert = 3f;
		
		//return (float) (nMaxWert * (Math.cos(Math.sqrt((x)*(x)+(y+1)*(y+1)))));
		return (float) (nMaxWert * Math.cos(1.5f * y * Math.sin(x)));
	}
	
	/**
	 * Funktion erzeugt ein Transform3D (entsprechend den übergebenen Werten)
	 * 
	 * @param x		X-Wert
	 * @param y		Y-Wert
	 * @param z		Z-Wert (Tiefenwert)
	 * @return		Transform3D
	 */
	static Transform3D translation(float x, float y, float z)
	{
		Transform3D transform3D = new Transform3D();
		transform3D.setTranslation(new Vector3f(x,y,z));
		
		return transform3D;
	}
}
```


----------

