commit a21071af6a597d40e019c1890d3e0c9aebb2921e Author: Kevin Levron Date: Mon Sep 14 16:57:11 2020 +0200 init diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..93240c8 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,40 @@ +/* eslint-disable quote-props */ +module.exports = { + env: { + browser: true, + es2020: true, + }, + extends: [ + 'plugin:vue/essential', + 'standard', + ], + parserOptions: { + ecmaVersion: 12, + sourceType: 'module', + }, + plugins: [ + 'vue', + ], + rules: { + 'semi': [2, 'always'], + 'space-before-function-paren': 'off', + 'one-var': 'off', + 'quotes': 'off', + 'quote-props': 'off', + 'object-curly-newline': 'off', + 'no-unused-vars': 'warn', + // 'comma-dangle': ['warn', 'always-multiline'], + 'comma-dangle': ['warn', { + 'arrays': 'always-multiline', + 'objects': 'always-multiline', + 'imports': 'always-multiline', + 'exports': 'always-multiline', + 'functions': 'never', + }], + 'indent': 'warn', + 'no-new': 'off', + 'object-property-newline': 'off', + 'eqeqeq': 'warn', + 'no-multiple-empty-lines': 'off', + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e42754a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +.DS_Store +dist +*.local \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..d49c18c --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..0d06ebf --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "test-vite", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "dependencies": { + "three": "^0.119", + "vue": "^3.0.0-rc.10", + "vuex": "^4.0.0-beta.4" + }, + "devDependencies": { + "@vue/compiler-sfc": "^3.0.0-rc.10", + "eslint": "^7.7.0", + "eslint-config-airbnb-base": "^14.2.0", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-vue": "^6.2.2", + "vite": "^1.0.0-rc.1" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..3710e9c --- /dev/null +++ b/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/components/Test.vue b/src/components/Test.vue new file mode 100644 index 0000000..321278d --- /dev/null +++ b/src/components/Test.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/core/PerspectiveCamera.js b/src/core/PerspectiveCamera.js new file mode 100644 index 0000000..3c1fe64 --- /dev/null +++ b/src/core/PerspectiveCamera.js @@ -0,0 +1,26 @@ +import { PerspectiveCamera, Vector3 } from 'three'; + +import { setFromProp } from '../tools.js'; + +export default { + inject: ['three'], + props: { + fov: { + type: Number, + default: 50, + }, + position: Object, + // position: { + // type: Object, + // default: new Vector3(), + // }, + }, + created() { + const camera = new PerspectiveCamera(this.fov); + setFromProp(camera.position, this.position); + this.three.camera = camera; + }, + render() { + return []; + }, +}; diff --git a/src/core/Renderer.vue b/src/core/Renderer.vue new file mode 100644 index 0000000..8f119df --- /dev/null +++ b/src/core/Renderer.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/src/core/Scene.js b/src/core/Scene.js new file mode 100644 index 0000000..a524b65 --- /dev/null +++ b/src/core/Scene.js @@ -0,0 +1,22 @@ +import { Scene } from 'three'; + +export default { + emits: ['scene-ready'], + inject: ['three'], + setup (props) { + const scene = new Scene(); + return { scene }; + }, + provide() { + return { + scene: this.scene, + }; + }, + mounted() { + this.three.scene = this.scene; + this.$emit('scene-ready'); + }, + render() { + return this.$slots.default(); + }, +}; diff --git a/src/core/index.js b/src/core/index.js new file mode 100644 index 0000000..6d8b046 --- /dev/null +++ b/src/core/index.js @@ -0,0 +1,3 @@ +export { default as Renderer } from './Renderer.vue'; +export { default as PerspectiveCamera } from './PerspectiveCamera.js'; +export { default as Scene } from './Scene.js'; diff --git a/src/core/useThree.js b/src/core/useThree.js new file mode 100644 index 0000000..150d3a2 --- /dev/null +++ b/src/core/useThree.js @@ -0,0 +1,175 @@ +import { + PerspectiveCamera, + Plane, + Raycaster, + Vector2, + Vector3, + WebGLRenderer, +} from 'three'; + +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; + +/** + * Three.js helper + */ +export default function useThree() { + // default conf + const conf = { + canvas: null, + antialias: true, + alpha: false, + camera_fov: 50, + camera_pos: new Vector3(0, 0, 100), + camera_ctrl: false, + mouse_move: false, + mouse_raycast: false, + window_resize: true, + }; + + // size + const size = { + width: 0, height: 0, + wWidth: 0, wHeight: 0, + ratio: 0, + }; + + // mouse tracking + const mouse = new Vector2(); + const mouseV3 = new Vector3(); + const mousePlane = new Plane(new Vector3(0, 0, 1), 0); + const raycaster = new Raycaster(); + + // returned object + const obj = { + conf, + renderer: null, + camera: null, + cameraCtrl: null, + materials: {}, + size, + mouse, mouseV3, + init, + dispose, + render, + setSize, + }; + + /** + * init three + */ + function init(params) { + if (params) { + for (const [key, value] of Object.entries(params)) { + conf[key] = value; + } + } + + obj.renderer = new WebGLRenderer({ canvas: conf.canvas, antialias: conf.antialias, alpha: conf.alpha }); + + // obj.camera = new PerspectiveCamera(conf.camera_fov); + // obj.camera.position.copy(conf.camera_pos); + + // if (conf.camera_ctrl) { + // obj.cameraCtrl = new OrbitControls(obj.camera, obj.renderer.domElement); + // if (conf.camera_ctrl instanceof Object) { + // for (const [key, value] of Object.entries(conf.camera_ctrl)) { + // obj.cameraCtrl[key] = value; + // } + // } + // } + + if (conf.window_resize) { + onResize(); + window.addEventListener('resize', onResize); + } + + if (conf.mouse_move) { + obj.renderer.domElement.addEventListener('mousemove', onMousemove); + obj.renderer.domElement.addEventListener('mouseleave', onMouseleave); + } + + return obj; + }; + + /** + * default render + */ + function render(scene) { + if (obj.cameraCtrl) obj.cameraCtrl.update(); + obj.renderer.render(scene, obj.camera); + } + + /** + * remove listeners + */ + function dispose() { + window.removeEventListener('resize', onResize); + obj.renderer.domElement.removeEventListener('mousemove', onMousemove); + obj.renderer.domElement.removeEventListener('mouseleave', onMouseleave); + } + + /** + * mousemove listener + */ + function onMousemove(e) { + mouse.x = (e.clientX / size.width) * 2 - 1; + mouse.y = -(e.clientY / size.height) * 2 + 1; + updateMouseV3(); + } + + /** + * mouseleave listener + */ + function onMouseleave(e) { + mouse.x = 0; + mouse.y = 0; + updateMouseV3(); + } + + /** + * get 3d mouse position + */ + function updateMouseV3() { + if (conf.mouse_raycast) { + obj.camera.getWorldDirection(mousePlane.normal); + mousePlane.normal.normalize(); + raycaster.setFromCamera(mouse, obj.camera); + raycaster.ray.intersectPlane(mousePlane, mouseV3); + } + } + + /** + * resize listener + */ + function onResize() { + setSize(window.innerWidth, window.innerHeight); + } + + /** + * update renderer size and camera + */ + function setSize(width, height) { + size.width = width; + size.height = height; + size.ratio = width / height; + + obj.renderer.setSize(width, height, false); + obj.camera.aspect = size.ratio; + obj.camera.updateProjectionMatrix(); + + const wsize = getCameraSize(); + size.wWidth = wsize[0]; size.wHeight = wsize[1]; + } + + /** + * calculate camera visible area size + */ + function getCameraSize() { + const vFOV = (obj.camera.fov * Math.PI) / 180; + const h = 2 * Math.tan(vFOV / 2) * Math.abs(obj.camera.position.z); + const w = h * obj.camera.aspect; + return [w, h]; + } + + return obj; +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..9845495 --- /dev/null +++ b/src/index.css @@ -0,0 +1,6 @@ +body { + margin: 0; +} + +#app { +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7aa724f --- /dev/null +++ b/src/index.js @@ -0,0 +1,4 @@ +export * from './core/index.js'; +export * from './lights/index.js'; +export * from './materials/index.js'; +export * from './meshes/index.js'; diff --git a/src/lights/Light.js b/src/lights/Light.js new file mode 100644 index 0000000..7b2eb23 --- /dev/null +++ b/src/lights/Light.js @@ -0,0 +1,39 @@ +import { + Vector3, +} from 'three'; + +import { setFromProp } from '../tools.js'; + +export default { + inject: ['scene'], + props: { + color: { + type: String, + default: '#ffffff', + }, + intensity: { + type: Number, + default: 1, + }, + distance: { + type: Number, + default: 0, + }, + decay: { + type: Number, + default: 1, + }, + position: Object, + // position: { + // type: Object, + // default: new Vector3(0, 0, 0), + // }, + }, + mounted() { + setFromProp(this.light.position, this.position); + this.scene.add(this.light); + }, + render() { + return []; + }, +}; diff --git a/src/lights/PointLight.js b/src/lights/PointLight.js new file mode 100644 index 0000000..f607a59 --- /dev/null +++ b/src/lights/PointLight.js @@ -0,0 +1,9 @@ +import { PointLight } from 'three'; +import Light from './Light.js'; + +export default { + extends: Light, + created() { + this.light = new PointLight(this.color, this.intensity, this.distance, this.decay); + }, +}; diff --git a/src/lights/index.js b/src/lights/index.js new file mode 100644 index 0000000..eaa3996 --- /dev/null +++ b/src/lights/index.js @@ -0,0 +1 @@ +export { default as PointLight } from './PointLight.js'; diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..b48a52e --- /dev/null +++ b/src/main.js @@ -0,0 +1,6 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import './index.css'; + +const app = createApp(App); +app.mount('#app'); diff --git a/src/materials/BasicMaterial.js b/src/materials/BasicMaterial.js new file mode 100644 index 0000000..313273a --- /dev/null +++ b/src/materials/BasicMaterial.js @@ -0,0 +1,11 @@ +import { MeshBasicMaterial } from 'three'; +import Material from './Material'; + +export default { + extends: Material, + created() { + this.material = new MeshBasicMaterial({ + color: this.color, + }); + }, +}; diff --git a/src/materials/LambertMaterial.js b/src/materials/LambertMaterial.js new file mode 100644 index 0000000..be54279 --- /dev/null +++ b/src/materials/LambertMaterial.js @@ -0,0 +1,11 @@ +import { MeshLambertMaterial } from 'three'; +import Material from './Material'; + +export default { + extends: Material, + created() { + this.material = new MeshLambertMaterial({ + color: this.color, + }); + }, +}; diff --git a/src/materials/Material.js b/src/materials/Material.js new file mode 100644 index 0000000..f65714e --- /dev/null +++ b/src/materials/Material.js @@ -0,0 +1,16 @@ +export default { + inject: ['three'], + props: { + name: String, + color: { + type: String, + default: '#ffffff', + }, + }, + mounted() { + this.three.materials[this.name] = this.material; + }, + render() { + return []; + }, +}; diff --git a/src/materials/PhongMaterial.js b/src/materials/PhongMaterial.js new file mode 100644 index 0000000..962843c --- /dev/null +++ b/src/materials/PhongMaterial.js @@ -0,0 +1,11 @@ +import { MeshPhongMaterial } from 'three'; +import Material from './Material'; + +export default { + extends: Material, + created() { + this.material = new MeshPhongMaterial({ + color: this.color, + }); + }, +}; diff --git a/src/materials/PhysicalMaterial.js b/src/materials/PhysicalMaterial.js new file mode 100644 index 0000000..60e1b04 --- /dev/null +++ b/src/materials/PhysicalMaterial.js @@ -0,0 +1,11 @@ +import { MeshPhysicalMaterial } from 'three'; +import Material from './Material'; + +export default { + extends: Material, + created() { + this.material = new MeshPhysicalMaterial({ + color: this.color, + }); + }, +}; diff --git a/src/materials/StandardMaterial.js b/src/materials/StandardMaterial.js new file mode 100644 index 0000000..c7b942d --- /dev/null +++ b/src/materials/StandardMaterial.js @@ -0,0 +1,11 @@ +import { MeshStandardMaterial } from 'three'; +import Material from './Material'; + +export default { + extends: Material, + created() { + this.material = new MeshStandardMaterial({ + color: this.color, + }); + }, +}; diff --git a/src/materials/index.js b/src/materials/index.js new file mode 100644 index 0000000..9bcd542 --- /dev/null +++ b/src/materials/index.js @@ -0,0 +1,5 @@ +export { default as BasicMaterial } from './BasicMaterial.js'; +export { default as LambertMaterial } from './LambertMaterial.js'; +export { default as PhongMaterial } from './PhongMaterial.js'; +export { default as PhysicalMaterial } from './PhysicalMaterial.js'; +export { default as StandardMaterial } from './StandardMaterial.js'; diff --git a/src/meshes/Box.js b/src/meshes/Box.js new file mode 100644 index 0000000..b18a0ad --- /dev/null +++ b/src/meshes/Box.js @@ -0,0 +1,33 @@ +import { + BoxBufferGeometry, +} from 'three'; + +import Mesh from './Mesh.js'; + +export default { + extends: Mesh, + props: { + size: { + type: Number, + }, + width: { + type: Number, + default: 1, + }, + height: { + type: Number, + default: 1, + }, + depth: { + type: Number, + default: 1, + }, + }, + created() { + if (this.size) { + this.geometry = new BoxBufferGeometry(this.size, this.size, this.size); + } else { + this.geometry = new BoxBufferGeometry(this.width, this.height, this.depth); + } + }, +}; diff --git a/src/meshes/Mesh.js b/src/meshes/Mesh.js new file mode 100644 index 0000000..ab92583 --- /dev/null +++ b/src/meshes/Mesh.js @@ -0,0 +1,34 @@ +import { Mesh } from 'three'; +import { setFromProp } from '../tools.js'; + +export default { + inject: ['three', 'scene'], + props: { + material: String, + position: Object, + rotation: Object, + scale: Object, + // position: { + // type: Object, + // default: new Vector3(), + // }, + // rotation: { + // type: Object, + // default: new Euler(), + // }, + // scale: { + // type: Object, + // default: new Vector3(1, 1, 1), + // }, + }, + mounted() { + this.mesh = new Mesh(this.geometry, this.three.materials[this.material]); + setFromProp(this.mesh.position, this.position); + setFromProp(this.mesh.rotation, this.rotation); + setFromProp(this.mesh.scale, this.scale); + this.scene.add(this.mesh); + }, + render() { + return []; + }, +}; diff --git a/src/meshes/Sphere.js b/src/meshes/Sphere.js new file mode 100644 index 0000000..cf5ccea --- /dev/null +++ b/src/meshes/Sphere.js @@ -0,0 +1,15 @@ +import { + SphereBufferGeometry, +} from 'three'; + +import Mesh from './Mesh.js'; + +export default { + extends: Mesh, + props: { + radius: Number, + }, + created() { + this.geometry = new SphereBufferGeometry(this.radius, 32, 32); + }, +}; diff --git a/src/meshes/index.js b/src/meshes/index.js new file mode 100644 index 0000000..ff7879e --- /dev/null +++ b/src/meshes/index.js @@ -0,0 +1,2 @@ +export { default as Box } from './Box.js'; +export { default as Sphere } from './Sphere.js'; diff --git a/src/tools.js b/src/tools.js new file mode 100644 index 0000000..e25ca1e --- /dev/null +++ b/src/tools.js @@ -0,0 +1,7 @@ +export function setFromProp(o, prop) { + if (prop instanceof Object) { + for (const [key, value] of Object.entries(prop)) { + o[key] = value; + } + } +};