https://www.jpct.net/wiki/index.php?title=Special:NewPages&feed=atom&limit=50&offset=&namespace=0&username=&tagfilter=
JPCT - New pages [en]
2024-03-28T11:57:44Z
From JPCT
MediaWiki 1.23.15
https://www.jpct.net/wiki/index.php?title=Particle_effect_example
Particle effect example
2017-09-21T05:13:48Z
<p>Corey: Created page with "=== Particle effect example === ==== Details ==== A straightforward particle effect demonstration that uses the opengl renderer. ==== Screen shot ==== File:Firepit_particl..."</p>
<hr />
<div>=== Particle effect example ===<br />
<br />
==== Details ====<br />
A straightforward particle effect demonstration that uses the opengl renderer.<br />
<br />
==== Screen shot ====<br />
[[File:Firepit_particle.png]]<br />
<br />
==== Source code ====<br />
<pre><br />
<br />
import java.awt.Color;<br />
import java.io.File;<br />
<br />
import org.lwjgl.LWJGLException;<br />
import org.lwjgl.input.Keyboard;<br />
import org.lwjgl.input.Mouse;<br />
import org.lwjgl.opengl.Display;<br />
<br />
import com.threed.jpct.*;<br />
<br />
/**<br />
* A simple particle demonstration.<br />
* In the demo we use a fire model, with 2 types of particles: flames and smoke.<br />
*<br />
* The code is not the best, but it should give you a general idea<br />
* on how to create particle effects for your own project.<br />
* <br />
* @author 'words' - jPCT.net<br />
*/<br />
public class ParticleDemo {<br />
<br />
private World world;<br />
private FrameBuffer frameBuffer;<br />
<br />
private Object3D fireModel;<br />
private GenericParticle particleTest;<br />
<br />
public ParticleDemo() {<br />
try {<br />
initGL();<br />
} catch (LWJGLException e) {<br />
e.printStackTrace();<br />
System.exit(1);<br />
}<br />
<br />
/*<br />
* Initialize the world<br />
*/<br />
this.world = new World();<br />
world.setAmbientLight(60, 60, 60);<br />
<br />
/*<br />
* Load the model<br />
*/<br />
TextureManager.getInstance().addTexture("flame", new Texture("res/flame.jpg"));<br />
TextureManager.getInstance().addTexture("smoke", new Texture("res/smoke.jpg"));<br />
fireModel = Object3D.mergeAll(Loader.load3DS("res/firepit.3ds", 4f));<br />
TextureManager.getInstance().addTexture("firepit", new Texture("res/firepit.jpg"));<br />
fireModel.setTexture("firepit");<br />
fireModel.rotateX((float) (-.5 * Math.PI)); // correct the rotation<br />
fireModel.rotateMesh();<br />
fireModel.setRotationMatrix(new Matrix());<br />
<br />
/*<br />
* Setup the camera<br />
*/<br />
Camera camera = world.getCamera();<br />
camera.moveCamera(Camera.CAMERA_MOVEOUT, 45); // zoom<br />
camera.moveCamera(Camera.CAMERA_MOVEUP, 45); // height<br />
camera.lookAt(new SimpleVector(10, 0, 10));<br />
camera.setFOV(1.5f);<br />
<br />
// Example!<br />
addFire(10, 0, 10);<br />
<br />
/*<br />
* Start the main loop:<br />
*/<br />
boolean running = true;<br />
while (running) {<br />
if (Display.isCloseRequested()) {<br />
running = false;<br />
return;<br />
}<br />
render();<br />
if (particleTest != null) {<br />
particleTest.update(world);<br />
}<br />
}<br />
shutdown();<br />
}<br />
<br />
private void addFire(float x, float y, float z) {<br />
Object3D fire = fireModel.cloneObject();<br />
fire.translate(x, y ,z);<br />
world.addObject(fire);<br />
this.particleTest = new GenericParticle(world, fire.getTransformedCenter());<br />
<br />
}<br />
<br />
private void initGL() throws LWJGLException {<br />
Logger.setLogLevel(Logger.LL_ONLY_ERRORS);<br />
Logger.setOnError(Logger.ON_ERROR_THROW_EXCEPTION);<br />
<br />
Config.glWindowName = "Particle Demo - jPCT " + Config.getVersion();<br />
Config.maxPolysVisible = 5000; // The maximum size of the VisList.<br />
<br />
this.frameBuffer = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY);<br />
frameBuffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);<br />
frameBuffer.enableRenderer(IRenderer.RENDERER_OPENGL);<br />
Display.setResizable(true);<br />
Mouse.create();<br />
}<br />
<br />
private void render() {<br />
frameBuffer.clear(Color.BLACK);<br />
world.renderScene(frameBuffer);<br />
world.draw(frameBuffer);<br />
frameBuffer.update();<br />
frameBuffer.displayGLOnly();<br />
<br />
pollKeyboard();<br />
pollMouse();<br />
}<br />
<br />
private void pollKeyboard() {<br />
while (Keyboard.next()) {<br />
final int keyCode = Keyboard.getEventKey();<br />
@SuppressWarnings("unused")<br />
final boolean pressed = Keyboard.getEventKeyState();<br />
if (keyCode == Keyboard.KEY_ESCAPE) {<br />
frameBuffer.dispose();<br />
System.exit(0);<br />
}<br />
}<br />
}<br />
<br />
private void pollMouse() {<br />
while (Mouse.next()) {<br />
if (Mouse.getEventButtonState()) {<br />
switch (Mouse.getEventButton()) {<br />
case 0: // left mouse button was clicked<br />
break;<br />
case 1: // right mouse button was clicked<br />
break;<br />
}<br />
}<br />
}<br />
}<br />
<br />
private void shutdown() {<br />
frameBuffer.dispose();<br />
Display.destroy();<br />
System.exit(0);<br />
}<br />
<br />
public static void main(String[] args) {<br />
String platform = System.getProperty("os.name").toLowerCase();<br />
String path = "./lib/native/";<br />
if (platform.indexOf("win") >= 0) {<br />
System.setProperty("org.lwjgl.librarypath", new File(path + "windows/").getAbsolutePath());<br />
} else if (platform.indexOf("mac") >= 0) {<br />
System.setProperty("org.lwjgl.librarypath", new File(path + "macosx/").getAbsolutePath());<br />
} else if ((platform.indexOf("nix") >= 0 || platform.indexOf("nux") >= 0 || platform.indexOf("aix") > 0)) {<br />
System.setProperty("org.lwjgl.librarypath", new File(path + "linux/").getAbsolutePath());<br />
} else if (platform.indexOf("sunos") >= 0) {<br />
System.setProperty("org.lwjgl.librarypath", new File(path + "solaris/").getAbsolutePath());<br />
}<br />
new ParticleDemo();<br />
}<br />
<br />
}<br />
</pre><br />
<pre><br />
<br />
import java.awt.Color;<br />
<br />
import com.threed.jpct.Matrix;<br />
import com.threed.jpct.Object3D;<br />
import com.threed.jpct.Primitives;<br />
import com.threed.jpct.SimpleVector;<br />
import com.threed.jpct.World;<br />
import com.threed.jpct.util.Light;<br />
<br />
/**<br />
* A generic particle handler.<br />
*<br />
*/<br />
public class GenericParticle {<br />
<br />
private final SimpleVector origin;<br />
private final Particle[] particles;<br />
private final int PARTICLE_COUNT = 1;<br />
private int count = 0;<br />
private long delay = System.currentTimeMillis();<br />
private int cycle = 0;<br />
private boolean firstTick = false;<br />
<br />
public GenericParticle(final World world, final SimpleVector origin) {<br />
this.origin = origin;<br />
this.particles = new Particle[25];<br />
}<br />
<br />
public void update(final World world) {<br />
if (!firstTick) {<br />
firstTick = true;<br />
Light light = new Light(world);<br />
light.setPosition(new SimpleVector(origin.x, origin.y - 40, origin.z));<br />
light.setAttenuation(-1);<br />
light.setIntensity(255, 255, 255);<br />
light.setDiscardDistance(500);<br />
light.enable();<br />
}<br />
if (System.currentTimeMillis() - delay > 120) {<br />
cycle++;<br />
if (cycle % 2 == 0) {<br />
for (int i = 0; i < PARTICLE_COUNT; i++) {<br />
// add particle<br />
Particle p = getParticle(world);<br />
p.setTranslationMatrix(new Matrix());<br />
p.setOrigin(origin);<br />
p.setVelocity(new SimpleVector(1 - Math.random() * 1, -1.4 - (Math.random() / 2f), 1 - Math.random() * 1));<br />
p.reset();<br />
<br />
}<br />
// move the visible particles<br />
for (int i = 0; i < count; i++) {<br />
Particle pp = particles[i];<br />
if (pp.getVisibility()) {<br />
pp.move(1);<br />
}<br />
}<br />
}<br />
delay = System.currentTimeMillis();<br />
}<br />
<br />
}<br />
<br />
private Particle getParticle(World w) {<br />
for (int i = 0; i < count; i++) {<br />
Particle pp = particles[i];<br />
if (!pp.getVisibility()) {<br />
pp.setVisibility(Object3D.OBJ_VISIBLE);<br />
return pp;<br />
}<br />
}<br />
Particle p = new Particle();<br />
w.addObject(p);<br />
particles[count] = p;<br />
count++;<br />
return p;<br />
}<br />
<br />
}<br />
<br />
@SuppressWarnings("serial")<br />
class Particle extends Object3D {<br />
<br />
private SimpleVector vel = new SimpleVector();<br />
private long time = 0;<br />
private long maxTime = 2000;<br />
private static final SimpleVector GRAV = new SimpleVector(0, -0.50f, 0);<br />
<br />
Particle() {<br />
super(Primitives.getPlane(2, 5));<br />
this.setBillboarding(true);<br />
this.setVisibility(true);<br />
this.setTransparency(12);<br />
this.setAdditionalColor(Color.WHITE);<br />
this.setLighting(Object3D.LIGHTING_NO_LIGHTS);<br />
this.enableLazyTransformations();<br />
this.build();<br />
reset();<br />
}<br />
<br />
void setVelocity(SimpleVector vel) {<br />
this.vel.set(vel);<br />
}<br />
<br />
void reset() {<br />
this.setTexture("flame");<br />
time = System.currentTimeMillis();<br />
getTranslationMatrix().setIdentity();<br />
}<br />
<br />
void move(int ticks) {<br />
if (getVisibility()) {<br />
if (System.currentTimeMillis() - time > 333) {<br />
this.setTexture("smoke");<br />
}<br />
for (int i = 0; i < ticks; i++) {<br />
vel.add(GRAV);<br />
this.translate(vel);<br />
}<br />
if (System.currentTimeMillis() - time > maxTime) {<br />
reset();<br />
this.setVisibility(Object3D.OBJ_INVISIBLE);<br />
}<br />
}<br />
}<br />
<br />
}<br />
</pre><br />
<br />
==== Download Link ====<br />
[http://www.mediafire.com/file/qzoo4arg67loa4z/fireparticle_res.zip http://www.mediafire.com/file/qzoo4arg67loa4z/fireparticle_res.zip]<br />
<br />
[[Category:jPCT]]</div>
Corey
https://www.jpct.net/wiki/index.php?title=Software_mode_demo
Software mode demo
2017-09-18T05:04:42Z
<p>Corey: /* Source code */</p>
<hr />
<div>=== Software mode demo ===<br />
<br />
==== Details ====<br />
This is a bare minimum software renderer example... This is nothing fancy, but may be helpful for first time jPCT users or anybody interested in using the jPCT software renderer mode to design their 3D application.<br />
<br />
==== Screen shot ====<br />
[[File:Software_Example320x320.png]]<br />
<br />
==== Source code ====<br />
<pre><br />
<br />
import java.awt.*;<br />
import java.awt.event.*;<br />
import java.util.Random;<br />
import java.util.concurrent.TimeUnit;<br />
<br />
import javax.swing.*;<br />
<br />
import com.threed.jpct.*;<br />
<br />
/**<br />
* A bare bones jPCT software mode application.<br />
* @author 'words' - jPCT.net<br />
*/<br />
@SuppressWarnings("serial")<br />
public class Engine extends JFrame implements KeyEventDispatcher, MouseListener, MouseMotionListener {<br />
<br />
// "Engine" variables...<br />
private final Graphics graphics;<br />
private FrameBuffer buffer;<br />
private final World world;<br />
private boolean keepAlive = false;<br />
private int mouseX = -1;<br />
private int mouseY = -1;<br />
private int frameCount = 0;<br />
<br />
// Program content variables...<br />
private Object3D cube;<br />
private Random random = new Random();<br />
<br />
static {<br />
Logger.setLogLevel(Logger.ERROR);<br />
<br />
/*<br />
* Configure the 3D engine (jPCT) settings.<br />
* http://www.jpct.net/doc/com/threed/jpct/Config.html<br />
*/<br />
<br />
// When using the software renderer on a multi core setup, it's a good idea to use all cores (or at least some of them) to speed up rendering.<br />
// To do so, set Config.useMultipleThreads to true before instantiating the frame buffer.<br />
// By default, jPCT will use four cores, which matches a quad core cpu. If the setup that runs the application uses less cores, it's advised to adjust this value before instantiating the frame buffer.<br />
// http://www.jpct.net/wiki/index.php?title=Multithreading#The_threading_framework<br />
Config.useMultipleThreads = Runtime.getRuntime().availableProcessors() > 1;<br />
<br />
// Dynamic load balancing usually is the better choice.<br />
// It's not enable by default simply because it can cause small artifacts in combination with the texel filtering that jPCT uses (some pixels may flicker from time to time).<br />
Config.loadBalancingStrategy = 1;<br />
<br />
// Usually, one have to call build() on all objects that have to be rendered.<br />
Config.autoBuild = true;<br />
<br />
// The maximum size of the VisList.<br />
Config.maxPolysVisible = 32000; // 32k<br />
<br />
/*<br />
* Submit a garbage collection to the virtual machine.<br />
*/<br />
Runtime.getRuntime().runFinalization();<br />
Runtime.getRuntime().gc();<br />
}<br />
<br />
public static void main(String[] args) {<br />
new Engine();<br />
}<br />
<br />
public Engine() {<br />
this.setTitle("jPCT " + Config.getVersion());<br />
this.setPreferredSize(new Dimension(320, 320));<br />
this.setBackground(Color.BLACK);<br />
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
this.pack();<br />
this.setSize(320, 320);<br />
this.setResizable(true);<br />
this.setLocationRelativeTo(null);<br />
this.setVisible(true);<br />
this.graphics = getGraphics();<br />
<br />
/*<br />
* Bind the hardware input listeners.<br />
*/<br />
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);<br />
this.addWindowListener(new WindowExitListener(this));<br />
this.addMouseListener(this);<br />
this.addMouseMotionListener(this);<br />
<br />
/*<br />
* Initialize a "re-sizable" frame buffer.<br />
*/<br />
this.buffer = new FrameBuffer(320, 320, FrameBuffer.SAMPLINGMODE_NORMAL);<br />
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);<br />
buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE);<br />
buffer.optimizeBufferAccess();<br />
this.addComponentListener(new ComponentListener() {<br />
@Override<br />
public void componentResized(ComponentEvent e) {<br />
int w = getWidth();<br />
int h = getHeight();<br />
buffer = new FrameBuffer(w, h, FrameBuffer.SAMPLINGMODE_NORMAL);<br />
buffer.refresh();<br />
}<br />
@Override<br />
public void componentHidden(ComponentEvent e) { }<br />
@Override<br />
public void componentMoved(ComponentEvent e) { }<br />
@Override<br />
public void componentShown(ComponentEvent e) { }<br />
});<br />
<br />
// Initialize worlds<br />
this.world = new World();<br />
world.setAmbientLight(85, 85, 85); // Overall world brightness<br />
<br />
// Create the cube object<br />
this.cube = Primitives.getCube(5);<br />
world.addObject(cube);<br />
<br />
// Move camera<br />
Camera cam = world.getCamera();<br />
cam.moveCamera(Camera.CAMERA_MOVEOUT, 30);<br />
cam.moveCamera(Camera.CAMERA_MOVEUP, 15);<br />
cam.lookAt(cube.getTransformedCenter());<br />
cam.setFOV(1.5f);<br />
<br />
/*<br />
* Run the program.<br />
*/<br />
keepAlive = true;<br />
execute();<br />
}<br />
<br />
@Override<br />
public void mousePressed(MouseEvent e) {<br />
int button = e.getButton();<br />
if (button == 1) {<br />
System.out.println("Mouse -> L click @ " + mouseX + "," + mouseY);<br />
}<br />
if (button == 3) {<br />
System.out.println("Mouse -> R click @ " + mouseX + "," + mouseY);<br />
}<br />
}<br />
<br />
@Override<br />
public void mouseReleased(MouseEvent e) {<br />
int button = e.getButton();<br />
if (button == 1) {<br />
System.out.println("Mouse -> left button released");<br />
}<br />
if (button == 3) {<br />
System.out.println("Mouse -> right button released");<br />
}<br />
}<br />
<br />
@Override<br />
public void mouseMoved(MouseEvent e) {<br />
this.mouseX = e.getX();<br />
this.mouseY = e.getY();<br />
}<br />
<br />
@Override<br />
public void mouseClicked(MouseEvent e) { }<br />
<br />
@Override<br />
public void mouseEntered(MouseEvent e) { }<br />
<br />
@Override<br />
public void mouseExited(MouseEvent e) { }<br />
<br />
@Override<br />
public void mouseDragged(MouseEvent e) { }<br />
<br />
@Override<br />
public boolean dispatchKeyEvent(KeyEvent e) {<br />
if (e.getID() == KeyEvent.KEY_RELEASED && e.getKeyCode() == KeyEvent.VK_ESCAPE) {<br />
keepAlive = false;<br />
}<br />
return true;<br />
}<br />
<br />
private void execute() {<br />
World.setDefaultThread(Thread.currentThread());<br />
<br />
long lastSecond = 0L;<br />
while (keepAlive) {<br />
long currentTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());<br />
<br />
// Reset the frame counter.<br />
if (lastSecond < currentTime - 1000) {<br />
lastSecond = currentTime;<br />
setTitle("FPS: " + frameCount);<br />
frameCount = 0;<br />
<br />
// Re-color the cube<br />
cube.setAdditionalColor(random.nextInt(255), random.nextInt(255), random.nextInt(255));<br />
}<br />
<br />
// Rotate the cube<br />
cube.rotateZ(0.01f * random.nextFloat());<br />
<br />
// Clear the previous frame.<br />
buffer.clear();<br />
<br />
/*<br />
* Draw the new frame.<br />
*/<br />
frameCount++;<br />
world.renderScene(buffer);<br />
world.draw(buffer);<br />
buffer.update();<br />
buffer.display(graphics);<br />
}<br />
<br />
/*<br />
* Terminate the program.<br />
*/<br />
System.exit(0);<br />
}<br />
<br />
/**<br />
* Listen for the window exit event and use exit prompt.<br />
*/<br />
public class WindowExitListener extends WindowAdapter {<br />
<br />
private final JFrame frame;<br />
<br />
public WindowExitListener(final JFrame frame) {<br />
this.frame = frame;<br />
}<br />
<br />
@Override<br />
public void windowClosing(final WindowEvent e) {<br />
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);<br />
String ObjButtons[] = { "Yes", "No" };<br />
int result = JOptionPane.showOptionDialog(null, "Exit jPCT + " + Config.getVersion() + " ?", "Confirm Exit", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, ObjButtons, ObjButtons[1]);<br />
if (result == JOptionPane.YES_OPTION) {<br />
System.exit(0);<br />
}<br />
}<br />
}<br />
<br />
}<br />
</pre><br />
<br />
[[Category:jPCT]]</div>
Corey
https://www.jpct.net/wiki/index.php?title=Terrain_with_foliage_example
Terrain with foliage example
2017-09-17T18:36:07Z
<p>Corey: Created page with "=== Terrain with foliage example === ==== Details ==== This is a terrain with a lot of foliage, lens flare, and sun. You can actually "paint" the splatting into a texture ins..."</p>
<hr />
<div>=== Terrain with foliage example ===<br />
<br />
==== Details ====<br />
This is a terrain with a lot of foliage, lens flare, and sun. You can actually "paint" the splatting into a texture instead of relying on the height and some dubious alpha values. <br />
Sources are included. Compatible with with jPCT 1.20 or higher.<br />
<br />
==== Screen shot ====<br />
[[File:Splatting.jpg]]<br />
<br />
<br />
==== Program keys ====<br />
<pre><br />
1+2 : Move the sun. (The lens flare doesn't take the mountains into account for performance reasons.)<br />
T: toggle trees (on by default)<br />
D: toggle detail mapping (off by default)<br />
CRSR+PGUP+PGDOWN: move<br />
ESC: Exit<br />
<br />
FPS will be printed to the console each second.<br />
</pre><br />
<br />
==== Download Link ====<br />
[http://www.jpct.net/download/misc/terrain.zip http://www.jpct.net/download/misc/terrain.zip]<br />
<br />
==== Final words ====<br />
<br />
It's pretty hacky, especially processing of the trees and generating the terrain texturing could be improved. It is recommended that you optimize the source if you intend to use it for a project.<br />
<br />
[[Category:jPCT]]</div>
Corey
https://www.jpct.net/wiki/index.php?title=Basic_shadow_mapping
Basic shadow mapping
2016-10-22T12:21:29Z
<p>Admin: </p>
<hr />
<div>This is a basic example of shadow mapping using jPCT-AE in OpenGL ES 2.0 mode and the ShadowHelper class. This example uses PCF filtering to smooth out the edges of the shadow. Depending on the application, you might want to disable this for performance reasons.<br />
<br />
The basic idea is simple: You create an instance of the ShadowHelper, assign a projector (i.e. the light source that should cast the shadow), some receivers and some casters and in the rendering process, you add an additional call to ShadowHelper.updateShadowMap(), if that's needed (i.e. if either the object(s) or the projector have been translated). The rest should happen automatically. If you are using your own shaders, it might happen automatically as well. That depends on how much your own shaders are based on jPCT-AE's default shaders. The shadow mapping code tries to modify existing shaders to make them support shadow mapping. Depening on the structure of the shader, this might or might not work. More information about this can be found here: [http://www.jpct.net/jpct-ae/doc/com/threed/jpct/GLSLShadowInjector.html GLSLShadowInjector]<br />
<br />
<pre><br />
package com.threed.jpct.examples;<br />
<br />
import javax.microedition.khronos.egl.EGLConfig;<br />
import javax.microedition.khronos.opengles.GL10;<br />
<br />
import android.app.Activity;<br />
import android.opengl.GLSurfaceView;<br />
import android.os.Bundle;<br />
import android.view.MotionEvent;<br />
<br />
import com.threed.jpct.Camera;<br />
import com.threed.jpct.Config;<br />
import com.threed.jpct.FrameBuffer;<br />
import com.threed.jpct.GLSLShadowInjector;<br />
import com.threed.jpct.Light;<br />
import com.threed.jpct.Logger;<br />
import com.threed.jpct.Object3D;<br />
import com.threed.jpct.Projector;<br />
import com.threed.jpct.RGBColor;<br />
import com.threed.jpct.SimpleVector;<br />
import com.threed.jpct.World;<br />
import com.threed.jpct.util.ExtendedPrimitives;<br />
import com.threed.jpct.util.MemoryHelper;<br />
import com.threed.jpct.util.ShadowHelper;<br />
<br />
public class ShadowExample extends Activity {<br />
<br />
private GLSurfaceView mGLView;<br />
private MyRenderer renderer = null;<br />
private FrameBuffer buffer = null;<br />
private World world = null;<br />
private RGBColor back = new RGBColor(50, 50, 100);<br />
<br />
private float touchTurn = 0;<br />
private float touchTurnUp = 0;<br />
<br />
private float xpos = -1;<br />
private float ypos = -1;<br />
<br />
private Object3D myObject = null;<br />
private Object3D plane = null;<br />
private int fps = 0;<br />
<br />
private Light sun = null;<br />
private ShadowHelper sh;<br />
<br />
protected void onCreate(Bundle savedInstanceState) {<br />
super.onCreate(savedInstanceState);<br />
mGLView = new GLSurfaceView(getApplication());<br />
mGLView.setEGLContextClientVersion(2);<br />
renderer = new MyRenderer();<br />
mGLView.setRenderer(renderer);<br />
setContentView(mGLView);<br />
}<br />
<br />
@Override<br />
protected void onPause() {<br />
super.onPause();<br />
mGLView.onPause();<br />
}<br />
<br />
@Override<br />
protected void onResume() {<br />
super.onResume();<br />
mGLView.onResume();<br />
}<br />
<br />
@Override<br />
protected void onStop() {<br />
super.onStop();<br />
System.exit(0);<br />
}<br />
<br />
public boolean onTouchEvent(MotionEvent me) {<br />
<br />
if (me.getAction() == MotionEvent.ACTION_DOWN) {<br />
xpos = me.getX();<br />
ypos = me.getY();<br />
return true;<br />
}<br />
<br />
if (me.getAction() == MotionEvent.ACTION_UP) {<br />
xpos = -1;<br />
ypos = -1;<br />
touchTurn = 0;<br />
touchTurnUp = 0;<br />
return true;<br />
}<br />
<br />
if (me.getAction() == MotionEvent.ACTION_MOVE) {<br />
float xd = me.getX() - xpos;<br />
float yd = me.getY() - ypos;<br />
<br />
xpos = me.getX();<br />
ypos = me.getY();<br />
<br />
touchTurn = xd / -100f;<br />
touchTurnUp = yd / -100f;<br />
return true;<br />
}<br />
return super.onTouchEvent(me);<br />
}<br />
<br />
protected boolean isFullscreenOpaque() {<br />
return true;<br />
}<br />
<br />
class MyRenderer implements GLSurfaceView.Renderer {<br />
<br />
private long time = System.currentTimeMillis();<br />
<br />
public MyRenderer() {<br />
Config.farPlane = 5000;<br />
}<br />
<br />
public void onSurfaceChanged(GL10 gl, int w, int h) {<br />
try {<br />
buffer = new FrameBuffer(w, h);<br />
<br />
world = new World();<br />
world.setAmbientLight(100, 100, 100);<br />
<br />
myObject = ExtendedPrimitives.createEllipsoid(new SimpleVector(20, 30, 15), 15, 1f, 1f);<br />
myObject.build();<br />
myObject.translate(0, 10, 0);<br />
world.addObject(myObject);<br />
<br />
plane = ExtendedPrimitives.createPlane(100, 2);<br />
plane.build();<br />
plane.translate(0, 30, 0);<br />
world.addObject(plane);<br />
<br />
Camera cam = world.getCamera();<br />
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);<br />
cam.lookAt(plane.getTransformedCenter());<br />
<br />
SimpleVector sv = new SimpleVector(myObject.getTransformedCenter());<br />
sv.y -= 100;<br />
sv.z += 30;<br />
<br />
sun = new Light(world);<br />
sun.setIntensity(250, 250, 250);<br />
sun.setPosition(sv);<br />
<br />
Projector projector = new Projector();<br />
projector.setClippingPlanes(1f, 300f);<br />
projector.setFOVLimits(0, 999);<br />
float fov = projector.convertDEGAngleIntoFOV(90);<br />
projector.setFOV(fov);<br />
projector.setYFOV(fov);<br />
projector.setPosition(sun.getPosition());<br />
projector.lookAt(myObject.getTransformedCenter());<br />
<br />
ShadowHelper.setShadowMode(GLSLShadowInjector.PCF_FILTERED_SHADOWS);<br />
<br />
sh = new ShadowHelper(buffer, projector, 1024);<br />
sh.setLightSource(projector);<br />
sh.setAmbientLight(new RGBColor(50, 50, 50));<br />
sh.addCaster(myObject);<br />
sh.addReceiver(plane);<br />
sh.setFilterSize(1);<br />
<br />
MemoryHelper.compact();<br />
} catch (Exception e) {<br />
throw new RuntimeException(e);<br />
}<br />
}<br />
<br />
public void onSurfaceCreated(GL10 gl, EGLConfig config) {<br />
//<br />
}<br />
<br />
public void onDrawFrame(GL10 gl) {<br />
if (touchTurn != 0) {<br />
myObject.rotateY(touchTurn);<br />
touchTurn = 0;<br />
}<br />
<br />
if (touchTurnUp != 0) {<br />
myObject.rotateX(touchTurnUp);<br />
touchTurnUp = 0;<br />
}<br />
<br />
sh.updateShadowMap(buffer, world);<br />
<br />
buffer.clear(back);<br />
world.renderScene(buffer);<br />
world.draw(buffer);<br />
<br />
buffer.display();<br />
<br />
if (System.currentTimeMillis() - time >= 1000) {<br />
Logger.log(fps + "fps");<br />
fps = 0;<br />
time = System.currentTimeMillis();<br />
}<br />
fps++;<br />
}<br />
}<br />
}<br />
</pre></div>
Admin
https://www.jpct.net/wiki/index.php?title=Instance_Batch_Rendering
Instance Batch Rendering
2016-07-13T16:11:06Z
<p>Redman: /* How It Works */</p>
<hr />
<div>== Overview ==<br />
Instance Batch Rendering (IBR) is a name I (alias Redman) dubbed for a lighter-weight method of handling and rendering multiple Object3D's of the same Mesh. Android applications need to be optimized for limited memory and speed. This method will help increase the speed of the rendering process with a lighter footprint, but requires work on your-end as well. IBR relies on GLSL, and does support bones using a hybrid GPU method. Features of jPCT may not be available for IBR.<br />
<br />
Example uses of IBR might be:<br />
<ul><br />
<li>an RTS with multiples of the same model;</li><br />
<li>an FPS with multiple enemies of the same model;</li><br />
<li>rendering trees on terrain;</li><br />
<li>etc...</li><br />
</ul><br />
<br />
I have added notes and personal recommendations. This architecture will most likely not be perfect for your project, so please alter and use to fit your needs.<br />
<br />
Comments and suggestions are welcome as I will be adding a FAQ to the bottom of the page.<br />
<br />
<br />
== Requirements ==<br />
<ul><br />
<li>jPCT (requires GL ES 2+ as it uses GLSL Shaders)</li><br />
</ul><br />
<br />
<br />
== How It Works ==<br />
IBR works by only creating and adding one Object3D to the world for a given object that can be rendered any number of times. This Object3D is always positioned in-front of camera so it is always picked up in the render pipeline (handled by the InstanceManager on update). A lighter-weight InstanceObject3D class is used instead of an Object3D for the instance's position, rotation, scale, and object type (reference to the Object3D's mesh). An IRenderHook is attached to the Object3D, which ties into jPCT's render pipeline. When the Object3D is rendered, it will loop X number of times through the render call for each instance. Each time calculating the ModelViewMatrix of your Instance3D and passing it into the GLSL Shader as a uniform.<br />
<br />
'''Gains:''' Speed: IBR taps into the jPCT's render pipeline to batch render all instances of a model synchronously. Memory Usage: lighter footprint per multiple instances.<br />
<br />
'''Losses:''' Many niceties that the Object3D offers: different texture data per model, octrees, etc... (These features will not come stock)<br />
<br />
Using IBR, you will be responsible for determining if each instance is visible by the camera. If there are no visible instances, the InstanceManager will automatically set the Object3D visibility to false, so it does not get added to the render visibility list.<br />
<br />
'''''Note:''''' IBR will most likely not work well if you use an Object3D with transparency for your InstanceObject3D. Please see the FAQ for more details.<br />
<br />
== The Code ==<br />
This code is free, open-source code and you are welcome to use it as you see fit.<br />
<br />
This code can be modified to remove Bones, but I will leave that up to you.<br />
<br />
<br />
'''InstanceManager Class:'''<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import com.threed.jpct.Camera;<br />
import com.threed.jpct.Matrix;<br />
import com.threed.jpct.Object3D;<br />
import com.threed.jpct.SimpleVector;<br />
import com.threed.jpct.World;<br />
<br />
import java.util.ArrayList;<br />
import java.util.HashMap;<br />
<br />
import raft.jpct.bones.Animated3D;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public class InstanceManager {<br />
private static InstanceManager instance = null;<br />
private InstanceList instanceFullList = new InstanceList();<br />
private InstanceObject3D[] instances = null;<br />
private HashMap<Short, Short> instanceIdMap = new HashMap<Short,Short>();<br />
private ArrayList<InstanceTypeList> instanceLists = new ArrayList<InstanceTypeList>();<br />
private InstanceShader shader = null;<br />
private IInstanceManagerHelper helper = null;<br />
private SimpleVector objectHolderPosition = new SimpleVector();<br />
private Object3D objectHolder = Object3D.createDummyObj();<br />
public Matrix cameraMatrix = new Matrix();<br />
public SimpleVector cameraBack = new SimpleVector();<br />
public float objectHolderOffsetDistance = 10f; //Change this to something within range of the camera near/far plane if necessary<br />
//Temp variable for memory saving purposes<br />
private InstanceTypeList tmpIL;<br />
private InstanceObject3D tmpIO3D;<br />
private Short tmpIndex;<br />
private short i;<br />
private Camera tmpCamera;<br />
<br />
protected InstanceManager() { ; }<br />
public static InstanceManager getInstance() {<br />
if(instance==null) {<br />
instance = new InstanceManager();<br />
}<br />
return instance;<br />
}<br />
<br />
/*** GETTERS & SETTERS ***/<br />
public InstanceShader getShader() {<br />
return shader;<br />
}<br />
public void setShader(InstanceShader pShader) {<br />
shader = pShader;<br />
}<br />
public IInstanceManagerHelper getHelper() {<br />
return helper;<br />
}<br />
public void setHelper(IInstanceManagerHelper pHelper) {<br />
helper = pHelper;<br />
}<br />
<br />
public Object3D getObject3DOfInstanceType(short pType) {<br />
tmpIndex = instanceIdMap.get(pType);<br />
if(tmpIndex!=null) {<br />
tmpIL = instanceLists.get(tmpIndex);<br />
if(tmpIL!=null && tmpIL.renderer!=null) {<br />
return tmpIL.renderer.renderObj3D;<br />
}<br />
}<br />
return null;<br />
}<br />
//Instance Renderer must be called for the ID or it will never get renderered (properly at least). Should happen automatically with spawn or add<br />
public InstanceObjectRenderer addInstanceRenderer(short pType, World pWorld) {<br />
if(!instanceIdMap.containsKey(pType)) {<br />
Object3D obj = helper!=null ? helper.fetchObject3D(pType) : null;<br />
<br />
if(obj!=null) {<br />
Object3D[] parents = obj.getParents();<br />
if(parents!=null && parents.length>0) {<br />
for(int j=0;j<parents.length;++j) {<br />
obj.removeParent(parents[j]);<br />
}<br />
}<br />
obj.addParent(objectHolder);<br />
pWorld.addObject(obj);<br />
<br />
if(shader!=null) {<br />
obj.setShader(shader);<br />
}<br />
tmpIL = new InstanceTypeList();<br />
tmpIL.type = pType;<br />
tmpIL.renderer = obj instanceof Animated3D ? new InstanceAnimatedRenderer(obj) : new InstanceObjectRenderer(obj);<br />
instanceLists.add(tmpIL);<br />
instanceIdMap.put(tmpIL.type, (short)(instanceLists.size()-1));<br />
<br />
return tmpIL.renderer;<br />
}<br />
} else {<br />
tmpIndex = instanceIdMap.get(pType);<br />
if(tmpIndex!=null) {<br />
tmpIL = instanceLists.get(tmpIndex);<br />
if (tmpIL != null) {<br />
return tmpIL.renderer;<br />
}<br />
}<br />
}<br />
return null;<br />
}<br />
public InstanceObjectRenderer getInstanceRenderer(short pType) {<br />
if(instanceLists.contains(pType)) {<br />
tmpIL = instanceLists.get(pType);<br />
if(tmpIL!=null) {<br />
return tmpIL.renderer;<br />
}<br />
}<br />
return null;<br />
}<br />
//To be called on tear down<br />
public void flush() {<br />
instanceFullList.clear();<br />
instanceIdMap.clear();<br />
instanceLists.clear();<br />
instances = null;<br />
tmpIL = null;<br />
tmpIO3D = null;<br />
tmpIndex = null;<br />
}<br />
public InstanceObject3D spawn(short pType, World pWorld) {<br />
InstanceObjectRenderer ior = addInstanceRenderer(pType, pWorld);<br />
boolean animated = ior!=null && ior instanceof InstanceAnimatedRenderer;<br />
InstanceObject3D inst = (helper!=null ? helper.spawn(pType, ior) : (animated ? new InstanceAnimated3D(pType, ior) : new InstanceObject3D(pType)));<br />
add(inst, pWorld);<br />
return inst;<br />
}<br />
public void add(InstanceObject3D pIO3D, World pWorld) {<br />
if(pIO3D!=null) {<br />
instanceFullList.add(pIO3D);<br />
addInstanceRenderer(pIO3D.type, pWorld);<br />
}<br />
}<br />
public void remove(InstanceObject3D pIO3D) {<br />
if(pIO3D!=null) {<br />
instanceFullList.remove(pIO3D);<br />
}<br />
}<br />
public void onUpdate(World pWorld, long pTimeSinceLastFrame) {<br />
cameraMatrix.setIdentity();<br />
if(pWorld!=null) {<br />
tmpCamera = pWorld.getCamera();<br />
cameraMatrix.setTo(tmpCamera.getBack());<br />
cameraMatrix.transformToGL();<br />
cameraBack.set(tmpCamera.getPosition());<br />
<br />
//Shift the objectHolder (which holds one instance of each Object3D) a distance of objectHolderOffsetDistance infront of the camera<br />
SimpleVector newObjectHolderPosition = tmpCamera.getDirection().normalize();<br />
newObjectHolderPosition.scalarMul(objectHolderOffsetDistance);<br />
newObjectHolderPosition.add(tmpCamera.getPosition());<br />
objectHolder.translate(newObjectHolderPosition.calcSub(objectHolderPosition));<br />
objectHolderPosition.set(newObjectHolderPosition);<br />
}<br />
<br />
//Reset Renderable<br />
for(i=0;i<instanceLists.size();++i) {<br />
instanceLists.get(i).visInstances.clear();<br />
}<br />
<br />
//Loop through all instances<br />
instances = instanceFullList.getList();<br />
if(instances!=null) {<br />
for (i = 0; i < instances.length; ++i) {<br />
tmpIO3D = instances[i];<br />
if (tmpIO3D != null) {<br />
tmpIO3D.update(pTimeSinceLastFrame);<br />
if (helper != null && helper.checkVisibility(tmpIO3D, tmpCamera)) {<br />
tmpIndex = instanceIdMap.get(tmpIO3D.type);<br />
if (tmpIndex != null) {<br />
tmpIL = instanceLists.get(tmpIndex);<br />
if (tmpIL != null && !tmpIL.visInstances.contains(tmpIO3D)) {<br />
//Add this instance3D to the visList!<br />
tmpIL.visInstances.add(tmpIO3D);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
//Finalize<br />
for(i=0;i<instanceLists.size();++i) {<br />
tmpIL = instanceLists.get(i);<br />
tmpIL.renderer.finalizeRenderableInstances(tmpIL.visInstances.getList());<br />
}<br />
}<br />
<br />
<br />
/*** Private Class ***/<br />
private class InstanceTypeList {<br />
public short type;<br />
public InstanceList visInstances = new InstanceList();<br />
public InstanceObjectRenderer renderer;<br />
}<br />
private class InstanceList {<br />
private static final short SIZE = 100;<br />
private InstanceObject3D[] listWithPad = null;<br />
private InstanceObject3D[] list = null;<br />
public int count = 0;<br />
private boolean changed = true;<br />
private int tmpI;<br />
<br />
public InstanceList() {<br />
listWithPad = new InstanceObject3D[InstanceList.SIZE];<br />
}<br />
<br />
/*** PUBLIC ***/<br />
public InstanceObject3D[] getList() {<br />
if(changed) {<br />
list = toArray();<br />
changed = false;<br />
}<br />
return list;<br />
}<br />
public InstanceObject3D get(int i) {<br />
return listWithPad[i];<br />
}<br />
public void clear() {<br />
for(tmpI = 0; tmpI < count; ++tmpI) {<br />
listWithPad[tmpI] = null;<br />
}<br />
if(listWithPad.length > 1000) {<br />
listWithPad = new InstanceObject3D[InstanceList.SIZE];<br />
}<br />
count = 0;<br />
changed = true;<br />
}<br />
public void add(InstanceObject3D obj) {<br />
if(count >= listWithPad.length) {<br />
InstanceObject3D[] tmp = new InstanceObject3D[InstanceList.SIZE + listWithPad.length];<br />
System.arraycopy(listWithPad, 0, tmp, 0, listWithPad.length);<br />
listWithPad = tmp;<br />
}<br />
<br />
listWithPad[count] = obj;<br />
++count;<br />
changed = true;<br />
}<br />
public void remove(int pIndex) {<br />
if(pIndex + 1 < count) {<br />
System.arraycopy(listWithPad, pIndex+1, listWithPad, pIndex, count - pIndex - 1);<br />
}<br />
<br />
--count;<br />
listWithPad[count] = null;<br />
changed = true;<br />
}<br />
boolean remove(InstanceObject3D pObj) {<br />
for(tmpI = 0; tmpI < count; ++tmpI) {<br />
if(listWithPad[tmpI].equals(pObj)) {<br />
remove(tmpI);<br />
return true;<br />
}<br />
}<br />
<br />
return false;<br />
}<br />
boolean contains(InstanceObject3D pObj) {<br />
for(tmpI = 0; tmpI < count; ++tmpI) {<br />
if(listWithPad[tmpI].equals(pObj)) {<br />
return true;<br />
}<br />
}<br />
return false;<br />
}<br />
<br />
/*** PRIVATE ***/<br />
private InstanceObject3D[] toArray() {<br />
InstanceObject3D[] res = new InstanceObject3D[count];<br />
System.arraycopy(listWithPad, 0, res, 0, count);<br />
return res;<br />
}<br />
}<br />
}<br />
</pre><br />
<br />
<br />
'''InstanceObjectRenderer Class:'''<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import com.threed.jpct.GLSLShader;<br />
import com.threed.jpct.IRenderHook;<br />
import com.threed.jpct.Matrix;<br />
import com.threed.jpct.Object3D;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public class InstanceObjectRenderer implements IRenderHook {<br />
protected short renderIndex=0, renderCount=0, tmpIndex;<br />
protected InstanceObject3D[] instances;<br />
protected GLSLShader shader;<br />
private InstanceObject3D instance;<br />
private Matrix mo = new Matrix();<br />
private InstanceManager instanceMgr;<br />
public Object3D renderObj3D = null;<br />
<br />
public InstanceObjectRenderer(Object3D pRenderObj3D) {<br />
renderObj3D = pRenderObj3D;<br />
if(renderObj3D!=null) {<br />
renderObj3D.setRenderHook(this);<br />
}<br />
instanceMgr = InstanceManager.getInstance();<br />
}<br />
<br />
public void finalizeRenderableInstances(InstanceObject3D[] pInstances) {<br />
instances = pInstances;<br />
if(renderObj3D!=null) {<br />
//Set the object3D visibility based off any visible instances<br />
renderObj3D.setVisibility(instances != null && instances.length > 0);<br />
}<br />
}<br />
<br />
@Override<br />
public void beforeRendering(int i) {<br />
renderIndex=0;<br />
renderCount=(short)(instances!=null ? instances.length : 0);<br />
setInstanceUniformValues(true);<br />
}<br />
@Override<br />
public void afterRendering(int i) {<br />
if(renderObj3D!=null) {<br />
shader = renderObj3D.getShader();<br />
if(shader != null && shader instanceof InstanceShader) {<br />
((InstanceShader) shader).setInstanceCleanupUniforms();<br />
}<br />
}<br />
}<br />
@Override<br />
public void setCurrentObject3D(Object3D object3D) { ; }<br />
@Override<br />
public void setCurrentShader(GLSLShader glslShader) { ; }<br />
@Override<br />
public void setTransparency(float v) { ; }<br />
@Override<br />
public void onDispose() { ; }<br />
@Override<br />
public boolean repeatRendering() {<br />
renderIndex++;<br />
if(renderIndex<renderCount) {<br />
setInstanceUniformValues(false);<br />
return true;<br />
}<br />
return false;<br />
}<br />
<br />
/*** Protected methods ***/<br />
protected void setAnimatedUniforms(InstanceAnimated3D pIA3D) { ; }<br />
<br />
/*** Private methods ***/<br />
private void setInstanceUniformValues(boolean pSetSingleUniforms) {<br />
if(renderObj3D!=null && instances!=null && renderIndex>=0 && renderIndex<instances.length) {<br />
shader = renderObj3D.getShader();<br />
instance = instances[renderIndex];<br />
if(shader!=null && shader instanceof InstanceShader && instance!=null) {<br />
if(pSetSingleUniforms) {<br />
((InstanceShader)shader).setInstanceStartUniforms();<br />
}<br />
<br />
mo.setTo(instance.getTransformMatrix());<br />
mo.translate(-instanceMgr.cameraBack.x, instanceMgr.cameraBack.y, instanceMgr.cameraBack.z);<br />
mo.matMul(instanceMgr.cameraMatrix);<br />
((InstanceShader)shader).setInstanceUniforms(mo);<br />
<br />
if(instance instanceof InstanceAnimated3D) {<br />
setAnimatedUniforms((InstanceAnimated3D)instance);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
</pre><br />
<br />
<br />
'''InstanceObject3D Class:'''<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import com.threed.jpct.Matrix;<br />
import com.threed.jpct.SimpleVector;<br />
import com.threed.jpct.World;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public class InstanceObject3D {<br />
protected SimpleVector position = new SimpleVector();<br />
protected float scaleFactor = 1.0f;<br />
protected Matrix rotationMatrix = new Matrix();<br />
protected Matrix translationMatrix = new Matrix();<br />
protected Matrix transformMatrix = new Matrix();<br />
protected boolean changed = true;<br />
public short type = -1;<br />
public boolean visible = true;<br />
<br />
public InstanceObject3D(short pType) {<br />
type = pType;<br />
transformMatrix.setIdentity();<br />
rotationMatrix.setIdentity();<br />
}<br />
<br />
public void translate(SimpleVector pVector) {<br />
changed = true;<br />
position.add(pVector);<br />
translationMatrix.translate(pVector);<br />
}<br />
public void setPosition(SimpleVector pPosition) {<br />
changed = true;<br />
position.set(pPosition);<br />
regenMatrix();<br />
}<br />
public SimpleVector getPosition() {<br />
return new SimpleVector(position);<br />
}<br />
public void setPosition(float pX, float pY, float pZ) {<br />
changed = true;<br />
position.set(pX, pY, pZ);<br />
regenMatrix();<br />
}<br />
public void setType(short pType, World pWorld) {<br />
type = pType;<br />
InstanceManager.getInstance().addInstanceRenderer(type, pWorld);<br />
}<br />
public void scale(float scale) {<br />
if(scale > 0.0F) {<br />
changed = true;<br />
scaleFactor *= scale;<br />
rotationMatrix.scalarMul(scale);<br />
}<br />
}<br />
public void setScale(float absScale) {<br />
if(scaleFactor != 0.0F && absScale > 0.0F) {<br />
float scale = absScale / scaleFactor;<br />
if(scale < 1.0E-4F) {<br />
scale = 1.0E-4F;<br />
}<br />
<br />
scale(scale);<br />
}<br />
}<br />
public void rotateX(float w) {<br />
changed = true;<br />
rotationMatrix.rotateX(w);<br />
}<br />
public void rotateY(float w) {<br />
changed = true;<br />
rotationMatrix.rotateY(w);<br />
}<br />
public void rotateZ(float w) {<br />
changed = true;<br />
rotationMatrix.rotateZ(w);<br />
}<br />
public void rotateAxis(SimpleVector axis, float angle) {<br />
changed = true;<br />
rotationMatrix.rotateAxis(axis, angle);<br />
}<br />
<br />
public boolean hasChanged() {<br />
return changed;<br />
}<br />
<br />
public Matrix getTransformMatrix() {<br />
if(changed) {<br />
changed = false;<br />
transformMatrix.setIdentity();<br />
transformMatrix.matMul(rotationMatrix);<br />
transformMatrix.matMul(translationMatrix);<br />
transformMatrix.transformToGL();<br />
}<br />
return transformMatrix;<br />
}<br />
<br />
public void update(long pTimeSinceLastFrame) {<br />
//Either add code here or extend class and override this method to add logic on frame update<br />
}<br />
<br />
private void regenMatrix() {<br />
translationMatrix.setIdentity();<br />
translationMatrix.translate(position);<br />
}<br />
}<br />
</pre><br />
<br />
<br />
'''InstanceShader Class:'''<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import com.threed.jpct.GLSLShader;<br />
import com.threed.jpct.Matrix;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public class InstanceShader extends GLSLShader {<br />
private static final String UNIFORM_ISINSTANCE = "isInstance";<br />
private static final String UNIFORM_IMVM = "iMVM";<br />
private InstanceManager instanceMgr;<br />
<br />
public InstanceShader(String vertexShaderSource, String fragmentShaderSource) {<br />
super(vertexShaderSource, fragmentShaderSource);<br />
instanceMgr = InstanceManager.getInstance();<br />
}<br />
<br />
public void setInstanceStartUniforms() {<br />
setUniform(UNIFORM_ISINSTANCE, 1);<br />
setUniform("skelPoseSize", 0);<br />
}<br />
<br />
public void setInstanceUniforms(Matrix pMatrix) {<br />
setUniform(UNIFORM_IMVM, pMatrix);<br />
}<br />
public void setInstanceCleanupUniforms() {<br />
setUniform(UNIFORM_ISINSTANCE, 0);<br />
}<br />
}<br />
</pre><br />
<br />
<br />
'''IInstanceManagerHelper Interface:'''<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import com.threed.jpct.Camera;<br />
import com.threed.jpct.Object3D;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public interface IInstanceManagerHelper {<br />
//Method is called to create an instance of pType with its respective InstanceObjectRenderer<br />
public InstanceObject3D spawn(short pType, InstanceObjectRenderer pRenderer);<br />
<br />
//Method is called to create an Object3D / Animated3D of pType.<br />
public Object3D fetchObject3D(short pType);<br />
<br />
//Method is called during update to see if an InstanceObject3D is visible by the camera<br />
public boolean checkVisibility(InstanceObject3D pIO3D, Camera pCamera);<br />
<br />
//Method is called on destruction / clearing of the InstanceManager <br />
public void onDestroy();<br />
}<br />
</pre><br />
<br />
<br />
'''InstanceAnimatedRenderer Class:'''<br />
<br />
Bones Animated Class. Requires BonesNamespaceUtils: [[Hybrid_GPU_Shader_Animations_for_Bones]]<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import yourpackagename.GPUAnimated3DShader;<br />
import com.threed.jpct.Object3D;<br />
<br />
import raft.jpct.bones.Animated3D;<br />
import raft.jpct.bones.BonesNamespaceUtils;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public class InstanceAnimatedRenderer extends InstanceObjectRenderer {<br />
public InstanceAnimatedRenderer(Object3D pRenderObj3D) {<br />
super(pRenderObj3D);<br />
if(pRenderObj3D!=null && pRenderObj3D instanceof Animated3D) {<br />
Animated3D pAnimated3D = (Animated3D)pRenderObj3D;<br />
pRenderObj3D.getMesh().removeVertexController();<br />
BonesNamespaceUtils.setSkinAttributes(pAnimated3D);<br />
}<br />
}<br />
<br />
@Override<br />
protected void setAnimatedUniforms(InstanceAnimated3D pIA3D) {<br />
if(pIA3D!=null && shader!=null && shader instanceof GPUAnimated3DShader) {<br />
((GPUAnimated3DShader)shader).updateBeforeRenderingObject( pIA3D );<br />
}<br />
}<br />
}<br />
</pre><br />
<br />
<br />
'''InstanceAnimated3D Class:'''<br />
<br />
Bones Animated Class. Requires BonesNamespaceUtils: [[Hybrid_GPU_Shader_Animations_for_Bones]]<br />
<pre><br />
package yourpackagename.instances;<br />
<br />
import raft.jpct.bones.Animated3D;<br />
import raft.jpct.bones.BonesNamespaceUtils;<br />
import raft.jpct.bones.SkeletonPose;<br />
import raft.jpct.bones.SkinClip;<br />
<br />
/**<br />
* Created by Dougie on 7/6/16.<br />
*/<br />
public class InstanceAnimated3D extends InstanceObject3D {<br />
protected Animated3D animated3D = null;<br />
public SkeletonPose pose = null;<br />
<br />
public InstanceAnimated3D(short pType, InstanceObjectRenderer pRenderer) {<br />
super(pType);<br />
if(pRenderer!=null && pRenderer.renderObj3D!=null && pRenderer.renderObj3D instanceof Animated3D) {<br />
animated3D = (Animated3D) pRenderer.renderObj3D;<br />
pose = new SkeletonPose(animated3D.getSkeleton());<br />
}<br />
}<br />
<br />
public void animateSkin(float index, int sequence) {<br />
if(animated3D!=null && animated3D.getSkinClipSequence()!=null) {<br />
if(sequence == 0) {<br />
BonesNamespaceUtils.animate(animated3D, index*animated3D.getSkinClipSequence().getTime(), pose);<br />
} else {<br />
SkinClip clip = animated3D.getSkinClipSequence().getClip(sequence - 1);<br />
clip.applyTo(index * clip.getTime(), pose);<br />
}<br />
pose.updateTransforms();<br />
}<br />
}<br />
}<br />
</pre><br />
<br />
<br />
'''InstanceAnimated3D Class:'''<br />
<br />
Bones Animated Class. Provided by: [[Hybrid_GPU_Shader_Animations_for_Bones]]. '''CHANGE EXTENDED CLASS TO InstanceShader'''<br />
<pre><br />
package yourpackagename;<br />
...<br />
<br />
public class GPUAnimated3DShader extends InstanceShader {<br />
...<br />
}<br />
</pre><br />
<br />
'''Vertex Shader Snippet:'''<br />
<br />
This just contains the necessary snippets of code to add to your Vertex Shader to have it work.<br />
<pre> <br />
...<br />
uniform mat4 modelViewMatrix;<br />
uniform mat4 modelViewProjectionMatrix;<br />
uniform mat4 projectionMatrix;<br />
uniform mat4 iMVM;<br />
uniform int isInstance;<br />
...<br />
uniform mat4 skelPose[50];<br />
uniform int skelPoseSize;<br />
<br />
attribute vec4 position;<br />
attribute vec3 normal;<br />
...<br />
attribute vec4 skinWeights;<br />
attribute vec4 jointIndices;<br />
...<br />
<br />
void main(void)<br />
{<br />
mat4 mvm = modelViewMatrix;<br />
mat4 mvpm = modelViewProjectionMatrix;<br />
if(isInstance>0) {<br />
mvm = iMVM;<br />
mvpm = projectionMatrix*mvm;<br />
}<br />
<br />
vec4 newPosition = vec4(0.0,0.0,0.0, position[3]);<br />
vec3 newNormal = vec3(0.0,0.0,0.0);<br />
if(skelPoseSize>0) {<br />
float weight;<br />
for(int j=0; j<4; ++j) {<br />
weight = skinWeights[j];<br />
if(weight != 0.0) {<br />
mat4 boneMat = skelPose[int(floor(jointIndices[j]+0.5))];<br />
newPosition.xyz += vec3(position.x*boneMat[0][0] + position.y*boneMat[1][0] + position.z*boneMat[2][0] + boneMat[3][0],<br />
position.x*boneMat[0][1] + position.y*boneMat[1][1] + position.z*boneMat[2][1] + boneMat[3][1],<br />
position.x*boneMat[0][2] + position.y*boneMat[1][2] + position.z*boneMat[2][2] + boneMat[3][2])*weight;<br />
newNormal += vec3(normal.x*boneMat[0][0] + normal.y*boneMat[1][0] + normal.z*boneMat[2][0],<br />
normal.x*boneMat[0][1] + normal.y*boneMat[1][1] + normal.z*boneMat[2][1],<br />
normal.x*boneMat[0][2] + normal.y*boneMat[1][2] + normal.z*boneMat[2][2])*weight;<br />
}<br />
}<br />
newPosition.yz *= -1.0;<br />
newNormal.yz *= -1.0;<br />
} else {<br />
newPosition = position;<br />
newNormal = normal;<br />
}<br />
<br />
...<br />
<br />
gl_Position = mvpm * newPosition;<br />
}<br />
</pre><br />
<br />
== How to Use the Code ==<br />
<br />
'''Create a new class of your own implementing the IInstanceManagerHelper Interface'''<br />
<br />
Example:<br />
<pre><br />
public class YourInstanceManagerHelper implements IInstanceManagerHelper {<br />
<<Add your logic here>><br />
}<br />
</pre><br />
<br />
'''Instantiating the InstanceManager:'''<br />
<pre><br />
InstanceManager instanceManager = InstanceManager.getInstance();<br />
InstanceShader instShader = <<Create InstanceShader>><br />
instanceManager.setShader(instShader);<br />
instanceManager.setHelper(new YourInstanceManagerHelper(world.getCamera()));<br />
</pre><br />
<br />
'''onDrawFrame, call the InstanceManager's onUpdate(...)'''<br />
<pre><br />
InstanceManager.getInstance().onUpdate(world, milliSecondsSinceLastFrame);<br />
</pre><br />
<br />
== FAQ ==<br />
<br />
'''How can one detect when an InstanceObject3D is touched?'''<br />
<br />
By default the mesh of an InstanceObject3D is not touched by IBR. If you do alter the mesh data, you already have access to the Mesh / Object3D as you've altered the vertices / uvs. If you do alter the mesh, note that it will affect all instances as it uses a shared mesh. If you need to get the Object3D of an InstanceObject3D type, use the InstanceManager.getObject3DOfInstanceType().<br />
<br />
As for the transform matrices of the InstanceObject3D, by default it does a sort of lazy-transformations. Any time you do a scale/translation/rotation, it sets a protected boolean changed to true. On call of getTransformMatrix(), it will re-create the transform matrix if changed==true, otherwise it just returns the previously calculated transform matrix. getTransformMatrix() get called when rendering each instance (every frame). I have added a method to the code called hasChanged(), which will return true if there has been a translate, scale or rotation since last getTransformMatrix(). I hope this answers your question.<br />
<br />
<br />
'''Does the order in which the InstanceObject3D's are drawn matter?'''<br />
<br />
It can, and Egon may be able to better explain some of this. It should only matter if your Object3D uses transparency or you set the OpenGL to disable the depth test, in which case it will render in synchronous order. To put it simply, I don't believe transparency InstanceObject3D's are going to play nicely with other transparent Object3D's, other transparent InstanceObject3D types, or overlaps of the multiple IntanceObject3D of that type. I believe jPCT has software-side ordering (based off the Object3D's origin distance from camera?) when it comes to transparent objects, and the transparent objects are rendered last as transparency requires the solid pixels behind it for the different pixel write modes (add, modulate, blend, etc...). Because all Object3D's for this method are stacked a certain distance from the camera, they will be picked up by jPCT render pipeline in regards to that distance from the camera. All InstanceObject3D's that are rendered would be synchronously rendered on top of one another according to the order when added. I could add simple sorting, but it won't solve all problems, as transparency is a complex subject that requires work arounds and tweaks for speed. But it will never work great with IBR as the distance from the camera will mess up jPCT's sorting order.</div>
Redman