# j3d mehr als ein Objekt rotieren



## bob_sheknowdas (27. Apr 2012)

Hi,
ich habe schon wieder ein Problem mit der Rotation in j3d. In einem anderen Threat wurde mir bereits dabei geholfen ein Objekt vernünftig zu rotieren. Jetzt habe ich ein 2. Objekte erzeugt und wollte es auf die selbe Weise drehen lassen. Das funktioniert aber nicht, die beiden Rotationen überschreiben sich und es kommt zu mysteriösen Ereignissen 

Hier mal fix die Klasse, die sich um die Bewegung kümmert (auf das Wichtigste reduziert)


```
class Steuerung implements KeyListener
{
    private final TransformGroup myTransformGroup;
    private final Flugzeug myFlugzeug;
    private final Enemy theEnemy;
    private Transform3D trans = new Transform3D();
    private Transform3D transE = new Transform3D();
    private final Spiel parent;
    private int enemyTurn=0;
    
    float xE = 0f;
    float yE = 0f;
    
    private final int upKey = KeyEvent.VK_W;
    private final int downKey = KeyEvent.VK_S;
    private final int leftKey = KeyEvent.VK_A;
    private final int rightKey = KeyEvent.VK_D;
    private final int speedUp = KeyEvent.VK_UP;
    private final int slowDown = KeyEvent.VK_DOWN;
    private final Thread thread;
 
    private float speed = 0f;
    
    private final Set<Integer> pressedKeys = new HashSet<Integer>();
    private Matrix4f currentRotation = new Matrix4f();
    private Matrix4f currentRotationE = new Matrix4f();
    
    public Steuerung(TransformGroup rotatedTransformGroup, Flugzeug flugzeug, Spiel spiel, Enemy enemy)
    {
        this.myTransformGroup = rotatedTransformGroup;
        this.myFlugzeug = flugzeug;
        this.parent = spiel;
        this.theEnemy = enemy;
        currentRotation.setIdentity();
        currentRotationE.setIdentity();
 
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    updateMovement();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }   
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
    
    private void updateMovement() {
        if (pressedKeys.contains(upKey)&& !pressedKeys.contains(downKey)){
            rotate(-0.1f, 0);
        }
        if (pressedKeys.contains(downKey)&& !pressedKeys.contains(upKey)){
            rotate(0.1f, 0);
        }
        if (pressedKeys.contains(leftKey)&& !pressedKeys.contains(rightKey)){
            rotate(0, 0.1f);
        }
        if (pressedKeys.contains(rightKey) && !pressedKeys.contains(leftKey)) {
            rotate(0, -0.1f);
        }
        if (pressedKeys.contains(slowDown) && !pressedKeys.contains(speedUp) && speed>=-0.05f) {
            speed-=0.001f;
        }
        if (pressedKeys.contains(speedUp) && !pressedKeys.contains(slowDown)&& speed<=0.15f) {
        	speed+=0.001f;
        }
        move();
        moveEnemy();
    }

	private void moveEnemy() {
		if(enemyTurn==50){
			xE = (float)Math.random()*(0.3f+0.3f)-0.3f;
			yE = (float)Math.random()*(0.3f+0.3f)-0.3f;
			rotateE(xE, yE);
		}
		else if (enemyTurn <90 && enemyTurn>50){
			rotateE(xE, yE);
		}
		else if (enemyTurn==90) enemyTurn=0;
		else enemyTurn++;
		
		Transform3D tempTransform1 = new Transform3D();
		Vector3f tempVector1 = new Vector3f();
		theEnemy.getSphere().getLocalToVworld(tempTransform1);
		tempTransform1.get(tempVector1);

		Transform3D tempTransform2 = new Transform3D();
		Vector3f tempVector2 = new Vector3f();
		theEnemy.getSphere2().getLocalToVworld(tempTransform2);
		tempTransform2.get(tempVector2);
		
		tempVector1.x += (tempVector1.x-tempVector2.x) * 0.13f;
		tempVector1.y += (tempVector1.y-tempVector2.y) * 0.13f;
		tempVector1.z += (tempVector1.z-tempVector2.z) * 0.13f;
		
		transE.setTranslation(tempVector1);
		transE.setAutoNormalize(true); 
		theEnemy.setTransform(transE );
		
	}

	private void rotateE(float angleX, float angleY) {
        Matrix4f m0 = new Matrix4f();
        m0.setIdentity();
        m0.rotX(angleX/4);
        Matrix4f m1 = new Matrix4f();
        m1.setIdentity();
        m1.rotY(angleY/4);
        Matrix4f result = new Matrix4f();
        result.mul(m0, m1);
        currentRotationE.mul(currentRotationE, result);
        transE = new Transform3D(currentRotationE);
		
	}

	private void rotate(float angleX, float angleY) {
        Matrix4f m0 = new Matrix4f();
        m0.setIdentity();
        m0.rotX(angleX/4);
        Matrix4f m1 = new Matrix4f();
        m1.setIdentity();
        m1.rotY(angleY/4);
        Matrix4f result = new Matrix4f();
        result.mul(m0, m1);
        currentRotation.mul(currentRotation, result);
        trans = new Transform3D(currentRotation);
    }
 
    @Override
    public void keyTyped(KeyEvent e){
    }
 
    @Override
    public void keyPressed(KeyEvent e){
        pressedKeys.add(e.getKeyCode());
    }
 
    @Override
    public void keyReleased(KeyEvent e){
        pressedKeys.remove(e.getKeyCode());
    }
	
	private void move(){
		
		Transform3D tempTransform1 = new Transform3D();
		Vector3f tempVector1 = new Vector3f();
		myFlugzeug.getSphere().getLocalToVworld(tempTransform1);
		tempTransform1.get(tempVector1);

		Transform3D tempTransform2 = new Transform3D();
		Vector3f tempVector2 = new Vector3f();
		myFlugzeug.getSphere2().getLocalToVworld(tempTransform2);
		tempTransform2.get(tempVector2);
		
		tempVector1.x += (tempVector1.x-tempVector2.x) * speed;
		tempVector1.y += (tempVector1.y-tempVector2.y) * speed;
		tempVector1.z += (tempVector1.z-tempVector2.z) * speed;
		
		trans.setTranslation(tempVector1);
		trans.setAutoNormalize(true); 
		myTransformGroup.setTransform(trans);
	}

}
```


