JPCT OPENGL Projekt mit dynamischer 2D Vordergrundgrafik (Menu Image)

Started by Marlon, November 04, 2011, 01:42:00 AM

Previous topic - Next topic

Marlon

Hallo mal wieder!
Ich habe zu diesem Thema verzweifelt gesucht, jedoch nichts brauchbares gefunden.

Eine kurze Erläuterung meines Problems:
Ich habe in Anlehnung an das hier zur Verfügung gestellte AdvancedExample ein eigenes Testprogramm geschrieben, dass genau wie die Vorlage die OPENGL Unterstützung verwendet.
Ich bin sehr zufrieden mit meinen Fortschritten, jedoch möchte ich nun zusätzlich zur 3D Ausgabe auch ein 2D Menu verwenden.
Da OPENGL nativ ist, steht keine AWT-Zeichentechnik zur Verfügung (habe ich da wirklich richtig verstanden?), das heisst ein g.drawImage() kann ich an dieser Stelle vergessen (Im Softwaremodus in einem JFrame hat das wunderbar geklappt).

Ich habe etwas von buffer.blit() gelesen und ausprobiert, jedoch stellen Pixelgrabber und Textur keine Lösung dar, da sich die Menuelemente ja zur Laufzeit verändern und das damit zu rechenintensiv wäre.
Ich könnte mir vorstellen alle Elemente so wie Fenster, Kontextmenu, Buttons, etc. auf ein BufferedImage zu malen und dieses dann nach dem Aufruf von buffer.displayGLOnly() zu zeichnen, jedoch weiss ich nicht, wie ich das am besten anstelle.

Wenn jemand an dieser Stelle performante Lösungen parat hat würde ich mich echt sehr freuen!
Danke im Vorraus! :)
Marlon
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Du kannst theoretisch das BufferedImage in eine Textur kopieren und die dann blitten. Das ist nicht so richtig schnell, aber könnte ausreichend sein. Dieses kleine Projekt zum Anzeigen von Animated GIFs in Texturen macht das z.B. irgendwie so: http://www.jpct.net/forum2/index.php/topic,1406.0.html

Marlon

Mhhh... habe mir die Demos angeschaut und den Sourcecode downgeloaded. Hierbei wird jedoch von JApplet, bzw. JFrame abgeleitet, so dass die grafische Komponente zur Verfügung steht und ganz bequem darauf gezeichnet werden kann. So habe ich das damals auch testweise mal gemacht, das hat auch alles wunderbar funktioniert.

Da ich nun aber versuche mein Projekt als OPENGL Projekt zu realisieren, brauche ich eine andere Lösung, da ich kein JApplet, bzw. JFrame zur Verfügung stehen habe.
Ausserdem ist das ziemlich inperformant 40 mal in der Sekunde eine neue Textur anzulegen und mit einem Bild zu füllen.

Leider bin ich noch relativ grün hinter den Ohren was die ganze 3D Entwicklung anbetrifft, evtl. denke ich hier in eine völlig falsche Richtung.
Aber andere Spieleentwickler müssen es ja auch irgendwie hinbekommen, ein 2D - Menu über die 3D Spielwelt zu legen sollte ja keine aussergewöhnliche, nur schwer zu bezwingbare Disziplin darstellen.

Mein 2D Spiel, welches online im Netz gespielt werden kann besitzt ein solches Menu: http://www.forgottenelements.com/images/screenshots/pic2.jpg
Es handelt sich also um eine dynamische Fläche, die alle grafischen Menuelemente beinhaltet. Diese soll am Ende gezeichnet werden (also über der Spielwelt liegen).

Falls noch irgendjemand Tipps, Links, Ideen oder sogar Quellcode hat, immer her damit. :)
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Ja, das ist zwar ein Applet, aber das hat nichts zu sagen. Es nutzt die AWTGLCanvas, malt aber dennoch nicht direkt darauf herum. Das ging auch gar nicht vernünftig. Die Idee ist nicht, ständig eine neue Textur zu erzeugen, sondern den Inhalt der alten zu ändern. Das geht über einen ITextureEffect und das sollte sich in dem Code finden lassen.

Marlon

Alles klar, ich danke dir!
Ich werde mich nochmal intensiv mit dem Code auseinandersetzen und schauen, was sich realisieren lässt.
Der ITextureEffect ist mir tatsächlich neu.
www.forgottenelements.com
Free Action JAVA MMORPG

Marlon

Entweder habe ich mich nicht gut ausdrücken können oder befinde mich irgendwie im falschen Film (passiert mir irgendwie öfters).

