Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ cert.cer

!package.json
!package-lock.json
recording/
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,54 @@

Software for CSCI-GA.3033-​097 Virtual Reality 2026 Spring.

---

## Final Project: ONE MATCH

* **Video Demo:** [Demo](https://drive.google.com/file/d/1hcZmToe40c4seTaOOcKJAmMABfISPNRJ/view?usp=drive_link)
* **Deck:** [Deck](https://docs.google.com/presentation/d/1cGTfUzx4aNKozdeDvtu0bSSdu9AfzkixCbP0M-sCCFM/edit?usp=sharing)
* **Code:**
* `js/scenes/final_project.js`
* `js/scenes/micro_world.js`

---

## Assignments Demo Videos

* **Assignment 1: Car**
* **Video Demo:** [Assignment 1 Demo](https://drive.google.com/file/d/1Gta9nbOM10y1SD-sA6ITCxklQPSvHXZj/view?usp=drive_link)
* **Code:** `js/scenes/car.js`

* **Assignment 2: Car Drive**
* **Video Demo:** [Assignment 2 Demo](https://drive.google.com/file/d/1PRdBgv2hsOrSPU_klBabnfIkiDOFTRDW/view?usp=drive_link)
* **Code:** `js/scenes/carDrive.js`


* **Assignment 3: Campfire**
* **Video Demo:** [Assignment 3 Demo](https://drive.google.com/file/d/1zaMHrFpwI21bqsKqNw5JxlqIVy6axHd6/view?usp=drive_link)
* **Code:** `js/scenes/campFire.js`

* **Assignment 4: Text Party**
* **Video Demo:** [Assignment 4 Demo](https://drive.google.com/file/d/1th_Ud-LDqwSxh8X0xEZjG6v583vUt9uo/view?usp=drive_link)
* **Code:** `js/scenes/textHW.js`

* **Assignment 5: Spirit Exercise**
* **Video Demo:** [Assignment 5 Demo](https://drive.google.com/file/d/1I7-1yRY8cPxwxCbqNgO94Vv5jAc6i_o-/view?usp=drive_link)
* **Code:** `js/scenes/spirit_exercise.js`

* **Assignment 6: Headgaze Exercise**
* **Video Demo:** [Assignment 6 Demo](https://drive.google.com/file/d/1wHeAjisn9--nFuDG4DWscihcDw95RE81/view?usp=drive_link)
* **Code:** `js/scenes/headGazeExercise.js`

* **Extra Exercise: Master**
* **Video Demo:** [Master Exercise Demo](https://drive.google.com/file/d/1FbCLOqYvVAQyVaVW9qjcx53Iw2fukeES/view?usp=drive_link)
* **Code:** `js/scenes/master2.js`

* **Final Project**
* **Video Demo:** [Final Project Demo](./media/videos/final_demo.mp4)
* **Code:** `js/scenes/final_project.js`

---
# How to setup the environment

install Node.js and npm if you haven't. This project was tested using **Node v18.20.8**; if you run into issues, we recommend switching to this version.
Expand Down
70 changes: 70 additions & 0 deletions js/scenes/campFire.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
This scene is an example of how to use procedural texture
to animate the shape of an object. In this case the object
is a waving flag. The noise function is used to animate
the position of each vertex of the flag geometry.
*/

import * as cg from "../render/core/cg.js";

function ensureCampfireMesh() {
if (!window.__campfireFlameMeshDefined) {
clay.defineMesh('flame', clay.createGrid(20, 30));
window.__campfireFlameMeshDefined = true;
}
}

export const renderCampfire = (model, t, options = {}) => {
ensureCampfireMesh();

const pos = options.pos || [0, 0, 0];
const scale = options.scale ?? 0.3;
const yaw = options.yaw ?? 0;
const lit = options.lit ?? true;

const root = model.add().move(...pos).turnY(yaw).scale(scale);

// Grounding elements: a low ember bed and a loose stone ring help the fire
// read as resting on terrain instead of floating over it.
root.add('sphere')
.move(0, -0.03, 0)
.scale(0.75, 0.07, 0.75)
.color(0.14, 0.08, 0.05);

for (let i = 0; i < 8; i++) {
const angle = i * Math.PI / 4;
root.add('sphere')
.move(Math.cos(angle) * 0.72, -0.01, Math.sin(angle) * 0.72)
.scale(0.14, 0.09, 0.12)
.color(0.34, 0.34, 0.36);
}

for (let i = 0; i < 6; i++) {
root.add('tubeX')
.color(0.36, 0.24, 0.18)
.move(0, 0.03, 0)
.turnY(i * Math.PI / 3)
.scale(1.3, 0.07, 0.07);
}

if (!lit) return root;

for (let i = 0; i < 6; i++) {
const fire = root.add('flame').color(1, 0.45, 0.05);
fire.turnY(i * Math.PI / 3);
fire.setVertices((u, v) => [
0.8 * (u - 0.5) * (1 - v),
2 * v,
0.3 * v * cg.noise(5 * u, 5 * v - t * 3, t),
]);
}

return root;
};

export const init = async model => {
model.scale(0.3).move(0,4,0).animate(() => {
while (model.nChildren() > 0) model.remove(0);
renderCampfire(model, model.time, { pos: [0, 0, 0], scale: 1, lit: true });
});
}
56 changes: 56 additions & 0 deletions js/scenes/car.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Create and animate hierarchical joints.
*/
let speed = 0.8

export const init = async model => {

model.txtrSrc(1, '../media/textures/tire.png');

// CREATE NODES WITH NO SHAPES AS JOINTS FOR ANIMATION.
let carbody = model.add();

//wheel center joint
let wheelFLCenter = carbody.add();
let wheelFRCenter = carbody.add();
let wheelBLCenter = carbody.add();
let wheelBRCenter = carbody.add();

//wheel joint
let wheelFL = wheelFLCenter.add();
let wheelFR = wheelFRCenter.add();
let wheelBL = wheelBLCenter.add();
let wheelBR = wheelBRCenter.add();

// CREATE AND PLACE SHAPES THAT WILL MOVE WITH EACH JOINT.
// carboday
carbody.add('cube').scale(.8,.25,.4).move(0.8,1.5,-1).color(1,0,0);
//car cabin
carbody.add('cube').scale(.5,.2,.35).move(0.8,3.8,-1.2).color(1,1,1);;

// wheel centers
wheelFRCenter.move(1.2,0,0)
wheelBLCenter.move(0,0,-0.8)
wheelFLCenter.move(1.2,0,-0.8)

wheelBR.add('torusZ').scale(.18,.18,.2).txtr(1);;
wheelFR.add('torusZ').scale(.18,.18,.2).txtr(1);;
wheelBL.add('torusZ').scale(.18,.18,.2).txtr(1);;
wheelFL.add('torusZ').scale(.18,.18,.2).txtr(1);;

// ANIMATE THE JOINTS OVER TIME.
model.scale(0.8,0.8,0.8).move(-0.5,1.3,0).animate(() => {
carbody.identity()
.turnY(Math.sin(speed*model.time)*.7+.7);

wheelFL.identity()
.turnZ(Math.sin(speed*model.time)*.7+.7);
wheelFR.identity()
.turnZ(Math.sin(speed*model.time)*.7+.7);
wheelBL.identity()
.turnZ(Math.sin(speed*model.time)*.7+.7);
wheelBR.identity()
.turnZ(Math.sin(speed*model.time)*.7+.7);
});
}

83 changes: 83 additions & 0 deletions js/scenes/carDrive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
This is a very simple example of how to use the
inputEvents object.

When the scene is in XR mode, the x position of
the left controller controls the red component
of the cube's color, and the x position of the
right controller controls the blue component of
the cube's color.
*/
export const init = async model => {
// See what the inputEvents can do
// console.log(inputEvents);

model.txtrSrc(1, '../media/textures/tire.png');

let speed = 0;
let color = [1,0,0];
// CREATE NODES WITH NO SHAPES AS JOINTS FOR ANIMATION.
let carbody = model.add();

//wheel center joint
let wheelFLCenter = carbody.add();
let wheelFRCenter = carbody.add();
let wheelBLCenter = carbody.add();
let wheelBRCenter = carbody.add();

//wheel joint
let wheelFL = wheelFLCenter.add();
let wheelFR = wheelFRCenter.add();
let wheelBL = wheelBLCenter.add();
let wheelBR = wheelBRCenter.add();

// CREATE AND PLACE SHAPES THAT WILL MOVE WITH EACH JOINT.
// carboday
let chassis = carbody.add('cube').scale(.8,.25,.4).move(0.8,1.5,-1).color(color);
//car cabin
carbody.add('cube').scale(.5,.2,.35).move(0.8,3.8,-1.2).color(1,1,1);;

// wheel centers
wheelFRCenter.move(1.2,0,0)
wheelBLCenter.move(0,0,-0.8)
wheelFLCenter.move(1.2,0,-0.8)

wheelBR.add('torusZ').scale(.18,.18,.2).txtr(1);;
wheelFR.add('torusZ').scale(.18,.18,.2).txtr(1);;
wheelBL.add('torusZ').scale(.18,.18,.2).txtr(1);;
wheelFL.add('torusZ').scale(.18,.18,.2).txtr(1);;

// USING THE GLOBAL inputEvents OBJECT

inputEvents.onMove = hand => {
if (isXR()) {
if (hand == 'left'){
color[0] = inputEvents.pos(hand)[0] * .5 + .5;
color[1] = inputEvents.pos(hand)[2] * .5 + .5;
}
}
}

model.scale(0.3).move(-0.5,4.5,0).animate(() => {
if (inputEvents.isPressed('right')) {
speed += 0.005;
} else {
speed *= 0.95;
}

//.identity resets everything
carbody.identity().move(speed * 2, 0, 0);
chassis.color(color);

wheelFL.identity()
.turnZ(-speed*model.time*.7);
wheelFR.identity()
.turnZ(-speed*model.time*.7);
wheelBL.identity()
.turnZ(-speed*model.time*.7);
wheelBR.identity()
.turnZ(-speed*model.time*.7);

});
}

Loading