Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions src/main/java/engine/animation/skeleton/Bone.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package engine.animation.skeleton;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import math.Matrix4;

public class Bone {

private final String name;
private Bone parent;
private final List<Bone> children;
private Matrix4 localTransform;
private Matrix4 globalTransform;
private Matrix4 inverseBindPose;
private boolean transformDirty = true;
private float weight;
private int index;

public Bone(String name) {
if (name == null) {
throw new IllegalArgumentException("Name cannot be null.");
}
this.name = name;
this.children = new ArrayList<>();
this.localTransform = new Matrix4().identity();
this.globalTransform = new Matrix4().identity();
this.weight = 1.0f;
}

public void updateGlobalTransform() {
if (!transformDirty) return;
globalTransform =
(parent != null) ? parent.globalTransform.multiply(localTransform) : localTransform;
transformDirty = false;
}

public void updateChildrenTransforms() {
for (Bone child : children) {
child.markDirty();
child.updateGlobalTransform();
child.updateChildrenTransforms();
}
}

public void markDirty() {
transformDirty = true;
}

public void addChild(Bone child) {
if (child == null) {
throw new IllegalArgumentException("Child bone cannot be null.");
}
if (isAncestorOf(child)) {
throw new IllegalArgumentException("Cannot add a child that would create a cycle.");
}
if (child.getParent() != null) {
child.getParent().removeChild(child);
}
children.add(child);
child.setParent(this);
}

private boolean isAncestorOf(Bone bone) {
Bone current = this;
while (current != null) {
if (current == bone) {
return true;
}
current = current.getParent();
}
return false;
}

public void removeChild(Bone child) {
if (children.remove(child)) {
child.setParent(null);
}
}

private void setParent(Bone parent) {
if (this.parent == parent) return;
this.parent = parent;
}

public String getName() {
return name;
}

public Bone getParent() {
return parent;
}

public List<Bone> getChildren() {
return Collections.unmodifiableList(children);
}

public Matrix4 getLocalTransform() {
return new Matrix4(localTransform);
}

public void setLocalTransform(Matrix4 localTransform) {
if (localTransform == null) {
throw new IllegalArgumentException("LocalTransform cannot be null.");
}
this.localTransform = localTransform;
markDirty();
}

public Matrix4 getGlobalTransform() {
return new Matrix4(globalTransform);
}

public Matrix4 getInverseBindPose() {
return new Matrix4(inverseBindPose);
}

public void setInverseBindPose(Matrix4 bindPose) {
this.inverseBindPose = bindPose.invert();
}

public float getWeight() {
return weight;
}

public void setWeight(float weight) {
if (weight < 0.0f || weight > 1.0f) {
throw new IllegalArgumentException("Weight must be between 0.0 and 1.0.");
}
this.weight = weight;
}

public int getIndex() {
return index;
}

public void setIndex(int index) {
this.index = index;
}

public void printHierarchy(int depth) {
System.out.println(" ".repeat(depth) + name);
for (Bone child : children) {
child.printHierarchy(depth + 1);
}
}
}
64 changes: 64 additions & 0 deletions src/main/java/engine/animation/skeleton/Skeleton.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package engine.animation.skeleton;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Skeleton {

private Bone rootBone;

private final Map<String, Bone> allBones;

public Skeleton(Bone rootBone) {
this.rootBone = rootBone;
this.allBones = new HashMap<>();
collectBones(rootBone);
}

private void collectBones(Bone bone) {
allBones.put(bone.getName(), bone);
for (Bone child : bone.getChildren()) {
collectBones(child);
}
}

public Bone getBone(String name) {
return allBones.get(name);
}

public void update() {
if (rootBone != null) {
rootBone.updateGlobalTransform();
}
}

public Bone getRootBone() {
return rootBone;
}

public void setRootBone(Bone rootBone) {
this.rootBone = rootBone;
allBones.clear();
collectBones(rootBone);
}

public List<Bone> getAllBones() {
return new ArrayList<>(allBones.values());
}

public void printHierarchy() {
printHierarchy(rootBone, 0);
}

private void printHierarchy(Bone bone, int level) {
for (int i = 0; i < level; i++) {
System.out.print(" ");
}
System.out.println(bone.getName());
for (Bone child : bone.getChildren()) {
printHierarchy(child, level + 1);
}
}
}
5 changes: 4 additions & 1 deletion src/main/java/engine/application/BasicApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import engine.Timer;
import engine.components.FlyByCameraControl;
import engine.components.SmoothFlyByCameraControl;
import engine.debug.DebugInfoUpdater;
import engine.debug.DebugOverlay;
import engine.debug.FpsGraph;
import engine.debug.FpsHistory;
import engine.input.Input;
import engine.input.Key;
import engine.processing.ProcessingApplication;
import engine.resources.Font;
import engine.scene.Scene;
import engine.scene.SceneNode;
import engine.scene.camera.PerspectiveCamera;
Expand Down Expand Up @@ -86,7 +88,7 @@ private void setupDefaultCamera() {
PerspectiveCamera defaultCamera = new PerspectiveCamera();
activeScene.setActiveCamera(defaultCamera);
SceneNode cameraNode = new SceneNode("DefaultCamera");
cameraNode.addComponent(new FlyByCameraControl(input, defaultCamera));
cameraNode.addComponent(new SmoothFlyByCameraControl(input, defaultCamera));
activeScene.addNode(cameraNode);
}

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