Die beiden zu bewegenden Objekte sind Flugzeug und Enemy. Eigentlich müssten beide Ihre eigenen Bewegungen ausführen können, ohne sich zu beeinflussen (es gibt ja trans und transE, sowie currentRotation und currentRotationE). Solange siche Enemy nicht dreht klappt das auch, aber sobald rotateE() aufgerufen wird lässt sich Flugzeug nicht mehr vernünftig steuern.

Ich hoffe jemand von euch sieht warum das so ist.
Wie immer vielen Dank im Vorraus...


----------



## bob_sheknowdas (27. Apr 2012)

Hab die Lösung selbst gefunden^^
Ich habe den Code oben mal editiert, so dass da jetzt das richtige steht...


----------



## Spacerat (27. Apr 2012)

Öhm... er 'nu wieder... XD
Hatte ich schon erwähnt, dass du dein Konzept von Grund auf ändern solltest? Normalerweise ist's in 3D-Welten so, dass jedes Objekt eine eigene 4*4-Element-Matrix (Transform3D) hat. Mit jedem Renderdurchlauf iteriert man nun über "lebende" Objekte und multipliziert die View-Matrix mit der jeweiligen Objekt-Matrix und erhält so eine Matrix, an welcher das Objekt gezeichnet werden soll.
Matritzen bestehen aus den Komponenten Translation, Scale und Rotation. Das bedeutet, dass eine solche Matrix Grösse, Lage und Position beinhaltet. Also: Egal, was so ein Objekt sonst noch für Methoden hat, wichtig sind auf jeden Fall [c]setPosition(x, y,z)[/c], [c]setRotation(angle, x, y, z)[/c] und [c]getTransform()[/c] welche schliesslich die Objekt-Matrix liefert. Es gibt zwar noch [c]setScale(x, y, z)[/c], aber das ist erstmal nicht wichtig. Ganz banal für Java3D also
	
	
	
	





