so I am importing glTF format into JavaFX. I'm experiencing a weird effect where I can see through the Triangle mesh and see other parts of it / see other meshes through it.
A few Pics
It would be fantastic if someone knows a solution to this. I already tried using CullFace.Front and CullFace.Back.
Here is the code the generate the Triangle Meshes:
package fx;
import gltf.GLTFParseException;
import gltf.mesh.GLTFMesh;
import gltf.mesh.GLTFMeshPrimitive;
import javafx.scene.Group;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import java.util.Arrays;
public class FXglTFMesh extends Group {
public static FXglTFMesh fromGLTFMesh(GLTFMesh mesh) throws GLTFParseException {
FXglTFMesh fxMesh = new FXglTFMesh();
GLTFMeshPrimitive[] primitives = mesh.getPrimitives();
MeshView[] meshViews = new MeshView[primitives.length];
for (int i = 0;i < meshViews.length; i++){
meshViews[i] = fromPrimitive(primitives[i]);
}
FXglTFMaterial material;
fxMesh.getChildren().addAll(meshViews);
return fxMesh;
}
private static MeshView fromPrimitive(GLTFMeshPrimitive primitive) throws GLTFParseException {
TriangleMesh mesh = new TriangleMesh();
MeshView view = new MeshView(mesh);
view.setCullFace(CullFace.BACK);
//Reading texture coords
float[][] texCoords = convertArrayToNested(2,
primitive.getAttribute().getTexCoord_0().readDataAsFloats());
mesh.getTexCoords().addAll(primitive.getAttribute().getTexCoord_0().readDataAsFloats());
if (primitive.getIndices() != null){
//Mesh is indexed
mesh.getPoints().addAll(primitive.getAttribute().getPosition().readDataAsFloats());
int[] indices = primitive.getIndices().readDataAsInts();
for (int i = 0;i < indices.length; i+=3){
mesh.getFaces().addAll(indices[i], 0, indices[i+1], 0, indices[i+2], 0);
}
} else {
//Mesh is not indexed
//Parse the vertices and faces
float[][] vertices = convertArrayToNested(3,
primitive.getAttribute().getPosition().readDataAsFloats());
for (int i = 0;i < vertices.length; i+=3){
mesh.getPoints().addAll(vertices[i]);
mesh.getPoints().addAll(vertices[i+1]);
mesh.getPoints().addAll(vertices[i+2]);
//add 3 points
mesh.getFaces().addAll(i,i, i+1,i+1, i+2,i+2 ); //Add those three points as face
}
}
//Material
FXglTFMaterial material = FXglTFMaterial.fromGLTFMaterial(primitive.getMaterial());
view.setMaterial(material);
return view;
}
private static float[][] convertArrayToNested(int factor, float[] array){
float[][] floats = new float[array.length / factor][];
for (int i = 0;i < floats.length; i++){
int dataOffset = i * factor;
floats[i] = Arrays.copyOfRange(array, dataOffset, dataOffset+factor);
}
return floats;
}
}
Full code: https://github.com/NekoLvds/GLTFImporter (Development branch)
Related
My application generates heatmap images as fast as the CPU can (around 30-60 per second) and I want to display them in a single "live heatmap". In AWT/Swing, I could just paint them into a JPanel which worked like a charm.
Recently, I switched to JavaFX and want to achieve the same here; at first, I tried with a Canvas, which was slow but okay-ish but had a severe memory leak problem, causing the application to crash. Now, I tried the ImageView component - which apparently is way too slow as the image gets quite laggy (using ImageView.setImage on every new iteration). As far as I understand, setImage does not guarantee that the image is actually displayed when the function finishes.
I am getting the impression that I am on the wrong track, using those components in a manner they are not made to. How can I display my 30-60 Images per second?
EDIT: A very simple test application. You will need the JHeatChart library.
Note that on a desktop machine, I get around 70-80 FPS and the visualization is okay and fluid, but on a smaller raspberry pi (my target machine), I get around 30 FPS but an extremly stucking visualization.
package sample;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.tc33.jheatchart.HeatChart;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
public class Main extends Application {
ImageView imageView = new ImageView();
final int scale = 15;
#Override
public void start(Stage primaryStage) {
Thread generator = new Thread(() -> {
int col = 0;
LinkedList<Long> fps = new LinkedList<>();
while (true) {
fps.add(System.currentTimeMillis());
double[][] matrix = new double[48][128];
for (int i = 0; i < 48; i++) {
for (int j = 0; j < 128; j++) {
matrix[i][j] = col == j ? Math.random() : 0;
}
}
col = (col + 1) % 128;
HeatChart heatChart = new HeatChart(matrix, 0, 1);
heatChart.setShowXAxisValues(false);
heatChart.setShowYAxisValues(false);
heatChart.setLowValueColour(java.awt.Color.black);
heatChart.setHighValueColour(java.awt.Color.white);
heatChart.setAxisThickness(0);
heatChart.setChartMargin(0);
heatChart.setCellSize(new Dimension(1, 1));
long currentTime = System.currentTimeMillis();
fps.removeIf(elem -> currentTime - elem > 1000);
System.out.println(fps.size());
imageView.setImage(SwingFXUtils.toFXImage((BufferedImage) scale(heatChart.getChartImage(), scale), null));
}
});
VBox box = new VBox();
box.getChildren().add(imageView);
Scene scene = new Scene(box, 1920, 720);
primaryStage.setScene(scene);
primaryStage.show();
generator.start();
}
public static void main(String[] args) {
launch(args);
}
private static Image scale(Image image, int scale) {
BufferedImage res = new BufferedImage(image.getWidth(null) * scale, image.getHeight(null) * scale,
BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
AffineTransformOp scaleOp =
new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
return scaleOp.filter((BufferedImage) image, res);
}
}
Your code updates the UI from a background thread, which is definitely not allowed. You need to ensure you update from the FX Application Thread. You also want to try to "throttle" the actual UI updates to occur no more than once per JavaFX frame rendering. The easiest way to do this is with an AnimationTimer, whose handle() method is invoked each time a frame is rendered.
Here's a version of your code which does that:
import java.awt.Dimension;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
import org.tc33.jheatchart.HeatChart;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
ImageView imageView = new ImageView();
final int scale = 15;
#Override
public void start(Stage primaryStage) {
AtomicReference<BufferedImage> image = new AtomicReference<>();
Thread generator = new Thread(() -> {
int col = 0;
LinkedList<Long> fps = new LinkedList<>();
while (true) {
fps.add(System.currentTimeMillis());
double[][] matrix = new double[48][128];
for (int i = 0; i < 48; i++) {
for (int j = 0; j < 128; j++) {
matrix[i][j] = col == j ? Math.random() : 0;
}
}
col = (col + 1) % 128;
HeatChart heatChart = new HeatChart(matrix, 0, 1);
heatChart.setShowXAxisValues(false);
heatChart.setShowYAxisValues(false);
heatChart.setLowValueColour(java.awt.Color.black);
heatChart.setHighValueColour(java.awt.Color.white);
heatChart.setAxisThickness(0);
heatChart.setChartMargin(0);
heatChart.setCellSize(new Dimension(1, 1));
long currentTime = System.currentTimeMillis();
fps.removeIf(elem -> currentTime - elem > 1000);
System.out.println(fps.size());
image.set((BufferedImage) scale(heatChart.getChartImage(), scale));
}
});
VBox box = new VBox();
box.getChildren().add(imageView);
Scene scene = new Scene(box, 1920, 720);
primaryStage.setScene(scene);
primaryStage.show();
generator.setDaemon(true);
generator.start();
AnimationTimer animation = new AnimationTimer() {
#Override
public void handle(long now) {
BufferedImage img = image.getAndSet(null);
if (img != null) {
imageView.setImage(SwingFXUtils.toFXImage(img, null));
}
}
};
animation.start();
}
public static void main(String[] args) {
launch(args);
}
private static Image scale(Image image, int scale) {
BufferedImage res = new BufferedImage(image.getWidth(null) * scale, image.getHeight(null) * scale,
BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
return scaleOp.filter((BufferedImage) image, res);
}
}
Using the AtomicReference to wrap the buffered image ensures that it is safely shared between the two threads.
On my machine, this generates about 130 images per second; note that not all are displayed, as only the latest one is shown each time the JavaFX graphics framework displays a frame (which is typically throttled at 60fps).
If you want to ensure you show all images that are generated, i.e. you throttle the image generation by the JavaFX framerate, then you can use a BlockingQueue to store the images:
// AtomicReference<BufferedImage> image = new AtomicReference<>();
// Size of the queue is a trade-off between memory consumption
// and smoothness (essentially works as a buffer size)
BlockingQueue<BufferedImage> image = new ArrayBlockingQueue<>(5);
// ...
// image.set((BufferedImage) scale(heatChart.getChartImage(), scale));
try {
image.put((BufferedImage) scale(heatChart.getChartImage(), scale));
} catch (InterruptedException exc) {
Thread.currentThread.interrupt();
}
and
#Override
public void handle(long now) {
BufferedImage img = image.poll();
if (img != null) {
imageView.setImage(SwingFXUtils.toFXImage(img, null));
}
}
The code is pretty inefficient, as you generate a new matrix, new HeatChart, etc, on every iteration. This causes many objects to be created on the heap and quickly discarded, which can cause the GC to be run too often, particularly on a small-memory machine. That said, I ran this with the maximum heap size set at 64MB, (-Xmx64m), and it still performed fine. You may be able to optimize the code, but using the AnimationTimer as shown above, generating images more quickly will not cause any additional stress on the JavaFX framework. I would recommend investigating using the mutability of HeatChart (i.e. setZValues()) to avoid creating too many objects, and/or using PixelBuffer to directly write data to the image view (this would need to be done on the FX Application Thread).
Here's a different example, which (almost) completely minimizes object creation, using one off-screen int[] array to compute data, and one on-screen int[] array to display it. There's a little low-level threading details to ensure the on-screen array is only seen in a consistent state. The on-screen array is used to underly a PixelBuffer, which in turn is used for a WritableImage.
This class generates the image data:
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
public class ImageGenerator {
private final int width;
private final int height;
// Keep two copies of the data: one which is not exposed
// that we modify on the fly during computation;
// another which we expose publicly.
// The publicly exposed one can be viewed only in a complete
// state if operations on it are synchronized on this object.
private final int[] privateData ;
private final int[] publicData ;
private final long[] frameTimes ;
private int currentFrameIndex ;
private final AtomicLong averageGenerationTime ;
private final ReentrantLock lock ;
private static final double TWO_PI = 2 * Math.PI;
private static final double PI_BY_TWELVE = Math.PI / 12; // 15 degrees
public ImageGenerator(int width, int height) {
super();
this.width = width;
this.height = height;
privateData = new int[width * height];
publicData = new int[width * height];
lock = new ReentrantLock();
this.frameTimes = new long[100];
this.averageGenerationTime = new AtomicLong();
}
public void generateImage(double angle) {
// compute in private data copy:
int minDim = Math.min(width, height);
int minR2 = minDim * minDim / 4;
for (int x = 0; x < width; x++) {
int xOff = x - width / 2;
int xOff2 = xOff * xOff;
for (int y = 0; y < height; y++) {
int index = x + y * width;
int yOff = y - height / 2;
int yOff2 = yOff * yOff;
int r2 = xOff2 + yOff2;
if (r2 > minR2) {
privateData[index] = 0xffffffff; // white
} else {
double theta = Math.atan2(yOff, xOff);
double delta = Math.abs(theta - angle);
if (delta > TWO_PI - PI_BY_TWELVE) {
delta = TWO_PI - delta;
}
if (delta < PI_BY_TWELVE) {
int green = (int) (255 * (1 - delta / PI_BY_TWELVE));
privateData[index] = (0xff << 24) | (green << 8); // green, fading away from center
} else {
privateData[index] = 0xff << 24; // black
}
}
}
}
// copy computed data to public data copy:
lock.lock();
try {
System.arraycopy(privateData, 0, publicData, 0, privateData.length);
} finally {
lock.unlock();
}
frameTimes[currentFrameIndex] = System.nanoTime() ;
int nextIndex = (currentFrameIndex + 1) % frameTimes.length ;
if (frameTimes[nextIndex] > 0) {
averageGenerationTime.set((frameTimes[currentFrameIndex] - frameTimes[nextIndex]) / frameTimes.length);
}
currentFrameIndex = nextIndex ;
}
public void consumeData(Consumer<int[]> consumer) {
lock.lock();
try {
consumer.accept(publicData);
} finally {
lock.unlock();
}
}
public long getAverageGenerationTime() {
return averageGenerationTime.get() ;
}
}
And here's the UI:
import java.nio.IntBuffer;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class AnimationApp extends Application {
private final int size = 400 ;
private IntBuffer buffer ;
#Override
public void start(Stage primaryStage) throws Exception {
// background image data generation:
ImageGenerator generator = new ImageGenerator(size, size);
// Generate new image data as fast as possible:
Thread thread = new Thread(() -> {
while( true ) {
long now = System.currentTimeMillis() ;
double angle = 2 * Math.PI * (now % 10000) / 10000 - Math.PI;
generator.generateImage(angle);
}
});
thread.setDaemon(true);
thread.start();
generator.consumeData(data -> buffer = IntBuffer.wrap(data));
PixelFormat<IntBuffer> format = PixelFormat.getIntArgbPreInstance() ;
PixelBuffer<IntBuffer> pixelBuffer = new PixelBuffer<>(size, size, buffer, format);
WritableImage image = new WritableImage(pixelBuffer);
BorderPane root = new BorderPane(new ImageView(image));
Label fps = new Label("FPS: ");
root.setTop(fps);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Give me a ping, Vasili. ");
primaryStage.show();
AnimationTimer animation = new AnimationTimer() {
#Override
public void handle(long now) {
// Update image, ensuring we only see the underlying
// data in a consistent state:
generator.consumeData(data -> {
pixelBuffer.updateBuffer(pb -> null);
});
long aveGenTime = generator.getAverageGenerationTime() ;
if (aveGenTime > 0) {
double aveFPS = 1.0 / (aveGenTime / 1_000_000_000.0);
fps.setText(String.format("FPS: %.2f", aveFPS));
}
}
};
animation.start();
}
public static void main(String[] args) {
Application.launch(args);
}
}
For a version that doesn't rely on the JavaFX 13 PixelBuffer, you can just modify this class to use a PixelWriter (AIUI this won't be quite as efficient, but works just as smoothly in this example):
// generator.consumeData(data -> buffer = IntBuffer.wrap(data));
PixelFormat<IntBuffer> format = PixelFormat.getIntArgbPreInstance() ;
// PixelBuffer<IntBuffer> pixelBuffer = new PixelBuffer<>(size, size, buffer, format);
// WritableImage image = new WritableImage(pixelBuffer);
WritableImage image = new WritableImage(size, size);
PixelWriter pixelWriter = image.getPixelWriter() ;
and
AnimationTimer animation = new AnimationTimer() {
#Override
public void handle(long now) {
// Update image, ensuring we only see the underlying
// data in a consistent state:
generator.consumeData(data -> {
// pixelBuffer.updateBuffer(pb -> null);
pixelWriter.setPixels(0, 0, size, size, format, data, 0, size);
});
long aveGenTime = generator.getAverageGenerationTime() ;
if (aveGenTime > 0) {
double aveFPS = 1.0 / (aveGenTime / 1_000_000_000.0);
fps.setText(String.format("FPS: %.2f", aveFPS));
}
}
};
I am working on an application that matches a photo that taken from the mobile phone camera and matches with a series of images that saved in a database. Following java code works fine to match 1 image with 1 template. Please help me to develop this program to match with several templates and return the best match. I am using android studio to develop the application.
Thank you
import org.bytedeco.javacv.*;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.indexer.FloatIndexer;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
import static org.bytedeco.javacpp.opencv_highgui.*;
import static org.bytedeco.javacpp.opencv_imgcodecs.*;
//import static org.bytedeco.javacpp.opencv_calib3d.*;
//import static org.bytedeco.javacpp.opencv_objdetect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class TemplateMatching {
public static void main(String[] args) throws Exception {
footPrint(args);
} //main method
public static void footPrint(String[] args){
//read in image default colors
Mat sourceColor=imread("F:\\image_processing\\Foot_Print_Temp_Match\\img.jpg", CV_LOAD_IMAGE_COLOR);//this image should captured by the camera
Mat sourceGrey = new Mat(sourceColor.size(), CV_8UC1);
cvtColor(sourceColor, sourceGrey, COLOR_BGR2GRAY);
//load in template in grey
Mat template1 = imread("F:\\image_processing\\templ.jpg",CV_LOAD_IMAGE_GRAYSCALE);//Template should load from the database
//Size for the result image
Size size = new Size(sourceGrey.cols()-template1.cols()+1, sourceGrey.rows()-template1.rows()+1);
Mat result = new Mat(size, CV_32FC1);//32 bit floating point signed depth in one channel
matchTemplate(sourceGrey, template1, result, TM_CCORR_NORMED) ;//Template matching function
DoublePointer minVal= new DoublePointer();
DoublePointer maxVal= new DoublePointer();
Point min = new Point();
Point max = new Point();
minMaxLoc(result, minVal, maxVal, min, max, null);
rectangle(sourceColor,new Rect(max.x(),max.y(),template1.cols(),template1.rows()), randColor(), 2, 0, 0);
imshow("Original marked", sourceColor);
imshow("Template", template1);
waitKey(0);
destroyAllWindows();
}
public static Scalar randColor(){
int b,g,r;
b= ThreadLocalRandom.current().nextInt(0, 255 + 1);
g= ThreadLocalRandom.current().nextInt(0, 255 + 1);
r= ThreadLocalRandom.current().nextInt(0, 255 + 1);
return new Scalar (b,g,r,0);
}
public static List<Point> getPointsFromMatAboveThreshold(Mat m, float t){
List<Point> matches = new ArrayList<Point>();
FloatIndexer indexer = m.createIndexer();
for (int y = 0; y < m.rows(); y++) {
for (int x = 0; x < m.cols(); x++) {
if (indexer.get(y,x)>t) {
System.out.println("(" + x + "," + y +") = "+ indexer.get(y,x));
matches.add(new Point(x, y));
}
}
}
return matches;
}
i have an application that basically does this.
Look at MatchingService and OpenCVUtils.
basically it should match a template, record the score and match the next template. keep list of scores associated to the template that made that score. then just get the max score.
public static float matchScore(Mat src,Mat tmp){
Size size = new Size(src.cols()-tmp.cols()+1, src.rows()-tmp.rows()+1);
Mat result = new Mat(size, CV_32FC1);
matchTemplate(src, tmp, result, TM_CCORR_NORMED);
DoublePointer minVal= new DoublePointer();
DoublePointer maxVal= new DoublePointer();
Point min = new Point();
Point max = new Point();
FloatIndexer fi = result.createIndexer();
minMaxLoc(result, minVal, maxVal, min, max, null);
return fi.get(max.y(),max.x());
}
Is it possible to convert .blend or .obj file to a float array for later use for rendering in the OpenGL? For example,
Vertices:
float[] vertices = {
-1, -1, 0,
// some vertices
};
Texture coordinates:
float[] texCoords = {
0, 0,
// some texture coordinates
};
Vertices indices:
float[] indices = {
0, 1, 2,
// some vertices indices
};
It would also be nice to get the type of figure that we draw:
glDrawElements(GL.GL_TRIANGLES /* type */, intbuff.capacity(), GL.GL_UNSIGNED_INT, intbuff);
Is it possible? And how can this be done?
Why do you want to open .blend files ?
Do you need an animation ? If not, you don't have to import .blend files.
For most OBJ files, it's overwhelming easy to create your own.
OBJ File Format Specifications
But this is not necessary.
Here is a complete one for OBJs : https://github.com/javagl/Obj
BTW, here's my Mesh class, only capable of loading single-group OBJs :
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
public class Mesh {
public float[] vertices;
public float[] texcoords;
public float[] normals;
public int[] indices;
public Mesh(float[] vertices, float[] texcoords, float[] normals, int[] indices) {
this.vertices = vertices;
this.texcoords = texcoords;
this.normals = normals;
this.indices = indices;
}
public Mesh(String filename) throws FileNotFoundException, IOException {
File f=new File(filename);
FileInputStream fis=new FileInputStream(f);
int c=fis.read();
String s="";
while (c != -1) {
s+=(char)c;
c=fis.read();
}
String[] lines=s.split("\n");
ArrayList<Float> vertices2=new ArrayList<Float>();
ArrayList<Float> normals2=new ArrayList<Float>();
ArrayList<Float> texcoords2=new ArrayList<Float>();
ArrayList<Integer> normalindices=new ArrayList<Integer>();
ArrayList<Integer> uvindices=new ArrayList<Integer>();
ArrayList<Integer> indices2=new ArrayList<Integer>();
for (String line:lines) {
String[] parts=line.split(" ");
if (parts[0].equals("v")) {
vertices2.add(Float.parseFloat(parts[1]));
vertices2.add(Float.parseFloat(parts[2]));
vertices2.add(Float.parseFloat(parts[3]));
}
else if (parts[0].equals("vt")) {
texcoords2.add(Float.parseFloat(parts[1]));
texcoords2.add(Float.parseFloat(parts[2]));
}
else if (parts[0].equals("vn")) {
normals2.add(Float.parseFloat(parts[1]));
normals2.add(Float.parseFloat(parts[2]));
normals2.add(Float.parseFloat(parts[3]));
}
else if (parts[0].equals("f")) {
for (int i=1; i < parts.length; i++) {
String part=parts[i];
String[] byslash=part.split("/");
int indi=Integer.parseInt(byslash[0]);
indices2.add(indi-1);
if (byslash.length > 1) {
indi=Integer.parseInt(byslash[1]);
uvindices.add(indi-1);
indi=Integer.parseInt(byslash[2]);
normalindices.add(indi-1);
}
else {
uvindices.add(indi-1);
normalindices.add(indi-1);
}
}
}
}
indices=new int[indices2.size()];
vertices=new float[indices2.size()*3];
texcoords=new float[indices2.size()*2];
normals=new float[indices2.size()*3];
for (int i=0; i < indices.length; i++) {
indices[i]=i;
int vuvi=indices2.get(i)*3;
int fuvi=uvindices.get(i)*2;
int nuvi=normalindices.get(i)*3;
vertices[i*3]=vertices2.get(vuvi);
vertices[i*3+1]=vertices2.get(vuvi+1);
vertices[i*3+2]=vertices2.get(vuvi+2);
texcoords[i*2]=texcoords2.get(fuvi);
texcoords[i*2+1]=texcoords2.get(fuvi+1);
normals[i*3]=normals2.get(nuvi);
normals[i*3+1]=normals2.get(nuvi+1);
normals[i*3+2]=normals2.get(nuvi+2);
}
}
}
It does not support Materials, which makes it library(JOGL/LWJGL) independent.
Hope it helps you.
In the following code am trying to read a grayscale image, store the pixel values in a 2D array and rewrite the image with a different name.
The code is
package dct;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.io.File;
import javax.imageio.ImageIO;
public class writeGrayScale
{
public static void main(String[] args)
{
File file = new File("lightning.jpg");
BufferedImage img = null;
try
{
img = ImageIO.read(file);
}
catch(Exception e)
{
e.printStackTrace();
}
int width = img.getWidth();
int height = img.getHeight();
int[][] arr = new int[width][height];
Raster raster = img.getData();
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
arr[i][j] = raster.getSample(i, j, 0);
}
}
BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_BYTE_GRAY);
byte[] raster1 = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(arr,0,raster1,0,raster1.length);
//
BufferedImage image1 = image;
try
{
File ouptut = new File("grayscale.jpg");
ImageIO.write(image1, "jpg", ouptut);
}
catch(Exception e)
{
e.printStackTrace();
}
}// main
}// class
For this code , the error is
Exception in thread "main" java.lang.ArrayStoreException
at java.lang.System.arraycopy(Native Method)
at dct.writeGrayScale.main(writeGrayScale.java:49)
Java Result: 1
How to remove this error to write the grayscale image?
I found this: "ArrayStoreException -- if an element in the src array could not be stored into the dest array because of a type mismatch." http://www.tutorialspoint.com/java/lang/system_arraycopy.htm
Two thoughts:
You're copying an int-array into a byte-array.
That's not part of the exceptions, but are the dimensions right? arr is a two-dimensional array, raster1 is a one-dimensional array.
And you can't just change the byte-array in a two-dimensional one ignoring the output of the method you're calling.
Change int[][] arr to byte[] arr like this.
byte[] arr = new byte[width * height * 4];
for (int i = 0, z = 0; i < width; i++) {
for (int j = 0; j < height; j++, z += 4) {
int v = getSample(i, j, 0);
for (int k = 3; k >= 0; --k) {
arr[z + k] = (byte)(v & 0xff);
v >>= 8;
}
}
}
I'm trying to get an array of objects to render into a gamescreen. I've looked around and found some similar questions asked by others, but I can't seem to apply the answers they've gotten to my program.
The problem seems to occur in the nested for-loops. In trying to solve this issue, I've gotten Java NullPointerExceptions at the first line within those for loops.
package com.frfanizz.agility;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class GameScreen implements Screen {
AgilityGame game;
OrthographicCamera camera;
SpriteBatch batch;
Hero hero;
Spark[] sparkArray;
int totalSparks;
public GameScreen(AgilityGame game) {
this.game = game;
camera = new OrthographicCamera();
camera.setToOrtho(true, 1920, 1080);
batch = new SpriteBatch();
hero = new Hero(60,540);
//Variables to set spark array
int screenX = 1400;
int screenY = 1200;
int numOfRow = 11;
int numOfCol = 8;
float spacingRow = screenY/(numOfRow + 1);
float spacingCol = screenX/(numOfCol + 1);
int totalSparks = numOfRow*numOfCol;
//Spark array
Spark[] sparkArray = new Spark[totalSparks];
//setting bounds for sparks
int index = 0;
for (int i=0;i<numOfCol;i++) {
for (int j=0; j<numOfRow;j++) {
//sparkArray[index] = new Spark();
sparkArray[index].bounds.x = (float) (60 + spacingCol + ((i)*spacingCol));
sparkArray[index].bounds.y = (float) (spacingRow + ((j-0.5)*spacingRow));
index++;
}
}
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1F, 1F, 1F, 1F);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
camera.update();
generalUpdate();
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Rendering code
batch.draw(Assets.spriteBackground, 0, 0);
batch.draw(hero.image,hero.bounds.x,hero.bounds.y);
for (int i=0; i<totalSparks; i++) {
batch.draw(sparkArray[i].image,sparkArray[i].bounds.x,sparkArray[i].bounds.y);
}
batch.end();
}
//Other gamescreen methods
}
The classes Hero and Spark are as follows:
package com.frfanizz.agility;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Rectangle;
public class Hero {
public Sprite image;
public Rectangle bounds;
public Hero(int spawnX, int spawnY) {
image = Assets.spriteHero;
image.flip(false, true);
bounds = new Rectangle(spawnX - 16, spawnY - 16, 32, 32);
}
}
and:
package com.frfanizz.agility;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Rectangle;
public class Spark {
public Sprite image;
public Rectangle bounds;
public Spark () {
image = Assets.spriteSpark;
image.flip(false, true);
bounds = new Rectangle();
bounds.height = 32;
bounds.width = 32;
}
}
Within the for loops, I've printed the values of sparkArray, so I think I have an issue with values rather than references from the other questions I've read the answers to.
(Here are the questions I (unsuccessfully) referenced:
Java. Array of objects ,
Java NullPointerException with objects in array)
Thanks in advance!
Uncomment the line
//sparkArray[index] = new Spark();
Your array of Spark objects contains null values until you put instances of Spark into it.
Consider using a two-dimensional array of Spark, if it's appropriate to what you're doing.
int numOfRow = 11;
int numOfCol = 8;
Spark[][] sparkArray = new Spark[numOfRow][numOfCol];
//setting bounds for sparks
for (int row=0; row<numOfRow; row++) {
for (int col=0; col<numOfCol; col++) {
sparkArray[row][col] = new Spark();
sparkArray[row][col].bounds.x = 1.23; // example of usage
}
}