Problem with adding multiple triangle meshes to a scene in javafx - javafx
I hope someone can help me with this problem.
I am using javafx and triangle mesh to construct a sphere-like object out of triangles (like a football). The different tiles of the shape are distinguished by color but I want to add lines between the tiles. Like in this football:
The provided 2D lines of javafx bring terrible performance in the 3D space.
Therefore I found the FXyzLib that provides a PolyLine3D. This is actually just another triangle mesh that creates a line in a 3D space.
With this I can create 3D lines. But if I want to add them to my original triangle mesh everything else turns black. It is also the other way around. I experimented with the provided example of the Libary found here. It creats a fancy 3D line but when I tried to add a simple red colored sphere to the scene the sphere was just black like this:
.
I am no expert in this and new to javafx and couldn't find the problem in the code of the PolyLine3D. It shouldn't be a problem to add mutliple triangle mesh to a scene. Are there some light or camera effects I am not aware of?
EDIT: I solved the problem. The ambient light used in the Polyline3D caused the problem. If you add light for your other objects it should be fine.
While there is nothing wrong about adding multiple triangle meshes to a scene, there is a more straightforward approach when you want to create a mesh like one from the football picture, so you can have the effect of highlighting some edges but not all the triangles from the mesh.
It can be done with a PolygonMesh that takes any valid close polygon as face.
This implementation already exists, in the 3DViewer project, which is open source and can be found here. There is a PolygonMeshView control that can render a PolygonMesh.
Note that if you use those two classes only in your project, you'll have to skip the subdivision mesh for now.
This answer already uses a quadrilateral mesh for a rendering a Box without the diagonal edges of a triangle mesh.
Under the hood, the Polygon mesh uses a triangle mesh, and internally converts the polygons you provide to triangles.
Truncated Icosahedron Mesh
So we can do something similar to generate the mesh of a truncated icosahedron, which is the name of the geometric figure that we can use to generate a simplified football model.
It has 12 regular pentagonal faces, 20 regular hexagonal faces and 60 vertices.
We need the 3D coordinates of those vertices, the 2D coordinates of the textures, and the indices of vertices and textures for each of the 32 faces.
I've used the free online sandbox WolframCloud resource to retrieve those values.
For instance, you can run:
Flatten[PolyhedronData["TruncatedIcosahedron","VertexCoordinates"]//N]
to get the list of vertex coordinates:
Out[1]= {-0.16246,-2.11803,1.27598,-0.16246,2.11803,...}
and, you can get the faces:
PolyhedronData["TruncatedIcosahedron","FaceIndices"]
Out[2]= {{53,11,24,23,9},{51,39,40,52,30},...}
Finally, you need the textures coordinates and indices, and that can be retrieved through the Net of the icosahedron:
PolyhedronData["TruncatedIcosahedron","Net"]
PolyhedronData["TruncatedIcosahedron","NetCoordinates"]
In this case, you get the 2D coordinates of the 32 faces. Given that we want to have the same texture for all the pentagons and the same for all hexagons, I've dome some manipulation of those coordinates to come up with this texture image:
with only 9 vertices, and their coordinates (in the JavaFX coordinate system).
This method contains the required information to create the mesh:
private PolygonMesh getTruncatedIcosahedron() {
float[] points = new float[]{
-0.16246f,-2.11803f,1.27598f, -0.16246f,2.11803f,1.27598f,
0.16246f,-2.11803f,-1.27598f, 0.16246f,2.11803f,-1.27598f,
-0.262866f,-0.809017f,-2.32744f, -0.262866f,-2.42705f,-0.425325f,
-0.262866f,0.809017f,-2.32744f, -0.262866f,2.42705f,-0.425325f,
0.262866f,-0.809017f,2.32744f, 0.262866f,-2.42705f,0.425325f,
0.262866f,0.809017f,2.32744f, 0.262866f,2.42705f,0.425325f,
0.688191f,-0.5f,-2.32744f, 0.688191f,0.5f,-2.32744f,
1.21392f,-2.11803f,0.425325f, 1.21392f,2.11803f,0.425325f,
-2.06457f,-0.5f,1.27598f, -2.06457f,0.5f,1.27598f,
-1.37638f,-1.f,1.80171f, -1.37638f,1.f,1.80171f,
-1.37638f,-1.61803f,-1.27598f, -1.37638f,1.61803f,-1.27598f,
-0.688191f,-0.5f,2.32744f, -0.688191f,0.5f,2.32744f,
1.37638f,-1.f,-1.80171f, 1.37638f,1.f,-1.80171f,
1.37638f,-1.61803f,1.27598f, 1.37638f,1.61803f,1.27598f,
-1.7013f,0.f,-1.80171f, 1.7013f,0.f,1.80171f,
-1.21392f,-2.11803f,-0.425325f, -1.21392f,2.11803f,-0.425325f,
-1.96417f,-0.809017f,-1.27598f, -1.96417f,0.809017f,-1.27598f,
2.06457f,-0.5f,-1.27598f, 2.06457f,0.5f,-1.27598f,
2.22703f,-1.f,-0.425325f, 2.22703f,1.f,-0.425325f,
2.38949f,-0.5f,0.425325f, 2.38949f,0.5f,0.425325f,
-1.11352f,-1.80902f,1.27598f, -1.11352f,1.80902f,1.27598f,
1.11352f,-1.80902f,-1.27598f, 1.11352f,1.80902f,-1.27598f,
-2.38949f,-0.5f,-0.425325f, -2.38949f,0.5f,-0.425325f,
-1.63925f,-1.80902f,0.425325f, -1.63925f,1.80902f,0.425325f,
1.63925f,-1.80902f,-0.425325f, 1.63925f,1.80902f,-0.425325f,
1.96417f,-0.809017f,1.27598f, 1.96417f,0.809017f,1.27598f,
0.850651f,0.f,2.32744f, -2.22703f,-1.f,0.425325f,
-2.22703f,1.f,0.425325f, -0.850651f,0.f,-2.32744f,
-0.525731f,-1.61803f,-1.80171f, -0.525731f,1.61803f,-1.80171f,
0.525731f,-1.61803f,1.80171f, 0.525731f,1.61803f,1.80171f};
float[] texCoords = new float[]{0.904508f,0.820298f, 0.75f,0.529535f, 0.25f,0.529535f, 0.0954915f,0.820298f, 0.5f,1f,
1f,0.264767f, 0.75f,0f, 0.25f,0f, 0f,0.264767f};
int faces[][] = new int[][]{{52,0,10,1,23,2,22,3,8,4},
{50,0,38,1,39,2,51,3,29,4},
{59,0,27,1,15,2,11,3,1,4},
{19,0,41,1,47,2,54,3,17,4},
{18,0,16,1,53,2,46,3,40,4},
{0,0,9,1,14,2,26,3,58,4},
{35,0,25,1,43,2,49,3,37,4},
{3,0,57,1,21,2,31,3,7,4},
{33,0,28,1,32,2,44,3,45,4},
{20,0,56,1,2,2,5,3,30,4},
{36,0,48,1,42,2,24,3,34,4},
{12,0,4,1,55,2,6,3,13,4},
{8,1,58,5,26,6,50,7,29,8,52,2},
{52,1,29,5,51,6,27,7,59,8,10,2},
{10,1,59,5,1,6,41,7,19,8,23,2},
{23,1,19,5,17,6,16,7,18,8,22,2},
{22,1,18,5,40,6,0,7,58,8,8,2},
{12,1,24,5,42,6,2,7,56,8,4,2},
{4,1,56,5,20,6,32,7,28,8,55,2},
{55,1,28,5,33,6,21,7,57,8,6,2},
{6,1,57,5,3,6,43,7,25,8,13,2},
{13,1,25,5,35,6,34,7,24,8,12,2},
{39,1,37,5,49,6,15,7,27,8,51,2},
{15,1,49,5,43,6,3,7,7,8,11,2},
{11,1,7,5,31,6,47,7,41,8,1,2},
{47,1,31,5,21,6,33,7,45,8,54,2},
{54,1,45,5,44,6,53,7,16,8,17,2},
{53,1,44,5,32,6,20,7,30,8,46,2},
{46,1,30,5,5,6,9,7,0,8,40,2},
{9,1,5,5,2,6,42,7,48,8,14,2},
{14,1,48,5,36,6,38,7,50,8,26,2},
{38,1,36,5,34,6,35,7,37,8,39,2}};
int[] smooth = new int[] {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32
};
PolygonMesh mesh = new PolygonMesh(points, texCoords, faces);
mesh.getFaceSmoothingGroups().addAll(smooth);
return mesh;
}
Now you can easily add it to an scene:
private double mouseOldX, mouseOldY = 0;
private final Rotate rotateX = new Rotate(0, Rotate.X_AXIS);
private final Rotate rotateY = new Rotate(0, Rotate.Y_AXIS);
#Override
public void start(Stage primaryStage) {
PolygonMeshView meshView = new PolygonMeshView(getTruncatedIcosahedron());
final PhongMaterial phongMaterial = new PhongMaterial();
meshView.setDrawMode(DrawMode.LINE);
meshView.setMaterial(phongMaterial);
final Group group = new Group(meshView);
group.getTransforms().add(new Scale(50, 50, 50));
Scene scene = new Scene(group, 500, 300, true, SceneAntialiasing.BALANCED);
scene.setOnMousePressed(event -> {
mouseOldX = event.getSceneX();
mouseOldY = event.getSceneY();
});
scene.setOnMouseDragged(event -> {
rotateX.setAngle(rotateX.getAngle() - (event.getSceneY() - mouseOldY));
rotateY.setAngle(rotateY.getAngle() + (event.getSceneX() - mouseOldX));
mouseOldX = event.getSceneX();
mouseOldY = event.getSceneY();
});
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setNearClip(0.1);
camera.setFarClip(1000.0);
camera.getTransforms().addAll(rotateX, rotateY, new Translate(-250, -150, 0));
scene.setCamera(camera);
primaryStage.setTitle("JavaFX 3D - Truncated Icosahedron");
primaryStage.setScene(scene);
primaryStage.show();
}
Will give you the wireframe:
And if you add the image of the texture, you will get your football:
phongMaterial.setDiffuseMap(new Image(getClass().getResourceAsStream("net3.png")));
meshView.setDrawMode(DrawMode.FILL);
Now it's up to you to manipulate to your convenience this information, and modify this mesh into the one you are looking for.
Related
Drawing shape with open lines in Java(f)x Canvas
Is it possible to draw a shape with open ends? E.g.: Let's say I want to draw a tree, which roots are open. Is there a elegant way to let the ends open, without overdrawing the already drawed lines? I could overdraw it with shapes, which are exactly as big as my openings and have the color of the background, but I don't think that is the elegant way and I don't find any option to let them open. Perhaps I'm just blind and I could make strokePolygon(...) in which not all points are linked, but I think that's neither the way to go. Let's have a simple shape: [ceate Scene and Stage, etc] Canvas sc = new Canvas(x, y); GraphicsContext gcCs = cs.getGraphicsContext2D(); gcCs.setStroke(Color.BLACK); double counter = 0.0; [calculate points, instantiate arrays, etc] for (int i = 0; i < arrayX.length; i++) { arrayX = shapeMidX + Math.cos(Math.toRadiants(counter * Math.PI)) * shapeSizeX / 2); arrayY = shapeMidY + Math.sin(Math.toRadiants(counter * Math.PI)) * shapeSizeY / 2); } gcCs.strokePolygon(arrayX, arrayY, arrayX.length); [making other things] stackPane.getChildren().add(sc); I know that I could use .strokeOval(), but I wanted to have a example that is near of my own code. I like to draw my shapes from the center. P.S.: I wrote the for() { } out of my head, it could be that there's something wrong. I've got no Internet at home at the moment, so my answers could be taking a lot of time. Thank you in advance.
You could draw individual lines using strokeLine and store the current position in variables allowing you to draw any combination of lines. You could also construct a path instead which allows you to use moveTo instead of lineTo to "skip" a segment. This way you don't need to keep track of the previous position for continuous lines. The following example draws every other line of a square this way: #Override public void start(Stage primaryStage) { Canvas canvas = new Canvas(400, 400); GraphicsContext gc = canvas.getGraphicsContext2D(); gc.moveTo(100, 100); gc.lineTo(100, 300); gc.moveTo(300, 300); gc.lineTo(300, 100); // gc.moveTo(100, 100); gc.stroke(); Scene scene = new Scene(new StackPane(canvas)); primaryStage.setScene(scene); primaryStage.show(); }
JavaFX Scaling differently sized nodes to the same size
I am drawing differently sized maps on a pane. Some look decent, others are just presented as a small shape and you have to zoom in to get it to the right size. I want those maps to appear roughly the same size each time I initialize (so I don't have to manually scale each map). I've got Point2D points for the min and max values of x and y of the pane they're drawn on and same goes for the map (which is a Group of polygons). How do I set the distance between, say, the minPoint of Pane and the minPoint of Group? Or am I approaching this the wrong way? edit: public void setDistance(Group map, Point2D paneSize, Point2D mapSize){ //um diese distance verschieben, if distance > 10px (scale) double d = paneSize.distance(mapSize); double scale = ?? map.setScaleX(scale); map.setScaleY(scale); } That's how I planned on doing it, not sure about that one line though.
To scale the node to the size of the parent node, the difference in the size is not important. What is important is the quotient of the sizes, more precisely the minimum of the quotients of the heights and the widths (assuming you want to fill the parent in one direction completely). Example: #Override public void start(Stage primaryStage) { Text text = new Text("Hello World!"); Pane root = new Pane(); root.getChildren().add(text); InvalidationListener listener = o -> { Bounds rootBounds = root.getLayoutBounds(); Bounds elementBounds = text.getLayoutBounds(); double scale = Math.min(rootBounds.getWidth() / elementBounds.getWidth(), rootBounds.getHeight() / elementBounds.getHeight()); text.setScaleX(scale); text.setScaleY(scale); // center the element elementBounds = text.getBoundsInParent(); double cx = (elementBounds.getMinX() + elementBounds.getMaxX()) / 2; double cy = (elementBounds.getMinY() + elementBounds.getMaxY()) / 2; text.setTranslateX(rootBounds.getWidth() / 2 - cx + text.getTranslateX()); text.setTranslateY(rootBounds.getHeight() / 2 - cy + text.getTranslateY()); }; root.layoutBoundsProperty().addListener(listener); text.layoutBoundsProperty().addListener(listener); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); }
Unity - Find a point for a gameobject to look at the mouse while camera is at any angle
I have a 3D game where I want an arrow to point in the direction base on the mouses angle of that object in a 2D view. Now from the camera looking down at the board from a 90 degree x-angle standpoint it works fine. The below image is when I am in a 90 Degree x-angle Camera angle facing down on my game and I have the arrow face where my cursor is: But now when we take a step back and have the camera at a 45 degree x-angle the direction the arrow is facing is a bit off. The below image is when I have the cursor face my mouse cursor when my camera is on a 45 degree x-angle : Now lets look at the above image but when the Camera is shifted back to 90 Degrees x-angle: My current code is: // Get the vectors of the 2 points, the pivot point which is the ball start and the position of the mouse. Vector2 objectPoint = Camera.main.WorldToScreenPoint(_arrowTransform.position); Vector2 mousePoint = (Vector2)Input.mousePosition; float angle = Mathf.Atan2( mousePoint.y - objectPoint.y, mousePoint.x - objectPoint.x ) * 180 / Mathf.PI; _arrowTransform.rotation = Quaternion.AngleAxis(-angle, Vector2.up) * Quaternion.Euler(90f, 0f, 0f); What would I have to add in my Mathf.Atan2() to compensate for the camera rotation on the x and/or y to make sure when the user wants to move the camera how they please it will make sure to provide an accurate direction? EDIT : The solution was in MotoSV's answer with using Plane. This allowed me to get the exact point no matter what my camera angles were based on my mouse position. Code that worked for me is below : void Update() { Plane groundPlane = new Plane(Vector3.up, new Vector3(_arrowTransform.position.x, _arrowTransform.position.y, _arrowTransform.position.z)); Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition); float distance; if (groundPlane.Raycast(ray, out distance)) { Vector3 point = ray.GetPoint(distance); _arrowTransform.LookAt(point); } }
Although this does not answer your question directly with regards to the Mathf.Atan2 method it is a alternative approach that may be useful. This would be placed onto the game object that represents the arrow: public class MouseController : MonoBehaviour { private Camera _camera; private void Start() { _camera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>(); } private void Update() { Plane groundPlane = new Plane(Vector3.up, this.transform.position); Ray ray = _camera.ScreenPointToRay(Input.mousePosition); float distance; Vector3 axis = Vector3.zero; if(groundPlane.Raycast(ray, out distance)) { Vector3 point = ray.GetPoint(distance); axis = (point - this.transform.position).normalized; axis = new Vector3(axis.x, 0f, axis.z); } this.transform.rotation = Quaternion.LookRotation(axis); } } The basic idea is to: Create a Plane instance centred at the game object's position Convert the mouse screen position into a Ray that heads into the world relative to the camer'a current position and rotation Then cast that ray onto the Plane created in step #1 If the ray intersects the plane, then you can use the GetPoint method to find out where on the plane the ray hit Then create a direction vector from the centre of the plane to the intersect point and create a LookRotation based on the vector You can find out more information about the Plane class on the Unity - Plane documentation page.
Show image using babylonjs
I am new to Babylonjs and i want to display/show image using BabylonJs and also i want to move the image using keyboard(like left arrow key, right arrow key, up arrow key, and down arrow key) with collision detection and i also want to disable all mouse events . I have wrote below code for showing image but i think that is not right way.. var playerMaterial = new BABYLON.StandardMaterial("ground", scene); playerMaterial.diffuseTexture = new BABYLON.Texture("/images/mail.png", scene); playerMaterial.specularColor = new BABYLON.Color3(0, 0, 0); player.material = playerMaterial; player.position.y = 1; player.position.x = -84; player.size = 20; Can someone help me how to do (if you can share the source code that may help even better)? Thanks Raviranjan
Your code looks basically right, assuming you also have a camera and light source. Here is a playground entry. And, for posterity, here is that code: var scene = new BABYLON.Scene(engine); //Create a light var light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(-60, 60, 80), scene); //Create an Arc Rotate Camera - aimed negative z this time var camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, 1.0, 210, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); //Creation of a repeated textured material var materialPlane = new BABYLON.StandardMaterial("texturePlane", scene); materialPlane.diffuseTexture = new BABYLON.Texture("textures/grass.jpg", scene); materialPlane.specularColor = new BABYLON.Color3(0, 0, 0); materialPlane.backFaceCulling = false;//Allways show the front and the back of an element //Creation of a plane var plane = BABYLON.Mesh.CreatePlane("plane", 120, scene); plane.rotation.x = Math.PI / 2; plane.material = materialPlane; I started with one of their demos, then hacked away most of the stuff to get the minimal example. I left in the backFaceCulling = false (as otherwise the image is only visible from one direction), and added in your specularColor setting. An alternative approach is to replace the diffuseTexture with emissiveTexture: materialPlane.emissiveTexture = new BABYLON.Texture("textures/grass.jpg", scene); Then you can comment out the light, and it will still be visible. (In fact, if you leave the light pointing at it, it will be overexposed.) (I would recommend starting a new question for your keyboard control and collision detection questions. Or work through the babylon samples and tutorial videos.)
var playerMaterial = new BABYLON.StandardMaterial("ground", scene); playerMaterial.diffuseTexture=new BABYLON.Texture("./yourImage.png", scene); playerMaterial.bumpTexture=new BABYLON.Texture("./yourImage.png", scene); i use that for parallax i help maybe
Plot points instead of lines? JFreeChart PolarChart
Currently, the PolarChart joins all the coordinates with lines creating a polygon. I just want it to plot each point with a dot and NOT join them together. Is this possible? I have tried using translateValueThetaRadiusToJava2D() and Graphics2D to draw circles but it's very clunky and contrived. Any suggestions welcome!
So the DefaultPolarItemRenderer takes in all the polar points, converts the polar points to regular Java2D coordinates, makes a Polygon with those points and then draws it. Here's how I got it to draw dots instead of a polygon: public class MyDefaultPolarItemRenderer extends DefaultPolarItemRenderer { #Override public void drawSeries(java.awt.Graphics2D g2, java.awt.geom.Rectangle2D dataArea, PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, int seriesIndex) { int numPoints = dataset.getItemCount(seriesIndex); for (int i = 0; i < numPoints; i++) { double theta = dataset.getXValue(seriesIndex, i); double radius = dataset.getYValue(seriesIndex, i); Point p = plot.translateValueThetaRadiusToJava2D(theta, radius, dataArea); Ellipse2D el = new Ellipse2D.Double(p.x, p.y, 5, 5); g2.fill(el); g2.draw(el); } } } and then instantiated this class elsewhere: MyDefaultPolarItemRenderer dpir = new MyDefaultPolarItemRenderer(); dpir.setPlot(plot); plot.setRenderer(dpir);
This one's a little harder. Given a PolarPlot, you can obtain its AbstractRenderer and set the shape. For example, PolarPlot plot = (PolarPlot) chart.getPlot(); AbstractRenderer ar = (AbstractRenderer) plot.getRenderer(); ar.setSeriesShape(0, ShapeUtilities.createDiamond(5), true); The diamond will appear in the legend, but the DefaultPolarItemRenderer neither renders shapes, nor provides line control. You'd have to extend the default renderer and override drawSeries(). XYLineAndShapeRenderer is good example for study; you can see how it's used in TimeSeriesChartDemo1. If this is terra incognita to you, I'd recommend The JFreeChart Developer Guide†. †Disclaimer: Not affiliated with Object Refinery Limited; I'm a satisfied customer and very minor contributor.
This is an excellent discussion, in case you want the function to pick up the color assigned by user to the series add ... Color c =(Color)this.lookupSeriesPaint(seriesIndex); g2.setColor(c); before ... g.draw(e1); there are other functions... use code completion to see what else functions are available against series rendereing with name starting from lookupSeries........(int seriesindex)
I found a rather strange way to get the points without any lines connecting them. I set the Stroke of the renderer to be a thin line, with a dash phase of 0, and length of 1e10: Stroke dashedStroke = new BasicStroke( 0.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[] {0.0f, 1e10f}, 1.0f ); renderer.setSeriesStroke(0, dashedStroke);