1
0
mirror of https://github.com/taigrr/skyline synced 2025-01-18 04:33:13 -08:00

WIP export changes

This commit is contained in:
Jason Long 2020-12-11 16:45:14 -05:00
parent c71a3c2b23
commit af1f5f610a
5 changed files with 1393 additions and 738 deletions

View File

@ -9,10 +9,10 @@ gulp.task('browser-sync', function() {
}) })
}) })
gulp.task('watch', ['browser-sync'], function () { gulp.task('watch', gulp.series('browser-sync', function() {
gulp.watch("css/*.css").on('change', bs.reload) gulp.watch("css/*.css").on('change', bs.reload)
gulp.watch("js/*.js").on('change', bs.reload) gulp.watch("js/*.js").on('change', bs.reload)
gulp.watch("*.html").on('change', bs.reload) gulp.watch("*.html").on('change', bs.reload)
}) }))
gulp.task('auto', ['browser-sync', 'watch']); gulp.task('auto', gulp.series('browser-sync', 'watch'));

342
js/OBJExporter.js Normal file
View File

@ -0,0 +1,342 @@
import {
BufferGeometry,
Color,
Geometry,
Line,
Matrix3,
Mesh,
Points,
Vector2,
Vector3
} from "./three.module.js";
var OBJExporter = function () {};
OBJExporter.prototype = {
constructor: OBJExporter,
parse: function ( object ) {
var output = '';
var indexVertex = 0;
var indexVertexUvs = 0;
var indexNormals = 0;
var vertex = new Vector3();
var color = new Color();
var normal = new Vector3();
var uv = new Vector2();
var i, j, k, l, m, face = [];
var parseMesh = function ( mesh ) {
var nbVertex = 0;
var nbNormals = 0;
var nbVertexUvs = 0;
var geometry = mesh.geometry;
var normalMatrixWorld = new Matrix3();
if ( geometry instanceof Geometry ) {
geometry = new BufferGeometry().setFromObject( mesh );
}
if ( geometry instanceof BufferGeometry ) {
// shortcuts
var vertices = geometry.getAttribute( 'position' );
var normals = geometry.getAttribute( 'normal' );
var uvs = geometry.getAttribute( 'uv' );
var indices = geometry.getIndex();
// name of the mesh object
output += 'o ' + mesh.name + '\n';
// name of the mesh material
if ( mesh.material && mesh.material.name ) {
output += 'usemtl ' + mesh.material.name + '\n';
}
// vertices
if ( vertices !== undefined ) {
for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
vertex.x = vertices.getX( i );
vertex.y = vertices.getY( i );
vertex.z = vertices.getZ( i );
// transform the vertex to world space
vertex.applyMatrix4( mesh.matrixWorld );
// transform the vertex to export format
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
}
}
// uvs
if ( uvs !== undefined ) {
for ( i = 0, l = uvs.count; i < l; i ++, nbVertexUvs ++ ) {
uv.x = uvs.getX( i );
uv.y = uvs.getY( i );
// transform the uv to export format
output += 'vt ' + uv.x + ' ' + uv.y + '\n';
}
}
// normals
if ( normals !== undefined ) {
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
for ( i = 0, l = normals.count; i < l; i ++, nbNormals ++ ) {
normal.x = normals.getX( i );
normal.y = normals.getY( i );
normal.z = normals.getZ( i );
// transform the normal to world space
normal.applyMatrix3( normalMatrixWorld ).normalize();
// transform the normal to export format
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
}
}
// faces
if ( indices !== null ) {
for ( i = 0, l = indices.count; i < l; i += 3 ) {
for ( m = 0; m < 3; m ++ ) {
j = indices.getX( i + m ) + 1;
face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
}
// transform the face to export format
output += 'f ' + face.join( ' ' ) + "\n";
}
} else {
for ( i = 0, l = vertices.count; i < l; i += 3 ) {
for ( m = 0; m < 3; m ++ ) {
j = i + m + 1;
face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
}
// transform the face to export format
output += 'f ' + face.join( ' ' ) + "\n";
}
}
} else {
console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', geometry );
}
// update index
indexVertex += nbVertex;
indexVertexUvs += nbVertexUvs;
indexNormals += nbNormals;
};
var parseLine = function ( line ) {
var nbVertex = 0;
var geometry = line.geometry;
var type = line.type;
if ( geometry instanceof Geometry ) {
geometry = new BufferGeometry().setFromObject( line );
}
if ( geometry instanceof BufferGeometry ) {
// shortcuts
var vertices = geometry.getAttribute( 'position' );
// name of the line object
output += 'o ' + line.name + '\n';
if ( vertices !== undefined ) {
for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
vertex.x = vertices.getX( i );
vertex.y = vertices.getY( i );
vertex.z = vertices.getZ( i );
// transform the vertex to world space
vertex.applyMatrix4( line.matrixWorld );
// transform the vertex to export format
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
}
}
if ( type === 'Line' ) {
output += 'l ';
for ( j = 1, l = vertices.count; j <= l; j ++ ) {
output += ( indexVertex + j ) + ' ';
}
output += '\n';
}
if ( type === 'LineSegments' ) {
for ( j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) {
output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n';
}
}
} else {
console.warn( 'THREE.OBJExporter.parseLine(): geometry type unsupported', geometry );
}
// update index
indexVertex += nbVertex;
};
var parsePoints = function ( points ) {
var nbVertex = 0;
var geometry = points.geometry;
if ( geometry instanceof Geometry ) {
geometry = new BufferGeometry().setFromObject( points );
}
if ( geometry instanceof BufferGeometry ) {
var vertices = geometry.getAttribute( 'position' );
var colors = geometry.getAttribute( 'color' );
output += 'o ' + points.name + '\n';
if ( vertices !== undefined ) {
for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
vertex.fromBufferAttribute( vertices, i );
vertex.applyMatrix4( points.matrixWorld );
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
if ( colors !== undefined ) {
color.fromBufferAttribute( colors, i );
output += ' ' + color.r + ' ' + color.g + ' ' + color.b;
}
output += '\n';
}
}
output += 'p ';
for ( j = 1, l = vertices.count; j <= l; j ++ ) {
output += ( indexVertex + j ) + ' ';
}
output += '\n';
} else {
console.warn( 'THREE.OBJExporter.parsePoints(): geometry type unsupported', geometry );
}
// update index
indexVertex += nbVertex;
};
object.traverse( function ( child ) {
if ( child instanceof Mesh ) {
parseMesh( child );
}
if ( child instanceof Line ) {
parseLine( child );
}
if ( child instanceof Points ) {
parsePoints( child );
}
} );
return output;
}
};
export { OBJExporter };

