Compare commits
92 Commits
master
...
experiment
Author | SHA1 | Date |
---|---|---|
|
74a1d8b219 | 9 years ago |
|
3bb01d6963 | 9 years ago |
|
4fcd575c47 | 9 years ago |
|
60d9d1b4d8 | 9 years ago |
|
4ab5a9f7dd | 9 years ago |
|
452c307d59 | 9 years ago |
|
a8fca2bcf6 | 9 years ago |
|
2fc4e5b607 | 9 years ago |
|
f28d74a1f6 | 9 years ago |
|
87af1f30b0 | 9 years ago |
|
e691de4459 | 9 years ago |
|
2dafd1e485 | 9 years ago |
|
c586bacbb0 | 9 years ago |
|
fde095458e | 9 years ago |
|
e8fd22223a | 9 years ago |
|
53d3b72478 | 9 years ago |
|
644b8167b8 | 9 years ago |
|
2cdb4a8486 | 9 years ago |
|
152a7638cd | 9 years ago |
|
adc5084f5d | 9 years ago |
|
42d76cfd29 | 9 years ago |
|
ba22487c38 | 9 years ago |
|
00b6d904af | 9 years ago |
|
2d2c394b42 | 9 years ago |
|
655457ab6a | 9 years ago |
|
7639657fc1 | 9 years ago |
|
e87f008244 | 9 years ago |
|
12b3c4a140 | 9 years ago |
|
ae7fb6984c | 9 years ago |
|
3b9e412f80 | 9 years ago |
|
0aaa28e66b | 9 years ago |
|
9b45189f48 | 9 years ago |
|
69eaf39da9 | 9 years ago |
|
8024babb47 | 9 years ago |
|
c7da1c4efd | 9 years ago |
|
ab8527770c | 9 years ago |
|
c6b568c125 | 9 years ago |
|
772330c308 | 9 years ago |
|
8413ed715c | 9 years ago |
|
d76cb99772 | 9 years ago |
|
454e210d3d | 9 years ago |
|
c6336c0781 | 9 years ago |
|
fda40563c5 | 9 years ago |
|
34aa21bfd9 | 9 years ago |
|
f9969008c3 | 9 years ago |
|
2893ac9156 | 9 years ago |
|
a3638f3e0c | 9 years ago |
|
27041e1341 | 9 years ago |
|
962ab22ef4 | 9 years ago |
|
f9500f955f | 9 years ago |
|
c50839796f | 9 years ago |
|
ff6b1be725 | 9 years ago |
|
97281de5c4 | 9 years ago |
|
8f54af3263 | 9 years ago |
|
79125f2f63 | 9 years ago |
|
908b37350d | 9 years ago |
|
f986043745 | 9 years ago |
|
961bf92734 | 9 years ago |
|
3d82f5c459 | 9 years ago |
|
4a646de49d | 9 years ago |
|
06e8210e5d | 9 years ago |
|
85feb305ef | 9 years ago |
|
15465a020f | 9 years ago |
|
f005c05f8d | 9 years ago |
|
42729b2302 | 9 years ago |
|
30855f5bb4 | 9 years ago |
|
352c02db8a | 9 years ago |
|
ea4d750d52 | 9 years ago |
|
6db1d15045 | 9 years ago |
|
f9ce9e246c | 9 years ago |
|
18db26292f | 9 years ago |
|
0d3ebf75bd | 10 years ago |
|
4e572605a8 | 10 years ago |
|
28e2b5650c | 10 years ago |
|
3d2a9b83e9 | 10 years ago |
|
618c8d02eb | 10 years ago |
|
3a00aff886 | 10 years ago |
|
fc680ea121 | 10 years ago |
|
12c001addc | 10 years ago |
|
7b147171bf | 10 years ago |
|
2e9a9f9f9e | 10 years ago |
|
c72b036c9f | 10 years ago |
|
81a76fdf69 | 10 years ago |
|
7b64e91681 | 10 years ago |
|
d2f38f8adb | 10 years ago |
|
937d97b8d7 | 10 years ago |
|
9e17f39cfb | 10 years ago |
|
2ced7653a7 | 10 years ago |
|
f0b63e7910 | 10 years ago |
|
860de88298 | 10 years ago |
|
e8f344a0db | 10 years ago |
|
bee759bddc | 10 years ago |
@ -1,42 +1,40 @@ |
|||||||
# Version number used for plugins, only 3 numbers (e.g. 3.1.3) |
# Version number used for plugins, only 3 numbers (e.g. 3.1.3) |
||||||
jmeVersion = 3.1.0 |
jmeVersion = 3.1.0 |
||||||
# Version used for application and settings folder, no spaces! |
# Version used for application and settings folder, no spaces! |
||||||
jmeMainVersion = 3.1 |
jmeMainVersion = 3.1 |
||||||
# Version addition pre-alpha-svn, Stable, Beta |
# Increment this each time jmeVersionTag changes but jmeVersion stays the same |
||||||
jmeVersionTag = SNAPSHOT |
jmeVersionTagID = 0 |
||||||
# Increment this each time jmeVersionTag changes but jmeVersion stays the same |
|
||||||
jmeVersionTagID = 0 |
# specify if JavaDoc should be built |
||||||
|
buildJavaDoc = false |
||||||
# specify if JavaDoc should be built |
|
||||||
buildJavaDoc = true |
# specify if SDK and Native libraries get built |
||||||
|
buildNativeProjects = false |
||||||
# specify if SDK and Native libraries get built |
buildAndroidExamples = false |
||||||
buildNativeProjects = false |
|
||||||
buildAndroidExamples = false |
# Path to android NDK for building native libraries |
||||||
|
#ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7 |
||||||
# Path to android NDK for building native libraries |
ndkPath = /opt/android-ndk-r10c |
||||||
#ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7 |
|
||||||
ndkPath = /opt/android-ndk-r10c |
# Path for downloading native Bullet |
||||||
|
bulletUrl = http://bullet.googlecode.com/files/bullet-2.82-r2704.zip |
||||||
# Path for downloading native Bullet |
bulletFolder = bullet-2.82-r2704 |
||||||
bulletUrl = http://bullet.googlecode.com/files/bullet-2.82-r2704.zip |
bulletZipFile = bullet.zip |
||||||
bulletFolder = bullet-2.82-r2704 |
|
||||||
bulletZipFile = bullet.zip |
# Path for downloading NetBeans Base |
||||||
|
netbeansUrl = http://download.netbeans.org/netbeans/8.0.2/final/zip/netbeans-8.0.2-201411181905-javase.zip |
||||||
# Path for downloading NetBeans Base |
|
||||||
netbeansUrl = http://download.netbeans.org/netbeans/8.0.2/final/zip/netbeans-8.0.2-201411181905-javase.zip |
# POM settings |
||||||
|
POM_NAME=jMonkeyEngine |
||||||
# POM settings |
POM_DESCRIPTION=jMonkeyEngine is a 3D game engine for adventurous Java developers |
||||||
POM_NAME=jMonkeyEngine |
POM_URL=http://jmonkeyengine.org |
||||||
POM_DESCRIPTION=jMonkeyEngine is a 3D game engine for adventurous Java developers |
POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine |
||||||
POM_URL=http://jmonkeyengine.org |
POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git |
||||||
POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine |
POM_SCM_DEVELOPER_CONNECTION=scm:git:git@github.com:jMonkeyEngine/jmonkeyengine.git |
||||||
POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git |
POM_LICENSE_NAME=New BSD (3-clause) License |
||||||
POM_SCM_DEVELOPER_CONNECTION=scm:git:git@github.com:jMonkeyEngine/jmonkeyengine.git |
POM_LICENSE_URL=http://opensource.org/licenses/BSD-3-Clause |
||||||
POM_LICENSE_NAME=New BSD (3-clause) License |
POM_LICENSE_DISTRIBUTION=repo |
||||||
POM_LICENSE_URL=http://opensource.org/licenses/BSD-3-Clause |
|
||||||
POM_LICENSE_DISTRIBUTION=repo |
# Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline |
||||||
|
bintray_user= |
||||||
# Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline |
bintray_api_key= |
||||||
bintray_user= |
|
||||||
bintray_api_key= |
|
||||||
|
@ -0,0 +1,280 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.renderer.opengl; |
||||||
|
|
||||||
|
import com.jme3.renderer.RenderContext; |
||||||
|
import com.jme3.renderer.RendererException; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.nio.IntBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.CancellationException; |
||||||
|
import java.util.concurrent.ExecutionException; |
||||||
|
import java.util.concurrent.Future; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.concurrent.TimeoutException; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Kirill Vainer |
||||||
|
*/ |
||||||
|
final class AsyncFrameReader { |
||||||
|
|
||||||
|
private final ArrayList<PixelBuffer> pboPool = new ArrayList<PixelBuffer>(); |
||||||
|
private final List<FrameBufferReadRequest> pending = Collections.synchronizedList(new ArrayList<FrameBufferReadRequest>()); |
||||||
|
private final GLRenderer renderer; |
||||||
|
private final GL gl; |
||||||
|
private final GLExt glext; |
||||||
|
private final IntBuffer intBuf = BufferUtils.createIntBuffer(1); |
||||||
|
private final RenderContext context; |
||||||
|
private final Thread glThread; |
||||||
|
|
||||||
|
AsyncFrameReader(GLRenderer renderer, GL gl, GLExt glext, RenderContext context) { |
||||||
|
this.renderer = renderer; |
||||||
|
this.gl = gl; |
||||||
|
this.glext = glext; |
||||||
|
this.context = context; |
||||||
|
this.glThread = Thread.currentThread(); |
||||||
|
} |
||||||
|
|
||||||
|
private PixelBuffer acquirePixelBuffer(int dataSize) { |
||||||
|
PixelBuffer pb; |
||||||
|
|
||||||
|
if (pboPool.isEmpty()) { |
||||||
|
// create PBO
|
||||||
|
pb = new PixelBuffer(); |
||||||
|
intBuf.clear(); |
||||||
|
gl.glGenBuffers(intBuf); |
||||||
|
pb.id = intBuf.get(0); |
||||||
|
} else { |
||||||
|
// reuse PBO.
|
||||||
|
pb = pboPool.remove(pboPool.size() - 1); |
||||||
|
} |
||||||
|
|
||||||
|
// resize or allocate PBO if required.
|
||||||
|
if (pb.size != dataSize) { |
||||||
|
if (context.boundPixelPackPBO != pb.id) { |
||||||
|
gl.glBindBuffer(GLExt.GL_PIXEL_PACK_BUFFER_ARB, pb.id); |
||||||
|
context.boundPixelPackPBO = pb.id; |
||||||
|
} |
||||||
|
gl.glBufferData(GLExt.GL_PIXEL_PACK_BUFFER_ARB, dataSize, GL.GL_STREAM_READ); |
||||||
|
} |
||||||
|
|
||||||
|
pb.size = dataSize; |
||||||
|
|
||||||
|
return pb; |
||||||
|
} |
||||||
|
|
||||||
|
private void readFrameBufferFromPBO(FrameBufferReadRequest fbrr) { |
||||||
|
// assumes waitForCompletion was already called!
|
||||||
|
if (context.boundPixelPackPBO != fbrr.pb.id) { |
||||||
|
gl.glBindBuffer(GLExt.GL_PIXEL_PACK_BUFFER_ARB, fbrr.pb.id); |
||||||
|
context.boundPixelPackPBO = fbrr.pb.id; |
||||||
|
} |
||||||
|
gl.glGetBufferSubData(GLExt.GL_PIXEL_PACK_BUFFER_ARB, 0, fbrr.targetBuf); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean waitForCompletion(FrameBufferReadRequest fbrr, long time, TimeUnit unit, boolean flush) { |
||||||
|
int flags = flush ? GLExt.GL_SYNC_FLUSH_COMMANDS_BIT : 0; |
||||||
|
long nanos = unit.toNanos(time); |
||||||
|
switch (glext.glClientWaitSync(fbrr.fence, flags, nanos)) { |
||||||
|
case GLExt.GL_ALREADY_SIGNALED: |
||||||
|
case GLExt.GL_CONDITION_SATISFIED: |
||||||
|
return true; |
||||||
|
case GLExt.GL_TIMEOUT_EXPIRED: |
||||||
|
return false; |
||||||
|
case GLExt.GL_WAIT_FAILED: |
||||||
|
throw new RendererException("Waiting for fence failed"); |
||||||
|
default: |
||||||
|
throw new RendererException("Unexpected result from glClientWaitSync"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void signalFinished(FrameBufferReadRequest fbrr) { |
||||||
|
fbrr.lock.lock(); |
||||||
|
try { |
||||||
|
fbrr.done = true; |
||||||
|
fbrr.cond.signalAll(); |
||||||
|
} finally { |
||||||
|
fbrr.lock.unlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void signalCancelled(FrameBufferReadRequest fbrr) { |
||||||
|
fbrr.lock.lock(); |
||||||
|
try { |
||||||
|
fbrr.cancelled = true; |
||||||
|
fbrr.cond.signalAll(); |
||||||
|
} finally { |
||||||
|
fbrr.lock.unlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void updateReadRequests() { |
||||||
|
// Update requests in the order they were made (e.g. earliest first)
|
||||||
|
for (Iterator<FrameBufferReadRequest> it = pending.iterator(); it.hasNext();) { |
||||||
|
FrameBufferReadRequest fbrr = it.next(); |
||||||
|
|
||||||
|
// Check status for the user... (non-blocking)
|
||||||
|
if (!fbrr.cancelled && !fbrr.done) { |
||||||
|
// Request a flush if we know clients are waiting
|
||||||
|
// (to speed up the process, or make it take finite time ..)
|
||||||
|
boolean flush = false; // fbrr.clientsWaiting.get() > 0;
|
||||||
|
if (waitForCompletion(fbrr, 0, TimeUnit.NANOSECONDS, flush)) { |
||||||
|
if (!fbrr.cancelled) { |
||||||
|
// Operation completed.
|
||||||
|
// Read data into user's ByteBuffer
|
||||||
|
readFrameBufferFromPBO(fbrr); |
||||||
|
|
||||||
|
// Signal any waiting threads that we are done.
|
||||||
|
// Also, set the done flag.
|
||||||
|
signalFinished(fbrr); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (fbrr.cancelled || fbrr.done) { |
||||||
|
// Cleanup
|
||||||
|
// Return the pixel buffer back into the pool.
|
||||||
|
if (!pboPool.contains(fbrr.pb)) { |
||||||
|
pboPool.add(fbrr.pb); |
||||||
|
} |
||||||
|
|
||||||
|
// Remove this request from the pending requests list.
|
||||||
|
it.remove(); |
||||||
|
|
||||||
|
// Get rid of the fence
|
||||||
|
glext.glDeleteSync(fbrr.fence); |
||||||
|
|
||||||
|
fbrr.pb = null; |
||||||
|
fbrr.fence = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ByteBuffer getFrameBufferData(FrameBufferReadRequest fbrr, long time, TimeUnit unit) |
||||||
|
throws InterruptedException, ExecutionException, TimeoutException { |
||||||
|
|
||||||
|
if (fbrr.cancelled) { |
||||||
|
throw new CancellationException(); |
||||||
|
} |
||||||
|
|
||||||
|
if (fbrr.done) { |
||||||
|
return fbrr.targetBuf; |
||||||
|
} |
||||||
|
|
||||||
|
if (glThread == Thread.currentThread()) { |
||||||
|
// Running on GL thread, hence can use GL commands ..
|
||||||
|
try { |
||||||
|
// Wait until we reach the fence..
|
||||||
|
|
||||||
|
// PROBLEM: if the user is holding any locks,
|
||||||
|
// they will not be released here,
|
||||||
|
// causing a potential deadlock!
|
||||||
|
if (!waitForCompletion(fbrr, time, unit, true)) { |
||||||
|
throw new TimeoutException(); |
||||||
|
} |
||||||
|
|
||||||
|
// Command stream reached this point.
|
||||||
|
if (fbrr.cancelled) { |
||||||
|
// User not interested in this anymore.
|
||||||
|
throw new CancellationException(); |
||||||
|
} else { |
||||||
|
// Read data into user's ByteBuffer
|
||||||
|
readFrameBufferFromPBO(fbrr); |
||||||
|
} |
||||||
|
|
||||||
|
// Mark it as done, so future get() calls always return.
|
||||||
|
signalFinished(fbrr); |
||||||
|
|
||||||
|
return fbrr.targetBuf; |
||||||
|
} catch (RendererException ex) { |
||||||
|
throw new ExecutionException(ex); |
||||||
|
} |
||||||
|
} else { |
||||||
|
long nanos = unit.toNanos(time); |
||||||
|
|
||||||
|
fbrr.lock.lock(); |
||||||
|
try { |
||||||
|
// Not running on GL thread, indicate that we are running
|
||||||
|
// so GL thread can request GPU to finish quicker ...
|
||||||
|
fbrr.clientsWaiting.getAndIncrement(); |
||||||
|
|
||||||
|
// Wait until we finish
|
||||||
|
while (!fbrr.done && !fbrr.cancelled) { |
||||||
|
if (nanos <= 0L) { |
||||||
|
throw new TimeoutException(); |
||||||
|
} |
||||||
|
|
||||||
|
nanos = fbrr.cond.awaitNanos(nanos); |
||||||
|
} |
||||||
|
|
||||||
|
if (fbrr.cancelled) { |
||||||
|
throw new CancellationException(); |
||||||
|
} |
||||||
|
|
||||||
|
return fbrr.targetBuf; |
||||||
|
} finally { |
||||||
|
fbrr.lock.unlock(); |
||||||
|
fbrr.clientsWaiting.getAndDecrement(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Future<ByteBuffer> readFrameBufferLater(FrameBuffer fb, ByteBuffer byteBuf) { |
||||||
|
// Create & allocate a PBO (or reuse an existing one if available)
|
||||||
|
FrameBufferReadRequest fbrr = new FrameBufferReadRequest(this); |
||||||
|
fbrr.targetBuf = byteBuf; |
||||||
|
|
||||||
|
int desiredSize = fb.getWidth() * fb.getHeight() * 4; |
||||||
|
|
||||||
|
if (byteBuf.remaining() != desiredSize) { |
||||||
|
throw new IllegalArgumentException("Ensure buffer size matches framebuffer size"); |
||||||
|
} |
||||||
|
|
||||||
|
fbrr.pb = acquirePixelBuffer(desiredSize); |
||||||
|
|
||||||
|
// Read into PBO (asynchronous)
|
||||||
|
// renderer.readFrameBufferWithGLFormat(fb, null, GL2.GL_BGRA, GL2.GL_UNSIGNED_BYTE, fbrr.pb.id);
|
||||||
|
|
||||||
|
// Insert fence into command stream.
|
||||||
|
fbrr.fence = glext.glFenceSync(GLExt.GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
||||||
|
|
||||||
|
// Insert into FIFO
|
||||||
|
pending.add(fbrr); |
||||||
|
|
||||||
|
return fbrr; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.renderer.opengl; |
||||||
|
|
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.concurrent.ExecutionException; |
||||||
|
import java.util.concurrent.Future; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.concurrent.TimeoutException; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
import java.util.concurrent.locks.Condition; |
||||||
|
import java.util.concurrent.locks.ReentrantLock; |
||||||
|
|
||||||
|
class PixelBuffer { |
||||||
|
int id = -1; |
||||||
|
int size = -1; |
||||||
|
} |
||||||
|
|
||||||
|
class FrameBufferReadRequest implements Future<ByteBuffer> { |
||||||
|
|
||||||
|
AsyncFrameReader reader; |
||||||
|
Object fence; |
||||||
|
PixelBuffer pb; |
||||||
|
ByteBuffer targetBuf; |
||||||
|
boolean cancelled; |
||||||
|
boolean done; |
||||||
|
|
||||||
|
final ReentrantLock lock = new ReentrantLock(); |
||||||
|
final Condition cond = lock.newCondition(); |
||||||
|
final AtomicInteger clientsWaiting = new AtomicInteger(0); |
||||||
|
|
||||||
|
public FrameBufferReadRequest(AsyncFrameReader reader) { |
||||||
|
this.reader = reader; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean cancel(boolean mayInterruptIfRunning) { |
||||||
|
if (isDone()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
reader.signalCancelled(this); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isCancelled() { |
||||||
|
return cancelled; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isDone() { |
||||||
|
return done; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ByteBuffer get(long l, TimeUnit tu) throws InterruptedException, ExecutionException, TimeoutException { |
||||||
|
return reader.getFrameBufferData(this, l, tu); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ByteBuffer get() throws InterruptedException, ExecutionException { |
||||||
|
try { |
||||||
|
return get(1, TimeUnit.SECONDS); |
||||||
|
} catch (TimeoutException ex) { |
||||||
|
throw new ExecutionException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3; |
||||||
|
|
||||||
|
public interface IntegrationTest { |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.jme3.app; |
||||||
|
|
||||||
|
import com.jme3.IntegrationTest; |
||||||
|
import com.jme3.scene.Mesh; |
||||||
|
import com.jme3.system.AppSettings; |
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.prefs.BackingStoreException; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
import org.junit.experimental.categories.Category; |
||||||
|
|
||||||
|
@Category(IntegrationTest.class) |
||||||
|
public class AppSettingsIT { |
||||||
|
|
||||||
|
private static final String APPSETTINGS_KEY = "JME_AppSettingsTest"; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testPreferencesSaveLoad() throws BackingStoreException { |
||||||
|
AppSettings settings = new AppSettings(false); |
||||||
|
settings.putBoolean("TestBool", true); |
||||||
|
settings.putInteger("TestInt", 123); |
||||||
|
settings.putString("TestStr", "HelloWorld"); |
||||||
|
settings.putFloat("TestFloat", 123.567f); |
||||||
|
settings.put("TestObj", new Mesh()); // Objects not supported by preferences
|
||||||
|
settings.save(APPSETTINGS_KEY); |
||||||
|
|
||||||
|
AppSettings loadedSettings = new AppSettings(false); |
||||||
|
loadedSettings.load(APPSETTINGS_KEY); |
||||||
|
|
||||||
|
assertEquals(true, loadedSettings.getBoolean("TestBool")); |
||||||
|
assertEquals(123, loadedSettings.getInteger("TestInt")); |
||||||
|
assertEquals("HelloWorld", loadedSettings.getString("TestStr")); |
||||||
|
assertEquals(123.567f, loadedSettings.get("TestFloat")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testStreamSaveLoad() throws IOException { |
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
||||||
|
|
||||||
|
AppSettings settings = new AppSettings(false); |
||||||
|
settings.putBoolean("TestBool", true); |
||||||
|
settings.putInteger("TestInt", 123); |
||||||
|
settings.putString("TestStr", "HelloWorld"); |
||||||
|
settings.putFloat("TestFloat", 123.567f); |
||||||
|
settings.put("TestObj", new Mesh()); // Objects not supported by file settings
|
||||||
|
settings.save(baos); |
||||||
|
|
||||||
|
AppSettings loadedSettings = new AppSettings(false); |
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); |
||||||
|
loadedSettings.load(bais); |
||||||
|
|
||||||
|
assertEquals(true, loadedSettings.getBoolean("TestBool")); |
||||||
|
assertEquals(123, loadedSettings.getInteger("TestInt")); |
||||||
|
assertEquals("HelloWorld", loadedSettings.getString("TestStr")); |
||||||
|
assertEquals(123.567f, loadedSettings.get("TestFloat")); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.system.awt; |
||||||
|
|
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import java.awt.Component; |
||||||
|
|
||||||
|
public interface JmePanel { |
||||||
|
|
||||||
|
public void attachTo(boolean overrideMainFramebuffer, ViewPort ... vps); |
||||||
|
|
||||||
|
public boolean isActiveDrawing(); |
||||||
|
|
||||||
|
public void onFrameBegin(); |
||||||
|
|
||||||
|
public void onFrameEnd(); |
||||||
|
|
||||||
|
public Component getComponent(); |
||||||
|
} |
@ -0,0 +1,382 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.system.awt; |
||||||
|
|
||||||
|
import com.jme3.post.SceneProcessor; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.renderer.opengl.GLRenderer; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import com.jme3.util.Screenshots; |
||||||
|
import java.awt.AWTException; |
||||||
|
import java.awt.BufferCapabilities; |
||||||
|
import java.awt.Canvas; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.ImageCapabilities; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.event.ComponentAdapter; |
||||||
|
import java.awt.event.ComponentEvent; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
import java.awt.image.AffineTransformOp; |
||||||
|
import java.awt.image.BufferStrategy; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.concurrent.ArrayBlockingQueue; |
||||||
|
import java.util.concurrent.ExecutionException; |
||||||
|
import java.util.concurrent.Future; |
||||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
import javax.swing.JPanel; |
||||||
|
|
||||||
|
public class SwingPanel extends JPanel implements JmePanel, SceneProcessor { |
||||||
|
|
||||||
|
private boolean attachAsMain = false; |
||||||
|
|
||||||
|
private BufferedImage img; |
||||||
|
// private FrameBuffer fb;
|
||||||
|
private RenderManager rm; |
||||||
|
private PaintMode paintMode; |
||||||
|
private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); |
||||||
|
|
||||||
|
// Visibility/drawing vars
|
||||||
|
private AffineTransformOp transformOp; |
||||||
|
private AtomicBoolean hasNativePeer = new AtomicBoolean(false); |
||||||
|
private AtomicBoolean showing = new AtomicBoolean(false); |
||||||
|
private AtomicBoolean repaintRequest = new AtomicBoolean(false); |
||||||
|
|
||||||
|
// Reshape vars
|
||||||
|
private int newWidth = 1; |
||||||
|
private int newHeight = 1; |
||||||
|
private AtomicBoolean reshapeNeeded = new AtomicBoolean(true); |
||||||
|
private final Object lock = new Object(); |
||||||
|
|
||||||
|
// Buffer pool and pending buffers
|
||||||
|
private final int NUM_FRAMES = 2; |
||||||
|
private final ArrayBlockingQueue<Future<ByteBuffer>> pendingFrames = new ArrayBlockingQueue<Future<ByteBuffer>>(NUM_FRAMES); |
||||||
|
private final ArrayBlockingQueue<ByteBuffer> bufferPool = new ArrayBlockingQueue<ByteBuffer>(NUM_FRAMES); |
||||||
|
private final ArrayList<FrameBuffer> fbs = new ArrayList<FrameBuffer>(NUM_FRAMES); |
||||||
|
private int frameIndex = 0; |
||||||
|
|
||||||
|
private final ComponentAdapter resizeListener = new ComponentAdapter() { |
||||||
|
@Override |
||||||
|
public void componentResized(ComponentEvent e) { |
||||||
|
onResize(e); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public SwingPanel(PaintMode paintMode, boolean srgb){ |
||||||
|
this.paintMode = paintMode; |
||||||
|
invalidatePendingFrames(); |
||||||
|
addComponentListener(resizeListener); |
||||||
|
} |
||||||
|
|
||||||
|
public void onResize(ComponentEvent e) { |
||||||
|
synchronized (lock) { |
||||||
|
int newWidth2 = Math.max(getWidth(), 1); |
||||||
|
int newHeight2 = Math.max(getHeight(), 1); |
||||||
|
if (newWidth != newWidth2 || newHeight != newHeight2) { |
||||||
|
newWidth = newWidth2; |
||||||
|
newHeight = newHeight2; |
||||||
|
reshapeNeeded.set(true); |
||||||
|
System.out.println("EDT: componentResized " + newWidth + ", " + newHeight); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Component getComponent() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addNotify(){ |
||||||
|
super.addNotify(); |
||||||
|
|
||||||
|
synchronized (lock){ |
||||||
|
hasNativePeer.set(true); |
||||||
|
System.out.println("EDT: addNotify"); |
||||||
|
} |
||||||
|
|
||||||
|
requestFocusInWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeNotify(){ |
||||||
|
synchronized (lock){ |
||||||
|
hasNativePeer.set(false); |
||||||
|
System.out.println("EDT: removeNotify"); |
||||||
|
} |
||||||
|
|
||||||
|
super.removeNotify(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean checkVisibilityState() { |
||||||
|
if (!hasNativePeer.get()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
boolean currentShowing = isShowing(); |
||||||
|
if (showing.getAndSet(currentShowing) != currentShowing) { |
||||||
|
if (currentShowing) { |
||||||
|
System.out.println("OGL: Enter showing state."); |
||||||
|
} else { |
||||||
|
System.out.println("OGL: Exit showing state."); |
||||||
|
} |
||||||
|
} |
||||||
|
return currentShowing; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paintComponent(Graphics g){ |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, |
||||||
|
RenderingHints.VALUE_RENDER_SPEED); |
||||||
|
|
||||||
|
ByteBuffer byteBuf = null; |
||||||
|
|
||||||
|
synchronized (lock){ |
||||||
|
if (pendingFrames.size() > NUM_FRAMES - 1) { |
||||||
|
byteBuf = acquireNextFrame(); |
||||||
|
} |
||||||
|
|
||||||
|
if (byteBuf != null) { |
||||||
|
// Convert the frame into the image so it can be rendered.
|
||||||
|
Screenshots.convertScreenShot2(byteBuf.asIntBuffer(), img); |
||||||
|
|
||||||
|
try { |
||||||
|
// return the frame back to its rightful owner.
|
||||||
|
bufferPool.put(byteBuf); |
||||||
|
} catch (InterruptedException ex) { |
||||||
|
Logger.getLogger(SwingPanel.class.getName()).log(Level.SEVERE, null, ex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
g2d.drawImage(img, transformOp, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public ByteBuffer acquireNextFrame() { |
||||||
|
if (pendingFrames.isEmpty()) { |
||||||
|
System.out.println("!!! No pending frames, returning null."); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
return pendingFrames.take().get(); |
||||||
|
} catch (InterruptedException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} catch (ExecutionException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Grabs an available buffer from the available frames pool, |
||||||
|
* reads the OpenGL backbuffer into it, then adds it to the pending frames pool. |
||||||
|
*/ |
||||||
|
public void readNextFrame() { |
||||||
|
if (bufferPool.isEmpty()) { |
||||||
|
System.out.println("??? Too many pending frames!"); |
||||||
|
return; // need to draw more frames ..
|
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
int size = fbs.get(frameIndex).getWidth() * fbs.get(frameIndex).getHeight() * 4; |
||||||
|
ByteBuffer byteBuf = bufferPool.take(); |
||||||
|
byteBuf = BufferUtils.ensureLargeEnough(byteBuf, size); |
||||||
|
byteBuf.clear(); |
||||||
|
|
||||||
|
GLRenderer renderer = (GLRenderer) rm.getRenderer(); |
||||||
|
// Future<ByteBuffer> future = renderer.readFrameBufferLater(fbs.get(frameIndex), byteBuf);
|
||||||
|
// if (!pendingFrames.offer(future)) {
|
||||||
|
// throw new AssertionError();
|
||||||
|
// }
|
||||||
|
|
||||||
|
frameIndex ++; |
||||||
|
if (frameIndex >= NUM_FRAMES) { |
||||||
|
frameIndex = 0; |
||||||
|
} |
||||||
|
} catch (InterruptedException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isActiveDrawing() { |
||||||
|
return paintMode != PaintMode.OnRequest && showing.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public void attachTo(boolean overrideMainFramebuffer, ViewPort ... vps){ |
||||||
|
if (viewPorts.size() > 0){ |
||||||
|
for (ViewPort vp : viewPorts){ |
||||||
|
vp.setOutputFrameBuffer(null); |
||||||
|
} |
||||||
|
viewPorts.get(viewPorts.size()-1).removeProcessor(this); |
||||||
|
} |
||||||
|
|
||||||
|
viewPorts.addAll(Arrays.asList(vps)); |
||||||
|
viewPorts.get(viewPorts.size()-1).addProcessor(this); |
||||||
|
|
||||||
|
this.attachAsMain = overrideMainFramebuffer; |
||||||
|
} |
||||||
|
|
||||||
|
public void initialize(RenderManager rm, ViewPort vp) { |
||||||
|
if (this.rm == null){ |
||||||
|
// First time called in OGL thread
|
||||||
|
this.rm = rm; |
||||||
|
// reshapeInThread(1, 1);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void invalidatePendingFrames() { |
||||||
|
// NOTE: all pending read requests are invalid!
|
||||||
|
for (Future<ByteBuffer> pendingRequest : pendingFrames) { |
||||||
|
pendingRequest.cancel(true); |
||||||
|
} |
||||||
|
pendingFrames.clear(); |
||||||
|
bufferPool.clear(); |
||||||
|
|
||||||
|
// Populate buffer pool.
|
||||||
|
int cap = bufferPool.remainingCapacity(); |
||||||
|
for (int i = 0; i < cap; i++) { |
||||||
|
bufferPool.add(BufferUtils.createByteBuffer(1 * 1 * 4)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void reshapeInThread(int width, int height) { |
||||||
|
invalidatePendingFrames(); |
||||||
|
|
||||||
|
for (FrameBuffer fb : fbs) { |
||||||
|
fb.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
fbs.clear(); |
||||||
|
|
||||||
|
for (int i = 0; i < NUM_FRAMES; i++) { |
||||||
|
FrameBuffer fb = new FrameBuffer(width, height, 1); |
||||||
|
fb.setDepthBuffer(Image.Format.Depth); |
||||||
|
fb.setColorBuffer(Image.Format.RGBA8); |
||||||
|
fbs.add(fb); |
||||||
|
} |
||||||
|
|
||||||
|
synchronized (lock){ |
||||||
|
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); |
||||||
|
|
||||||
|
AffineTransform tx = AffineTransform.getScaleInstance(1, -1); |
||||||
|
tx.translate(0, -img.getHeight()); |
||||||
|
transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); |
||||||
|
} |
||||||
|
|
||||||
|
if (attachAsMain) { |
||||||
|
rm.notifyReshape(width, height); |
||||||
|
} else { |
||||||
|
for (ViewPort vp : viewPorts){ |
||||||
|
vp.getCamera().resize(width, height, true); |
||||||
|
|
||||||
|
// NOTE: Hack alert. This is done ONLY for custom framebuffers.
|
||||||
|
// Main framebuffer should use RenderManager.notifyReshape().
|
||||||
|
for (SceneProcessor sp : vp.getProcessors()){ |
||||||
|
sp.reshape(vp, width, height); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isInitialized() { |
||||||
|
return rm != null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void preFrame(float tpf) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void postQueue(RenderQueue rq) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void invalidate(){ |
||||||
|
// For "PaintMode.OnDemand" only.
|
||||||
|
repaintRequest.set(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFrameBegin() { |
||||||
|
if (attachAsMain && rm != null){ |
||||||
|
rm.getRenderer().setMainFrameBufferOverride(fbs.get(frameIndex)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFrameEnd() { |
||||||
|
if (reshapeNeeded.getAndSet(false)) { |
||||||
|
reshapeInThread(newWidth, newHeight); |
||||||
|
} else { |
||||||
|
if (!checkVisibilityState()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
switch (paintMode) { |
||||||
|
case Accelerated: |
||||||
|
case Repaint: |
||||||
|
readNextFrame(); |
||||||
|
repaint(); |
||||||
|
break; |
||||||
|
case OnRequest: |
||||||
|
if (repaintRequest.getAndSet(false)) { |
||||||
|
readNextFrame(); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void postFrame(FrameBuffer out) { |
||||||
|
} |
||||||
|
|
||||||
|
public void reshape(ViewPort vp, int w, int h) { |
||||||
|
} |
||||||
|
|
||||||
|
public void cleanup() { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.system; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.channels.FileLock; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
import org.junit.FixMethodOrder; |
||||||
|
import org.junit.experimental.categories.Category; |
||||||
|
|
||||||
|
import com.jme3.IntegrationTest; |
||||||
|
import org.junit.Ignore; |
||||||
|
|
||||||
|
/** |
||||||
|
* Integration test for {@link NativeLibraryLoader}. |
||||||
|
* |
||||||
|
* Note that it uses the file system. |
||||||
|
* |
||||||
|
* @author Kirill Vainer |
||||||
|
*/ |
||||||
|
@Ignore |
||||||
|
@Category(IntegrationTest.class) |
||||||
|
@FixMethodOrder |
||||||
|
public class NativeLibraryLoaderIT { |
||||||
|
|
||||||
|
private File extractFolder; |
||||||
|
|
||||||
|
static { |
||||||
|
NativeLibraryLoader.registerNativeLibrary("test", Platform.Linux64, "natives/linux64/libtest.so"); |
||||||
|
NativeLibraryLoader.registerNativeLibrary("notexist", Platform.Linux64, "natives/linux64/libnotexist.so"); |
||||||
|
NativeLibraryLoader.registerNativeLibrary("nativesfolder", Platform.Linux64, "natives/linux64/libnativesfolder.so"); |
||||||
|
NativeLibraryLoader.registerNativeLibrary("jarroot", Platform.Linux64, "natives/linux64/libjarroot.so"); |
||||||
|
NativeLibraryLoader.registerNativeLibrary("nullpath", Platform.Linux64, null); |
||||||
|
NativeLibraryLoader.registerNativeLibrary("jawt", Platform.Linux64, "whatever/doesnt/matter/libjawt.so"); |
||||||
|
NativeLibraryLoader.registerNativeLibrary("asname", Platform.Linux64, "natives/linux64/libasname.so", "other.name"); |
||||||
|
} |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
extractFolder = NativeLibraryLoader.getExtractionFolder(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = UnsatisfiedLinkError.class) |
||||||
|
public void testRequiredNonExistentFile() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("notexist", true, false); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOptionalNonExistentFile() throws Exception { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("notexist", false, false); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = UnsatisfiedLinkError.class) |
||||||
|
public void testRequiredUnregisteredLibrary() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("unregistered", true, false); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOptionalUnregisteredLibrary() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("unregistered", false, false); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testLibraryNullPath() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("nullpath", true, false); |
||||||
|
NativeLibraryLoader.loadNativeLibrary("nullpath", false, false); |
||||||
|
} |
||||||
|
|
||||||
|
private static void fudgeLastModifiedTime(File file) { |
||||||
|
// fudge last modified date to force extraction attempt
|
||||||
|
long yesterdayModifiedtime = file.lastModified() - 24 * 60 * 60 * 1000; |
||||||
|
assertTrue(file.setLastModified(yesterdayModifiedtime)); |
||||||
|
assertTrue(Math.abs(file.lastModified() - yesterdayModifiedtime) < 10000); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDifferentLastModifiedDates() throws IOException { |
||||||
|
File libFile = new File(extractFolder, "libtest.so"); |
||||||
|
|
||||||
|
assertTrue(libFile.createNewFile()); |
||||||
|
assertTrue(libFile.exists() && libFile.length() == 0); |
||||||
|
|
||||||
|
fudgeLastModifiedTime(libFile); |
||||||
|
NativeLibraryLoader.loadNativeLibrary("test", true, false); |
||||||
|
assertTrue(libFile.length() == 12); |
||||||
|
|
||||||
|
assertTrue(libFile.delete()); |
||||||
|
assertTrue(!libFile.exists()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testLibraryInUse() throws IOException { |
||||||
|
File libFile = new File(extractFolder, "libtest.so"); |
||||||
|
|
||||||
|
NativeLibraryLoader.loadNativeLibrary("test", true, false); |
||||||
|
assertTrue(libFile.exists()); |
||||||
|
|
||||||
|
fudgeLastModifiedTime(libFile); |
||||||
|
|
||||||
|
FileOutputStream out = null; |
||||||
|
try { |
||||||
|
out = new FileOutputStream(libFile); |
||||||
|
FileLock lock = out.getChannel().lock(); |
||||||
|
assertTrue(lock.isValid()); |
||||||
|
|
||||||
|
NativeLibraryLoader.loadNativeLibrary("test", true, false); |
||||||
|
} finally { |
||||||
|
if (out != null) { |
||||||
|
out.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
libFile.delete(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testLoadSystemLibrary() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("jawt", true, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testExtractAsName() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("asname", true, false); |
||||||
|
assertTrue(new File(extractFolder, "other.name").exists()); |
||||||
|
assertTrue(new File(extractFolder, "other.name").delete()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCustomExtractFolder() { |
||||||
|
File customExtractFolder = new File(System.getProperty("java.io.tmpdir"), "jme3_test_tmp"); |
||||||
|
if (!customExtractFolder.exists()) { |
||||||
|
assertTrue(customExtractFolder.mkdir()); |
||||||
|
} |
||||||
|
|
||||||
|
NativeLibraryLoader.setCustomExtractionFolder(customExtractFolder.getAbsolutePath()); |
||||||
|
NativeLibraryLoader.loadNativeLibrary("test", true, false); |
||||||
|
|
||||||
|
assertTrue(new File(customExtractFolder, "libtest.so").exists()); |
||||||
|
assertTrue(new File(customExtractFolder, "libtest.so").delete()); |
||||||
|
assertTrue(!new File(customExtractFolder, "libtest.so").exists()); |
||||||
|
|
||||||
|
NativeLibraryLoader.setCustomExtractionFolder(null); |
||||||
|
NativeLibraryLoader.loadNativeLibrary("test", true, false); |
||||||
|
|
||||||
|
assertTrue(new File(extractFolder, "libtest.so").exists()); |
||||||
|
new File(extractFolder, "libtest.so").delete(); |
||||||
|
customExtractFolder.delete(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testExtractFromNativesFolderInJar() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("nativesfolder", true, false); |
||||||
|
|
||||||
|
File libFile = new File(extractFolder, "libnativesfolder.so"); |
||||||
|
assertTrue(libFile.exists() && libFile.length() == 12); |
||||||
|
|
||||||
|
libFile.delete(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testExtractFromJarRoot() { |
||||||
|
NativeLibraryLoader.loadNativeLibrary("jarroot", true, false); |
||||||
|
|
||||||
|
File libFile = new File(extractFolder, "libjarroot.so"); |
||||||
|
assertTrue(libFile.exists() && libFile.length() == 12); |
||||||
|
|
||||||
|
libFile.delete(); |
||||||
|
} |
||||||
|
} |
@ -1,88 +0,0 @@ |
|||||||
package jme3test.app; |
|
||||||
|
|
||||||
import com.jme3.scene.Mesh; |
|
||||||
import com.jme3.system.AppSettings; |
|
||||||
import java.io.ByteArrayInputStream; |
|
||||||
import java.io.ByteArrayOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.util.prefs.BackingStoreException; |
|
||||||
|
|
||||||
public class TestCustomAppSettings { |
|
||||||
|
|
||||||
private static final String APPSETTINGS_KEY = "JME_AppSettingsTest"; |
|
||||||
|
|
||||||
private static void assertEqual(Object a, Object b) { |
|
||||||
if (!a.equals(b)){ |
|
||||||
throw new AssertionError(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Tests preference based AppSettings. |
|
||||||
*/ |
|
||||||
private static void testPreferenceSettings() { |
|
||||||
AppSettings settings = new AppSettings(false); |
|
||||||
settings.putBoolean("TestBool", true); |
|
||||||
settings.putInteger("TestInt", 123); |
|
||||||
settings.putString("TestStr", "HelloWorld"); |
|
||||||
settings.putFloat("TestFloat", 123.567f); |
|
||||||
settings.put("TestObj", new Mesh()); // Objects not supported by preferences
|
|
||||||
|
|
||||||
try { |
|
||||||
settings.save(APPSETTINGS_KEY); |
|
||||||
} catch (BackingStoreException ex) { |
|
||||||
ex.printStackTrace(); |
|
||||||
} |
|
||||||
|
|
||||||
AppSettings loadedSettings = new AppSettings(false); |
|
||||||
try { |
|
||||||
loadedSettings.load(APPSETTINGS_KEY); |
|
||||||
} catch (BackingStoreException ex) { |
|
||||||
ex.printStackTrace(); |
|
||||||
} |
|
||||||
|
|
||||||
assertEqual(loadedSettings.getBoolean("TestBool"), true); |
|
||||||
assertEqual(loadedSettings.getInteger("TestInt"), 123); |
|
||||||
assertEqual(loadedSettings.getString("TestStr"), "HelloWorld"); |
|
||||||
assertEqual(loadedSettings.get("TestFloat"), 123.567f); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Test Java properties file based AppSettings. |
|
||||||
*/ |
|
||||||
private static void testFileSettings() { |
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
||||||
|
|
||||||
AppSettings settings = new AppSettings(false); |
|
||||||
settings.putBoolean("TestBool", true); |
|
||||||
settings.putInteger("TestInt", 123); |
|
||||||
settings.putString("TestStr", "HelloWorld"); |
|
||||||
settings.putFloat("TestFloat", 123.567f); |
|
||||||
settings.put("TestObj", new Mesh()); // Objects not supported by file settings
|
|
||||||
|
|
||||||
try { |
|
||||||
settings.save(baos); |
|
||||||
} catch (IOException ex) { |
|
||||||
ex.printStackTrace(); |
|
||||||
} |
|
||||||
|
|
||||||
AppSettings loadedSettings = new AppSettings(false); |
|
||||||
try { |
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); |
|
||||||
loadedSettings.load(bais); |
|
||||||
} catch (IOException ex) { |
|
||||||
ex.printStackTrace(); |
|
||||||
} |
|
||||||
|
|
||||||
assertEqual(loadedSettings.getBoolean("TestBool"), true); |
|
||||||
assertEqual(loadedSettings.getInteger("TestInt"), 123); |
|
||||||
assertEqual(loadedSettings.getString("TestStr"), "HelloWorld"); |
|
||||||
assertEqual(loadedSettings.get("TestFloat"), 123.567f); |
|
||||||
} |
|
||||||
|
|
||||||
public static void main(String[] args){ |
|
||||||
testPreferenceSettings(); |
|
||||||
testFileSettings(); |
|
||||||
System.out.println("All OK"); |
|
||||||
} |
|
||||||
} |
|
@ -1,112 +0,0 @@ |
|||||||
package jme3test.awt; |
|
||||||
|
|
||||||
import com.jme3.app.SimpleApplication; |
|
||||||
import com.jme3.material.Material; |
|
||||||
import com.jme3.math.Vector3f; |
|
||||||
import com.jme3.scene.Geometry; |
|
||||||
import com.jme3.scene.shape.Box; |
|
||||||
import com.jme3.system.AppSettings; |
|
||||||
import com.jme3.system.awt.AwtPanel; |
|
||||||
import com.jme3.system.awt.AwtPanelsContext; |
|
||||||
import com.jme3.system.awt.PaintMode; |
|
||||||
import java.awt.BorderLayout; |
|
||||||
import java.awt.Dimension; |
|
||||||
import java.awt.Toolkit; |
|
||||||
import java.awt.event.WindowAdapter; |
|
||||||
import java.awt.event.WindowEvent; |
|
||||||
import java.util.concurrent.CountDownLatch; |
|
||||||
import java.util.logging.Level; |
|
||||||
import java.util.logging.Logger; |
|
||||||
import javax.swing.JFrame; |
|
||||||
import javax.swing.SwingUtilities; |
|
||||||
|
|
||||||
public class TestAwtPanels extends SimpleApplication { |
|
||||||
|
|
||||||
final private static CountDownLatch panelsAreReady = new CountDownLatch(1); |
|
||||||
private static TestAwtPanels app; |
|
||||||
private static AwtPanel panel, panel2; |
|
||||||
private static int panelsClosed = 0; |
|
||||||
|
|
||||||
private static void createWindowForPanel(AwtPanel panel, int location){ |
|
||||||
JFrame frame = new JFrame("Render Display " + location); |
|
||||||
frame.getContentPane().setLayout(new BorderLayout()); |
|
||||||
frame.getContentPane().add(panel, BorderLayout.CENTER); |
|
||||||
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); |
|
||||||
frame.addWindowListener(new WindowAdapter() { |
|
||||||
@Override |
|
||||||
public void windowClosed(WindowEvent e) { |
|
||||||
if (++panelsClosed == 2){ |
|
||||||
app.stop(); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
frame.pack(); |
|
||||||
frame.setLocation(location, Toolkit.getDefaultToolkit().getScreenSize().height - 400); |
|
||||||
frame.setVisible(true); |
|
||||||
} |
|
||||||
|
|
||||||
public static void main(String[] args){ |
|
||||||
Logger.getLogger("com.jme3").setLevel(Level.WARNING); |
|
||||||
|
|
||||||
app = new TestAwtPanels(); |
|
||||||
app.setShowSettings(false); |
|
||||||
AppSettings settings = new AppSettings(true); |
|
||||||
settings.setCustomRenderer(AwtPanelsContext.class); |
|
||||||
settings.setFrameRate(60); |
|
||||||
app.setSettings(settings); |
|
||||||
app.start(); |
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable(){ |
|
||||||
public void run(){ |
|
||||||
/* |
|
||||||
* Sleep 2 seconds to ensure there's no race condition. |
|
||||||
* The sleep is not required for correctness. |
|
||||||
*/ |
|
||||||
try { |
|
||||||
Thread.sleep(2000); |
|
||||||
} catch (InterruptedException exception) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
final AwtPanelsContext ctx = (AwtPanelsContext) app.getContext(); |
|
||||||
panel = ctx.createPanel(PaintMode.Accelerated); |
|
||||||
panel.setPreferredSize(new Dimension(400, 300)); |
|
||||||
ctx.setInputSource(panel); |
|
||||||
|
|
||||||
panel2 = ctx.createPanel(PaintMode.Accelerated); |
|
||||||
panel2.setPreferredSize(new Dimension(400, 300)); |
|
||||||
|
|
||||||
createWindowForPanel(panel, 300); |
|
||||||
createWindowForPanel(panel2, 700); |
|
||||||
/* |
|
||||||
* Both panels are ready. |
|
||||||
*/ |
|
||||||
panelsAreReady.countDown(); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void simpleInitApp() { |
|
||||||
flyCam.setDragToRotate(true); |
|
||||||
|
|
||||||
Box b = new Box(Vector3f.ZERO, 1, 1, 1); |
|
||||||
Geometry geom = new Geometry("Box", b); |
|
||||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
|
||||||
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); |
|
||||||
geom.setMaterial(mat); |
|
||||||
rootNode.attachChild(geom); |
|
||||||
/* |
|
||||||
* Wait until both AWT panels are ready. |
|
||||||
*/ |
|
||||||
try { |
|
||||||
panelsAreReady.await(); |
|
||||||
} catch (InterruptedException exception) { |
|
||||||
throw new RuntimeException("Interrupted while waiting for panels", exception); |
|
||||||
} |
|
||||||
|
|
||||||
panel.attachTo(true, viewPort); |
|
||||||
guiViewPort.setClearFlags(true, true, true); |
|
||||||
panel2.attachTo(false, guiViewPort); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,141 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package jme3test.stress; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.system.AppSettings; |
||||||
|
|
||||||
|
// Let's see if we can render 2500 batches in 60 fps.
|
||||||
|
// We'll use 50 materials with various combinations of textures and colors
|
||||||
|
// to make things wild.
|
||||||
|
public class TestUniqueGeometries extends SimpleApplication { |
||||||
|
|
||||||
|
private Material[] randomMaterials = new Material[50]; |
||||||
|
|
||||||
|
private String[] textureList = new String[] { |
||||||
|
"Blender/2.4x/textures/Concrete_Wall.PNG", |
||||||
|
"Blender/2.4x/textures/Grass_256.png", |
||||||
|
"Blender/2.4x/textures/SandDesert_StartTower.png", |
||||||
|
"Blender/2.4x/textures/Tar_Cracked.png", |
||||||
|
"Blender/2.4x/textures/WarningStrip.png", |
||||||
|
"Blender/2.4x/WoodCrate_lighter.png", |
||||||
|
"Interface/Logo/Monkey.jpg", |
||||||
|
"Interface/Logo/Monkey.png", |
||||||
|
"Models/Boat/boat.png", |
||||||
|
"Models/Ninja/Ninja.jpg", |
||||||
|
"Models/Tree/BarkColor.jpg", |
||||||
|
"Textures/Terrain/BrickWall/BrickWall.jpg", |
||||||
|
"Textures/Terrain/Pond/Pond.jpg", |
||||||
|
"Textures/Terrain/Pond/Pond_normal.png", |
||||||
|
"Textures/Terrain/Rock/Rock.PNG", |
||||||
|
"Textures/Terrain/Rock/Rock_normal.png", |
||||||
|
"Textures/Terrain/Rock2/rock.jpg", |
||||||
|
"Textures/Terrain/Rocky/RockyNormals.jpg", |
||||||
|
"Textures/Terrain/Rocky/RockyTexture.jpg", |
||||||
|
"Textures/Terrain/splat/alpha1.png", |
||||||
|
"Textures/Terrain/splat/alpha2.png", |
||||||
|
"Textures/Terrain/splat/alphamap.png", |
||||||
|
"Textures/Terrain/splat/alphamap2.png", |
||||||
|
"Textures/Terrain/splat/dirt.jpg", |
||||||
|
"Textures/Terrain/splat/dirt_normal.png", |
||||||
|
"Textures/Terrain/splat/fortress512.png", |
||||||
|
"Textures/Terrain/splat/grass.jpg", |
||||||
|
"Textures/Terrain/splat/grass_normal.jpg", |
||||||
|
"Textures/Terrain/splat/mountains128.png", |
||||||
|
"Textures/Terrain/splat/road.jpg", |
||||||
|
"Textures/Terrain/splat/road_normal.png", |
||||||
|
}; |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestUniqueGeometries app = new TestUniqueGeometries(); |
||||||
|
AppSettings settings = new AppSettings(true); |
||||||
|
settings.putBoolean("GraphicsTrace", false); |
||||||
|
settings.putBoolean("GraphicsTiming", true); |
||||||
|
app.setSettings(settings); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
private void loadRandomMaterials() { |
||||||
|
for (int i = 0; i < randomMaterials.length; i++) { |
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
mat.setBoolean("VertexLighting", true); |
||||||
|
mat.setBoolean("UseMaterialColors", true); |
||||||
|
mat.setColor("Ambient", ColorRGBA.Black); |
||||||
|
mat.setColor("Diffuse", ColorRGBA.White); |
||||||
|
mat.setColor("Specular", ColorRGBA.White); |
||||||
|
mat.setFloat("Shininess", 32); |
||||||
|
mat.setTexture("DiffuseMap", assetManager.loadTexture(textureList[i % textureList.length])); |
||||||
|
randomMaterials[i] = mat; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
flyCam.setDragToRotate(true); |
||||||
|
|
||||||
|
cam.setLocation(new Vector3f(22.717342f, 18.366547f, 22.043106f)); |
||||||
|
cam.setRotation(new Quaternion(-0.11630201f, 0.8794429f, -0.27703872f, -0.36919326f)); |
||||||
|
|
||||||
|
DirectionalLight dl = new DirectionalLight(); |
||||||
|
dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); |
||||||
|
rootNode.addLight(dl); |
||||||
|
|
||||||
|
flyCam.setMoveSpeed(5); |
||||||
|
|
||||||
|
loadRandomMaterials(); |
||||||
|
|
||||||
|
// Box box = new Box(1,1,1);
|
||||||
|
|
||||||
|
for (int y = -25; y < 25; y++) { |
||||||
|
for (int x = -25; x < 25; x++) { |
||||||
|
Material mat = randomMaterials[0]; // randomMaterials[FastMath.nextRandomInt(0, randomMaterials.length - 1)];
|
||||||
|
|
||||||
|
Box box = new Box(1,1,1); |
||||||
|
Geometry boxClone = new Geometry("box", box); |
||||||
|
boxClone.setMaterial(mat); |
||||||
|
|
||||||
|
boxClone.setLocalTranslation(x * .5f, 0, y * .5f); |
||||||
|
boxClone.setLocalScale(.15f); |
||||||
|
boxClone.setMaterial(mat); |
||||||
|
rootNode.attachChild(boxClone); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
if (!hasProperty('mainClass')) { |
||||||
|
ext.mainClass = '' |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
compile project(':jme3-core') |
||||||
|
compile project(':jme3-desktop') |
||||||
|
compile 'net.java.jinput:jinput:2.0.6' |
||||||
|
compile 'net.java.jinput:jinput-platform:2.0.6' |
||||||
|
|
||||||
|
testCompile project(path: ':jme3-core', configuration: 'testOutput') |
||||||
|
} |
@ -1,529 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine |
|
||||||
* All rights reserved. |
|
||||||
* |
|
||||||
* Redistribution and use in source and binary forms, with or without |
|
||||||
* modification, are permitted provided that the following conditions are |
|
||||||
* met: |
|
||||||
* |
|
||||||
* * Redistributions of source code must retain the above copyright |
|
||||||
* notice, this list of conditions and the following disclaimer. |
|
||||||
* |
|
||||||
* * Redistributions in binary form must reproduce the above copyright |
|
||||||
* notice, this list of conditions and the following disclaimer in the |
|
||||||
* documentation and/or other materials provided with the distribution. |
|
||||||
* |
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
|
||||||
* may be used to endorse or promote products derived from this software |
|
||||||
* without specific prior written permission. |
|
||||||
* |
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.jme3.renderer.jogl; |
|
||||||
|
|
||||||
import com.jme3.renderer.RendererException; |
|
||||||
import com.jme3.texture.Image; |
|
||||||
import com.jme3.texture.Image.Format; |
|
||||||
import com.jme3.texture.image.ColorSpace; |
|
||||||
|
|
||||||
import java.nio.ByteBuffer; |
|
||||||
import java.util.logging.Level; |
|
||||||
import java.util.logging.Logger; |
|
||||||
|
|
||||||
import com.jogamp.opengl.GL; |
|
||||||
import com.jogamp.opengl.GL2; |
|
||||||
import com.jogamp.opengl.GL2ES2; |
|
||||||
import com.jogamp.opengl.GL2ES3; |
|
||||||
import com.jogamp.opengl.GL2GL3; |
|
||||||
import com.jogamp.opengl.GLContext; |
|
||||||
|
|
||||||
public class TextureUtil { |
|
||||||
|
|
||||||
private static boolean abgrToRgbaConversionEnabled = false; |
|
||||||
|
|
||||||
public static int convertTextureFormat(Format fmt) { |
|
||||||
switch (fmt) { |
|
||||||
case Alpha8: |
|
||||||
return GL.GL_ALPHA; |
|
||||||
case Luminance8Alpha8: |
|
||||||
return GL.GL_LUMINANCE_ALPHA; |
|
||||||
case Luminance8: |
|
||||||
return GL.GL_LUMINANCE; |
|
||||||
case BGR8: |
|
||||||
case RGB8: |
|
||||||
case RGB565: |
|
||||||
return GL.GL_RGB; |
|
||||||
case RGB5A1: |
|
||||||
case RGBA8: |
|
||||||
return GL.GL_RGBA; |
|
||||||
case Depth: |
|
||||||
return GL2ES2.GL_DEPTH_COMPONENT; |
|
||||||
default: |
|
||||||
throw new UnsupportedOperationException("Unrecognized format: " + fmt); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static class GLImageFormat { |
|
||||||
|
|
||||||
int internalFormat; |
|
||||||
int format; |
|
||||||
int dataType; |
|
||||||
boolean compressed; |
|
||||||
|
|
||||||
public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed) { |
|
||||||
this.internalFormat = internalFormat; |
|
||||||
this.format = format; |
|
||||||
this.dataType = dataType; |
|
||||||
this.compressed = compressed; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static final GLImageFormat[] formatToGL = new GLImageFormat[Format.values().length]; |
|
||||||
|
|
||||||
private static void setFormat(Format format, int glInternalFormat, int glFormat, int glDataType, boolean glCompressed){ |
|
||||||
formatToGL[format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, glCompressed); |
|
||||||
} |
|
||||||
|
|
||||||
static { |
|
||||||
// Alpha formats
|
|
||||||
setFormat(Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
|
|
||||||
// Luminance formats
|
|
||||||
setFormat(Format.Luminance8, GL2.GL_LUMINANCE8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.Luminance16F, GL2.GL_LUMINANCE16F, GL.GL_LUMINANCE, GL.GL_HALF_FLOAT, false); |
|
||||||
setFormat(Format.Luminance32F, GL2.GL_LUMINANCE32F, GL.GL_LUMINANCE, GL.GL_FLOAT, false); |
|
||||||
|
|
||||||
// Luminance alpha formats
|
|
||||||
setFormat(Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.Luminance16FAlpha16F, GL2.GL_LUMINANCE_ALPHA16F, GL.GL_LUMINANCE_ALPHA, GL.GL_HALF_FLOAT, false); |
|
||||||
|
|
||||||
// Depth formats
|
|
||||||
setFormat(Format.Depth, GL2ES2.GL_DEPTH_COMPONENT, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.Depth16, GL.GL_DEPTH_COMPONENT16, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_SHORT, false); |
|
||||||
setFormat(Format.Depth24, GL.GL_DEPTH_COMPONENT24, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, false); |
|
||||||
setFormat(Format.Depth32, GL.GL_DEPTH_COMPONENT32, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, false); |
|
||||||
setFormat(Format.Depth32F, GL2GL3.GL_DEPTH_COMPONENT32F, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, false); |
|
||||||
|
|
||||||
// Depth stencil formats
|
|
||||||
setFormat(Format.Depth24Stencil8, GL.GL_DEPTH24_STENCIL8, GL.GL_DEPTH_STENCIL, GL.GL_UNSIGNED_INT_24_8, false); |
|
||||||
|
|
||||||
// RGB formats
|
|
||||||
setFormat(Format.BGR8, GL.GL_RGB8, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.ARGB8, GL.GL_RGBA8, GL.GL_BGRA, GL2.GL_UNSIGNED_INT_8_8_8_8_REV, false); |
|
||||||
setFormat(Format.BGRA8, GL.GL_RGBA8, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.RGB8, GL.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.RGB16F, GL2ES2.GL_RGB16F, GL.GL_RGB, GL.GL_HALF_FLOAT, false); |
|
||||||
setFormat(Format.RGB32F, GL.GL_RGB32F, GL.GL_RGB, GL.GL_FLOAT, false); |
|
||||||
|
|
||||||
// Special RGB formats
|
|
||||||
setFormat(Format.RGB111110F, GL2ES3.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_UNSIGNED_INT_10F_11F_11F_REV, false); |
|
||||||
setFormat(Format.RGB9E5, GL2GL3.GL_RGB9_E5, GL.GL_RGB, GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV, false); |
|
||||||
setFormat(Format.RGB16F_to_RGB111110F, GL2ES3.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_HALF_FLOAT, false); |
|
||||||
setFormat(Format.RGB16F_to_RGB9E5, GL2.GL_RGB9_E5, GL.GL_RGB, GL.GL_HALF_FLOAT, false); |
|
||||||
|
|
||||||
// RGBA formats
|
|
||||||
setFormat(Format.ABGR8, GL.GL_RGBA8, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.RGB5A1, GL.GL_RGB5_A1, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1, false); |
|
||||||
setFormat(Format.RGBA8, GL.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
setFormat(Format.RGBA16F, GL2ES2.GL_RGBA16F, GL.GL_RGBA, GL.GL_HALF_FLOAT, false); |
|
||||||
setFormat(Format.RGBA32F, GL.GL_RGBA32F, GL.GL_RGBA, GL.GL_FLOAT, false); |
|
||||||
|
|
||||||
// DXT formats
|
|
||||||
setFormat(Format.DXT1, GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
setFormat(Format.DXT1A, GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
setFormat(Format.DXT3, GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
setFormat(Format.DXT5, GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
} |
|
||||||
|
|
||||||
//sRGB formats
|
|
||||||
private static final GLImageFormat sRGB_RGB8 = new GLImageFormat(GL2.GL_SRGB8,GL.GL_RGB, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
private static final GLImageFormat sRGB_RGBA8 = new GLImageFormat(GL.GL_SRGB8_ALPHA8,GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
private static final GLImageFormat sRGB_Luminance8 = new GLImageFormat(GL2.GL_SLUMINANCE8,GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
private static final GLImageFormat sRGB_LuminanceAlpha8 = new GLImageFormat(GL2.GL_SLUMINANCE8_ALPHA8,GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
private static final GLImageFormat sRGB_BGR8 = new GLImageFormat(GL2.GL_SRGB8, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
private static final GLImageFormat sRGB_ABGR8 = new GLImageFormat(GL2.GL_SRGB8_ALPHA8, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
|
|
||||||
//FIXME cannot find GL_COMPRESSED_RGB_S3TC_DXT1,GL_COMPRESSED_RGBA_S3TC_DXT1,GL_COMPRESSED_RGB_S3TC_DXT3,GL_COMPRESSED_RGB_S3TC_DXT5 in JOGL used constants
|
|
||||||
//GL_COMPRESSED_RGB_S3TC_DXT1 = 33776;
|
|
||||||
//GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 33777;
|
|
||||||
//GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 33778;
|
|
||||||
//GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 33779;
|
|
||||||
private static final GLImageFormat sRGB_DXT1 = new GLImageFormat(33776, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
private static final GLImageFormat sRGB_DXT1A = new GLImageFormat( 33777, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
private static final GLImageFormat sRGB_DXT3 = new GLImageFormat(33778, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
private static final GLImageFormat sRGB_DXT5 = new GLImageFormat( 33779, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |
|
||||||
|
|
||||||
|
|
||||||
public static GLImageFormat getImageFormat(Format fmt, boolean isSrgb){ |
|
||||||
GL gl = GLContext.getCurrentGL(); |
|
||||||
switch (fmt){ |
|
||||||
case ABGR8: |
|
||||||
if (!gl.isExtensionAvailable("GL_EXT_abgr") && !abgrToRgbaConversionEnabled) { |
|
||||||
setFormat(Format.ABGR8, GL.GL_RGBA, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false); |
|
||||||
abgrToRgbaConversionEnabled = true; |
|
||||||
} |
|
||||||
break; |
|
||||||
case BGR8: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_1_2") && !gl.isExtensionAvailable("EXT_bgra")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case DXT1: |
|
||||||
case DXT1A: |
|
||||||
case DXT3: |
|
||||||
case DXT5: |
|
||||||
if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc")) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Depth: |
|
||||||
case Depth16: |
|
||||||
case Depth24: |
|
||||||
case Depth32: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_1_4") && !gl.isExtensionAvailable("ARB_depth_texture")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Depth24Stencil8: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_3_0")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Luminance16F: |
|
||||||
case Luminance16FAlpha16F: |
|
||||||
case Luminance32F: |
|
||||||
if (!gl.isExtensionAvailable("GL_ARB_texture_float")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case RGB16F: |
|
||||||
case RGB32F: |
|
||||||
case RGBA16F: |
|
||||||
case RGBA32F: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_ARB_texture_float")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Depth32F: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_NV_depth_buffer_float")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case RGB9E5: |
|
||||||
case RGB16F_to_RGB9E5: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_EXT_texture_shared_exponent")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
case RGB111110F: |
|
||||||
case RGB16F_to_RGB111110F: |
|
||||||
if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_EXT_packed_float")){ |
|
||||||
return null; |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
if(isSrgb){ |
|
||||||
return getSrgbFormat(fmt); |
|
||||||
} |
|
||||||
return formatToGL[fmt.ordinal()]; |
|
||||||
} |
|
||||||
|
|
||||||
public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) { |
|
||||||
GLImageFormat glFmt = getImageFormat(fmt, isSrgb); |
|
||||||
if (glFmt == null) { |
|
||||||
throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); |
|
||||||
} |
|
||||||
return glFmt; |
|
||||||
} |
|
||||||
|
|
||||||
private static GLImageFormat getSrgbFormat(Format fmt){ |
|
||||||
switch (fmt){ |
|
||||||
case RGB8 : return sRGB_RGB8; |
|
||||||
case RGBA8 : return sRGB_RGBA8; |
|
||||||
case BGR8 : return sRGB_BGR8; |
|
||||||
case ABGR8 : return sRGB_ABGR8; |
|
||||||
case Luminance8 : return sRGB_Luminance8; |
|
||||||
case Luminance8Alpha8 : return sRGB_LuminanceAlpha8; |
|
||||||
case DXT1 : return sRGB_DXT1; |
|
||||||
case DXT1A : return sRGB_DXT1A; |
|
||||||
case DXT3 : return sRGB_DXT3; |
|
||||||
case DXT5 : return sRGB_DXT5; |
|
||||||
default : Logger.getLogger(TextureUtil.class.getName()).log(Level.WARNING, "Format {0} has no sRGB equivalent, using linear format.", fmt.toString()); |
|
||||||
return formatToGL[fmt.ordinal()]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static void uploadTexture(Image image, |
|
||||||
int target, |
|
||||||
int index, |
|
||||||
int border, |
|
||||||
boolean linearizeSrgb){ |
|
||||||
GL gl = GLContext.getCurrentGL(); |
|
||||||
Image.Format fmt = image.getFormat(); |
|
||||||
GLImageFormat glFmt = getImageFormatWithError(fmt, image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb); |
|
||||||
|
|
||||||
ByteBuffer data; |
|
||||||
if (index >= 0 && image.getData() != null && image.getData().size() > 0){ |
|
||||||
data = image.getData(index); |
|
||||||
}else{ |
|
||||||
data = null; |
|
||||||
} |
|
||||||
|
|
||||||
int width = image.getWidth(); |
|
||||||
int height = image.getHeight(); |
|
||||||
int depth = image.getDepth(); |
|
||||||
|
|
||||||
if (data != null) { |
|
||||||
if (abgrToRgbaConversionEnabled) { |
|
||||||
convertABGRtoRGBA(data); |
|
||||||
} |
|
||||||
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); |
|
||||||
} |
|
||||||
|
|
||||||
int[] mipSizes = image.getMipMapSizes(); |
|
||||||
int pos = 0; |
|
||||||
// TODO: Remove unneccessary allocation
|
|
||||||
if (mipSizes == null){ |
|
||||||
if (data != null) |
|
||||||
mipSizes = new int[]{ data.capacity() }; |
|
||||||
else |
|
||||||
mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; |
|
||||||
} |
|
||||||
|
|
||||||
boolean subtex = false; |
|
||||||
int samples = image.getMultiSamples(); |
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++){ |
|
||||||
int mipWidth = Math.max(1, width >> i); |
|
||||||
int mipHeight = Math.max(1, height >> i); |
|
||||||
int mipDepth = Math.max(1, depth >> i); |
|
||||||
|
|
||||||
if (data != null){ |
|
||||||
data.position(pos); |
|
||||||
data.limit(pos + mipSizes[i]); |
|
||||||
} |
|
||||||
|
|
||||||
if (glFmt.compressed && data != null){ |
|
||||||
if (target == GL2ES2.GL_TEXTURE_3D){ |
|
||||||
gl.getGL2ES2().glCompressedTexImage3D(target, |
|
||||||
i, |
|
||||||
glFmt.internalFormat, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
mipDepth, |
|
||||||
data.remaining(), |
|
||||||
border, |
|
||||||
data); |
|
||||||
}else{ |
|
||||||
//all other targets use 2D: array, cubemap, 2d
|
|
||||||
gl.glCompressedTexImage2D(target, |
|
||||||
i, |
|
||||||
glFmt.internalFormat, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
data.remaining(), |
|
||||||
border, |
|
||||||
data); |
|
||||||
} |
|
||||||
}else{ |
|
||||||
if (target == GL2ES2.GL_TEXTURE_3D){ |
|
||||||
gl.getGL2ES2().glTexImage3D(target, |
|
||||||
i, |
|
||||||
glFmt.internalFormat, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
mipDepth, |
|
||||||
border, |
|
||||||
glFmt.format, |
|
||||||
glFmt.dataType, |
|
||||||
data); |
|
||||||
}else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY){ |
|
||||||
// prepare data for 2D array
|
|
||||||
// or upload slice
|
|
||||||
if (index == -1){ |
|
||||||
gl.getGL2ES2().glTexImage3D(target, |
|
||||||
0, |
|
||||||
glFmt.internalFormat, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
image.getData().size(), //# of slices
|
|
||||||
border, |
|
||||||
glFmt.format, |
|
||||||
glFmt.dataType, |
|
||||||
data); |
|
||||||
}else{ |
|
||||||
gl.getGL2ES2().glTexSubImage3D(target, |
|
||||||
i, // level
|
|
||||||
0, // xoffset
|
|
||||||
0, // yoffset
|
|
||||||
index, // zoffset
|
|
||||||
width, // width
|
|
||||||
height, // height
|
|
||||||
1, // depth
|
|
||||||
glFmt.format, |
|
||||||
glFmt.dataType, |
|
||||||
data); |
|
||||||
} |
|
||||||
}else{ |
|
||||||
if (subtex){ |
|
||||||
if (samples > 1){ |
|
||||||
throw new IllegalStateException("Cannot update multisample textures"); |
|
||||||
} |
|
||||||
|
|
||||||
gl.glTexSubImage2D(target, |
|
||||||
i, |
|
||||||
0, 0, |
|
||||||
mipWidth, mipHeight, |
|
||||||
glFmt.format, |
|
||||||
glFmt.dataType, |
|
||||||
data); |
|
||||||
}else{ |
|
||||||
if (samples > 1){ |
|
||||||
if (gl.isGL2GL3()) { |
|
||||||
gl.getGL3().glTexImage2DMultisample(target, |
|
||||||
samples, |
|
||||||
glFmt.internalFormat, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
true); |
|
||||||
} |
|
||||||
} else { |
|
||||||
gl.glTexImage2D(target, |
|
||||||
i, |
|
||||||
glFmt.internalFormat, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
border, |
|
||||||
glFmt.format, |
|
||||||
glFmt.dataType, |
|
||||||
data); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pos += mipSizes[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static void convertABGRtoRGBA(ByteBuffer buffer) { |
|
||||||
|
|
||||||
for (int i = 0; i < buffer.capacity(); i++) { |
|
||||||
|
|
||||||
int a = buffer.get(i++); |
|
||||||
int b = buffer.get(i++); |
|
||||||
int g = buffer.get(i++); |
|
||||||
int r = buffer.get(i); |
|
||||||
|
|
||||||
buffer.put(i - 3, (byte) r); |
|
||||||
buffer.put(i - 2, (byte) g); |
|
||||||
buffer.put(i - 1, (byte) b); |
|
||||||
buffer.put(i, (byte) a); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update the texture currently bound to target at with data from the given Image at position x and y. The parameter |
|
||||||
* index is used as the zoffset in case a 3d texture or texture 2d array is being updated. |
|
||||||
* |
|
||||||
* @param image Image with the source data (this data will be put into the texture) |
|
||||||
* @param target the target texture |
|
||||||
* @param index the mipmap level to update |
|
||||||
* @param x the x position where to put the image in the texture |
|
||||||
* @param y the y position where to put the image in the texture |
|
||||||
*/ |
|
||||||
public static void uploadSubTexture( |
|
||||||
Image image, |
|
||||||
int target, |
|
||||||
int index, |
|
||||||
int x, |
|
||||||
int y, |
|
||||||
boolean linearizeSrgb) { |
|
||||||
GL gl = GLContext.getCurrentGL(); |
|
||||||
Image.Format fmt = image.getFormat(); |
|
||||||
GLImageFormat glFmt = getImageFormatWithError(fmt, image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb); |
|
||||||
|
|
||||||
ByteBuffer data = null; |
|
||||||
if (index >= 0 && image.getData() != null && image.getData().size() > 0) { |
|
||||||
data = image.getData(index); |
|
||||||
} |
|
||||||
|
|
||||||
int width = image.getWidth(); |
|
||||||
int height = image.getHeight(); |
|
||||||
int depth = image.getDepth(); |
|
||||||
|
|
||||||
if (data != null) { |
|
||||||
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); |
|
||||||
} |
|
||||||
|
|
||||||
int[] mipSizes = image.getMipMapSizes(); |
|
||||||
int pos = 0; |
|
||||||
|
|
||||||
// TODO: Remove unneccessary allocation
|
|
||||||
if (mipSizes == null){ |
|
||||||
if (data != null) { |
|
||||||
mipSizes = new int[]{ data.capacity() }; |
|
||||||
} else { |
|
||||||
mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int samples = image.getMultiSamples(); |
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++){ |
|
||||||
int mipWidth = Math.max(1, width >> i); |
|
||||||
int mipHeight = Math.max(1, height >> i); |
|
||||||
int mipDepth = Math.max(1, depth >> i); |
|
||||||
|
|
||||||
if (data != null){ |
|
||||||
data.position(pos); |
|
||||||
data.limit(pos + mipSizes[i]); |
|
||||||
} |
|
||||||
|
|
||||||
// to remove the cumbersome if/then/else stuff below we'll update the pos right here and use continue after each
|
|
||||||
// gl*Image call in an attempt to unclutter things a bit
|
|
||||||
pos += mipSizes[i]; |
|
||||||
|
|
||||||
int glFmtInternal = glFmt.internalFormat; |
|
||||||
int glFmtFormat = glFmt.format; |
|
||||||
int glFmtDataType = glFmt.dataType; |
|
||||||
|
|
||||||
if (glFmt.compressed && data != null){ |
|
||||||
if (target == GL2ES2.GL_TEXTURE_3D){ |
|
||||||
gl.getGL2ES2().glCompressedTexSubImage3D(target, i, x, y, index, mipWidth, mipHeight, mipDepth, glFmtInternal, data.limit(), data); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// all other targets use 2D: array, cubemap, 2d
|
|
||||||
gl.getGL2ES2().glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, glFmtInternal, data.limit(), data); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (target == GL2ES2.GL_TEXTURE_3D){ |
|
||||||
gl.getGL2ES2().glTexSubImage3D(target, i, x, y, index, mipWidth, mipHeight, mipDepth, glFmtFormat, glFmtDataType, data); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (samples > 1){ |
|
||||||
throw new IllegalStateException("Cannot update multisample textures"); |
|
||||||
} |
|
||||||
|
|
||||||
gl.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, glFmtFormat, glFmtDataType, data); |
|
||||||
continue; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,112 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.scene.plugins.fbx.anim; |
||||||
|
|
||||||
|
import com.jme3.animation.Bone; |
||||||
|
import com.jme3.animation.Skeleton; |
||||||
|
import com.jme3.scene.plugins.fbx.node.FbxNode; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Similar to {@link Skeleton jME skeleton} except |
||||||
|
* contains {@link FbxLimbNode limb nodes}. |
||||||
|
* |
||||||
|
* This is used to determine the bone indices (for assigning clusters to meshes) |
||||||
|
* as well as the limb hierarchy when creating the jME3 Skeleton. |
||||||
|
* |
||||||
|
* @author Kirill Vainer |
||||||
|
*/ |
||||||
|
public class FbxSkeleton { |
||||||
|
|
||||||
|
FbxLimbNode[] rootLimbs; |
||||||
|
FbxLimbNode[] allLimbs; |
||||||
|
HashMap<FbxLimbNode, Integer> limbToIndexMap = new HashMap<FbxLimbNode, Integer>(); |
||||||
|
|
||||||
|
private FbxSkeleton() { |
||||||
|
} |
||||||
|
|
||||||
|
public static void populateSkeletonData(FbxNode skeletonRoot) { |
||||||
|
// if (skeletonRoot instanceof FbxLimbNode) {
|
||||||
|
// throw new UnsupportedOperationException("Limb node cannot be a skeleton root");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// FbxSkeleton skeleton = new FbxSkeleton();
|
||||||
|
// skeleton.scanLimbs(skeletonRoot);
|
||||||
|
// skeletonRoot.setFbxSkeleton(skeleton);
|
||||||
|
} |
||||||
|
|
||||||
|
private void scanLimbs(FbxNode skeletonRoot, FbxLimbNode limb, List<FbxLimbNode> limbList) { |
||||||
|
// limb.skeletonRoot = skeletonRoot;
|
||||||
|
// limbList.add(limb);
|
||||||
|
// for (FbxNode child : limb.getChildren()) {
|
||||||
|
// if (child instanceof FbxLimbNode) {
|
||||||
|
// FbxLimbNode childLimb = (FbxLimbNode) child;
|
||||||
|
// scanLimbs(skeletonRoot, childLimb, limbList);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
private void scanLimbs(FbxNode skeletonRoot) { |
||||||
|
List<FbxLimbNode> limbList = new ArrayList<FbxLimbNode>(); |
||||||
|
List<FbxLimbNode> rootList = new ArrayList<FbxLimbNode>(); |
||||||
|
|
||||||
|
for (FbxNode child : skeletonRoot.getChildren()) { |
||||||
|
if (child instanceof FbxLimbNode) { |
||||||
|
FbxLimbNode limb = (FbxLimbNode) child; |
||||||
|
rootList.add(limb); |
||||||
|
scanLimbs(skeletonRoot, limb, limbList); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
allLimbs = limbList.toArray(new FbxLimbNode[0]); |
||||||
|
rootLimbs = rootList.toArray(new FbxLimbNode[0]); |
||||||
|
|
||||||
|
for (int i = 0; i < allLimbs.length; i++) { |
||||||
|
limbToIndexMap.put(allLimbs[i], i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int getLimbIndex(FbxLimbNode limbNode) { |
||||||
|
return limbToIndexMap.get(limbNode); |
||||||
|
} |
||||||
|
|
||||||
|
public FbxLimbNode getLimb(int index) { |
||||||
|
return allLimbs[index]; |
||||||
|
} |
||||||
|
|
||||||
|
public FbxLimbNode[] getRootLimbs() { |
||||||
|
return rootLimbs; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,241 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.scene.plugins.triangulator; |
||||||
|
|
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Matrix3f; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.plugins.IrPolygon; |
||||||
|
import com.jme3.scene.plugins.IrVertex; |
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
/** |
||||||
|
* Implemented according to |
||||||
|
* <ul> |
||||||
|
* <li>http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf</li>
|
||||||
|
* <li>http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/algorithm2.html</li>
|
||||||
|
* </ul> |
||||||
|
*/ |
||||||
|
public final class EarClippingTriangulator { |
||||||
|
|
||||||
|
private static enum VertexType { |
||||||
|
Convex, |
||||||
|
Reflex, |
||||||
|
Ear; |
||||||
|
} |
||||||
|
|
||||||
|
private final ArrayList<Integer> indices = new ArrayList<Integer>(); |
||||||
|
private final ArrayList<VertexType> types = new ArrayList<VertexType>(); |
||||||
|
private final ArrayList<Vector2f> positions = new ArrayList<Vector2f>(); |
||||||
|
|
||||||
|
public EarClippingTriangulator() { |
||||||
|
} |
||||||
|
|
||||||
|
private static int ccw(Vector2f p0, Vector2f p1, Vector2f p2) { |
||||||
|
float result = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x); |
||||||
|
if (result > 0) { |
||||||
|
return 1; |
||||||
|
} else if (result < 0) { |
||||||
|
return -1; |
||||||
|
} else { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean pointInTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) { |
||||||
|
float d = ((t1.y - t2.y) * (t0.x - t2.x) + (t2.x - t1.x) * (t0.y - t2.y)); |
||||||
|
float a = ((t1.y - t2.y) * (p.x - t2.x) + (t2.x - t1.x) * (p.y - t2.y)) / d; |
||||||
|
float b = ((t2.y - t0.y) * (p.x - t2.x) + (t0.x - t2.x) * (p.y - t2.y)) / d; |
||||||
|
float c = 1 - a - b; |
||||||
|
return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1; |
||||||
|
} |
||||||
|
|
||||||
|
private static Matrix3f normalToMatrix(Vector3f norm) { |
||||||
|
Vector3f tang1 = norm.cross(Vector3f.UNIT_X); |
||||||
|
if (tang1.lengthSquared() < FastMath.ZERO_TOLERANCE) { |
||||||
|
tang1 = norm.cross(Vector3f.UNIT_Y); |
||||||
|
} |
||||||
|
tang1.normalizeLocal(); |
||||||
|
Vector3f tang2 = norm.cross(tang1).normalizeLocal(); |
||||||
|
|
||||||
|
return new Matrix3f( |
||||||
|
tang1.x, tang1.y, tang1.z, |
||||||
|
tang2.x, tang2.y, tang2.z, |
||||||
|
norm.x, norm.y, norm.z); |
||||||
|
} |
||||||
|
|
||||||
|
private int prev(int index) { |
||||||
|
if (index == 0) { |
||||||
|
return indices.size() - 1; |
||||||
|
} else { |
||||||
|
return index - 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int next(int index) { |
||||||
|
if (index == indices.size() - 1) { |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
return index + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private VertexType calcType(int index) { |
||||||
|
int prev = prev(index); |
||||||
|
int next = next(index); |
||||||
|
|
||||||
|
Vector2f p0 = positions.get(prev); |
||||||
|
Vector2f p1 = positions.get(index); |
||||||
|
Vector2f p2 = positions.get(next); |
||||||
|
|
||||||
|
if (ccw(p0, p1, p2) <= 0) { |
||||||
|
return VertexType.Reflex; |
||||||
|
} else { |
||||||
|
for (int i = 0; i < positions.size() - 3; i++) { |
||||||
|
int testIndex = (index + 2 + i) % positions.size(); |
||||||
|
if (types.get(testIndex) != VertexType.Reflex) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
Vector2f p = positions.get(testIndex); |
||||||
|
if (pointInTriangle(p0, p1, p2, p)) { |
||||||
|
return VertexType.Convex; |
||||||
|
} |
||||||
|
} |
||||||
|
return VertexType.Ear; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void updateType(int index) { |
||||||
|
if (types.get(index) == VertexType.Convex) { |
||||||
|
return; |
||||||
|
} |
||||||
|
types.set(index, calcType(index)); |
||||||
|
} |
||||||
|
|
||||||
|
private void loadVertices(IrVertex[] vertices) { |
||||||
|
indices.ensureCapacity(vertices.length); |
||||||
|
types.ensureCapacity(vertices.length); |
||||||
|
positions.ensureCapacity(vertices.length); |
||||||
|
|
||||||
|
Vector3f normal = FastMath.computeNormal( |
||||||
|
vertices[0].pos, |
||||||
|
vertices[1].pos, |
||||||
|
vertices[2].pos); |
||||||
|
|
||||||
|
Matrix3f transform = normalToMatrix(normal); |
||||||
|
|
||||||
|
for (int i = 0; i < vertices.length; i++) { |
||||||
|
Vector3f projected = transform.mult(vertices[i].pos); |
||||||
|
indices.add(i); |
||||||
|
positions.add(new Vector2f(projected.x, projected.y)); |
||||||
|
types.add(VertexType.Reflex); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < vertices.length; i++) { |
||||||
|
types.set(i, calcType(i)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private IrPolygon createTriangle(IrPolygon polygon, int prev, int index, int next) { |
||||||
|
int p0 = indices.get(prev); |
||||||
|
int p1 = indices.get(index); |
||||||
|
int p2 = indices.get(next); |
||||||
|
IrPolygon triangle = new IrPolygon(); |
||||||
|
triangle.vertices = new IrVertex[] { |
||||||
|
polygon.vertices[p0], |
||||||
|
polygon.vertices[p1], |
||||||
|
polygon.vertices[p2], |
||||||
|
}; |
||||||
|
return triangle; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Triangulates the given polygon. |
||||||
|
* |
||||||
|
* Five or more vertices are required, if less are given, an exception |
||||||
|
* is thrown. |
||||||
|
* |
||||||
|
* @param polygon The polygon to triangulate. |
||||||
|
* @return N - 2 triangles, where N is the number of vertices in the polygon. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException If the polygon has less than 5 vertices. |
||||||
|
*/ |
||||||
|
public IrPolygon[] triangulate(IrPolygon polygon) { |
||||||
|
if (polygon.vertices.length < 5) { |
||||||
|
throw new IllegalArgumentException("Only polygons with 5 or more vertices are supported"); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
int numTris = 0; |
||||||
|
IrPolygon[] triangles = new IrPolygon[polygon.vertices.length - 2]; |
||||||
|
|
||||||
|
loadVertices(polygon.vertices); |
||||||
|
|
||||||
|
int index = 0; |
||||||
|
while (types.size() > 3) { |
||||||
|
if (types.get(index) == VertexType.Ear) { |
||||||
|
int prev = prev(index); |
||||||
|
int next = next(index); |
||||||
|
|
||||||
|
triangles[numTris++] = createTriangle(polygon, prev, index, next); |
||||||
|
|
||||||
|
indices.remove(index); |
||||||
|
types.remove(index); |
||||||
|
positions.remove(index); |
||||||
|
|
||||||
|
next = next(prev); |
||||||
|
updateType(prev); |
||||||
|
updateType(next); |
||||||
|
|
||||||
|
index = next(next); |
||||||
|
} else { |
||||||
|
index = next(index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (types.size() == 3) { |
||||||
|
triangles[numTris++] = createTriangle(polygon, 0, 1, 2); |
||||||
|
} |
||||||
|
|
||||||
|
if (numTris != triangles.length) { |
||||||
|
throw new AssertionError("Triangulation failed to generate enough triangles"); |
||||||
|
} |
||||||
|
|
||||||
|
return triangles; |
||||||
|
} finally { |
||||||
|
indices.clear(); |
||||||
|
positions.clear(); |
||||||
|
types.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.scene.plugins.triangulator; |
||||||
|
|
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.plugins.IrPolygon; |
||||||
|
import com.jme3.scene.plugins.IrVertex; |
||||||
|
import junit.framework.TestCase; |
||||||
|
|
||||||
|
public class TriangulatorTest extends TestCase { |
||||||
|
|
||||||
|
public void testTriangulator() { |
||||||
|
Vector3f[] dataSet = new Vector3f[]{ |
||||||
|
new Vector3f(0.75f, 0.3f, 1.2f), |
||||||
|
new Vector3f(0.75f, 0.3f, 0.0f), |
||||||
|
new Vector3f(0.75f, 0.17f, 0.0f), |
||||||
|
new Vector3f(0.75000095f, 0.17f, 1.02f), |
||||||
|
new Vector3f(0.75f, -0.17f, 1.02f), |
||||||
|
new Vector3f(0.75f, -0.17f, 0.0f), |
||||||
|
new Vector3f(0.75f, -0.3f, 0.0f), |
||||||
|
new Vector3f(0.75f, -0.3f, 1.2f) |
||||||
|
}; |
||||||
|
|
||||||
|
IrPolygon poly = new IrPolygon(); |
||||||
|
poly.vertices = new IrVertex[dataSet.length]; |
||||||
|
for (int i = 0; i < dataSet.length; i++) { |
||||||
|
poly.vertices[i] = new IrVertex(); |
||||||
|
poly.vertices[i].pos = dataSet[i]; |
||||||
|
} |
||||||
|
|
||||||
|
EarClippingTriangulator triangulator = new EarClippingTriangulator(); |
||||||
|
triangulator.triangulate(poly); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue