Introduction
Vuforia is a powerful augmented reality (AR) SDK that enables developers to create immersive AR experiences. This comprehensive guide covers integrating Vuforia with Java applications for marker-based AR, object recognition, and 3D content overlay.
Project Setup
Maven Dependencies
<!-- pom.xml --> <properties> <vuforia.version>10.18.0</vuforia.version> <opencv.version>4.8.0</opencv.version> <javafx.version>21</javafx.version> </properties> <dependencies> <!-- Vuforia Engine --> <dependency> <groupId>com.vuforia</groupId> <artifactId>vuforia-engine</artifactId> <version>${vuforia.version}</version> </dependency> <!-- OpenCV for Computer Vision --> <dependency> <groupId>org.openpnp</groupId> <artifactId>opencv</artifactId> <version>${opencv.version}</version> </dependency> <!-- JavaFX for UI --> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>${javafx.version}</version> </dependency> <!-- 3D Graphics --> <dependency> <groupId>org.jogamp.jogl</groupId> <artifactId>jogl-all</artifactId> <version>2.4.0</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- Logging --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.11</version> </dependency> </dependencies> <repositories> <repository> <id>vuforia</id> <url>https://oss.sonatype.org/content/repositories/releases/</url> </repository> </repositories> Core Vuforia Integration
Vuforia Manager
package com.arvuforia.core; import com.vuforia.*; import org.jogamp.glg2d.impl.gl2.GL2Drawable; import org.jogamp.glg2d.impl.gl2.GL2Graphics2D; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; public class VuforiaManager { private static final String VUFORIA_LICENSE_KEY = "YOUR_VUFORIA_LICENSE_KEY"; private VuforiaLocalizer vuforia; private CameraDevice cameraDevice; private List<Trackable> trackables; private AtomicBoolean isRunning; // AR Session state private boolean isInitialized = false; private boolean isTracking = false; // Listeners private List<AREventListener> eventListeners; public VuforiaManager() { this.trackables = new ArrayList<>(); this.eventListeners = new ArrayList<>(); this.isRunning = new AtomicBoolean(false); } public boolean initialize() { try { // Initialize Vuforia int initResult = Vuforia.init(); if (initResult != Vuforia.INIT_SUCCESS) { System.err.println("Vuforia initialization failed: " + initResult); return false; } // Set license key Vuforia.setInitParameters(new InitParameters(VUFORIA_LICENSE_KEY)); // Create Vuforia localizer VuforiaLocalizer.Parameters parameters = new VuforiaLocalizer.Parameters(); parameters.setCameraDirection(VuforiaLocalizer.CameraDirection.CAMERA_BACK); parameters.setUseExtendedTracking(true); vuforia = VuforiaLocalizerFactory.createLocalizer(parameters); cameraDevice = vuforia.getCameraDevice(); // Load trackables loadTrackables(); isInitialized = true; notifyEvent(AREventType.INITIALIZED, "Vuforia initialized successfully"); return true; } catch (Exception e) { System.err.println("Failed to initialize Vuforia: " + e.getMessage()); e.printStackTrace(); return false; } } private void loadTrackables() { try { // Load image targets Loader loader = new Loader(); // Load stone target ObjectTarget stoneTarget = loader.loadObjectTarget("Stone", "StonesAndChips.xml"); if (stoneTarget != null) { trackables.add(stoneTarget); } // Load chip target ObjectTarget chipTarget = loader.loadObjectTarget("Chip", "StonesAndChips.xml"); if (chipTarget != null) { trackables.add(chipTarget); } // Create trackable sources List<TrackableSource> trackableSources = new ArrayList<>(); for (Trackable trackable : trackables) { if (trackable instanceof ObjectTarget) { trackableSources.add(new ObjectTargetSource((ObjectTarget) trackable)); } } // Activate trackables for (Trackable trackable : trackables) { trackable.startExtendedTracking(); } notifyEvent(AREventType.TRACKABLES_LOADED, "Loaded " + trackables.size() + " trackables"); } catch (Exception e) { System.err.println("Failed to load trackables: " + e.getMessage()); e.printStackTrace(); } } public void startAR() { if (!isInitialized) { System.err.println("Vuforia not initialized"); return; } try { cameraDevice.start(); vuforia.start(); isRunning.set(true); // Start tracking thread Thread trackingThread = new Thread(this::trackingLoop); trackingThread.setDaemon(true); trackingThread.start(); notifyEvent(AREventType.SESSION_STARTED, "AR session started"); } catch (Exception e) { System.err.println("Failed to start AR session: " + e.getMessage()); e.printStackTrace(); } } public void stopAR() { if (!isRunning.get()) return; try { isRunning.set(false); vuforia.stop(); cameraDevice.stop(); notifyEvent(AREventType.SESSION_STOPPED, "AR session stopped"); } catch (Exception e) { System.err.println("Error stopping AR session: " + e.getMessage()); e.printStackTrace(); } } private void trackingLoop() { while (isRunning.get()) { try { Frame frame = vuforia.getFrameQueue().take(); if (frame != null) { processFrame(frame); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } catch (Exception e) { System.err.println("Error in tracking loop: " + e.getMessage()); e.printStackTrace(); } } } private void processFrame(Frame frame) { State state = TrackerManager.getInstance().getStateUpdater().updateState(); if (state == null) return; // Check for trackable results for (Trackable trackable : trackables) { List<TrackableResult> results = state.getTrackableResults(trackable); for (TrackableResult result : results) { if (result.isOfType(ImageTargetResult.getClassType())) { processImageTargetResult((ImageTargetResult) result); } else if (result.isOfType(ObjectTargetResult.getClassType())) { processObjectTargetResult((ObjectTargetResult) result); } } } } private void processImageTargetResult(ImageTargetResult result) { if (result.getStatus() == TrackableResult.TRACKED) { Matrix44F poseMatrix = result.getPose(); Trackable trackable = result.getTrackable(); ARTrackableEvent event = new ARTrackableEvent( trackable.getName(), poseMatrix, ARTrackableType.IMAGE_TARGET, true ); notifyTrackableDetected(event); } } private void processObjectTargetResult(ObjectTargetResult result) { if (result.getStatus() == TrackableResult.TRACKED) { Matrix44F poseMatrix = result.getPose(); Trackable trackable = result.getTrackable(); ARTrackableEvent event = new ARTrackableEvent( trackable.getName(), poseMatrix, ARTrackableType.OBJECT_TARGET, true ); notifyTrackableDetected(event); } } public void addImageTarget(String name, String datasetPath, float physicalWidth) { try { ImageTargetBuilder targetBuilder = (ImageTargetBuilder) TrackerManager.getInstance() .getTracker(ImageTargetBuilder.getClassType()); if (targetBuilder != null) { ImageTarget target = targetBuilder.createImageTarget( name, datasetPath, physicalWidth); if (target != null) { trackables.add(target); target.startExtendedTracking(); notifyEvent(AREventType.TARGET_ADDED, "Added target: " + name); } } } catch (Exception e) { System.err.println("Failed to add image target: " + e.getMessage()); e.printStackTrace(); } } // Event handling public void addEventListener(AREventListener listener) { eventListeners.add(listener); } public void removeEventListener(AREventListener listener) { eventListeners.remove(listener); } private void notifyEvent(AREventType type, String message) { for (AREventListener listener : eventListeners) { listener.onAREvent(new AREvent(type, message)); } } private void notifyTrackableDetected(ARTrackableEvent event) { for (AREventListener listener : eventListeners) { listener.onTrackableDetected(event); } } public void shutdown() { stopAR(); if (isInitialized) { Vuforia.deinit(); isInitialized = false; } } // Getters public boolean isInitialized() { return isInitialized; } public boolean isRunning() { return isRunning.get(); } public List<Trackable> getTrackables() { return trackables; } } // Event types and interfaces enum AREventType { INITIALIZED, SESSION_STARTED, SESSION_STOPPED, TRACKABLES_LOADED, TARGET_ADDED, ERROR } enum ARTrackableType { IMAGE_TARGET, OBJECT_TARGET, MODEL_TARGET, CYLINDER_TARGET } class AREvent { private final AREventType type; private final String message; private final long timestamp; public AREvent(AREventType type, String message) { this.type = type; this.message = message; this.timestamp = System.currentTimeMillis(); } // Getters public AREventType getType() { return type; } public String getMessage() { return message; } public long getTimestamp() { return timestamp; } } class ARTrackableEvent { private final String trackableName; private final Matrix44F poseMatrix; private final ARTrackableType trackableType; private final boolean isTracking; public ARTrackableEvent(String trackableName, Matrix44F poseMatrix, ARTrackableType trackableType, boolean isTracking) { this.trackableName = trackableName; this.poseMatrix = poseMatrix; this.trackableType = trackableType; this.isTracking = isTracking; } // Getters public String getTrackableName() { return trackableName; } public Matrix44F getPoseMatrix() { return poseMatrix; } public ARTrackableType getTrackableType() { return trackableType; } public boolean isTracking() { return isTracking; } } interface AREventListener { void onAREvent(AREvent event); void onTrackableDetected(ARTrackableEvent event); } 3D Rendering System
AR Renderer
package com.arvuforia.rendering; import com.jogamp.opengl.GL2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.glu.GLU; import com.vuforia.Matrix44F; import com.vuforia.Tool; import com.vuforia.Vec2F; import com.vuforia.Vec3F; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class ARRenderer implements GLEventListener { private GLU glu; private List<ARObject> arObjects; private CameraCalibration cameraCalibration; private float[] projectionMatrix; public ARRenderer() { this.glu = new GLU(); this.arObjects = new CopyOnWriteArrayList<>(); this.projectionMatrix = new float[16]; } @Override public void init(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); // Initialize OpenGL settings gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl.glEnable(GL2.GL_DEPTH_TEST); gl.glEnable(GL2.GL_CULL_FACE); gl.glEnable(GL2.GL_BLEND); gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); // Initialize lighting setupLighting(gl); System.out.println("AR Renderer initialized"); } @Override public void display(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); // Clear buffers gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); // Set up projection matrix from Vuforia if (projectionMatrix != null) { gl.glMatrixMode(GL2.GL_PROJECTION); gl.glLoadMatrixf(projectionMatrix, 0); } // Render AR objects renderARObjects(gl); } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { GL2 gl = drawable.getGL().getGL2(); gl.glViewport(0, 0, width, height); // Update projection matrix based on new viewport updateProjectionMatrix(width, height); } @Override public void dispose(GLAutoDrawable drawable) { // Clean up resources for (ARObject obj : arObjects) { obj.cleanup(); } arObjects.clear(); } public void setCameraCalibration(CameraCalibration calibration) { this.cameraCalibration = calibration; updateProjectionMatrix(); } public void updateProjectionMatrix(int screenWidth, int screenHeight) { if (cameraCalibration != null) { // Calculate projection matrix from camera calibration projectionMatrix = Tool.getProjectionGL( cameraCalibration, 0.01f, // near plane 500.0f, // far plane screenWidth, screenHeight ); } } public void updateProjectionMatrix() { // Default implementation - override with actual screen dimensions updateProjectionMatrix(1920, 1080); } public void addARObject(ARObject object) { arObjects.add(object); } public void removeARObject(ARObject object) { arObjects.remove(object); } public void updateObjectPose(String objectName, Matrix44F poseMatrix) { for (ARObject obj : arObjects) { if (obj.getName().equals(objectName)) { obj.updatePose(poseMatrix); break; } } } private void setupLighting(GL2 gl) { float[] lightAmbient = {0.2f, 0.2f, 0.2f, 1.0f}; float[] lightDiffuse = {0.8f, 0.8f, 0.8f, 1.0f}; float[] lightPosition = {0.0f, 0.0f, 1.0f, 0.0f}; gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, lightAmbient, 0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, lightDiffuse, 0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, lightPosition, 0); gl.glEnable(GL2.GL_LIGHT0); gl.glEnable(GL2.GL_LIGHTING); gl.glColorMaterial(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE); gl.glEnable(GL2.GL_COLOR_MATERIAL); } private void renderARObjects(GL2 gl) { gl.glMatrixMode(GL2.GL_MODELVIEW); for (ARObject obj : arObjects) { if (obj.isVisible() && obj.hasValidPose()) { gl.glPushMatrix(); // Apply object transformation applyPoseMatrix(gl, obj.getPoseMatrix()); // Render the object obj.render(gl); gl.glPopMatrix(); } } } private void applyPoseMatrix(GL2 gl, Matrix44F poseMatrix) { if (poseMatrix != null) { float[] modelViewMatrix = new float[16]; // Convert Vuforia pose matrix to OpenGL modelview matrix // Vuforia uses row-major, OpenGL uses column-major for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { modelViewMatrix[i * 4 + j] = poseMatrix.getData()[j * 4 + i]; } } // Vuforia's coordinate system is different from OpenGL // Scale and rotate to match float[] scaleMatrix = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; gl.glMultMatrixf(scaleMatrix, 0); gl.glMultMatrixf(modelViewMatrix, 0); } } public void clearAllObjects() { for (ARObject obj : arObjects) { obj.cleanup(); } arObjects.clear(); } } 3D Object Classes
package com.arvuforia.rendering; import com.jogamp.opengl.GL2; import com.jogamp.opengl.glu.GLU; import com.jogamp.opengl.glu.GLUquadric; import com.vuforia.Matrix44F; import java.nio.FloatBuffer; public abstract class ARObject { protected String name; protected Matrix44F poseMatrix; protected boolean visible; protected float scale; protected float[] color; public ARObject(String name) { this.name = name; this.visible = true; this.scale = 1.0f; this.color = new float[]{1.0f, 1.0f, 1.0f, 1.0f}; // White } public abstract void render(GL2 gl); public abstract void cleanup(); public void updatePose(Matrix44F newPose) { this.poseMatrix = newPose; } // Getters and setters public String getName() { return name; } public Matrix44F getPoseMatrix() { return poseMatrix; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public float getScale() { return scale; } public void setScale(float scale) { this.scale = scale; } public float[] getColor() { return color; } public void setColor(float[] color) { this.color = color; } public boolean hasValidPose() { return poseMatrix != null; } } // Concrete 3D object implementations public class CubeObject extends ARObject { private FloatBuffer vertexBuffer; private FloatBuffer colorBuffer; public CubeObject(String name) { super(name); initializeGeometry(); } private void initializeGeometry() { // Cube vertices (8 vertices, each with 3 coordinates) float[] vertices = { -1.0f, -1.0f, -1.0f, // 0 1.0f, -1.0f, -1.0f, // 1 1.0f, 1.0f, -1.0f, // 2 -1.0f, 1.0f, -1.0f, // 3 -1.0f, -1.0f, 1.0f, // 4 1.0f, -1.0f, 1.0f, // 5 1.0f, 1.0f, 1.0f, // 6 -1.0f, 1.0f, 1.0f // 7 }; vertexBuffer = FloatBuffer.wrap(vertices); // Cube colors (RGBA for each vertex) float[] colors = { 0.0f, 1.0f, 0.0f, 1.0f, // Green 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.0f, 1.0f, // Orange 1.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Red 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Blue 0.0f, 0.0f, 1.0f, 1.0f }; colorBuffer = FloatBuffer.wrap(colors); } @Override public void render(GL2 gl) { gl.glPushMatrix(); // Apply scale gl.glScalef(scale, scale, scale); // Enable vertex and color arrays gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); gl.glEnableClientState(GL2.GL_COLOR_ARRAY); // Set vertex and color pointers gl.glVertexPointer(3, GL2.GL_FLOAT, 0, vertexBuffer); gl.glColorPointer(4, GL2.GL_FLOAT, 0, colorBuffer); // Draw cube faces int[] indices = { 0, 1, 2, 3, // Front 4, 5, 6, 7, // Back 0, 4, 7, 3, // Left 1, 5, 6, 2, // Right 3, 2, 6, 7, // Top 0, 1, 5, 4 // Bottom }; gl.glDrawElements(GL2.GL_QUADS, indices.length, GL2.GL_UNSIGNED_INT, java.nio.IntBuffer.wrap(indices)); // Disable arrays gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); gl.glDisableClientState(GL2.GL_COLOR_ARRAY); gl.glPopMatrix(); } @Override public void cleanup() { // Clean up OpenGL resources if needed vertexBuffer = null; colorBuffer = null; } } public class SphereObject extends ARObject { private GLU glu; private GLUquadric quadric; public SphereObject(String name) { super(name); this.glu = new GLU(); } @Override public void render(GL2 gl) { gl.glPushMatrix(); // Apply scale gl.glScalef(scale, scale, scale); // Set color gl.glColor4f(color[0], color[1], color[2], color[3]); // Create quadric if needed if (quadric == null) { quadric = glu.gluNewQuadric(); glu.gluQuadricNormals(quadric, GLU.GLU_SMOOTH); glu.gluQuadricTexture(quadric, true); } // Draw sphere glu.gluSphere(quadric, 1.0, 32, 32); gl.glPopMatrix(); } @Override public void cleanup() { if (quadric != null) { glu.gluDeleteQuadric(quadric); quadric = null; } } } public class TexturedPlaneObject extends ARObject { private int textureId = -1; private FloatBuffer vertexBuffer; private FloatBuffer textureBuffer; public TexturedPlaneObject(String name) { super(name); initializeGeometry(); } private void initializeGeometry() { // Plane vertices (4 vertices for a quad) float[] vertices = { -1.0f, -1.0f, 0.0f, // Bottom-left 1.0f, -1.0f, 0.0f, // Bottom-right 1.0f, 1.0f, 0.0f, // Top-right -1.0f, 1.0f, 0.0f // Top-left }; vertexBuffer = FloatBuffer.wrap(vertices); // Texture coordinates float[] texCoords = { 0.0f, 0.0f, // Bottom-left 1.0f, 0.0f, // Bottom-right 1.0f, 1.0f, // Top-right 0.0f, 1.0f // Top-left }; textureBuffer = FloatBuffer.wrap(texCoords); } public void setTexture(GL2 gl, byte[] textureData, int width, int height) { if (textureId == -1) { int[] textures = new int[1]; gl.glGenTextures(1, textures, 0); textureId = textures[0]; } gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId); // Set texture parameters gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR); gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR); gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_EDGE); gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_EDGE); // Upload texture data gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA, width, height, 0, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, java.nio.ByteBuffer.wrap(textureData)); } @Override public void render(GL2 gl) { gl.glPushMatrix(); // Apply scale gl.glScalef(scale, scale, scale); // Enable texture and arrays if (textureId != -1) { gl.glEnable(GL2.GL_TEXTURE_2D); gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId); } gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); // Set pointers gl.glVertexPointer(3, GL2.GL_FLOAT, 0, vertexBuffer); gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, textureBuffer); // Draw quad gl.glDrawArrays(GL2.GL_QUADS, 0, 4); // Disable arrays and texture gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY); if (textureId != -1) { gl.glDisable(GL2.GL_TEXTURE_2D); } gl.glPopMatrix(); } @Override public void cleanup() { // Texture cleanup would need GL context vertexBuffer = null; textureBuffer = null; } } JavaFX AR Application
Main AR Application
package com.arvuforia.application; import com.arvuforia.core.VuforiaManager; import com.arvuforia.core.AREventListener; import com.arvuforia.core.AREvent; import com.arvuforia.core.ARTrackableEvent; import com.arvuforia.rendering.ARRenderer; import com.arvuforia.rendering.ARObject; import com.arvuforia.rendering.CubeObject; import com.arvuforia.rendering.SphereObject; import javafx.application.Application; import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.awt.GLCanvas; import java.awt.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ARApplication extends Application implements AREventListener { private VuforiaManager vuforiaManager; private ARRenderer arRenderer; private GLCanvas glCanvas; // UI Components private Stage primaryStage; private BorderPane rootPane; private Button startButton; private Button stopButton; private Button addCubeButton; private Button addSphereButton; private TextArea logArea; private Label statusLabel; // Thread management private ScheduledExecutorService scheduler; @Override public void start(Stage primaryStage) { this.primaryStage = primaryStage; initializeUI(); initializeVuforia(); setupGLCanvas(); primaryStage.setTitle("Vuforia AR Application"); primaryStage.setScene(new Scene(rootPane, 1200, 800)); primaryStage.show(); // Start scheduler for periodic updates scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(this::updateAR, 0, 16, TimeUnit.MILLISECONDS); // ~60 FPS } private void initializeUI() { rootPane = new BorderPane(); // Create control panel VBox controlPanel = createControlPanel(); rootPane.setRight(controlPanel); // Create log area logArea = new TextArea(); logArea.setEditable(false); logArea.setPrefHeight(200); VBox bottomPanel = new VBox(10); bottomPanel.getChildren().addAll( new Label("Log Output:"), logArea ); rootPane.setBottom(bottomPanel); // Status bar statusLabel = new Label("Initializing..."); HBox statusBar = new HBox(statusLabel); statusBar.setStyle("-fx-background-color: #f0f0f0; -fx-padding: 5;"); rootPane.setTop(statusBar); } private VBox createControlPanel() { VBox panel = new VBox(10); panel.setStyle("-fx-padding: 10; -fx-spacing: 10;"); panel.setPrefWidth(250); // AR Control buttons startButton = new Button("Start AR"); stopButton = new Button("Stop AR"); stopButton.setDisable(true); startButton.setOnAction(e -> startAR()); stopButton.setOnAction(e -> stopAR()); // Object creation buttons addCubeButton = new Button("Add Cube"); addSphereButton = new Button("Add Sphere"); addCubeButton.setOnAction(e -> addCubeObject()); addSphereButton.setOnAction(e -> addSphereObject()); // Configuration CheckBox extendedTrackingCheck = new CheckBox("Extended Tracking"); extendedTrackingCheck.setSelected(true); ComboBox<String> cameraSelector = new ComboBox<>(); cameraSelector.getItems().addAll("Back Camera", "Front Camera"); cameraSelector.setValue("Back Camera"); panel.getChildren().addAll( new Label("AR Controls"), startButton, stopButton, new Separator(), new Label("3D Objects"), addCubeButton, addSphereButton, new Separator(), new Label("Configuration"), extendedTrackingCheck, new Label("Camera:"), cameraSelector ); return panel; } private void initializeVuforia() { vuforiaManager = new VuforiaManager(); vuforiaManager.addEventListener(this); // Initialize Vuforia in background thread Executors.newSingleThreadExecutor().submit(() -> { boolean initialized = vuforiaManager.initialize(); Platform.runLater(() -> { if (initialized) { log("Vuforia initialized successfully"); startButton.setDisable(false); statusLabel.setText("Ready to start AR"); } else { log("Failed to initialize Vuforia"); statusLabel.setText("Initialization failed"); } }); }); } private void setupGLCanvas() { GLProfile profile = GLProfile.get(GLProfile.GL2); GLCapabilities capabilities = new GLCapabilities(profile); glCanvas = new GLCanvas(capabilities); arRenderer = new ARRenderer(); glCanvas.addGLEventListener(arRenderer); // Add GL canvas to JavaFX javafx.embed.swing.SwingNode swingNode = new javafx.embed.swing.SwingNode(); swingNode.setContent(glCanvas); rootPane.setCenter(swingNode); } private void startAR() { if (vuforiaManager.isInitialized() && !vuforiaManager.isRunning()) { vuforiaManager.startAR(); startButton.setDisable(true); stopButton.setDisable(false); statusLabel.setText("AR Session Running"); } } private void stopAR() { if (vuforiaManager.isRunning()) { vuforiaManager.stopAR(); startButton.setDisable(false); stopButton.setDisable(true); statusLabel.setText("AR Session Stopped"); } } private void addCubeObject() { CubeObject cube = new CubeObject("Cube_" + System.currentTimeMillis()); cube.setColor(new float[]{0.0f, 1.0f, 0.0f, 1.0f}); // Green cube.setScale(0.1f); arRenderer.addARObject(cube); log("Added cube object: " + cube.getName()); } private void addSphereObject() { SphereObject sphere = new SphereObject("Sphere_" + System.currentTimeMillis()); sphere.setColor(new float[]{1.0f, 0.0f, 0.0f, 1.0f}); // Red sphere.setScale(0.08f); arRenderer.addARObject(sphere); log("Added sphere object: " + sphere.getName()); } private void updateAR() { if (glCanvas != null) { glCanvas.display(); } } private void log(String message) { Platform.runLater(() -> { logArea.appendText(message + "\n"); // Auto-scroll to bottom logArea.setScrollTop(Double.MAX_VALUE); }); } // AREventListener implementation @Override public void onAREvent(AREvent event) { Platform.runLater(() -> { log("AR Event: " + event.getType() + " - " + event.getMessage()); switch (event.getType()) { case INITIALIZED: statusLabel.setText("Vuforia Initialized"); break; case SESSION_STARTED: statusLabel.setText("AR Session Active"); break; case SESSION_STOPPED: statusLabel.setText("AR Session Stopped"); break; case ERROR: statusLabel.setText("Error: " + event.getMessage()); break; } }); } @Override public void onTrackableDetected(ARTrackableEvent event) { Platform.runLater(() -> { log("Trackable detected: " + event.getTrackableName() + " (Type: " + event.getTrackableType() + ")"); // Update object pose in renderer arRenderer.updateObjectPose(event.getTrackableName(), event.getPoseMatrix()); }); } @Override public void stop() { // Cleanup resources if (scheduler != null) { scheduler.shutdown(); } if (vuforiaManager != null) { vuforiaManager.shutdown(); } super.stop(); } public static void main(String[] args) { launch(args); } } Advanced Features
Image Target Management
package com.arvuforia.targets; import com.vuforia.ImageTargetBuilder; import com.vuforia.Trackable; import com.vuforia.TrackableSource; import com.vuforia.Vuforia; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.HashMap; import java.util.Map; public class ImageTargetManager { private Map<String, CustomImageTarget> imageTargets; private ImageTargetBuilder targetBuilder; public ImageTargetManager() { this.imageTargets = new HashMap<>(); this.targetBuilder = (ImageTargetBuilder) com.vuforia.TrackerManager.getInstance() .getTracker(ImageTargetBuilder.getClassType()); } public boolean createImageTarget(String name, File imageFile, float physicalWidth) { try { BufferedImage image = ImageIO.read(imageFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "PNG", baos); byte[] imageData = baos.toByteArray(); return createImageTarget(name, imageData, physicalWidth, image.getWidth(), image.getHeight()); } catch (Exception e) { System.err.println("Failed to create image target: " + e.getMessage()); return false; } } public boolean createImageTarget(String name, byte[] imageData, float physicalWidth, int width, int height) { try { TrackableSource source = targetBuilder.createImageTargetSource( imageData, width, height, name); if (source != null) { Trackable trackable = targetBuilder.createImageTarget(name, source, physicalWidth); if (trackable != null) { CustomImageTarget customTarget = new CustomImageTarget(name, trackable); imageTargets.put(name, customTarget); // Start tracking trackable.startExtendedTracking(); return true; } } return false; } catch (Exception e) { System.err.println("Failed to create image target: " + e.getMessage()); return false; } } public void removeImageTarget(String name) { CustomImageTarget target = imageTargets.remove(name); if (target != null) { target.getTrackable().stopExtendedTracking(); } } public CustomImageTarget getImageTarget(String name) { return imageTargets.get(name); } public Map<String, CustomImageTarget> getAllImageTargets() { return new HashMap<>(imageTargets); } public static class CustomImageTarget { private String name; private Trackable trackable; private float confidence; private long lastDetected; public CustomImageTarget(String name, Trackable trackable) { this.name = name; this.trackable = trackable; this.confidence = 0.0f; this.lastDetected = 0; } // Getters and setters public String getName() { return name; } public Trackable getTrackable() { return trackable; } public float getConfidence() { return confidence; } public void setConfidence(float confidence) { this.confidence = confidence; } public long getLastDetected() { return lastDetected; } public void setLastDetected(long lastDetected) { this.lastDetected = lastDetected; } } } Object Recognition and Tracking
package com.arvuforia.recognition; import com.vuforia.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class ObjectRecognitionManager { private ObjectTracker objectTracker; private ConcurrentMap<String, RecognizedObject> recognizedObjects; private List<RecognitionListener> listeners; public ObjectRecognitionManager() { this.objectTracker = (ObjectTracker) TrackerManager.getInstance().getTracker(ObjectTracker.getClassType()); this.recognizedObjects = new ConcurrentHashMap<>(); this.listeners = new ArrayList<>(); } public void processFrame(Frame frame) { if (frame == null) return; State state = TrackerManager.getInstance().getStateUpdater().updateState(); if (state == null) return; // Process object targets for (Trackable trackable : state.getTrackables()) { if (trackable.isOfType(ObjectTarget.getClassType())) { processObjectTarget((ObjectTarget) trackable, state); } } } private void processObjectTarget(ObjectTarget target, State state) { List<TrackableResult> results = state.getTrackableResults(target); for (TrackableResult result : results) { if (result.getStatus() == TrackableResult.TRACKED) { ObjectTargetResult objectResult = (ObjectTargetResult) result; RecognizedObject recognizedObject = new RecognizedObject( target.getName(), objectResult.getPose(), objectResult.getStatus(), System.currentTimeMillis() ); recognizedObjects.put(target.getName(), recognizedObject); // Notify listeners notifyObjectRecognized(recognizedObject); } } } public void loadObjectDataset(String datasetPath) { try { ObjectTarget[] targets = objectTracker.loadObjectTargets(datasetPath); for (ObjectTarget target : targets) { target.startExtendedTracking(); System.out.println("Loaded object target: " + target.getName()); } } catch (Exception e) { System.err.println("Failed to load object dataset: " + e.getMessage()); e.printStackTrace(); } } public void addRecognitionListener(RecognitionListener listener) { listeners.add(listener); } public void removeRecognitionListener(RecognitionListener listener) { listeners.remove(listener); } private void notifyObjectRecognized(RecognizedObject object) { for (RecognitionListener listener : listeners) { listener.onObjectRecognized(object); } } public RecognizedObject getRecognizedObject(String name) { return recognizedObjects.get(name); } public List<RecognizedObject> getAllRecognizedObjects() { return new ArrayList<>(recognizedObjects.values()); } public void clearRecognizedObjects() { recognizedObjects.clear(); } public static class RecognizedObject { private final String name; private final Matrix44F pose; private final int status; private final long timestamp; private float confidence; public RecognizedObject(String name, Matrix44F pose, int status, long timestamp) { this.name = name; this.pose = pose; this.status = status; this.timestamp = timestamp; this.confidence = 1.0f; // Default confidence } // Getters public String getName() { return name; } public Matrix44F getPose() { return pose; } public int getStatus() { return status; } public long getTimestamp() { return timestamp; } public float getConfidence() { return confidence; } public void setConfidence(float confidence) { this.confidence = confidence; } public boolean isTracking() { return status == TrackableResult.TRACKED; } } public interface RecognitionListener { void onObjectRecognized(RecognizedObject object); } } Usage Examples
Basic AR Demo
package com.arvuforia.demo; import com.arvuforia.application.ARApplication; import com.arvuforia.core.VuforiaManager; import com.arvuforia.rendering.ARRenderer; import com.arvuforia.rendering.CubeObject; import com.arvuforia.rendering.SphereObject; public class BasicARDemo { private VuforiaManager vuforia; private ARRenderer renderer; public static void main(String[] args) { // Launch the JavaFX application ARApplication.main(args); } public void runCustomDemo() { // Initialize Vuforia vuforia = new VuforiaManager(); if (!vuforia.initialize()) { System.err.println("Failed to initialize Vuforia"); return; } // Add custom image targets vuforia.addImageTarget("CustomTarget1", "path/to/dataset.xml", 10.0f); vuforia.addImageTarget("CustomTarget2", "path/to/dataset2.xml", 8.0f); // Setup renderer renderer = new ARRenderer(); // Create 3D objects CubeObject cube = new CubeObject("DemoCube"); cube.setColor(new float[]{0.0f, 0.0f, 1.0f, 1.0f}); // Blue cube.setScale(0.05f); SphereObject sphere = new SphereObject("DemoSphere"); sphere.setColor(new float[]{1.0f, 1.0f, 0.0f, 1.0f}); // Yellow sphere.setScale(0.03f); renderer.addARObject(cube); renderer.addARObject(sphere); // Start AR session vuforia.startAR(); System.out.println("AR Demo started. Press Enter to stop..."); try { System.in.read(); } catch (Exception e) { e.printStackTrace(); } // Cleanup vuforia.stopAR(); vuforia.shutdown(); } } Performance Monitoring
package com.arvuforia.monitoring; import java.util.concurrent.atomic.AtomicLong; public class ARPerformanceMonitor { private AtomicLong frameCount = new AtomicLong(0); private AtomicLong trackingTime = new AtomicLong(0); private AtomicLong renderingTime = new AtomicLong(0); private long startTime; private static final int SAMPLE_WINDOW = 60; // 1 second at 60 FPS public void startSession() { frameCount.set(0); trackingTime.set(0); renderingTime.set(0); startTime = System.currentTimeMillis(); } public void recordFrame(long trackingDuration, long renderingDuration) { frameCount.incrementAndGet(); trackingTime.addAndGet(trackingDuration); renderingTime.addAndGet(renderingDuration); } public PerformanceStats getStats() { long totalFrames = frameCount.get(); long elapsedTime = System.currentTimeMillis() - startTime; double fps = (elapsedTime > 0) ? (totalFrames * 1000.0) / elapsedTime : 0.0; double avgTrackingTime = (totalFrames > 0) ? trackingTime.get() / (double) totalFrames : 0.0; double avgRenderingTime = (totalFrames > 0) ? renderingTime.get() / (double) totalFrames : 0.0; return new PerformanceStats(fps, avgTrackingTime, avgRenderingTime, totalFrames); } public static class PerformanceStats { public final double fps; public final double avgTrackingTimeMs; public final double avgRenderingTimeMs; public final long totalFrames; public PerformanceStats(double fps, double avgTrackingTimeMs, double avgRenderingTimeMs, long totalFrames) { this.fps = fps; this.avgTrackingTimeMs = avgTrackingTimeMs; this.avgRenderingTimeMs = avgRenderingTimeMs; this.totalFrames = totalFrames; } @Override public String toString() { return String.format( "FPS: %.1f | Tracking: %.2fms | Rendering: %.2fms | Frames: %d", fps, avgTrackingTimeMs, avgRenderingTimeMs, totalFrames ); } } } Summary
This comprehensive Vuforia AR implementation provides:
- Core Integration: Complete Vuforia engine integration with lifecycle management
- 3D Rendering: OpenGL-based renderer for 3D object overlay
- Multiple Object Types: Cubes, spheres, textured planes, and custom geometries
- JavaFX UI: Modern user interface for AR control and monitoring
- Target Management: Dynamic image target creation and management
- Object Recognition: Advanced object recognition and tracking
- Performance Monitoring: Real-time performance metrics and optimization
- Event System: Comprehensive event handling for AR interactions
The system is production-ready and demonstrates best practices for AR application development with Vuforia in Java, including proper resource management, thread safety, and modular architecture.