private void renderDebugUi(Graphics g) {
if (!displayInfo) return;
g.setFont(new Font("Lucida Sans", 12, Font.PLAIN));
debugOverlay.render(g);
fpsGraph.render(g);
}
Expand Down
121 changes: 121 additions & 0 deletions src/main/java/engine/components/CrossLineReticle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* The CrossLineReticle class represents a visual reticle consisting of cross lines rendered on a
* plane. It is designed to be part of a 3D scene and implements the {@link RenderableComponent}
* interface for rendering capabilities.
*
* <p>The reticle is created as a textured plane using a {@link Mesh3D} and is configurable with
* parameters like radius, thickness, and color. The texture is generated dynamically.
*/
package engine.components;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import engine.resources.FilterMode;
import engine.resources.Texture;
import engine.resources.TextureManager;
import math.Color;
import math.Mathf;
import mesh.Mesh3D;
import mesh.creator.primitives.PlaneCreatorUV;
import mesh.modifier.RotateXModifier;
import workspace.ui.Graphics;

public class CrossLineReticle extends AbstractComponent implements RenderableComponent {

/** The radius of the reticle, defining its size. */
private int radius;

/** The thickness of the cross lines. */
private int thickness;

/** The color of the cross lines. */
private Color color;

/** The mesh used to represent the plane of the reticle. */
private Mesh3D mesh;

/** The texture used for rendering the reticle. */
private Texture texture;

/** Creates a default CrossLineReticle with a radius of 9, thickness of 2, and white color. */
public CrossLineReticle() {
this(9, 2, Color.WHITE);
}

/**
* Creates a CrossLineReticle with the specified radius, thickness, and color.
*
* @param radius The radius of the reticle.
* @param thickness The thickness of the cross lines.
* @param color The color of the cross lines.
*/
public CrossLineReticle(int radius, int thickness, Color color) {
this.radius = radius;
this.color = color;
this.thickness = thickness;
this.mesh = new PlaneCreatorUV(radius).create();
this.mesh.apply(new RotateXModifier(-Mathf.HALF_PI));
this.texture = createTexture();
}

/**
* Renders the reticle onto the provided {@link Graphics} context.
*
* @param g The graphics context used for rendering.
*/
@Override
public void render(Graphics g) {
float centerX = g.getWidth() / 2.0f;
float centerY = g.getHeight() / 2.0f;
g.pushMatrix();
g.translate(centerX, centerY);
g.bindTexture(texture, 0);
g.fillFaces(mesh);
g.unbindTexture(0);
g.popMatrix();
}

/**
* Creates a {@link Texture} for the reticle using a dynamically generated {@link BufferedImage}.
*
* @return The generated texture.
*/
private Texture createTexture() {
BufferedImage image = createTextureImage();
Texture texture = TextureManager.getInstance().createTexture(image);
texture.setFilterMode(FilterMode.POINT);
return texture;
}

/**
* Generates a {@link BufferedImage} containing the cross lines of the reticle.
*
* @return The generated image.
*/
private BufferedImage createTextureImage() {
int size = radius + radius;
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setColor(new java.awt.Color(this.color.getRGBA()));
g2d.fillRect(radius - (thickness / 2), 0, thickness, size);
g2d.fillRect(0, radius - (thickness / 2), size, thickness);
return image;
}

/**
* Called during each update cycle. This reticle does not require updates.
*
* @param tpf The time per frame in seconds.
*/
@Override
public void onUpdate(float tpf) {}

/** Called when the component is attached to a {@link engine.SceneNode}. */
@Override
public void onAttach() {}

/** Called when the component is detached from a {@link engine.SceneNode}. */
@Override
public void onDetach() {}
}
3 changes: 2 additions & 1 deletion src/main/java/engine/demos/landmass/NoiseMapDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ private void createPlaneMesh() {
*/
public void setPixels(int[] pixels) {
texture.setPixels(pixels);
Material material = new Material.Builder().setDiffuseTexture(texture).build();
Material material = new Material();
material.setDiffuseTexture(texture);
planeGeometry = new Geometry(planeMesh, material);
}

Expand Down
Loading
Loading