```
public abstract class J3DObject
extends TransformGroup
{
  private final Tramsform3D transform;

  protected Java3DObject()
  {
    this(new Transform3D());
  }

  private Java3DObject(Transform3D transform)
  {
    super(transform);
    this.transform = transform;
    setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
  }

  public void seRotation(float angle, float x, float y, float z)
  {
    transform.setRotation(new AxisAngle4f(x, y, z, angle));
  }

  public void setPosition(float x, float y, float z)
  {
    transform.setPosition(new Vector3f(x, y, z));
  }

  public void getTransform()
  {
    return new Transform3D(transform);
  }
}
```
Wie du siehst, ist die Objekt-Matrix völlig von äusseren Zugriffen isoliert, so dass man halt gezwungen ist, zum Positionieren usw. die dafür vorgesehenen Methoden zu verwenden. Was ich in Java3D im übrigen vermisse sind Methoden zum echten Bewegen, sprich entsprechende additive [c]rotate()[/c]-, [c]scale()[/c]- und [c]translate()[/c]-Methoden, damit Position, Rotation und Scale nicht auch noch wo anders gespeichert werden müssen. Aber das ist halt Java3D... komplizierter geht's nicht.
[EDIT]Das Beispiel ist im übrigen auch keineswegs das Mass der Dinge. Im Zweifelsfalle würde das nämlich viel zu oft "new SonstWas()" bedeuten, womit man höchstens *höchstens* (das steht aus gutem Grund zweimal hier ) den GC beschäftigt. Normalerweise entwickelt man hier per Object-Reuse.[/EDIT][EDIT]Ach dus Sch... Was ist das denn? Ich experimentiere hier grad' ein bissl' und da fällt mir auf, das sich die transform der TransformGroup ja nicht mal mit aktualisiert... aaaahhhhh[/EDIT][EDIT]Ok... update... soweit so gut... vorerst[/EDIT]


----------



## Marco13 (28. Apr 2012)

Spacerat hat gesagt.:


> Normalerweise ist's in 3D-Welten so, dass jedes Objekt eine eigene 4*4-Element-Matrix (Transform3D) hat. Mit jedem Renderdurchlauf iteriert man nun über "lebende" Objekte und multipliziert die View-Matrix mit der jeweiligen Objekt-Matrix und erhält so eine Matrix, an welcher das Objekt gezeichnet werden soll.



Sowohl den Hinweis als auch den Code würde ich mit Skepsis betrachten. Java3D ist eben Szenegraph-basiert, und mit einer "View-Matrix" hat man da i.a. nicht mehr viel zu tun. BTW: "Viele kleine" Objekte erzeugen ist spätestens bei Java7 dank escape analysis gar nicht mehr so kritisch... aber... zugegeben, ich bin auch noch teilweise beim "alten Muster"


----------



## Spacerat (28. Apr 2012)

Marco13 hat gesagt.:


> Sowohl den Hinweis als auch den Code würde ich mit Skepsis betrachten.


Also den Code kann man auch nur skeptisch betrachten, vor allem die Zeilen 28 bis 31 - verdammtes copy paste 


Marco13 hat gesagt.:


> BTW: "Viele kleine" Objekte erzeugen ist spätestens bei Java7 dank escape analysis gar nicht mehr so kritisch... aber... zugegeben, ich bin auch noch teilweise beim "alten Muster"