Die erste Demo der verlinkten Seite malt tatsächlich auf der Graphics Komponente des Applets, beim 2. werden im Vorfeld geladene Texturen an die einzelnen Polygone des Objekts geklebt.
ITextureEffect bringt mich im Moment leider wirklich nicht weiter, Referenzen suche ich vergebens. :(

Ich erinnere mich aber, wie ich damals (vor rund 10 Monaten) für ein Projekt meines Arbeitsgebers versucht hatte das Bild einer Webcam im Hintergrund einer 3D Szene darzustellen (Stichpunkt: Augmented Reality).
Mit einem Kollegen habe ich dann geschraubt und gewerkelt, leider hat das aber nicht funktionert, das Blitting und Texturieren hat einfach zu viel Rechenzeit verschlungen. Wir sind dann zwangsläufig auf C# und XNA umgestiegen.  :-\
Ich muss mich also damit abfinden, dass dynamische Bilder über OPENGL nicht darstellbar sind.

Ich denke ich habe aber eine Möglichkeit gefunden, das Problem trotz allem zu bewältigen.
Die einzelnen Menugrafiken sind natürlich statisch (Kontextmenu mit Text, Buttons, Fenster, Bilder, usw...). Wenn ich nun anstatt alle Einzelelemente in Bufferedimages zu laden um diese zu einem bildschirmgrossen Bild zusammenzufügen, diese dem Texturmanager übergebe, kann ich diese nun einzeln rendern.

Leider birgt dies viel Arbeit und Neuaufwand, sollte aber dann hoffentlich performant und fehlerfrei laufen...
So, erstmal schönes Wochenende und danke nochmal!  :-X
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Vielleicht habe ich das auch verpeilt und der verlinkte Thread ist nicht der richtige. Jedenfalls kann man mit OpenGL durchaus einigermaßen effektiv Texturen ändern. Wenn du ein Beispiel für einen ITextureEffect benötigst, kann ich das machen. Die Methode, das Zeichnen in ein BufferedImage durch verschiedene Blits zu ersetzen, ist aber in jedem Fall besser. Hatte ich nur nicht vorgeschlagen, weil es irgendwie so klang, as sei das keine Option.

Marlon

Ich glaube, dass ich hier irgendwie grundlegend was falsch mache.
Ich habe einfachheits halber erstmal versucht, lediglich eine statische Textur zu laden und über die 3D Welt zu legen.
Unglücklicherweise scheitere ich bereits daran :(
Die Textur ist 1024*768 gross, genau wie das Fenster, welches die 3D Welt beinhaltet.
Nach den buffer.update(); buffer.displayGLOnly() Methoden habe ich also folgende Zeilen eingefügt:


Texture texture=TextureManager.getInstance().getTexture("menu");
buffer.blit(texture, 0, 0, 0, 0, 1024, 768, true);
buffer.display(null);


Das Resultat ist ein flackerndes Bild, was ausserdem 3 Mal in der Höhe übereinandergestapelt und total verzerrt ist.

Wünschenswert wäre ein Bild wie dieses hier, welches die 3D Szene im Hintergrund und das 2D Menu (am besten ein BufferedImage, welches mit Java2D dynamisch zur Laufzeit beschrieben werden kann) im Vordergrund hat:


Falls du irgendeinen Beispielcode für den ITextureEffect parat hättest, oder mir verraten kannst was ich falsch mache, wäre ich dir echt sehr dankbar!
Ich meine irgendeine Lösung für dieses Problem müsste es doch geben...

Danke auf jedenfall für deine Hilfsbereichtschaft und deine schnellen Antworten! :)
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

1024*768 als Größe akzeptiert jPCT nicht. Eine Textur muss immer eine Zweierpotenz als Höhe und Breite haben. 1024*1024 würde also gehen. Wenn du was anderes lädst, wird jPCT das skalieren (das sollte im Log auftauchen). Wenn du dann mit den "alten" Größen blittest, dann stimmt das Ergebnis nicht. Flackern sollte es aber eigentlich dennoch nicht.... ???

EgonOlsen

Ein einfaches Beispiel, in 15 Minuten zusammengehackt...also bitte keine Wunder erwarten. Es rendert einen drehenden Würfel und blitted oben drüber ein BufferedImage, in das in jedem Frame ein Haufen zufälliger blauer Quadrate gemalt werden. Vielleicht hilft das.


import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

import org.lwjgl.opengl.Display;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.ITextureEffect;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.World;
import com.threed.jpct.util.Light;

public class BlitExample {

public static void main(String[] args) {
new BlitExample().doIt();
}

private void doIt() {
FrameBuffer buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);
World world = new World();
Object3D cube = Primitives.getCube(10);
cube.translate(0, 0, 50);
Light light = new Light(world);
light.setIntensity(255, 0, 0);
light.setPosition(new SimpleVector(-100, 0, 0));
world.addObject(cube);
world.buildAllObjects();

BufferedImage image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_ARGB);
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

Texture blitTexture = new Texture(512, 512);
blitTexture.setEffect(new ITextureEffect() {

@Override
public void init(Texture tex) {
// TODO Auto-generated method stub
}

@Override
public void apply(int[] dest, int[] source) {
System.arraycopy(pixels, 0, dest, 0, pixels.length);
}

@Override
public boolean containsAlpha() {
return true;
}
});

long time = System.currentTimeMillis();
int fps = 0;

while (!Display.isCloseRequested()) {
cube.rotateY(0.01f);
buffer.clear();
world.renderScene(buffer);
world.draw(buffer);
updateAndBlit(buffer, image, blitTexture);
buffer.update();
buffer.displayGLOnly();
fps++;

if (System.currentTimeMillis() - time >= 1000) {
System.out.println(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
}
}

private void updateAndBlit(FrameBuffer buffer, BufferedImage image, Texture blitTexture) {

Graphics2D g = image.createGraphics();
g.setBackground(new Color(0, 0, 0, 0));
g.clearRect(0, 0, image.getWidth(), image.getHeight());
g.setColor(new Color(0, 0, 255, 255));
for (int i = 0; i < 5; i++) {
int x = (int) (Math.random() * image.getWidth());
int y = (int) (Math.random() * image.getHeight());
g.fillRect(x, y, 100, 100);
}
g.dispose();

blitTexture.applyEffect();
buffer.blit(blitTexture, 0, 0, 0, 0, 512, 512, buffer.getOutputWidth(), buffer.getOutputHeight(), 15, false, null);
}

}



ABER: Um etwa wie im Bild oben zu rendern, würde ich das niemals so machen. Da sollte die GUI tatsächlich komplett per Blitting erstellt werden. Wenn Sie nur zeitweise zu sehen ist und/oder sich nicht so oft ändert, mag die Lösung mit einem BufferedImage akzeptabel sein.

Marlon

Wow, super, danke für deine Mühe!  :)
Habe das Beispiel bei mir angetestet und es läuft mit 22 FPS.

