diff --git a/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java b/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java new file mode 100644 index 000000000..9a8361f44 --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java @@ -0,0 +1,552 @@ +/* + * 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.app.state; + +import android.graphics.Bitmap; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Released under BSD License + * @author monceaux, normenhansen, entrusC + */ +public class MjpegFileWriter { + private static final Logger logger = Logger.getLogger(MjpegFileWriter.class.getName()); + + int width = 0; + int height = 0; + double framerate = 0; + int numFrames = 0; + File aviFile = null; + FileOutputStream aviOutput = null; + FileChannel aviChannel = null; + long riffOffset = 0; + long aviMovieOffset = 0; + AVIIndexList indexlist = null; + + public MjpegFileWriter(File aviFile, int width, int height, double framerate) throws Exception { + this(aviFile, width, height, framerate, 0); + } + + public MjpegFileWriter(File aviFile, int width, int height, double framerate, int numFrames) throws Exception { + this.aviFile = aviFile; + this.width = width; + this.height = height; + this.framerate = framerate; + this.numFrames = numFrames; + aviOutput = new FileOutputStream(aviFile); + aviChannel = aviOutput.getChannel(); + + RIFFHeader rh = new RIFFHeader(); + aviOutput.write(rh.toBytes()); + aviOutput.write(new AVIMainHeader().toBytes()); + aviOutput.write(new AVIStreamList().toBytes()); + aviOutput.write(new AVIStreamHeader().toBytes()); + aviOutput.write(new AVIStreamFormat().toBytes()); + aviOutput.write(new AVIJunk().toBytes()); + aviMovieOffset = aviChannel.position(); + aviOutput.write(new AVIMovieList().toBytes()); + indexlist = new AVIIndexList(); + } + + public void addImage(Bitmap image) throws Exception { + addImage(image, 0.8f); + } + + public void addImage(Bitmap image, float quality) throws Exception { + addImage(writeImageToBytes(image, quality)); + } + + public void addImage(byte[] imagedata) throws Exception { + byte[] fcc = new byte[]{'0', '0', 'd', 'b'}; + int useLength = imagedata.length; + long position = aviChannel.position(); + int extra = (useLength + (int) position) % 4; + if (extra > 0) { + useLength = useLength + extra; + } + + indexlist.addAVIIndex((int) position, useLength); + + aviOutput.write(fcc); + aviOutput.write(intBytes(swapInt(useLength))); + aviOutput.write(imagedata); + if (extra > 0) { + for (int i = 0; i < extra; i++) { + aviOutput.write(0); + } + } + imagedata = null; + + numFrames++; //add a frame + } + + public void finishAVI() throws Exception { + logger.log(Level.INFO, "finishAVI"); + byte[] indexlistBytes = indexlist.toBytes(); + aviOutput.write(indexlistBytes); + aviOutput.close(); + int fileSize = (int)aviFile.length(); + logger.log(Level.INFO, "fileSize: {0}", fileSize); + int listSize = (int) (fileSize - 8 - aviMovieOffset - indexlistBytes.length); + logger.log(Level.INFO, "listSize: {0}", listSize); + logger.log(Level.INFO, "aviFile canWrite: {0}", aviFile.canWrite()); + logger.log(Level.INFO, "aviFile AbsolutePath: {0}", aviFile.getAbsolutePath()); + logger.log(Level.INFO, "aviFile numFrames: {0}", numFrames); + + RandomAccessFile raf = new RandomAccessFile(aviFile, "rw"); + + //add header and length by writing the headers again + //with the now available information + raf.write(new RIFFHeader(fileSize).toBytes()); + raf.write(new AVIMainHeader().toBytes()); + raf.write(new AVIStreamList().toBytes()); + raf.write(new AVIStreamHeader().toBytes()); + raf.write(new AVIStreamFormat().toBytes()); + raf.write(new AVIJunk().toBytes()); + raf.write(new AVIMovieList(listSize).toBytes()); + + raf.close(); + } + + // public void writeAVI(File file) throws Exception + // { + // OutputStream os = new FileOutputStream(file); + // + // // RIFFHeader + // // AVIMainHeader + // // AVIStreamList + // // AVIStreamHeader + // // AVIStreamFormat + // // write 00db and image bytes... + // } + public static int swapInt(int v) { + return (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000) | ((v >> 8) & 0x0000FF00); + } + + public static short swapShort(short v) { + return (short) ((v >>> 8) | (v << 8)); + } + + public static byte[] intBytes(int i) { + byte[] b = new byte[4]; + b[0] = (byte) (i >>> 24); + b[1] = (byte) ((i >>> 16) & 0x000000FF); + b[2] = (byte) ((i >>> 8) & 0x000000FF); + b[3] = (byte) (i & 0x000000FF); + + return b; + } + + public static byte[] shortBytes(short i) { + byte[] b = new byte[2]; + b[0] = (byte) (i >>> 8); + b[1] = (byte) (i & 0x000000FF); + + return b; + } + + private class RIFFHeader { + + public byte[] fcc = new byte[]{'R', 'I', 'F', 'F'}; + public int fileSize = 0; + public byte[] fcc2 = new byte[]{'A', 'V', 'I', ' '}; + public byte[] fcc3 = new byte[]{'L', 'I', 'S', 'T'}; + public int listSize = 200; + public byte[] fcc4 = new byte[]{'h', 'd', 'r', 'l'}; + + public RIFFHeader() { + } + + public RIFFHeader(int fileSize) { + this.fileSize = fileSize; + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(fileSize))); + baos.write(fcc2); + baos.write(fcc3); + baos.write(intBytes(swapInt(listSize))); + baos.write(fcc4); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIMainHeader { + /* + * + * FOURCC fcc; DWORD cb; DWORD dwMicroSecPerFrame; DWORD + * dwMaxBytesPerSec; DWORD dwPaddingGranularity; DWORD dwFlags; DWORD + * dwTotalFrames; DWORD dwInitialFrames; DWORD dwStreams; DWORD + * dwSuggestedBufferSize; DWORD dwWidth; DWORD dwHeight; DWORD + * dwReserved[4]; + */ + + public byte[] fcc = new byte[]{'a', 'v', 'i', 'h'}; + public int cb = 56; + public int dwMicroSecPerFrame = 0; // (1 + // / + // frames + // per + // sec) + // * + // 1,000,000 + public int dwMaxBytesPerSec = 10000000; + public int dwPaddingGranularity = 0; + public int dwFlags = 65552; + public int dwTotalFrames = 0; // replace + // with + // correct + // value + public int dwInitialFrames = 0; + public int dwStreams = 1; + public int dwSuggestedBufferSize = 0; + public int dwWidth = 0; // replace + // with + // correct + // value + public int dwHeight = 0; // replace + // with + // correct + // value + public int[] dwReserved = new int[4]; + + public AVIMainHeader() { + dwMicroSecPerFrame = (int) ((1.0 / framerate) * 1000000.0); + dwWidth = width; + dwHeight = height; + dwTotalFrames = numFrames; + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(cb))); + baos.write(intBytes(swapInt(dwMicroSecPerFrame))); + baos.write(intBytes(swapInt(dwMaxBytesPerSec))); + baos.write(intBytes(swapInt(dwPaddingGranularity))); + baos.write(intBytes(swapInt(dwFlags))); + baos.write(intBytes(swapInt(dwTotalFrames))); + baos.write(intBytes(swapInt(dwInitialFrames))); + baos.write(intBytes(swapInt(dwStreams))); + baos.write(intBytes(swapInt(dwSuggestedBufferSize))); + baos.write(intBytes(swapInt(dwWidth))); + baos.write(intBytes(swapInt(dwHeight))); + baos.write(intBytes(swapInt(dwReserved[0]))); + baos.write(intBytes(swapInt(dwReserved[1]))); + baos.write(intBytes(swapInt(dwReserved[2]))); + baos.write(intBytes(swapInt(dwReserved[3]))); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIStreamList { + + public byte[] fcc = new byte[]{'L', 'I', 'S', 'T'}; + public int size = 124; + public byte[] fcc2 = new byte[]{'s', 't', 'r', 'l'}; + + public AVIStreamList() { + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(size))); + baos.write(fcc2); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIStreamHeader { + /* + * FOURCC fcc; DWORD cb; FOURCC fccType; FOURCC fccHandler; DWORD + * dwFlags; WORD wPriority; WORD wLanguage; DWORD dwInitialFrames; DWORD + * dwScale; DWORD dwRate; DWORD dwStart; DWORD dwLength; DWORD + * dwSuggestedBufferSize; DWORD dwQuality; DWORD dwSampleSize; struct { + * short int left; short int top; short int right; short int bottom; } + * rcFrame; + */ + + public byte[] fcc = new byte[]{'s', 't', 'r', 'h'}; + public int cb = 64; + public byte[] fccType = new byte[]{'v', 'i', 'd', 's'}; + public byte[] fccHandler = new byte[]{'M', 'J', 'P', 'G'}; + public int dwFlags = 0; + public short wPriority = 0; + public short wLanguage = 0; + public int dwInitialFrames = 0; + public int dwScale = 0; // microseconds + // per + // frame + public int dwRate = 1000000; // dwRate + // / + // dwScale + // = + // frame + // rate + public int dwStart = 0; + public int dwLength = 0; // num + // frames + public int dwSuggestedBufferSize = 0; + public int dwQuality = -1; + public int dwSampleSize = 0; + public int left = 0; + public int top = 0; + public int right = 0; + public int bottom = 0; + + public AVIStreamHeader() { + dwScale = (int) ((1.0 / framerate) * 1000000.0); + dwLength = numFrames; + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(cb))); + baos.write(fccType); + baos.write(fccHandler); + baos.write(intBytes(swapInt(dwFlags))); + baos.write(shortBytes(swapShort(wPriority))); + baos.write(shortBytes(swapShort(wLanguage))); + baos.write(intBytes(swapInt(dwInitialFrames))); + baos.write(intBytes(swapInt(dwScale))); + baos.write(intBytes(swapInt(dwRate))); + baos.write(intBytes(swapInt(dwStart))); + baos.write(intBytes(swapInt(dwLength))); + baos.write(intBytes(swapInt(dwSuggestedBufferSize))); + baos.write(intBytes(swapInt(dwQuality))); + baos.write(intBytes(swapInt(dwSampleSize))); + baos.write(intBytes(swapInt(left))); + baos.write(intBytes(swapInt(top))); + baos.write(intBytes(swapInt(right))); + baos.write(intBytes(swapInt(bottom))); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIStreamFormat { + /* + * FOURCC fcc; DWORD cb; DWORD biSize; LONG biWidth; LONG biHeight; WORD + * biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; + * LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD + * biClrImportant; + */ + + public byte[] fcc = new byte[]{'s', 't', 'r', 'f'}; + public int cb = 40; + public int biSize = 40; // same + // as + // cb + public int biWidth = 0; + public int biHeight = 0; + public short biPlanes = 1; + public short biBitCount = 24; + public byte[] biCompression = new byte[]{'M', 'J', 'P', 'G'}; + public int biSizeImage = 0; // width + // x + // height + // in + // pixels + public int biXPelsPerMeter = 0; + public int biYPelsPerMeter = 0; + public int biClrUsed = 0; + public int biClrImportant = 0; + + public AVIStreamFormat() { + biWidth = width; + biHeight = height; + biSizeImage = width * height; + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(cb))); + baos.write(intBytes(swapInt(biSize))); + baos.write(intBytes(swapInt(biWidth))); + baos.write(intBytes(swapInt(biHeight))); + baos.write(shortBytes(swapShort(biPlanes))); + baos.write(shortBytes(swapShort(biBitCount))); + baos.write(biCompression); + baos.write(intBytes(swapInt(biSizeImage))); + baos.write(intBytes(swapInt(biXPelsPerMeter))); + baos.write(intBytes(swapInt(biYPelsPerMeter))); + baos.write(intBytes(swapInt(biClrUsed))); + baos.write(intBytes(swapInt(biClrImportant))); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIMovieList { + + public byte[] fcc = new byte[]{'L', 'I', 'S', 'T'}; + public int listSize = 0; + public byte[] fcc2 = new byte[]{'m', 'o', 'v', 'i'}; + + // 00db size jpg image data ... + public AVIMovieList() { + } + + public AVIMovieList(int listSize) { + this.listSize = listSize; + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(listSize))); + baos.write(fcc2); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIIndexList { + + public byte[] fcc = new byte[]{'i', 'd', 'x', '1'}; + public int cb = 0; + public List ind = new ArrayList(); + + public AVIIndexList() { + } + + @SuppressWarnings("unused") + public void addAVIIndex(AVIIndex ai) { + ind.add(ai); + } + + public void addAVIIndex(int dwOffset, int dwSize) { + ind.add(new AVIIndex(dwOffset, dwSize)); + } + + public byte[] toBytes() throws Exception { + cb = 16 * ind.size(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(cb))); + for (int i = 0; i < ind.size(); i++) { + AVIIndex in = (AVIIndex) ind.get(i); + baos.write(in.toBytes()); + } + + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIIndex { + + public byte[] fcc = new byte[]{'0', '0', 'd', 'b'}; + public int dwFlags = 16; + public int dwOffset = 0; + public int dwSize = 0; + + public AVIIndex(int dwOffset, int dwSize) { + this.dwOffset = dwOffset; + this.dwSize = dwSize; + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(dwFlags))); + baos.write(intBytes(swapInt(dwOffset))); + baos.write(intBytes(swapInt(dwSize))); + baos.close(); + + return baos.toByteArray(); + } + } + + private class AVIJunk { + + public byte[] fcc = new byte[]{'J', 'U', 'N', 'K'}; + public int size = 1808; + public byte[] data = new byte[size]; + + public AVIJunk() { + Arrays.fill(data, (byte) 0); + } + + public byte[] toBytes() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(fcc); + baos.write(intBytes(swapInt(size))); + baos.write(data); + baos.close(); + + return baos.toByteArray(); + } + } + + public byte[] writeImageToBytes(Bitmap image, float quality) throws Exception { + Bitmap bi; + if (image.getConfig() == Bitmap.Config.RGB_565) { + bi = image; + } else { + bi = image.copy(Bitmap.Config.RGB_565, false); + if (bi == null) { + throw new IllegalStateException("Could not convert Bitmap to RGB_565"); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + bi.compress(Bitmap.CompressFormat.JPEG, (int)(quality*100), baos); + baos.close(); + return baos.toByteArray(); + } +} diff --git a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java new file mode 100644 index 000000000..6c9669df1 --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -0,0 +1,364 @@ +/* + * 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.app.state; + +import android.graphics.Bitmap; +import com.jme3.app.Application; +import com.jme3.post.SceneProcessor; +import com.jme3.renderer.Camera; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.system.JmeSystem; +import com.jme3.system.Timer; +import com.jme3.texture.FrameBuffer; +import com.jme3.util.AndroidScreenshots; +import com.jme3.util.BufferUtils; +import java.io.File; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A Video recording AppState that records the screen output into an AVI file with + * M-JPEG content. The file should be playable on any OS in any video player.
+ * The video recording starts when the state is attached and stops when it is detached + * or the application is quit. You can set the fileName of the file to be written when the + * state is detached, else the old file will be overwritten. If you specify no file + * the AppState will attempt to write a file into the user home directory, made unique + * by a timestamp. + * @author normenhansen, Robert McIntyre, entrusC + */ +public class VideoRecorderAppState extends AbstractAppState { + private static final Logger logger = Logger.getLogger(VideoRecorderAppState.class.getName()); + + private int numFrames = 0; + private int framerate = 30; + private VideoProcessor processor; + private File file; + private Application app; + private ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() { + + public Thread newThread(Runnable r) { + Thread th = new Thread(r); + th.setName("jME Video Processing Thread"); + th.setDaemon(true); + return th; + } + }); + private int numCpus = Runtime.getRuntime().availableProcessors(); + private ViewPort lastViewPort; + private float quality; + private Timer oldTimer; + + /** + * Using this constructor the video files will be written sequentially to the user's home directory with + * a quality of 0.8 and a framerate of 30fps. + */ + public VideoRecorderAppState() { + this(null, 0.8f); + } + + /** + * Using this constructor the video files will be written sequentially to the user's home directory. + * @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) + */ + public VideoRecorderAppState(float quality) { + this(null, quality); + } + + /** + * Using this constructor the video files will be written sequentially to the user's home directory. + * @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) + * @param framerate the frame rate of the resulting video, the application will be locked to this framerate + */ + public VideoRecorderAppState(float quality, int framerate) { + this(null, quality, framerate); + } + + /** + * This constructor allows you to specify the output file of the video. The quality is set + * to 0.8 and framerate to 30 fps. + * @param file the video file + */ + public VideoRecorderAppState(File file) { + this(file, 0.8f); + } + + /** + * This constructor allows you to specify the output file of the video as well as the quality + * @param file the video file + * @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) + * @param framerate the frame rate of the resulting video, the application will be locked to this framerate + */ + public VideoRecorderAppState(File file, float quality) { + this.file = file; + this.quality = quality; + Logger.getLogger(this.getClass().getName()).log(Level.FINE, "JME3 VideoRecorder running on {0} CPU's", numCpus); + } + + /** + * This constructor allows you to specify the output file of the video as well as the quality + * @param file the video file + * @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) + */ + public VideoRecorderAppState(File file, float quality, int framerate) { + this.file = file; + this.quality = quality; + this.framerate = framerate; + Logger.getLogger(this.getClass().getName()).log(Level.FINE, "JME3 VideoRecorder running on {0} CPU's", numCpus); + } + + public File getFile() { + return file; + } + + public void setFile(File file) { + if (isInitialized()) { + throw new IllegalStateException("Cannot set file while attached!"); + } + this.file = file; + } + + /** + * Get the quality used to compress the video images. + * @return the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) + */ + public float getQuality() { + return quality; + } + + /** + * Set the video image quality from 0(worst/smallest) to 1(best/largest). + * @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) + */ + public void setQuality(float quality) { + this.quality = quality; + } + + @Override + public void initialize(AppStateManager stateManager, Application app) { + super.initialize(stateManager, app); + this.app = app; + this.oldTimer = app.getTimer(); + app.setTimer(new IsoTimer(framerate)); + if (file == null) { + String filename = JmeSystem.getStorageFolder(JmeSystem.StorageFolderType.External) + File.separator + "jMonkey-" + System.currentTimeMillis() / 1000 + ".avi"; + logger.log(Level.INFO, "fileName: {0}", filename); + file = new File(filename); + } + processor = new VideoProcessor(); + List vps = app.getRenderManager().getPostViews(); + + for (int i = vps.size() - 1; i >= 0; i-- ) { + lastViewPort = vps.get(i); + if (lastViewPort.isEnabled()) { + break; + } + } + lastViewPort.addProcessor(processor); + } + + @Override + public void cleanup() { + logger.log(Level.INFO, "removing processor"); + lastViewPort.removeProcessor(processor); + app.setTimer(oldTimer); + initialized = false; + file = null; + super.cleanup(); + } + + private class WorkItem { + + ByteBuffer buffer; + Bitmap image; + byte[] data; + + public WorkItem(int width, int height) { + image = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + buffer = BufferUtils.createByteBuffer(width * height * 4); + } + } + + private class VideoProcessor implements SceneProcessor { + + private Camera camera; + private int width; + private int height; + private RenderManager renderManager; + private boolean isInitilized = false; + private LinkedBlockingQueue freeItems; + private LinkedBlockingQueue usedItems = new LinkedBlockingQueue(); + private MjpegFileWriter writer; + private boolean fastMode = true; + + public void addImage(Renderer renderer, FrameBuffer out) { + if (freeItems == null) { + return; + } + try { + final WorkItem item = freeItems.take(); + usedItems.add(item); + item.buffer.clear(); + renderer.readFrameBuffer(out, item.buffer); + executor.submit(new Callable() { + + public Void call() throws Exception { + if (fastMode) { + item.data = item.buffer.array(); + } else { + AndroidScreenshots.convertScreenShot(item.buffer, item.image); + item.data = writer.writeImageToBytes(item.image, quality); + } + while (usedItems.peek() != item) { + Thread.sleep(1); + } + writer.addImage(item.data); + usedItems.poll(); + freeItems.add(item); + return null; + } + }); + } catch (InterruptedException ex) { + Logger.getLogger(VideoRecorderAppState.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void initialize(RenderManager rm, ViewPort viewPort) { + logger.log(Level.INFO, "initialize in VideoProcessor"); + this.camera = viewPort.getCamera(); + this.width = camera.getWidth(); + this.height = camera.getHeight(); + this.renderManager = rm; + this.isInitilized = true; + if (freeItems == null) { + freeItems = new LinkedBlockingQueue(); + for (int i = 0; i < numCpus; i++) { + freeItems.add(new WorkItem(width, height)); + } + } + } + + public void reshape(ViewPort vp, int w, int h) { + } + + public boolean isInitialized() { + return this.isInitilized; + } + + public void preFrame(float tpf) { + if (null == writer) { + try { + writer = new MjpegFileWriter(file, width, height, framerate); + } catch (Exception ex) { + Logger.getLogger(VideoRecorderAppState.class.getName()).log(Level.SEVERE, "Error creating file writer: {0}", ex); + } + } + } + + public void postQueue(RenderQueue rq) { + } + + public void postFrame(FrameBuffer out) { + numFrames++; + addImage(renderManager.getRenderer(), out); + } + + public void cleanup() { + logger.log(Level.INFO, "cleanup in VideoProcessor"); + logger.log(Level.INFO, "VideoProcessor numFrames: {0}", numFrames); + try { + while (freeItems.size() < numCpus) { + Thread.sleep(10); + } + logger.log(Level.INFO, "finishAVI in VideoProcessor"); + writer.finishAVI(); + } catch (Exception ex) { + Logger.getLogger(VideoRecorderAppState.class.getName()).log(Level.SEVERE, "Error closing video: {0}", ex); + } + writer = null; + } + } + + public static final class IsoTimer extends com.jme3.system.Timer { + + private float framerate; + private int ticks; + private long lastTime = 0; + + public IsoTimer(float framerate) { + this.framerate = framerate; + this.ticks = 0; + } + + public long getTime() { + return (long) (this.ticks * (1.0f / this.framerate) * 1000f); + } + + public long getResolution() { + return 1000L; + } + + public float getFrameRate() { + return this.framerate; + } + + public float getTimePerFrame() { + return (float) (1.0f / this.framerate); + } + + public void update() { + long time = System.currentTimeMillis(); + long difference = time - lastTime; + lastTime = time; + if (difference < (1.0f / this.framerate) * 1000.0f) { + try { + Thread.sleep(difference); + } catch (InterruptedException ex) { + } + } else { + logger.log(Level.INFO, "actual tpf(ms): {0}, 1/framerate(ms): {1}", + new Object[]{difference, (1.0f / this.framerate) * 1000.0f}); + } + this.ticks++; + } + + public void reset() { + this.ticks = 0; + } + } +} diff --git a/jme3-bullet-native/src/native/android/Android.mk b/jme3-bullet-native/src/native/android/Android.mk deleted file mode 100644 index 45258d04b..000000000 --- a/jme3-bullet-native/src/native/android/Android.mk +++ /dev/null @@ -1,260 +0,0 @@ -# /* -# Bullet Continuous Collision Detection and Physics Library for Android NDK -# Copyright (c) 2006-2009 Noritsuna Imamura http://www.siprop.org/ -# -# This software is provided 'as-is', without any express or implied warranty. -# In no event will the authors be held liable for any damages arising from the use of this software. -# Permission is granted to anyone to use this software for any purpose, -# including commercial applications, and to alter it and redistribute it freely, -# subject to the following restrictions: -# -# 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -# 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -# 3. This notice may not be removed or altered from any source distribution. -# */ -LOCAL_PATH:= $(call my-dir) -JME3_PATH:= -BULLET_PATH:= - -include $(CLEAR_VARS) - -LOCAL_MODULE := bulletjme -LOCAL_C_INCLUDES := $(BULLET_PATH)/\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch\ - $(BULLET_PATH)/BulletCollision/CollisionShapes\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver\ - $(BULLET_PATH)/BulletDynamics/Dynamics\ - $(BULLET_PATH)/BulletDynamics/Vehicle\ - $(BULLET_PATH)/LinearMath\ - $(BULLET_PATH)/BulletCollision\ - $(BULLET_PATH)/BulletDynamics\ - $(BULLET_PATH)/BulletMultiThreaded\ - $(BULLET_PATH)/BulletSoftBody\ - $(BULLET_PATH)/ibmsdk\ - $(BULLET_PATH)/LinearMath\ - $(BULLET_PATH)/MiniCL\ - $(BULLET_PATH)/vectormath\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch\ - $(BULLET_PATH)/BulletCollision/CollisionShapes\ - $(BULLET_PATH)/BulletCollision/Gimpact\ - $(BULLET_PATH)/BulletCollision/ibmsdk\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision\ - $(BULLET_PATH)/BulletDynamics/Character\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver\ - $(BULLET_PATH)/BulletDynamics/Dynamics\ - $(BULLET_PATH)/BulletDynamics/ibmsdk\ - $(BULLET_PATH)/BulletDynamics/Vehicle\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers\ - $(BULLET_PATH)/BulletMultiThreaded/out\ - $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask\ - $(BULLET_PATH)/BulletMultiThreaded/SpuSampleTask\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/CPU\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/DX11\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/DX11/HLSL\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/AMD\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Apple\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/NVidia\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/OpenCLC\ - $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/OpenCLC10\ - $(BULLET_PATH)/LinearMath/ibmsdk\ - $(BULLET_PATH)/MiniCL/MiniCLTask\ - $(BULLET_PATH)/vectormath/scalar\ - $(BULLET_PATH)/vectormath/sse - -LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl -lm -llog - -LOCAL_SRC_FILES := $(JME3_PATH)/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_PhysicsCollisionObject.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_CollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_ConeJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_HingeJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_PhysicsJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_Point2PointJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_SixDofJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_SixDofSpringJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_joints_SliderJoint.cpp\ - $(JME3_PATH)/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp\ - $(JME3_PATH)/com_jme3_bullet_objects_PhysicsCharacter.cpp\ - $(JME3_PATH)/com_jme3_bullet_objects_PhysicsGhostObject.cpp\ - $(JME3_PATH)/com_jme3_bullet_objects_PhysicsRigidBody.cpp\ - $(JME3_PATH)/com_jme3_bullet_objects_PhysicsVehicle.cpp\ - $(JME3_PATH)/com_jme3_bullet_objects_VehicleWheel.cpp\ - $(JME3_PATH)/com_jme3_bullet_PhysicsSpace.cpp\ - $(JME3_PATH)/com_jme3_bullet_util_DebugShapeFactory.cpp\ - $(JME3_PATH)/com_jme3_bullet_util_NativeMeshUtil.cpp\ - $(JME3_PATH)/jmeBulletUtil.cpp\ - $(JME3_PATH)/jmeClasses.cpp\ - $(JME3_PATH)/jmeMotionState.cpp\ - $(JME3_PATH)/jmePhysicsSpace.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btDbvt.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btDispatcher.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp\ - $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCollisionObject.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCollisionWorld.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btGhostObject.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btManifoldResult.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/btUnionFind.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btBox2dShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btBoxShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btCapsuleShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btCollisionShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btCompoundShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConcaveShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConeShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvex2dShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexHullShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexInternalShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btCylinderShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btEmptyShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btMultiSphereShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btOptimizedBvh.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btShapeHull.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btSphereShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTetrahedronShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleBuffer.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleCallback.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleMesh.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp\ - $(BULLET_PATH)/BulletCollision/CollisionShapes/btUniformScalingShape.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btContactProcessing.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btGenericPoolAllocator.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactBvh.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactShape.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/btTriangleShapeEx.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/gim_box_set.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/gim_contact.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/gim_memory.cpp\ - $(BULLET_PATH)/BulletCollision/Gimpact/gim_tri_collision.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp\ - $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp\ - $(BULLET_PATH)/BulletDynamics/Character/btKinematicCharacterController.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btContactConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp\ - $(BULLET_PATH)/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp\ - $(BULLET_PATH)/BulletDynamics/Dynamics/btRigidBody.cpp\ - $(BULLET_PATH)/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp\ - $(BULLET_PATH)/BulletDynamics/Dynamics/Bullet-C-API.cpp\ - $(BULLET_PATH)/BulletDynamics/Vehicle/btRaycastVehicle.cpp\ - $(BULLET_PATH)/BulletDynamics/Vehicle/btWheelInfo.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/btGpu3DGridBroadphase.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/btParallelConstraintSolver.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/btThreadSupportInterface.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/PosixThreadSupport.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SequentialThreadSupport.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuCollisionObjectWrapper.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuCollisionTaskProcess.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuFakeDma.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuLibspe2Support.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuSampleTaskProcess.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/Win32ThreadSupport.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp\ - $(BULLET_PATH)/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.cpp\ - $(BULLET_PATH)/BulletSoftBody/btDefaultSoftBodySolver.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftBody.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftBodyHelpers.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftRigidDynamicsWorld.cpp\ - $(BULLET_PATH)/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp\ - $(BULLET_PATH)/LinearMath/btAlignedAllocator.cpp\ - $(BULLET_PATH)/LinearMath/btConvexHull.cpp\ - $(BULLET_PATH)/LinearMath/btConvexHullComputer.cpp\ - $(BULLET_PATH)/LinearMath/btGeometryUtil.cpp\ - $(BULLET_PATH)/LinearMath/btQuickprof.cpp\ - $(BULLET_PATH)/LinearMath/btSerializer.cpp\ - $(BULLET_PATH)/LinearMath/btPolarDecomposition.cpp\ - $(BULLET_PATH)/MiniCL/MiniCL.cpp\ - $(BULLET_PATH)/MiniCL/MiniCLTaskScheduler.cpp\ - $(BULLET_PATH)/MiniCL/MiniCLTask/MiniCLTask.cpp -# $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/CPU/btSoftBodySolver_CPU.cpp\ -# $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL/MiniCLTaskWrap.cpp\ - -include $(BUILD_SHARED_LIBRARY) diff --git a/jme3-bullet-native/src/native/android/Application.mk b/jme3-bullet-native/src/native/android/Application.mk deleted file mode 100644 index 9e8be3571..000000000 --- a/jme3-bullet-native/src/native/android/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -APP_MODULES := bulletjme -APP_ABI := all \ No newline at end of file diff --git a/jme3-desktop/src/main/java/com/jme3/system/ErrorDialog.java b/jme3-desktop/src/main/java/com/jme3/system/ErrorDialog.java new file mode 100644 index 000000000..41f37a9e4 --- /dev/null +++ b/jme3-desktop/src/main/java/com/jme3/system/ErrorDialog.java @@ -0,0 +1,65 @@ +package com.jme3.system; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +/** + * Simple dialog for diplaying error messages, + * + * @author kwando + */ +public class ErrorDialog extends JDialog { + public static String DEFAULT_TITLE = "Error in application"; + public static int PADDING = 8; + + /** + * Create a new Dialog with a title and a message. + * @param message + * @param title + */ + public ErrorDialog(String message, String title) { + setTitle(title); + setSize(new Dimension(600, 400)); + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setLocationRelativeTo(null); + + Container container = getContentPane(); + container.setLayout(new BorderLayout()); + + JTextArea textArea = new JTextArea(); + textArea.setText(message); + textArea.setEditable(false); + textArea.setMargin(new Insets(PADDING, PADDING, PADDING, PADDING)); + add(new JScrollPane(textArea), BorderLayout.CENTER); + + final JDialog dialog = this; + JButton button = new JButton(new AbstractAction("OK"){ + @Override + public void actionPerformed(ActionEvent e) { + dialog.dispose(); + } + }); + add(button, BorderLayout.SOUTH); + } + + public ErrorDialog(String message){ + this(message, DEFAULT_TITLE); + } + + /** + * Show a dialog with the proved message. + * @param message + */ + public static void showDialog(String message){ + ErrorDialog dialog = new ErrorDialog("Opps, this was bad =S"); + dialog.setVisible(true); + } +} diff --git a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java index a377a21c6..5374e2496 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java +++ b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java @@ -52,7 +52,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import javax.imageio.ImageIO; -import javax.swing.JOptionPane; import javax.swing.SwingUtilities; /** @@ -87,10 +86,9 @@ public class JmeDesktopSystem extends JmeSystemDelegate { @Override public void showErrorDialog(String message) { final String msg = message; - final String title = "Error in application"; EventQueue.invokeLater(new Runnable() { public void run() { - JOptionPane.showMessageDialog(null, msg, title, JOptionPane.ERROR_MESSAGE); + ErrorDialog.showDialog(msg); } }); }