[OT]Was bitte hat Escapeanalyse mit Object-Reuse zu tun? Sie zeigt der JVM doch nur, ob Objekte ausserhalb anderer Objekte erreichbar sein können, nicht aber ob sie noch erreichbar sind. Ich erinner mich in dieser Beziehung da gerade an unser Thema mit BufferedImages - Weisst du wie das SunWritableRaster feststellt, ob sein Datenpuffer "stolen" ist, bzw. sein könnte? Bislang hatte dieser Mechanismus für mich allerdings noch keinen Namen, dank dir, jetzt schon (Obwohl, das SWR hat diese "Analyse" schon lange bevor Java 1.6_14 durchgeführt). Objekte, die ausserhalb eines Anweisungsblocks weiterverwendet werden können belasten den GC immer noch. Das bedeutet, du darfst dir in einem solchen Block, soviele Objekte erstellen, wie du willst, unescaped Objects sind beim Verlassen eines solchen sofort Geschichte, da braucht's nicht mal einen GC (mit dem Rücksetzen eines Stapelzeigers in Assembler vergleichbar). Escaped Objects aber liegen in ganz anderen, meist viel globaleren Adressbereichen, im Zweifelsfalle vllt. irgend was mit "A4-Relativ" (Amiga-Assembler) vergleichbarem (dem Java-Heap), jedenfalls weis ich, woher mir diese Techniken bekannt vor kommen, ich aber keinen Namen dazu habe. So neu sind die also gar nicht.[/OT]


----------



## Marco13 (28. Apr 2012)

Da der Thread erledigt ist, sind ein, zwei OT-Nachträge hofentlich OK...:

_Was bitte hat Escapeanalyse mit Object-Reuse zu tun?_

Ich erinnere mich an meine ersten Programme mit Java3D (und allgemein zeitkritische Berechnungen, z.B. mit der Vecmath), wo man sowas wie

```
float compute(Vector3f v0, Vector3f v1)
{
     Vector3f t0 = new Vector3f();
     Vector3f t1 = new Vector3f();
     t0.set(v0); 
     t0.normalize();
     t1.set(v1); 
     t1.normalize();
     return t0.dot(t1);
}
```
für die maximale Performance (und JA, es ging da wirklich um das zweitkritischste, was es gibt!) tunlichst als

```
private static final Vector3f t0 = new Vector3f();
private static final Vector3f t1 = new Vector3f();
float compute(Vector3f v0, Vector3f v1)
{
     //Vector3f t0 = new Vector3f(); // Rausgezogen
     //Vector3f t1 = new Vector3f(); // Rausgezogen
     t0.set(v0); 
     t0.normalize();
     t1.set(v1); 
     t1.normalize();
     return t0.dot(t1);
}
```
schreiben sollte. Aber wie du schon geschrieben hast: Mit der Escape Analysis wird erkannt, dass diese Vektoren nur lokal gebraucht werden. D.h. sie werden auf dem Stack erzeugt, und belasten den GC nicht (Genaugenommen können in diesem Fall sogar sämtliche Berechung ge'inline't werden, aber das ist nochmal ein anderer Punkt). 

Die Kurve zum BufferedImage krieg' ich jetzt so spontan nicht (und das DirectBufferedImage liegt immernoch in der Queue ... sorry....  obwohl ich schon überlegt habe, ob man das nicht im Zusammenhang mit jcuda.org - JNpp irgendwie verwursten könnte... Ich muss mir erst nochmal deine Erkenntnisse aus dem DirectBufferedImage-Thread und (vor allem!) den dazugehörigen Code durchlesen...). Aber für das "managed" oder "stolen" wird keine Escape Analysis verwendet. Zumindest gibt es in "SunWritableRaster" eine ganz lapidare Methode "setStolen" (die ich in diesem Beitrag unten schonmal erwähnt hatte). Es könnte zwar theoretisch sein, dass sich da mit Java7 was ändert, aber ... das Raster ist ja NIE nur in einer Methode vorhanden (also nicht lokal, und kann nicht auf dem Stack erstellt werden), deswegen sehe ich zumindest keine Verwandtschaft zur Escape Analysis. :bahnhof:


----------



## Spacerat (28. Apr 2012)

Dieses lapidare "setStolen()" macht nur häufig keinen Sinn, weil die JVM trotzdem merkt, ob der Buffer noch zu erreichen ist, allerdings macht sie das an existierenden Klassen fest. BufferedImages, die keine bekannte ColorModel-SampleModel-Raster-Struktur haben sind manchmal sogar auch dann lahm, wenn "stolen" false ist, erst recht dann, wenn ein DirectBuffer verwendet wird. Das mag jetzt zwar wenig mit der sogenannten Escape-Analyse zu tun haben aber diese ist halt auch nur ein Verfahren zur Unterscheidung zwischen Daten bekannter (offensichtlicher) und unbekannter Herkunft, um herauszufinden, ob man die Daten entweder schnell abarbeiten kann oder aber ständig validieren muss.

Dein "was man tunlichst machen sollte" oben ist hoffentlich nur auf die schnelle zusammen gefrickelt, denn so wäre der Preis für maximale Performance nämlich unter Umständen Dateninkonsistenz. "t0" und "t1" müssten nämlich Threadsicher gemacht werden und das auf statische Objekte? ???:L
Ich löse das immer so:

```
float compute(Vector3f v0, Vector3f v1, Vector3f t0, Vector3f t1)
{
     t0.set(v0); 
     t0.normalize();
     t1.set(v1); 
     t1.normalize();
     return t0.dot(t1);
}
```
In diesem Fall geht's zwar nicht, aber mein Hammer für (automatisches) Object-Reuse ist folgendes:

```
Object getData(Object outData)
{
  if(outData == null) {
    outData = new int[1]; // Standardtyp z.B. int[1]
  }
  if(outData instanceof int[]) {
    int[] tmp = (int[]) outData;
    int[0] = this.color // Klasse ist vllt. eine Art Farbmodell
  } else if(outData instanceof SomeKindOfOtherSupportedType) {
    // fill outData in other ways...
  } else {
    throw new IllegalArgumentException("type of outData not supported");
  }
  return outData;
}

// Verwendung wie folgt:

Object data = null;
for(int n = 0; n < width; n++) {
  data = getData(data); // diese Zuweisung ist lebenswichtig!
  // do something with data
}
```
Das kommt davon, wenn man sich eindringlich mit z.B. BufferedImages beschäftigt, da wird's im Prinzip überall so gemacht.  Allerdings muss man in all den Klassen, wo man es so macht abrufbar machen, wie sich Object zusammensetzt. Natürlich klappt das auch mit anderen Klassen, z.B. so, wie oben mit Transform3D.


----------



## Marco13 (28. Apr 2012)

Zu "setStolen": Ich hatte diese Methode nur auf meiner Odyssee durch die Sun-BufferedImage-Klassen entdeckt, und es schien, als wäre das der einzige Unterschied zu dem, was man auch mit der öffentlichen API erreichen kann, aber das müßte ich auch erst nochmal auffrischen. 


_Dein "was man tunlichst machen sollte" oben ist hoffentlich nur auf die schnelle zusammen gefrickelt, denn so wäre der Preis für maximale Performance nämlich unter Umständen Dateninkonsistenz. _

Nein... die Methode ist eben private und als "nicht threadsicher" gekennzeichnet. (Wenn sowas im Anwendungsfall 2% Geschwindigkeitsvorteil gebracht hat, war es das wert...).


_Ich löse das immer so:_

Das ist eine Option - verschiebt die Frage aber ggf. nur einen Methodenaufruf weiter nach oben  


_In diesem Fall geht's zwar nicht, aber mein Hammer für (automatisches) Object-Reuse ist folgendes:_

Den konkreten Anwendungsfall konnte ich daran zwar nicht erkennen, aber allgemein gibt es verschiedene "Muster", mit denen man ""Optimierungen"" machen kann (oder konnte) um dem GC das Leben leichter zu machen. Einige davon werden in Zukunft vielleicht obsolet. Bleibt nur zu hoffen, dass sie sich nicht als nachteilhaft herausstellen (was durchaus sein kann...)


----------



## Spacerat (28. Apr 2012)

Marco13 hat gesagt.:


> Den konkreten Anwendungsfall konnte ich daran zwar nicht erkennen...