View File

@ -1,7 +1,8 @@
import * as THREE from "./three.module.js"; import * as THREE from "./three.module.js"
import { GLTFLoader } from './GLTFLoader.js'; import { GLTFLoader } from './GLTFLoader.js'
import { OrbitControls } from './OrbitControls.js'; import { OrbitControls } from './OrbitControls.js'
import { GUI } from './dat.gui.module.js'; import { OBJExporter } from './OBJExporter.js'
import { GUI } from './dat.gui.module.js'
const BASE_LENGTH = 0.834 const BASE_LENGTH = 0.834
const BASE_WIDTH = 0.167 const BASE_WIDTH = 0.167
@ -10,25 +11,25 @@ const CUBE_SIZE = 0.0143
const MAX_HEIGHT = 0.14 const MAX_HEIGHT = 0.14
const FACE_ANGLE = 104.79 const FACE_ANGLE = 104.79
let username = "jasonlong"
let year = "2018"
let json = {} let json = {}
let font = undefined let font = undefined
let fontSize = 0.025 let fontSize = 0.025
let fontHeight = 0.00658 // Extrusion thickness let fontHeight = 0.00658 // Extrusion thickness
let nameMesh
let yearMesh
let camera, scene, renderer let camera, scene, renderer, barGroup
let bronzeMaterial let bronzeMaterial
var urlParams = new URLSearchParams(window.location.search); const gui = new GUI()
const params = {
if (urlParams.has('username')) { username: 'jasonlong',
username = urlParams.get('username') year: '2019'
} }
if (urlParams.has('year')) { let usernameController
year = urlParams.get('year') let yearController
} let exporter = new OBJExporter()
// Import JSON data // Import JSON data
async function loadJSON(username, year) { async function loadJSON(username, year) {
@ -36,25 +37,38 @@ async function loadJSON(username, year) {
let response = await fetch(url) let response = await fetch(url)
if (response.ok) { if (response.ok) {
json = await response.json() json = await response.json()
init() addBars()
addText()
render() render()
} else { } else {
alert("HTTP-Error: " + response.status) alert("HTTP-Error: " + response.status)
} }
} }
loadJSON(username, year) usernameController = gui.add(params, 'username').name('Username')
usernameController.onChange(() => {
removeBars()
removeText()
loadJSON(params.username, params.year)
})
yearController = gui.add(params, 'year').name('Year')
yearController.onChange(() => {
removeBars()
removeText()
loadJSON(params.username, params.year)
})
const fixSideNormals = (geometry) => { const fixSideNormals = (geometry) => {
let triangle = new THREE.Triangle() let triangle = new THREE.Triangle()
// "fix" side normals by removing z-component of normals for side faces // "fix" side normals by removing z-component of normals for side faces
var triangleAreaHeuristics = 0.1 * ( fontHeight * fontSize ); var triangleAreaHeuristics = 0.1 * (fontHeight * fontSize)
for (var i = 0; i < geometry.faces.length; i++) { for (var i = 0; i < geometry.faces.length; i++) {
let face = geometry.faces[i] let face = geometry.faces[i]
if ( face.materialIndex == 1 ) { if (face.materialIndex == 1) {
for ( var j = 0; j < face.vertexNormals.length; j ++ ) { for (var j = 0; j < face.vertexNormals.length; j++) {
face.vertexNormals[ j ].z = 0 face.vertexNormals[ j ].z = 0
face.vertexNormals[ j ].normalize() face.vertexNormals[ j ].normalize()
} }
@ -63,10 +77,10 @@ const fixSideNormals = (geometry) => {
let vb = geometry.vertices[ face.b ] let vb = geometry.vertices[ face.b ]
let vc = geometry.vertices[ face.c ] let vc = geometry.vertices[ face.c ]
let s = triangle.set( va, vb, vc ).getArea() let s = triangle.set(va, vb, vc).getArea()
if ( s > triangleAreaHeuristics ) { if (s > triangleAreaHeuristics) {
for ( var j = 0; j < face.vertexNormals.length; j ++ ) { for (var j = 0; j < face.vertexNormals.length; j++) {
face.vertexNormals[ j ].copy(face.normal) face.vertexNormals[ j ].copy(face.normal)
} }
} }
@ -75,7 +89,7 @@ const fixSideNormals = (geometry) => {
} }
const createText = () => { const createText = () => {
let nameGeo = new THREE.TextGeometry(username, { let nameGeo = new THREE.TextGeometry(params.username, {
font: font, font: font,
size: fontSize, size: fontSize,
height: fontHeight height: fontHeight
@ -84,7 +98,7 @@ const createText = () => {
nameGeo.computeBoundingBox() nameGeo.computeBoundingBox()
nameGeo.computeVertexNormals() nameGeo.computeVertexNormals()
let yearGeo = new THREE.TextGeometry(year, { let yearGeo = new THREE.TextGeometry(params.year, {
font: font, font: font,
size: fontSize, size: fontSize,
height: fontHeight height: fontHeight
@ -97,7 +111,7 @@ const createText = () => {
fixSideNormals(yearGeo) fixSideNormals(yearGeo)
nameGeo = new THREE.BufferGeometry().fromGeometry(nameGeo) nameGeo = new THREE.BufferGeometry().fromGeometry(nameGeo)
let nameMesh = new THREE.Mesh(nameGeo, bronzeMaterial) nameMesh = new THREE.Mesh(nameGeo, bronzeMaterial)
nameMesh.position.x = -0.295 nameMesh.position.x = -0.295
nameMesh.position.y = -0.075 nameMesh.position.y = -0.075
@ -105,10 +119,11 @@ const createText = () => {
nameMesh.geometry.rotateX(FACE_ANGLE * Math.PI / 2) nameMesh.geometry.rotateX(FACE_ANGLE * Math.PI / 2)
nameMesh.geometry.rotateY(Math.PI * 2) nameMesh.geometry.rotateY(Math.PI * 2)
nameMesh.name = "name_mesh"
scene.add(nameMesh) scene.add(nameMesh)
yearGeo = new THREE.BufferGeometry().fromGeometry(yearGeo) yearGeo = new THREE.BufferGeometry().fromGeometry(yearGeo)
let yearMesh = new THREE.Mesh(yearGeo, bronzeMaterial) yearMesh = new THREE.Mesh(yearGeo, bronzeMaterial)
yearMesh.position.x = 0.280 yearMesh.position.x = 0.280
yearMesh.position.y = -0.075 yearMesh.position.y = -0.075
@ -116,27 +131,32 @@ const createText = () => {
yearMesh.geometry.rotateX(FACE_ANGLE * Math.PI / 2) yearMesh.geometry.rotateX(FACE_ANGLE * Math.PI / 2)
yearMesh.geometry.rotateY(Math.PI * 2) yearMesh.geometry.rotateY(Math.PI * 2)
yearMesh.name = "year_mesh"
scene.add(yearMesh) scene.add(yearMesh)
} }
const render = () => {
renderer.render(scene, camera)
}
const init = () => { const init = () => {
// SCENE // SCENE
scene = new THREE.Scene() scene = new THREE.Scene()
// CAMERA // CAMERA
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.01, 100 ) camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 100)
// RENDERER // RENDERER
renderer = new THREE.WebGLRenderer({ antialias: true }) renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio( window.devicePixelRatio ) renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize( window.innerWidth, window.innerHeight ) renderer.setSize(window.innerWidth, window.innerHeight)
renderer.outputEncoding = THREE.sRGBEncoding renderer.outputEncoding = THREE.sRGBEncoding
renderer.setClearColor(0xffffff, 1) renderer.setClearColor(0xffffff, 1)
document.body.appendChild(renderer.domElement) document.body.appendChild(renderer.domElement)
// MATERIALS // MATERIALS
let phongMaterial = new THREE.MeshPhongMaterial( { color: 0x40c463, transparent: true, opacity: 0.2, side: THREE.DoubleSide } ) let phongMaterial = new THREE.MeshPhongMaterial({ color: 0x40c463, transparent: true, opacity: 0.2, side: THREE.DoubleSide })
bronzeMaterial = new THREE.MeshPhysicalMaterial( { color: 0x645f61, metalness: 1, clearcoat: 0.5, clearcoatRoughness: 0.5, side: THREE.DoubleSide } ) bronzeMaterial = new THREE.MeshPhysicalMaterial({ color: 0x645f61, metalness: 1, clearcoat: 0.5, clearcoatRoughness: 0.5, side: THREE.DoubleSide })
// LIGHTS // LIGHTS
const dLight1 = new THREE.DirectionalLight(0xebeb8c, 0.8) const dLight1 = new THREE.DirectionalLight(0xebeb8c, 0.8)
@ -182,15 +202,21 @@ const init = () => {
scene.add(base.scene) scene.add(base.scene)
}) })
// USERNAME + YEAR const box = new THREE.Box3().setFromObject(scene)
let fontLoader = new THREE.FontLoader() const center = box.getCenter(new THREE.Vector3())
fontLoader.load('../fonts/helvetiker_regular.typeface.json', function (response) { camera.lookAt(center)
font = response camera.position.y = -0.4
createText() camera.position.z = 0.3
})
let controls = new OrbitControls(camera, renderer.domElement)
controls.autoRotate = false
controls.screenSpacePanning = true
controls.addEventListener('change', render)
}
const addBars = () => {
// CONTRIBUTION BARS // CONTRIBUTION BARS
let barGroup = new THREE.Group() barGroup = new THREE.Group()
let maxCount = json.max let maxCount = json.max
let x = 0 let x = 0
let y = 0 let y = 0
@ -213,33 +239,63 @@ const init = () => {
barGroup.position.x -= groupCenter.x barGroup.position.x -= groupCenter.x
barGroup.position.y -= groupCenter.y barGroup.position.y -= groupCenter.y
scene.add(barGroup) scene.add(barGroup)
render()
const box = new THREE.Box3().setFromObject(scene)
const center = box.getCenter(new THREE.Vector3())
camera.lookAt(center)
camera.position.y = -0.4
camera.position.z = 0.3
let controls = new OrbitControls(camera, renderer.domElement)
controls.autoRotate = false
controls.screenSpacePanning = true
controls.addEventListener('change', render);
} }
const render = () => { const removeBars = () => {
renderer.render(scene, camera) scene.remove(barGroup)
barGroup = null
render()
}
const addText = () => {
let fontLoader = new THREE.FontLoader()
fontLoader.load('../fonts/helvetiker_regular.typeface.json', function (response) {
font = response
createText()
render()
})
}
const removeText = () => {
scene.remove(nameMesh)
scene.remove(yearMesh)
nameMesh = null
yearMesh = null
render()
} }
// //
// Event listeners // Event listeners
// //
const onExport = () => {
const result = exporter.parse(mesh)
save(new Blob([ text ], { type: 'text/plain' }), filename)
}
const save = (blob, filename) => {
downloadLink.href = URL.createObjectURL(blob)
downloadLink.download = filename
downloadLink.click()
}
const onWindowResize = () => { const onWindowResize = () => {
let canvasWidth = window.innerWidth; let canvasWidth = window.innerWidth
let canvasHeight = window.innerHeight; let canvasHeight = window.innerHeight
renderer.setSize( canvasWidth, canvasHeight ); renderer.setSize(canvasWidth, canvasHeight)
camera.aspect = canvasWidth / canvasHeight; camera.aspect = canvasWidth / canvasHeight
camera.updateProjectionMatrix(); camera.updateProjectionMatrix()
render() render()
} }
window.addEventListener('resize', onWindowResize, false) window.addEventListener('resize', onWindowResize, false)
///////////////////////////////////////////////////////////////////////
//
// ENTRY POINT
//
///////////////////////////////////////////////////////////////////////
init()
loadJSON(params.username, params.year)

1609
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,10 @@
}, },
"devDependencies": { "devDependencies": {
"browser-sync": "^2.18.2", "browser-sync": "^2.18.2",
"gulp": "^3.9.1", "gulp": "^4.0.2",
"gulp-watch": "^4.3.11" "gulp-watch": "^5.0.1"
}, },
"dependencies": { "dependencies": {
"three": "^0.118.1" "three": "^0.123.0"
} }
} }