Die GUI würde sich allerdings immer dann ändern, wenn man ein Fenster schliesst/öffnet, Gegenstände im Rucksack verschiebt, Kontextmenues öffnet, etc...
Ich habe dein Programm mal so umgeschrieben, dass mein Menubild 1024 * 768 darüber gelegt wird. Das sieht tatsächlich nicht verkehrt aus, allerdings läuft es mit 6 FPS wenn man das Bild nach jedem Rechenschritt neu setzt (-> blitTexture.applyEffect()). Verzichtet man auf diesen Auftruf läuft das Programm konstant mit 150 FPS.

Hier mein Code:


import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.lwjgl.opengl.Display;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.ITextureEffect;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.World;
import com.threed.jpct.util.Light;

public class BlitExample {
private BufferedImage defimage;

public static void main(String[] args) {
new BlitExample().doIt();
}

private void doIt() {
FrameBuffer buffer = new FrameBuffer(1024, 768, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);
World world = new World();
Object3D cube = Primitives.getCube(10);
cube.translate(0, 0, 50);
Light light = new Light(world);
light.setIntensity(255, 0, 0);
light.setPosition(new SimpleVector(-100, 0, 0));
world.addObject(cube);
world.buildAllObjects();

BufferedImage image = new BufferedImage(1024, 768, BufferedImage.TYPE_INT_ARGB);
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
try {
    defimage = ImageIO.read(new File("example/menu.png"));
} catch (IOException e) {
}

Texture blitTexture = new Texture(1024, 1024);
blitTexture.setEffect(new ITextureEffect() {

@Override
public void init(Texture tex) {
// TODO Auto-generated method stub
}

@Override
public void apply(int[] dest, int[] source) {
System.arraycopy(pixels, 0, dest, 0, pixels.length);
}

@Override
public boolean containsAlpha() {
return true;
}
});

long time = System.currentTimeMillis();
int fps = 0;

while (!Display.isCloseRequested()) {
cube.rotateY(0.01f);
buffer.clear();
world.renderScene(buffer);
world.draw(buffer);
updateAndBlit(buffer, image, blitTexture);
buffer.update();
buffer.displayGLOnly();
fps++;

if (System.currentTimeMillis() - time >= 1000) {
System.out.println(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
}
}

private void updateAndBlit(FrameBuffer buffer, BufferedImage image, Texture blitTexture) {

Graphics2D g = image.createGraphics();
g.drawImage(defimage, 0, 0, 1024, 768, 0, 0, 1024, 768, null, null);
g.dispose();

blitTexture.applyEffect();
buffer.blit(blitTexture, 0, 0, 0, 0, 1024, 768, buffer.getOutputWidth(), buffer.getOutputHeight(), 15, false, null);
}

}



Ich müsste also jedes Menuelement einzeln blitten, dann sollte es (hoffentlich) klappen.
So, werde jetzt mal ins Wochenende verschwinden und mich dann später nochmal genauer damit befassen und einige Tests machen.
Schönes WE und bis dann!
www.forgottenelements.com
Free Action JAVA MMORPG