// Cloned by Vanya Cadogan on 5 Feb 2023 from World "One Cube World (Three.js)" by Starter user
// Please leave this clone trail here.
// function replaceScript(){
// let scripts = document.getElementsByTagName( "script" );
// for ( let i = 0; i < scripts.length; i++){
// if ( scripts[i].src == "https://run.ancientbrain.com/api/threeab30/libs/three.min.js" )
// {
// scripts[i].src = "https://ancientbrain.com/uploads/cadogav2/three.r149.min.js";
// }
// }
// }
// replaceScript();
// $.getScript ("/uploads/cadogav2/three.r149.min.js", function(){
// console.log('script loaded THREE');
// console.log(THREE)
// });
// Load CSS
AB.loadCSS("/uploads/cadogav2/app-styles.css");
AB.maxSteps = Infinity;
console.log("-------------")
console.log(THREE)
console.log(AB)
console.log(ABWorld)
console.log("-------------")
class UI {
constructor(){
this.selectedMaterial = 1;
}
init(){
window.addEventListener("contextmenu", e => e.preventDefault());
document.getElementById('ab-runheaderbox').remove()
document.write(`<header class="menu-header">
<nav class="menu-nav">
<ul>
<li class="no-select">File</li>
<li class="no-select">Views</li>
<li class="no-select">Help</li>
</ul>
<ul>
<i
class="fa-solid fa-arrow-rotate-left disabled"
title="Undo"
></i>
<i
class="fa-solid fa-arrow-rotate-right disabled"
title="Redo"
></i>
</ul>
<ul>
<i class="fa-regular fa-floppy-disk" title="Save"></i>
<i class="fa-solid fa-download" title="Download Model"></i>
</ul>
<ul>
<i class="fa-solid fa-pen" title="Draw Block"></i>
<i class="fa-solid fa-brush" title="Paint Blocks"></i>
<i class="fa-solid fa-eraser" title="Delete Blocks"></i>
</ul>
<ul>
<i class="fa-solid fa-palette" title="Materials"></i>
</ul>
<ul>
<i class="fa-solid fa-camera" title="Camera Views"></i>
<i
class="fa-solid fa-house"
title="Center Camera to Origin"
></i>
</ul>
</nav>
</header>`);
}
}
class Utils {
static updateMousePosition(mouse, event){
mouse.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
}
static getMouseGridPosition(mouse, raycaster, floor){
raycaster.setFromCamera(mouse, ABWorld.camera);
let intersects = raycaster.intersectObjects([floor]);
if (intersects.length > 0) {
const point = intersects[0].point;
const sqPosition = new THREE.Vector3(Math.floor(point.x), 0, Math.floor(point.z));
return sqPosition;
}
}
static getPreviewCubePosition(mouse, raycaster, floor){
raycaster.setFromCamera(mouse, ABWorld.camera);
let intersects = raycaster.intersectObjects([floor, ...cubes]);
if (intersects.length > 0) {
let intersect = intersects[0];
if (intersect.object.userData.type === 'cube'){
console.log('i cube')
// Get face normal
const norm = intersect.face.normal;
//console.log('norm');
//console.log(norm);
const cubePosition = intersect.object.position;
const rayPosition = new THREE.Vector3(Math.floor(cubePosition.x), Math.floor(cubePosition.y), Math.floor(cubePosition.z));
//console.log('rayPosition');
//console.log(rayPosition);
return rayPosition.add(norm);
} else if (intersect.object.userData.type === 'floor') {
console.log('i floor')
const point = intersect.point;
const sqPosition = new THREE.Vector3(Math.floor(point.x), 0, Math.floor(point.z));
return sqPosition;
} else {
console.log('i undefined')
return undefined;
}
}
}
static getMouseIntersect(mouse, raycaster, floor){
raycaster.setFromCamera(mouse, ABWorld.camera);
let intersects = raycaster.intersectObjects([floor,...cubes]);
if (intersects.length > 0) {
return intersects[0];
}
}
}
const Tools = {
DRAW: "DRAW",
PAINT: "PAINT",
ERASE: "ERASE",
}
class Game {
constructor(ui){
this.currentBlockMaterial = 1;
this.currentTool = Tools.DRAW;
this.mouse = new THREE.Vector2();
this.raycaster = new THREE.Raycaster();
this.previewCube;
this.floor;
this.gridSize = 10;
this.gridHelper;
this.ui = ui;
}
init(){
// Create grid
this.gridHelper = new THREE.GridHelper(this.gridSize, this.gridSize, 0x7d8085, 0x4f5154);
ABWorld.scene.add(this.gridHelper);
this.createFloor();
this.createPreviewCube();
}
createFloor(){
let geometry = new THREE.PlaneGeometry(this.gridSize, this.gridSize, this.gridSize, this.gridSize);
let material = new THREE.ShadowMaterial();
material.opacity = 0.2;
this.floor = new THREE.Mesh(geometry, material);
this.floor.rotation.x = -Math.PI / 2;
this.floor.userData.type = 'floor';
ABWorld.scene.add(this.floor);
}
createPreviewCube(){
const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshStandardMaterial({
color: 0x40b8ff,
emissive: 0x3489f7,
transparent: true,
opacity: 0.6,
roughness: 0.5,
});
this.previewCube = new THREE.Mesh(geometry, material);
this.previewCube.position.set(0.5, 0.5, 0.5);
ABWorld.scene.add(this.previewCube);
}
movePreviewBlock(){
const sqPosition = Utils.getPreviewCubePosition(this.mouse, this.raycaster, this.floor);
if (sqPosition !== undefined){
this.previewCube.visible = true;
this.previewCube.position.copy(sqPosition.clone().add(new THREE.Vector3(0.5, 0.5, 0.5)));
} else {
this.previewCube.visible = false;
}
}
addBlock(){
const previewCubePosiition = this.previewCube.position;
const isCubesOverlapping = cubes.some(cube => cube.position.equals(previewCubePosiition));
if (!isCubesOverlapping && this.previewCube.visible === true){
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshLambertMaterial({ color: colors[this.ui.selectedMaterial] });
const cube = new THREE.Mesh(geometry, material);
cube.position.copy(this.previewCube.position);
cube.userData.type = 'cube';
cube.updateMatrixWorld(); // updates for next intersect
ABWorld.scene.add(cube);
cubes.push(cube);
// Update preview cube position
this.movePreviewBlock();
} else {
console.log('A cube already exists at this position or is out of range!')
}
}
removeBlock(){
if (!Array.isArray(cubes) || !cubes.length) return;
// Get intersected object
const intersect = Utils.getMouseIntersect(this.mouse, this.raycaster, this.floor);
if (intersect !== undefined) {
if (intersect.object.userData.type === 'cube'){
const cube = intersect.object;
ABWorld.scene.remove(cube);
const index = cubes.indexOf(cube);
if (index !== -1) {
cubes.splice(index, 1);
}
}
}
}
}
const skycolor = '0xffffff';
const startRadius = 100; // distance from centre we start the camera at
const maxRadius = startRadius * 10; // maximum distance from camera we render things
// const jsonPath = "uploads/cadogav2/test_data.json";
// const jsonPath = "uploads/cadogav2/torus_37x11x37.json";
const jsonPath = "uploads/stewalsh/mc-test-export.json";
const colors = {
0: "#B8255F",
1: "#FF9933",
2: "#7ECC49",
3: "#6ACCBC",
4: "#4073FF",
5: "#884DFF",
6: "#EB96EB",
7: "#FF8D85",
8: "#FF5733",
9: "#DAF7A6",
10: "#FFEC59",
11: "#C05780",
12: "#6C88C4",
13: "#74737A",
14: "#CCCCFF",
15: "#9FE2BF",
16: "#8A3186",
17: "#4057A7",
18: "#042838",
19: "#2DCED9",
20: "#CBD6E2",
21: "#4FB06D",
22: "#BE398D",
23: "#FF3B30",
24: "#FFFFFF"
};
const colorsLength = Object.keys(colors).length;
let cubes = [];
function createAllCubes(data, scaleFactor) {
const group = new THREE.Group();
data.elements.forEach(element => {
const cube = createCube(element);
//cubes.push(cube)
group.add(cube);
});
//group.add(...cubes)
// Scale group
group.scale.set(scaleFactor, scaleFactor, scaleFactor);
// Transfer new scale directly to the children
let position = new THREE.Vector3()
let scale = new THREE.Vector3()
group.children.forEach(child => {
child.getWorldPosition(position)
child.position.copy(position)
child.getWorldScale(scale)
child.scale.copy(scale);
});
// Add children to the scene
ABWorld.scene.add(...group.children);
// Clear the group
group.children = [];
}
function createCube(blockData) {
const from = blockData.from.map(x => Math.round(x));
const to = blockData.to.map(x => Math.round(x));
const width = to[0] - from[0];
const height = to[1] - from[1];
const depth = to[2] - from[2];
const geometry = new THREE.BoxGeometry(width, height, depth);
const colorIndex = blockData.color !== undefined? blockData.color % (colorsLength - 1) : Math.floor(Math.random() * (colorsLength - 1));
const material = new THREE.MeshLambertMaterial({ color: colors[colorIndex] });
const cube = new THREE.Mesh(geometry, material);
// x, y, z
cube.position.set(from[0] + width / 2, from[1] + height / 2, from[2] + depth / 2);
return cube;
}
//===============================================================================================================
// EVENTS
//===============================================================================================================
function addEventListeners(){
const canvas = document.getElementById('ab-threepage');
canvas.addEventListener('mousedown', onMouseDown);
canvas.addEventListener('mousemove', onMouseMove);
}
function onMouseDown(event){
console.log('click down');
// event.button, 0 = left, 1 = middle, 2 = right
// console.log(event.button)
Utils.updateMousePosition(game.mouse, event);
if (event.button === 0){
game.addBlock();
} else if (event.button === 2){
game.removeBlock();
}
}
function onMouseMove(event){
Utils.updateMousePosition(game.mouse, event);
game.movePreviewBlock();
}
const ui = new UI();
const game = new Game(ui);
// Define what the World does at the start of a run:
AB.world.newRun = function()
{
ui.init();
ABWorld.renderer = new THREE.WebGLRenderer({ antialias: true, alpha:true });
ABWorld.renderer.shadowMap.enabled = true;
ABWorld.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// start a 3D scene:
ABWorld.init3d(startRadius, maxRadius, skycolor);
// Transparent background
ABWorld.renderer.setClearColor(0xffffff,0);
// Must come after scene has been initiated
game.init();
console.log('cam')
ABWorld.camera.fov = 45;
ABWorld.camera.updateProjectionMatrix();
// Lights
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(0, 10, 10);
directionalLight.castShadow = true;
ABWorld.scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0xd3def5, 1)//(0x3258a8);
ABWorld.scene.add(ambientLight);
// Events
addEventListeners();
const loader = new THREE.FileLoader();
loader.load(jsonPath, function ( data ) {
jsonData = JSON.parse(data)
createAllCubes(jsonData, 0.5);
});
};