I’m having difficulties figuring out how to load in objects at runtime. I’ve tried a lot of methods but I need assistance. here’s the code its a small engine I threw into itself to form itself in like a few hours this is like my 1st real java project so I don’t have much skill and have been riding online forums for the algorithms a lot.
here is ALL the code starting with the three important files:
Main.java:
package neptunian_engine;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("3D Engine");
Scene scene = new Scene();
Camera camera = new Camera(new Vector3D(0, 0, 5), new Vector3D(0, 0, 0));
Renderer renderer = new Renderer(scene, camera);
SoundBox soundbox = new SoundBox(); // Initialize your SoundBox and load sounds
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.add(renderer);
frame.setVisible(true);
// Add a sample object
try {
Object3D object = ObjLoader.loadObj("Guard_torso.obj"); // Ensure the file path is correct
Object3D object2 = ObjLoader.loadObj("test1.obj"); // Ensure the file path is correct
scene.addObject(object);
scene.addObject(object2);
} catch (Exception e) {
e.printStackTrace();
}
// Camera controls
CameraController cameraController = new CameraController(camera, soundbox);
MouseController mouseController = new MouseController(camera);
renderer.addKeyListener(cameraController);
renderer.addMouseMotionListener(mouseController);
renderer.setFocusable(true);
renderer.requestFocusInWindow();
// Timer to update the scene
Timer timer = new Timer(16, e -> {
cameraController.updateCameraMovement();
renderer.repaint();
});
timer.start();
});
}
}
ObjLoader.java:
package neptunian_engine;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ObjLoader {
public static Object3D loadObj(String filename) throws IOException {
List<Vector3D> vertices = new ArrayList<>();
List<int[]> faces = new ArrayList<>();
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("v ")) {
String[] parts = line.split(" ");
float x = Float.parseFloat(parts[1]);
float y = Float.parseFloat(parts[2]);
float z = Float.parseFloat(parts[3]);
vertices.add(new Vector3D(x, y, z));
} else if (line.startsWith("f ")) {
String[] parts = line.split(" ");
int[] face = new int[parts.length - 1];
for (int i = 1; i < parts.length; i++) {
face[i - 1] = Integer.parseInt(parts[i].split("/")[0]) - 1; // assuming faces are 1-indexed
}
faces.add(face);
}
}
reader.close();
return new Object3D(vertices, faces);
}
}
Object3D.java:
package neptunian_engine;
import java.util.List;
public class Object3D {
private List<Vector3D> vertices;
private List<int[]> faces;
private Vector3D position;
public Object3D(List<Vector3D> vertices, List<int[]> faces) {
this.vertices = vertices;
this.faces = faces;
this.position = new Vector3D(0, 0, 0);
}
public List<Vector3D> getVertices() {
return vertices;
}
public List<int[]> getFaces() {
return faces;
}
public Vector3D getPosition() {
return position;
}
public void setPosition(Vector3D position) {
this.position = position;
}
}
here is the rest of the codebase
Vector3D.java:
package neptunian_engine;
public class Vector3D {
public float x, y, z;
public Vector3D(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public void add(Vector3D other) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
}
public void scale(float scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
}
}
Camera.java:
package neptunian_engine;
public class Camera {
private Vector3D position;
private Vector3D rotation;
public Camera(Vector3D position, Vector3D rotation) {
this.position = position;
this.rotation = rotation;
}
public Vector3D getPosition() {
return position;
}
public void setPosition(Vector3D position) {
this.position = position;
}
public Vector3D getRotation() {
return rotation;
}
public void setRotation(Vector3D rotation) {
this.rotation = rotation;
}
public void move(Vector3D delta) {
this.position.x += delta.x;
this.position.y += delta.y;
this.position.z += delta.z;
}
public void rotate(Vector3D delta) {
this.rotation.x += delta.x;
this.rotation.y += delta.y;
this.rotation.z += delta.z;
}
public Vector3D getForwardVector() {
return new Vector3D(
(float) Math.cos(Math.toRadians(rotation.y)),
0,
(float) Math.sin(Math.toRadians(rotation.y))
);
}
public Vector3D getRightVector() {
return new Vector3D(
(float) Math.sin(Math.toRadians(rotation.y)),
0,
(float) -Math.cos(Math.toRadians(rotation.y))
);
}
}
Renderer.java:
package neptunian_engine;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import java.util.Random;
public class Renderer extends JPanel {
private Scene scene;
private Camera camera;
public Renderer(Scene scene, Camera camera) {
this.scene = scene;
this.camera = camera;
this.setFocusable(true);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
renderScene(g);
}
private void renderScene(Graphics g) {
for (Object3D object : scene.getObjects()) {
drawObject(g, object);
}
}
private void drawObject(Graphics g, Object3D object) {
List<Vector3D> vertices = object.getVertices();
List<int[]> faces = object.getFaces();
Random rand = new Random();
for (int[] face : faces) {
Polygon polygon = new Polygon();
for (int vertexIndex : face) {
Vector3D vertex = vertices.get(vertexIndex);
int x = (int) ((vertex.x + object.getPosition().x) * 100 + getWidth() / 2);
int y = (int) ((-vertex.y - object.getPosition().y) * 100 + getHeight() / 2);
polygon.addPoint(x, y);
}
g.setColor(new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
g.fillPolygon(polygon);
}
}
}
Scene.java:
package neptunian_engine;
import java.util.ArrayList;
import java.util.List;
public class Scene {
private List<Object3D> objects;
public Scene() {
objects = new ArrayList<>();
}
public void addObject(Object3D object) {
objects.add(object);
}
public List<Object3D> getObjects() {
return objects;
}
}
CameraController:
package neptunian_engine;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class CameraController extends KeyAdapter {
private Camera camera;
private boolean[] keys = new boolean[256];
private SoundBox soundbox;
public CameraController(Camera camera, SoundBox soundbox) {
this.camera = camera;
this.soundbox = soundbox;
}
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key >= 0 && key < keys.length) {
keys[key] = true;
}
}
@Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key >= 0 && key < keys.length) {
keys[key] = false;
}
}
public void updateCameraMovement() {
Vector3D forward = camera.getForwardVector();
Vector3D right = camera.getRightVector();
Vector3D movement = new Vector3D(0, 0, 0);
float speed = 0.1f;
if (keys[KeyEvent.VK_W]) {
movement.add(forward);
}
if (keys[KeyEvent.VK_S]) {
movement.add(new Vector3D(-forward.x, -forward.y, -forward.z));
}
if (keys[KeyEvent.VK_A]) {
movement.add(new Vector3D(-right.x, -right.y, -right.z));
}
if (keys[KeyEvent.VK_D]) {
movement.add(right);
soundbox.play("Gunfire"); // Play sound when moving right
}
if (keys[KeyEvent.VK_SPACE]) {
movement.y += speed;
}
if (keys[KeyEvent.VK_SHIFT]) {
movement.y -= speed;
}
movement.scale(speed);
camera.move(movement);
}
}
MouseController:
package neptunian_engine;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MouseController extends MouseAdapter {
private Camera camera;
private int lastX, lastY;
public MouseController(Camera camera) {
this.camera = camera;
}
@Override
public void mouseDragged(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int deltaX = x - lastX;
int deltaY = y - lastY;
camera.rotate(new Vector3D(deltaY * 0.1f, deltaX * 0.1f, 0));
lastX = x;
lastY = y;
}
@Override
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
}
}
SoundBox.java:
package neptunian_engine;
import javax.sound.sampled.*;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class SoundBox {
private Map<String, Clip> soundMap;
public SoundBox() {
this.soundMap = new HashMap<>();
// Initialize sound loaders here
loadSound("Gunfire", "sound_effects/Beefy-custom-rife.wav"); // guns
//loadSound("Gunfire2", "sound_effects/Beefy-custom-rife.wav"); // Example sound
// Add more sounds here in a list
}
private void loadSound(String soundName, String filePath) {
try {
File soundFile = new File(filePath);
if (!soundFile.exists()) {
throw new FileNotFoundException("File not found: " + filePath);
}
AudioInputStream audioIn = AudioSystem.getAudioInputStream(soundFile);
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
soundMap.put(soundName, clip);
} catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
e.printStackTrace();
}
}
public void play(String soundName) {
Clip clip = soundMap.get(soundName);
if (clip != null) {
clip.setFramePosition(0); // Rewind to the beginning
clip.start();
}
}
// Optional: Stop a specific sound
public void stop(String soundName) {
Clip clip = soundMap.get(soundName);
if (clip != null && clip.isRunning()) {
clip.stop();
}
}
// Optional: Stop all sounds
public void stopAll() {
for (Clip clip : soundMap.values()) {
if (clip.isRunning()) {
clip.stop();
}
}
}
}
SkeletonBox.java:
package neptunian_engine;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SkeletonBox {
private Map<String, Animation> animations = new HashMap<>();
private Skeleton skeleton;
private float animationTime = 0;
// Class definitions for supporting classes (Matrix4x4, Vector3D, Quaternion, Bone, Skeleton, Keyframe, Animation) here
public SkeletonBox() {
// Constructor: Initialize necessary data
}
public void loadSkeleton(String filePath) {
List<Bone> bones = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
String[] tokens = line.split(" ");
if (tokens[0].equals("bone")) {
String name = tokens[1];
int parentIndex = Integer.parseInt(tokens[2]);
float px = Float.parseFloat(tokens[3]);
float py = Float.parseFloat(tokens[4]);
float pz = Float.parseFloat(tokens[5]);
float qx = Float.parseFloat(tokens[6]);
float qy = Float.parseFloat(tokens[7]);
float qz = Float.parseFloat(tokens[8]);
float qw = Float.parseFloat(tokens[9]);
Vector3D position = new Vector3D(px, py, pz);
Quaternion rotation = new Quaternion(qx, qy, qz, qw);
bones.add(new Bone(name, parentIndex, position, rotation));
}
}
} catch (IOException e) {
e.printStackTrace();
}
this.skeleton = new Skeleton(bones);
}
public void loadAnimation(String name, String filePath) {
List<Keyframe> keyframes = new ArrayList<>();
float duration = 0;
float ticksPerSecond = 24;
List<Matrix4x4> transforms = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
String[] tokens = line.split(" ");
switch (tokens[0]) {
case "duration":
duration = Float.parseFloat(tokens[1]);
break;
case "ticks_per_second":
ticksPerSecond = Float.parseFloat(tokens[1]);
break;
case "bone":
// No operation, handled below in keyframe
break;
case "keyframe":
float time = Float.parseFloat(tokens[1]);
float px = Float.parseFloat(tokens[2]);
float py = Float.parseFloat(tokens[3]);
float pz = Float.parseFloat(tokens[4]);
float qx = Float.parseFloat(tokens[5]);
float qy = Float.parseFloat(tokens[6]);
float qz = Float.parseFloat(tokens[7]);
float qw = Float.parseFloat(tokens[8]);
Vector3D position = new Vector3D(px, py, pz);
Quaternion rotation = new Quaternion(qx, qy, qz, qw);
transforms.add(Matrix4x4.fromPositionRotation(position, rotation));
keyframes.add(new Keyframe(time, new ArrayList<>(transforms)));
transforms.clear();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
Animation animation = new Animation(name, keyframes, duration);
animations.put(name, animation);
}
public void play(String animationName, float deltaTime) {
Animation animation = animations.get(animationName);
if (animation == null) {
System.out.println("Animation not found: " + animationName);
return;
}
animationTime += deltaTime;
if (animationTime > animation.duration) {
animationTime = 0; // Loop the animation
}
List<Matrix4x4> boneTransforms = animation.getTransformsAtTime(animationTime);
skeleton.applyPose(boneTransforms);
}
// Class definitions
public static class Matrix4x4 {
// Placeholder for matrix data and operations
public static Matrix4x4 identity() {
return new Matrix4x4();
}
public static Matrix4x4 fromPositionRotation(Vector3D position, Quaternion rotation) {
return new Matrix4x4();
}
public Matrix4x4 multiply(Matrix4x4 other) {
return new Matrix4x4();
}
public void set(Matrix4x4 other) {
// Set this matrix to the values of the other matrix
}
}
public static class Vector3D {
public float x, y, z;
public Vector3D(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public static class Quaternion {
public float x, y, z, w;
public Quaternion(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}
public static class Bone {
public String name;
public int parentIndex;
public Vector3D position;
public Quaternion rotation;
public Bone(String name, int parentIndex, Vector3D position, Quaternion rotation) {
this.name = name;
this.parentIndex = parentIndex;
this.position = position;
this.rotation = rotation;
}
}
public static class Skeleton {
public List<Bone> bones;
public Skeleton(List<Bone> bones) {
this.bones = bones;
}
public void applyPose(List<Matrix4x4> boneTransforms) {
for (int i = 0; i < bones.size(); i++) {
Bone bone = bones.get(i);
if (bone.parentIndex >= 0) {
boneTransforms.get(i).set(boneTransforms.get(bone.parentIndex).multiply(Matrix4x4.fromPositionRotation(bone.position, bone.rotation)));
} else {
boneTransforms.get(i).set(Matrix4x4.fromPositionRotation(bone.position, bone.rotation));
}
}
}
}
public static class Keyframe {
public float time;
public List<Matrix4x4> transforms;
public Keyframe(float time, List<Matrix4x4> transforms) {
this.time = time;
this.transforms = transforms;
}
}
public static class Animation {
public String name;
public List<Keyframe> keyframes;
public float duration;
public Animation(String name, List<Keyframe> keyframes, float duration) {
this.name = name;
this.keyframes = keyframes;
this.duration = duration;
}
public List<Matrix4x4> getTransformsAtTime(float time) {
// Interpolate between keyframes to get bone transforms at the given time
// This is a simplified example, actual interpolation logic should be implemented
Keyframe current = keyframes.get(0);
for (Keyframe kf : keyframes) {
if (kf.time > time) break;
current = kf;
}
return current.transforms;
}
}
}
Physics.java:
package neptunian_engine;
import java.util.Vector;
public class Physics {
public void velocity3D (){
// add all the junk so I can have my HEADCRABS idk
}
}
}
I tried so many things
hashmap layers
File reader but I couldn’t figure anything out, and I refuse to stain my code with chatgpt
also the camera doesn’t work.