???:L Schau' dir bei Zeiten ruhig noch mal die BufferedImage-Klasse - insbesondere die Methode [c]setRGB(int x, int x, int w, int h, int[] pixels, int offset, int scansize)[/c] und dann diverse Colormodelle und vllt. auch noch einige Samplemodelle an, da wird das exxessiv verwendet. Oder noch viel besser... probier's aus. Vllt. hasts ja nicht gesehen, aber outData wird nur einmal zugewiesen. Diese so über alle Maße *lebenswichtige* Zuweisung im äusseren Anweisungsblock dient nur sehr nebensächlich dazu, das Objekt dort verfügbar zu machen (es ist dort längst verfügbar, es wurde ja dort definiert ) sondern mehr dazu, es in den folgenden Aufrufen genau so übergeben zu können, wie man es bekommen hat, also zumindest nicht mehr als Nullreferenz wie beim ersten Aufruf. Die Klasse Objekt verwendet man nur, wenn der Rückgabetyp nicht vorhersehbar ist (Images können z.B. mehrere Bänder haben, dann kämen Arrays mit mehr als nur einem Element zurück). Wenn der Datentyp aber feststeht, kann man das Objekt durchaus auch im äusseren Anweisungsblock initialisieren. In Sachen 3D schiebe ich auf diese Art ein float[16], ein double[16] oder gar entsprechende nio.Buffer als Matrix durch komplette Objektbäume oder besser durch die gesamte Szene. Deswegen wundert es mich ja auch, warum es in Java3D nicht so toll funktioniert. Ständig muss man dort die Transform3D einer TransformGroup neu setzen. Was für'n Krampf.


----------



## Marco13 (28. Apr 2012)

Ja, bestimmte Parallelen zum Code aus den BufferedImage-Klassen hatte ich erkannt. Irgendwann hatte ich auch mal etwas verdutzt geschaut, als ich gesehen habe, dass dort in solchen Fällen anscheinend öfter mal ein int[1] erstellt wird, um einzelne Pixel abzuholen :autsch: Aber ich hätte sowohl den geposteten als auch den BufferedImage-Code noch genauer vergleichen müssen. Ich fand es aber auch immer schwierig, herauszufinden, von wo bestimmte Methoden aus Raster & Co aufgerufen werden - solange man nicht den gesamten Code in eine IDE packt...


----------



## Spacerat (29. Apr 2012)

Marco13 hat gesagt.:


> Bleibt nur zu hoffen, dass sie sich nicht als nachteilhaft herausstellen (was durchaus sein kann...)


