Skip to content

Commit 1dc5fa9

Browse files
Merge pull request #70 from ArtifactForms/working2
Working2
2 parents 21be82f + 647b448 commit 1dc5fa9

24 files changed

+1617
-249
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package engine.animation.skeleton;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import math.Matrix4;
7+
8+
public class Bone {
9+
10+
private final String name;
11+
private Bone parent;
12+
private final List<Bone> children;
13+
private Matrix4 localTransform;
14+
private Matrix4 globalTransform;
15+
private Matrix4 inverseBindPose;
16+
private boolean transformDirty = true;
17+
private float weight;
18+
private int index;
19+
20+
public Bone(String name) {
21+
if (name == null) {
22+
throw new IllegalArgumentException("Name cannot be null.");
23+
}
24+
this.name = name;
25+
this.children = new ArrayList<>();
26+
this.localTransform = new Matrix4().identity();
27+
this.globalTransform = new Matrix4().identity();
28+
this.weight = 1.0f;
29+
}
30+
31+
public void updateGlobalTransform() {
32+
if (!transformDirty) return;
33+
globalTransform =
34+
(parent != null) ? parent.globalTransform.multiply(localTransform) : localTransform;
35+
transformDirty = false;
36+
}
37+
38+
public void updateChildrenTransforms() {
39+
for (Bone child : children) {
40+
child.markDirty();
41+
child.updateGlobalTransform();
42+
child.updateChildrenTransforms();
43+
}
44+
}
45+
46+
public void markDirty() {
47+
transformDirty = true;
48+
}
49+
50+
public void addChild(Bone child) {
51+
if (child == null) {
52+
throw new IllegalArgumentException("Child bone cannot be null.");
53+
}
54+
if (isAncestorOf(child)) {
55+
throw new IllegalArgumentException("Cannot add a child that would create a cycle.");
56+
}
57+
if (child.getParent() != null) {
58+
child.getParent().removeChild(child);
59+
}
60+
children.add(child);
61+
child.setParent(this);
62+
}
63+
64+
private boolean isAncestorOf(Bone bone) {
65+
Bone current = this;
66+
while (current != null) {
67+
if (current == bone) {
68+
return true;
69+
}
70+
current = current.getParent();
71+
}
72+
return false;
73+
}
74+
75+
public void removeChild(Bone child) {
76+
if (children.remove(child)) {
77+
child.setParent(null);
78+
}
79+
}
80+
81+
private void setParent(Bone parent) {
82+
if (this.parent == parent) return;
83+
this.parent = parent;
84+
}
85+
86+
public String getName() {
87+
return name;
88+
}
89+
90+
public Bone getParent() {
91+
return parent;
92+
}
93+
94+
public List<Bone> getChildren() {
95+
return Collections.unmodifiableList(children);
96+
}
97+
98+
public Matrix4 getLocalTransform() {
99+
return new Matrix4(localTransform);
100+
}
101+
102+
public void setLocalTransform(Matrix4 localTransform) {
103+
if (localTransform == null) {
104+
throw new IllegalArgumentException("LocalTransform cannot be null.");
105+
}
106+
this.localTransform = localTransform;
107+
markDirty();
108+
}
109+
110+
public Matrix4 getGlobalTransform() {
111+
return new Matrix4(globalTransform);
112+
}
113+
114+
public Matrix4 getInverseBindPose() {
115+
return new Matrix4(inverseBindPose);
116+
}
117+
118+
public void setInverseBindPose(Matrix4 bindPose) {
119+
this.inverseBindPose = bindPose.invert();
120+
}
121+
122+
public float getWeight() {
123+
return weight;
124+
}
125+
126+
public void setWeight(float weight) {
127+
if (weight < 0.0f || weight > 1.0f) {
128+
throw new IllegalArgumentException("Weight must be between 0.0 and 1.0.");
129+
}
130+
this.weight = weight;
131+
}
132+
133+
public int getIndex() {
134+
return index;
135+
}
136+
137+
public void setIndex(int index) {
138+
this.index = index;
139+
}
140+
141+
public void printHierarchy(int depth) {
142+
System.out.println(" ".repeat(depth) + name);
143+
for (Bone child : children) {
144+
child.printHierarchy(depth + 1);
145+
}
146+
}
147+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package engine.animation.skeleton;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
public class Skeleton {
9+
10+
private Bone rootBone;
11+
12+
private final Map<String, Bone> allBones;
13+
14+
public Skeleton(Bone rootBone) {
15+
this.rootBone = rootBone;
16+
this.allBones = new HashMap<>();
17+
collectBones(rootBone);
18+
}
19+
20+
private void collectBones(Bone bone) {
21+
allBones.put(bone.getName(), bone);
22+
for (Bone child : bone.getChildren()) {
23+
collectBones(child);
24+
}
25+
}
26+
27+
public Bone getBone(String name) {
28+
return allBones.get(name);
29+
}
30+
31+
public void update() {
32+
if (rootBone != null) {
33+
rootBone.updateGlobalTransform();
34+
}
35+
}
36+
37+
public Bone getRootBone() {
38+
return rootBone;
39+
}
40+
41+
public void setRootBone(Bone rootBone) {
42+
this.rootBone = rootBone;
43+
allBones.clear();
44+
collectBones(rootBone);
45+
}
46+
47+
public List<Bone> getAllBones() {
48+
return new ArrayList<>(allBones.values());
49+
}
50+
51+
public void printHierarchy() {
52+
printHierarchy(rootBone, 0);
53+
}
54+
55+
private void printHierarchy(Bone bone, int level) {
56+
for (int i = 0; i < level; i++) {
57+
System.out.print(" ");
58+
}
59+
System.out.println(bone.getName());
60+
for (Bone child : bone.getChildren()) {
61+
printHierarchy(child, level + 1);
62+
}
63+
}
64+
}

src/main/java/engine/application/BasicApplication.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import engine.Timer;
44
import engine.components.FlyByCameraControl;
5+
import engine.components.SmoothFlyByCameraControl;
56
import engine.debug.DebugInfoUpdater;
67
import engine.debug.DebugOverlay;
78
import engine.debug.FpsGraph;
89
import engine.debug.FpsHistory;
910
import engine.input.Input;
1011
import engine.input.Key;
1112
import engine.processing.ProcessingApplication;
13+
import engine.resources.Font;
1214
import engine.scene.Scene;
1315
import engine.scene.SceneNode;
1416
import engine.scene.camera.PerspectiveCamera;
@@ -86,7 +88,7 @@ private void setupDefaultCamera() {
8688
PerspectiveCamera defaultCamera = new PerspectiveCamera();
8789
activeScene.setActiveCamera(defaultCamera);
8890
SceneNode cameraNode = new SceneNode("DefaultCamera");
89-
cameraNode.addComponent(new FlyByCameraControl(input, defaultCamera));
91+
cameraNode.addComponent(new SmoothFlyByCameraControl(input, defaultCamera));
9092
activeScene.addNode(cameraNode);
9193
}
9294

@@ -148,6 +150,7 @@ private void renderUi(Graphics g) {
148150

149151
private void renderDebugUi(Graphics g) {
150152
if (!displayInfo) return;
153+
g.setFont(new Font("Lucida Sans", 12, Font.PLAIN));
151154
debugOverlay.render(g);
152155
fpsGraph.render(g);
153156
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* The CrossLineReticle class represents a visual reticle consisting of cross lines rendered on a
3+
* plane. It is designed to be part of a 3D scene and implements the {@link RenderableComponent}
4+
* interface for rendering capabilities.
5+
*
6+
* <p>The reticle is created as a textured plane using a {@link Mesh3D} and is configurable with
7+
* parameters like radius, thickness, and color. The texture is generated dynamically.
8+
*/
9+
package engine.components;
10+
11+
import java.awt.Graphics2D;
12+
import java.awt.image.BufferedImage;
13+
14+
import engine.resources.FilterMode;
15+
import engine.resources.Texture;
16+
import engine.resources.TextureManager;
17+
import math.Color;
18+
import math.Mathf;
19+
import mesh.Mesh3D;
20+
import mesh.creator.primitives.PlaneCreatorUV;
21+
import mesh.modifier.RotateXModifier;
22+
import workspace.ui.Graphics;
23+
24+
public class CrossLineReticle extends AbstractComponent implements RenderableComponent {
25+
26+
/** The radius of the reticle, defining its size. */
27+
private int radius;
28+
29+
/** The thickness of the cross lines. */
30+
private int thickness;
31+
32+
/** The color of the cross lines. */
33+
private Color color;
34+
35+
/** The mesh used to represent the plane of the reticle. */
36+
private Mesh3D mesh;
37+
38+
/** The texture used for rendering the reticle. */
39+
private Texture texture;
40+
41+
/** Creates a default CrossLineReticle with a radius of 9, thickness of 2, and white color. */
42+
public CrossLineReticle() {
43+
this(9, 2, Color.WHITE);
44+
}
45+
46+
/**
47+
* Creates a CrossLineReticle with the specified radius, thickness, and color.
48+
*
49+
* @param radius The radius of the reticle.
50+
* @param thickness The thickness of the cross lines.
51+
* @param color The color of the cross lines.
52+
*/
53+
public CrossLineReticle(int radius, int thickness, Color color) {
54+
this.radius = radius;
55+
this.color = color;
56+
this.thickness = thickness;
57+
this.mesh = new PlaneCreatorUV(radius).create();
58+
this.mesh.apply(new RotateXModifier(-Mathf.HALF_PI));
59+
this.texture = createTexture();
60+
}
61+
62+
/**
63+
* Renders the reticle onto the provided {@link Graphics} context.
64+
*
65+
* @param g The graphics context used for rendering.
66+
*/
67+
@Override
68+
public void render(Graphics g) {
69+
float centerX = g.getWidth() / 2.0f;
70+
float centerY = g.getHeight() / 2.0f;
71+
g.pushMatrix();
72+
g.translate(centerX, centerY);
73+
g.bindTexture(texture, 0);
74+
g.fillFaces(mesh);
75+
g.unbindTexture(0);
76+
g.popMatrix();
77+
}
78+
79+
/**
80+
* Creates a {@link Texture} for the reticle using a dynamically generated {@link BufferedImage}.
81+
*
82+
* @return The generated texture.
83+
*/
84+
private Texture createTexture() {
85+
BufferedImage image = createTextureImage();
86+
Texture texture = TextureManager.getInstance().createTexture(image);
87+
texture.setFilterMode(FilterMode.POINT);
88+
return texture;
89+
}
90+
91+
/**
92+
* Generates a {@link BufferedImage} containing the cross lines of the reticle.
93+
*
94+
* @return The generated image.
95+
*/
96+
private BufferedImage createTextureImage() {
97+
int size = radius + radius;
98+
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
99+
Graphics2D g2d = (Graphics2D) image.getGraphics();
100+
g2d.setColor(new java.awt.Color(this.color.getRGBA()));
101+
g2d.fillRect(radius - (thickness / 2), 0, thickness, size);
102+
g2d.fillRect(0, radius - (thickness / 2), size, thickness);
103+
return image;
104+
}
105+
106+
/**
107+
* Called during each update cycle. This reticle does not require updates.
108+
*
109+
* @param tpf The time per frame in seconds.
110+
*/
111+
@Override
112+
public void onUpdate(float tpf) {}
113+
114+
/** Called when the component is attached to a {@link engine.SceneNode}. */
115+
@Override
116+
public void onAttach() {}
117+
118+
/** Called when the component is detached from a {@link engine.SceneNode}. */
119+
@Override
120+
public void onDetach() {}
121+
}

src/main/java/engine/demos/landmass/NoiseMapDisplay.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ private void createPlaneMesh() {
7373
*/
7474
public void setPixels(int[] pixels) {
7575
texture.setPixels(pixels);
76-
Material material = new Material.Builder().setDiffuseTexture(texture).build();
76+
Material material = new Material();
77+
material.setDiffuseTexture(texture);
7778
planeGeometry = new Geometry(planeMesh, material);
7879
}
7980

0 commit comments

Comments
 (0)