Auf jeden Fall... hoffen! Denn wenn sich diese (Hmm, hab' mal wieder keinen Namen dafür - nenn' ich's mal...) Shared-Instance-Methode als solches herausstellen sollte, wäre das nicht nur für Java2D Bilder fatal, sondern auch für meine Methode zum Rendern einer OpenGL-Szene...
	
	
	
	





```
void renderObject(Link3D object, DoubleBuffer transform) {
  transform = (DoubleBuffer) object.getTransform(transform);
  glPushMatrix();
  glMultMatrix(transform);
  object.display();
  for (Link3D l : object) {
    renderObject(l, transform);
  }
  glPopMatrix();
}
```
Link3D ist dabei eine Klasse, welche auf ein echtes Object3D linkt. Echte Object3D können so mehrfach verwendet werden - jedes halt mit eigener Transformation, belegen aber sonst nur einmal Speicher.


----------



## Marco13 (29. Apr 2012)

Hmja - auch da kann ich mir den Anwendungsfall und den größeren Kontext nicht ganz vorstellen. Aber welche "Tricks" man sich auch immer für Optimierungen überlegt hat: Ich glaube, dass allgemein der Fall, dass so ein "Trick" durch eine neue JVM oder einen neuen JIT _langsamer_ wird unwahrscheinlicher ist, als dass ein _anderer_ Trick danach schneller sein könnte, als der alte. 

Im Zweifelsfall gilt in den meisten Fällen ja immernoch die Regel Write Dumb Code (durch die Oracle-Übernahme leider nur noch im Google cache...)


----------



## Spacerat (30. Apr 2012)

Der Anwendungsfall ist Klar. Halbautomatisches Object-Reuse mit bekannten oder unbekannten Transferobjekten (Buffer oder Arrays). Du weisst nicht, was für Daten zurückkommen? Frag' die Datenklasse nach dem Transfertypen. Du weisst nicht, wieviele Elemente pro transfer zurückkommen? Frag' die Datenklasse nach der Länge. Du weisst nicht, ob Buffer erlaubt sind? Erstelle einen mit den von der Datenklasse geforderten Daten (Transfertyp und Länge) und lass ihn von der Datenklasse befüllen. Wird das Transferobjekt nicht unterstützt fliegt meistens 'ne Exception. Z.B. braucht man es bei DataBuffern, Color- und SampleModellen (kurz gesagt bei Sun-Standard-Rastern für Bilder) mit Buffern gar nicht erst versuchen, wie gesagt, ich finde, das ist nicht mehr zeitgemäss. Deswegen kann man es bei meinen überarbeiteten Klassen des ständig besser werdenden 3D-APIs meiner schon oft erwähnten Dateitypenerkennung auch getrost verwenden, die Implementation war auch schwierig genug.
Schau dir doch mal die ganzen VecMath-Klassen an, was da dran faul sein könnte. Schön, man kann z.B. jedes einzelne Element einzeln ändern und Objekt-Reuse wird sogar auch unterstützt. Aber diese ganzen dafür erforderlichen Methoden... (z.B. 17 * set bei Matrix4f) ist das "Good will hunting"?


----------



## Marco13 (30. Apr 2012)

Spacerat hat gesagt.:


> ... das ist nicht mehr zeitgemäss. Deswegen kann man es .... auch getrost verwenden, die Implementation war auch schwierig genug.


Irgendwie kann ich dir hier nicht mehr ganz folgen, bzw. es klingt widersrpüchlich, aber ich bin im Moment auch nicht ganz fit... 

Die Vecmath... das "faulste" daran finde ich eigentlich die publich fields ... (Vector3f x,y,z & Co). Was wäre die Alternative zu den settern? Immutable Objekte? Hätte theoretische Vorteile, aber wäre aus Effizienzgründen (speziell zu der Zeit, als die entwickelt wurden - aber ggf. auch heute noch) kaum praktikabel....


----------



## Spacerat (30. Apr 2012)

Hier mal ein minimalistisches KSKB, mit einer Matrix. Die mult-Methode ist vllt. nicht ganz korrekt, aber darum geht's ja auch nicht. Es geht ums Datenschaufeln per set und get mit Object-Reuse, schnell, effizient, komfortabel und vor allem performant, speichersparend und GC entlastend.
	
	
	
	





```
import java.util.ArrayList;
import java.util.List;


public class TransferObjectDemo
{
	public enum TransferType
	{
//		BYTE,
//		SHORT,
//		CHAR,
//		INT,
//		LONG,
//		FLOAT,
		DOUBLE, // zu Demonstrationszwecken reicht DOUBLE
	}

	private static final double[] IDENTITY = {
		1.0, 0.0, 0.0, 0.0,
		0.0, 1.0, 0.0, 0.0,
		0.0, 0.0, 1.0, 0.0,
		0.0, 0.0, 0.0, 1.0,
	};

	private final double[] data = new double[16];

	private TransferObjectDemo(double[] initial)
	{
		setData(initial);
	}

	private TransferObjectDemo()
	{
		loadIdentity();
	}

	public void loadIdentity()
	{
		setData(IDENTITY);
	}

	public int getNumElements()
	{
		return 16;
	}

	public TransferType getTransferType()
	{
		return TransferType.DOUBLE;
	}

	public boolean supportsBuffer()
	{
		return false;
	}

	public void setData(Object inData)
	{
		System.arraycopy(inData, 0, data, 0, 16);
	}

	public Object getData(Object outData)
	{
		if(outData == null) {
			outData = new double[16];
		}
		System.arraycopy(data, 0, outData, 0, 16);
		return outData;
	}

	public void mult(Object inData)
	{
		double[] tmp = (double[]) inData;
		for(int n = 0; n < 16; n++) {
			data[n] *= tmp[n];
		}
	}

	public static void main(String[] args)
	{
		List<TransferObjectDemo> renderTree = new ArrayList<TransferObjectDemo>();
		for(int n = 0; n < 2000; n++) {
			renderTree.add(new TransferObjectDemo(createRandomArray()));
		}
		Object trans = null;
		TransferObjectDemo view = new TransferObjectDemo();
		for(int n = 0; n < 100; n++) {
			view.loadIdentity();
			for(TransferObjectDemo t : renderTree) {
				trans = t.getData(trans);
				view.mult(trans);
			}
		}
	}

	private static double[] createRandomArray()
	{
		double[] rc = new double[16];
		for(int n = 0; n < 15; n++) {
			rc[n] = Math.random();
		}
		rc[15] = 1.0;
		return rc;
	}
}
```
Kannst in Zeile 91 ja mal spasseshalber einen Haltepunkt setzen, um zu zählen, wie oft trans instanziert wird. Aber vor der ersten Ausführung des Programms raten, wie lange es wohl braucht un 200000 4*4 Matritzen zu Multiplizieren. Kannst auch mit den Zahlen ein wenig experimentieren. Davon bekommt man Experimentierung... ich mein' Experience... oder auch Erfahrung. XD


----------



## Marco13 (1. Mai 2012)

Hmja... um die Performance beurteilen zu können, müßte man das zu einem Microbenchmark aufbohren. An sich spricht ja nichts gegen dieses Muster - z.B. bei Bildern ist das wohl das flexibelste, was man machen kann:

```
// Writes the data into the given output image and and returns the 
// outputImage. If outputImage is null, then a new object is created 
// and returned
BufferedImage filter(BufferedImage inputImage, BufferedImage outputImage) { ... }
```
Jedem steht es frei, dort zu reusen oder sonstwas zu machen. 

Aber abgesehen von einigen Innereien der BufferedImage/Raster/ColorModel-Sachen würden mir nicht viele Fälle einfallen, wo man das "Object" nicht durch irgendwas spezifischeres ersetzen könnte (ggf. mit Generics - die gab es damals noch nicht... (Wir hatten ja nichts, damals ... ) )

Vielleicht würde es sich doch langsam lohnen, den Teil nach dem "28.04.2012 09:41" Beitrag (rein der Ordnung halber) abzutrennen... (Mit welchem Titel auch immer  )


----------



## Spacerat (1. Mai 2012)

Marco13 hat gesagt.:


> Jedem steht es frei, dort zu reusen oder sonstwas zu machen.  ... Vielleicht würde es sich doch langsam lohnen, den Teil nach dem "28.04.2012 09:41" Beitrag (rein der Ordnung halber) abzutrennen... (Mit welchem Titel auch immer  )


Naja, abtrennen vllt. nicht, eigentlich ist doch alles darüber gesagt. Aber vllt. könnte man ein neues Thema eröffnen, in welchem die Frage nach dem "sonst was" geklärt wird. Ehrlich gesagt, ist meine Art des Object-Reuse nämlich zuweilen unheimlich umständlich zu implementieren, erst recht, wenn man flexibel sein will.


----------



## Marco13 (1. Mai 2012)

Hm... Vielleicht ist es diese Flexibilität, von der ich mir gerade nich vorstellen kann, was damit gemeint ist... Und auch das "übergeordnete" Ziel des KSKBs... Also, im schlimmsten Fall (z.B. wenn die bei 10000 Objekten die TransferTypes _abwechselnd_ Float und Double sind) bringt das ja nichts... und NOCH weiter übergeordnet die Frage, ob die gesparten Objekterzeugung die Mühen wert sind, die sich aus der Verwendung von 'Objects' den damit verbundenen Typ-Checks und der wiederum damit verbundenen "Infrastruktur" ergeben. (Ich hab' bei meiner ""Rendering-Enginge"" einfach überall dumm-dreist die Matrix4f's rumgereicht - wenn DAS wirklich das Bottleneck ist... .... ... muss man eben komplexere Modelle rendern  )


----------



## Spacerat (3. Mai 2012)

Okay, wir haben unseren eigenen Thread... weisst du zufällig, wie man "fatal" buchstabiert? ;(
http://www.java-forum.org/allgemeine-java-themen/135700-unerwarteter-methodenaufruf.html#post894344


----------

