Skip to content

Commit

Permalink
feat(core): v-if working on custom renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarosabu committed Mar 12, 2023
1 parent d480b36 commit e19da3a
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 65 deletions.
4 changes: 3 additions & 1 deletion packages/tres/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ function enter(e) {
</script>

<template>
<button @click="click">{{ gridVisible }}</button>
<p v-if="gridVisible">Soc invisible</p>
<TresCanvas v-bind="gl">
<TresPerspectiveCamera :args="[75, 1, 0.1, 1000]" :position="[0, 2, 7]"></TresPerspectiveCamera>
<TresAmbientLight :color="0xffffff" :intensity="0.75" />
Expand All @@ -53,7 +55,7 @@ function enter(e) {
<TresSphereGeometry :args="[1, 32, 16]"></TresSphereGeometry>
<TresMeshToonMaterial color="teal"></TresMeshToonMaterial>
</TresMesh>
<TresGridHelper :args="[4, 4]"></TresGridHelper>
<TresGridHelper v-if="gridVisible" :args="[4, 4]"></TresGridHelper>
</TresCanvas>
</template>

Expand Down
19 changes: 19 additions & 0 deletions packages/tres/src/components/TestRenderer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup lang="ts">
import { ref } from 'vue'
const gridVisible = ref(false)
setTimeout(() => {
gridVisible.value = true
}, 4000)
</script>
<template>
<TresPerspectiveCamera :args="[75, 1, 0.1, 1000]" :position="[0, 2, 7]"></TresPerspectiveCamera>
<TresAmbientLight :color="0xffffff" :intensity="0.75" />
<TresDirectionalLight :color="0xffffff" :intensity="2" :position="[-2, 2, 0]" />
<TresMesh :position="[0, 1, 0]">
<TresSphereGeometry :args="[1, 32, 16]"></TresSphereGeometry>
<TresMeshToonMaterial color="teal"></TresMeshToonMaterial>
</TresMesh>
<TresGridHelper v-if="gridVisible" :args="[4, 4]"></TresGridHelper>
</template>
29 changes: 17 additions & 12 deletions packages/tres/src/components/TresCanvas.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { defineComponent, h, PropType, ref, watch } from 'vue'
import { defineComponent, h, PropType, ref, watch, watchEffect, compile } from 'vue'
/* eslint-disable vue/one-component-per-file */
import * as THREE from 'three'
import { ShadowMapType, TextureEncoding, ToneMapping, Scene } from 'three'
import { createTres } from '/@/core/renderer'
import { useCamera, useRenderer, useRenderLoop, useRaycaster } from '/@/composables'
import { TresObject } from '/@/types'
import TestRenderer from './TestRenderer.vue'

export const TresCanvas = defineComponent({
name: 'TresCanvas',
Expand Down Expand Up @@ -52,21 +52,23 @@ export const TresCanvas = defineComponent({
renderer.value?.render(scene, activeCamera.value)
})

const internal = slots.default ? slots?.default() : [] || []
const app = createTres(slots)
app.mount(scene)

const internalComponent = defineComponent({
__name: 'tres-wrapper',
__scopeId: 'data-v-tres-supreme',
setup() {
return () => internal
},
watchEffect(() => {
if (slots) {
console.log('slots', slots)
}
})

const app = createTres(internalComponent)
app.mount(scene as unknown as TresObject)
console.log({
app,
scene,
TestRenderer,
})
expose({
scene,
app,
/* app, */
})
})

Expand All @@ -76,6 +78,8 @@ export const TresCanvas = defineComponent({
'div',
{
ref: container,
'data-v-app': true,
id: 'container',
style: {
position: 'relative',
width: '100%',
Expand Down Expand Up @@ -106,6 +110,7 @@ export const TresCanvas = defineComponent({
left: 0,
},
}),
/* ...(slots.default ? slots?.default() : [] || []), */
],
),
],
Expand Down
159 changes: 111 additions & 48 deletions packages/tres/src/core/nodeOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ import { Mesh } from 'three'
import { useEventListener } from '@vueuse/core'
import { TresEvent, TresObject } from '../types'

const HTML_TAGS =
'html,body,base,head,link,meta,style,title,address,article,aside,footer,' +
'header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,' +
'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,' +
'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,' +
'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,' +
'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,' +
'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,' +
'option,output,progress,select,textarea,details,dialog,menu,' +
'summary,template,blockquote,iframe,tfoot'

export const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS)

export function makeMap(str: string, expectsLowerCase?: boolean): (key: string) => boolean {
const map: Record<string, boolean> = Object.create(null)
const list: Array<string> = str.split(',')
for (let i = 0; i < list.length; i++) {
map[list[i]] = true
}
return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val]
}

const { logWarning } = useLogger()

function hasEvents(obj: any) {
Expand All @@ -21,20 +43,36 @@ function noop(fn: string): any {
fn
}

export const nodeOps: RendererOptions<TresObject, TresObject> = {
createElement(type, _isSVG, _isCustomizedBuiltIn, props) {
if (type === 'template') return null
if (type === 'div') return null
const doc = (typeof document !== 'undefined' ? document : null) as Document
export const svgNS = 'http://www.w3.org/2000/svg'

const templateContainer = doc && /*#__PURE__*/ doc.createElement('template')

let scene = null

export const nodeOps: RendererOptions<any, any> = {
createElement(tag, isSVG, anchor, props) {
// Vue core
/* const el = isSVG ? doc.createElementNS(svgNS, tag) : doc.createElement(tag, anchor ? { anchor } : undefined)
if (tag === 'select' && props && props.multiple != null) {
;(el as HTMLSelectElement).setAttribute('multiple', props.multiple)
}
return el */
// Tres
if (tag === 'template') return null
if (isHTMLTag(tag)) return null
let instance

if (props === null) {
props = {}
}

if (props?.arg) {
instance = new catalogue[type.replace('Tres', '')](...props.args)
instance = new catalogue[tag.replace('Tres', '')](...props.args)
} else {
instance = new catalogue[type.replace('Tres', '')]()
instance = new catalogue[tag.replace('Tres', '')]()
}

if (instance.isCamera) {
Expand All @@ -55,22 +93,30 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
}

console.log({
type,
tag,

instance,
threeObj: catalogue[type.replace('Tres', '')],
threeObj: catalogue[tag.replace('Tres', '')],
})

return instance
},
insert(child, parent, beforeChild) {
insert(child, parent, anchor) {
if (scene === null && parent.isScene) scene = parent
if (parent === null) parent = scene
//vue core
/* parent.insertBefore(child, anchor || null) */
if (parent?.isObject3D && child?.isObject3D) {
const index = beforeChild ? parent.children.indexOf(beforeChild) : 0
console.log('insert', { child, parent, anchor })
const index = anchor ? parent.children.indexOf(anchor) : 0
child.parent = parent
parent.children.splice(index, 0, child)
child.dispatchEvent({ type: 'added' })
} else if (typeof child?.attach === 'string') {
child.__previousAttach = child[parent.attach]
parent[child.attach] = child
child.__previousAttach = child[parent?.attach]
if (parent) {
parent[child.attach] = child
}
}

const { onLoop } = useRenderLoop()
Expand All @@ -82,7 +128,7 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
const { raycaster } = useRaycaster()
if (child && child instanceof Mesh && hasEvents(child)) {
onLoop(() => {
if (parent.children && child && raycaster) {
if (parent?.children && child && raycaster) {
const intersects = raycaster.value.intersectObjects(parent.children)

if (intersects.length > 0 && intersects[0].object.uuid === child.uuid) {
Expand Down Expand Up @@ -111,7 +157,12 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
}
},
remove(node) {
if (!node) return
// Vue Core
const parent = node.parentNode
if (parent) {
parent.removeChild(node)
}
/* if (!node) return
const parent = node.parent
if (parent) {
if (parent.isObject3D && node.isObject3D) {
Expand All @@ -126,46 +177,58 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
node.dispose?.()
node.traverse?.(node => {
;(node as TresObject).dispose?.()
})
}) */
},
patchProp(node, prop, prevValue, nextValue) {
let root = node
let key = prop
let target = root[key]

// Traverse pierced props (e.g. foo-bar=value => foo.bar = value)
/* if (key.includes('-')) {
const chain = key.split('-')
target = chain.reduce((acc, key) => acc[key], root)
key = chain.pop() as string
if (!target?.set) root = chain.reduce((acc, key) => acc[key], root)
} */

const value = nextValue
/* try {
const num = parseFloat(value)
value = isNaN(num) ? value : num
} catch (_) {} */

// Set prop, prefer atomic methods if applicable
if (!target?.set) root[key] = value
else if (target.constructor === value.constructor && target?.copy) target?.copy(value)
else if (Array.isArray(value)) target.set(...value)
else if (!target.isColor && target.setScalar) target.setScalar(value)
else target.set(value)
if (node) {
let root = node
let key = prop
let target = root?.[key]
// Traverse pierced props (e.g. foo-bar=value => foo.bar = value)
/* if (key.includes('-')) {
const chain = key.split('-')
target = chain.reduce((acc, key) => acc[key], root)
key = chain.pop() as string
if (!target?.set) root = chain.reduce((acc, key) => acc[key], root)
} */
const value = nextValue
/* try {
const num = parseFloat(value)
value = isNaN(num) ? value : num
} catch (_) {} */
// Set prop, prefer atomic methods if applicable
if (!target?.set) root[key] = value
else if (target.constructor === value.constructor && target?.copy) target?.copy(value)
else if (Array.isArray(value)) target.set(...value)
else if (!target.isColor && target.setScalar) target.setScalar(value)
else target.set(value)
}
},

parentNode(node) {
return node?.parent || null
// Vue core
return node.parentNode as Element | null
/* return node?.parent || null */
},
createText: text => doc.createTextNode(text),

createComment: text => doc.createComment(text),

setText: (node, text) => {
node.nodeValue = text
},

setElementText: (el, text) => {
el.textContent = text
},
nextSibling: node => node.nextSibling,

querySelector: selector => doc.querySelector(selector),

setScopeId(el, id) {
/* el.setAttribute(id, '') */
},
createText: () => noop('createText'),
createComment: () => noop('createComment'),
setText: () => noop('setText'),
setElementText: () => noop('setElementText'),
nextSibling: () => noop('nextSibling'),
querySelector: () => noop('querySelector'),
setScopeId: () => noop('setScopeId'),
cloneNode: () => noop('cloneNode'),
insertStaticContent: () => noop('insertStaticContent'),

Expand Down
35 changes: 31 additions & 4 deletions packages/tres/src/core/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import { isString } from '@vueuse/core'
import * as THREE from 'three'

import { createRenderer } from 'vue'
import { extend } from './catalogue'
import { nodeOps } from './nodeOps'

function normalizeContainer(container: Element | ShadowRoot | string): Element | null {
if (isString(container)) {
const res = document.querySelector(container)
/* if (__DEV__ && !res) {
console.warn(`Failed to mount app: mount target selector "${container}" returned null.`)
} */
return res
}
/* if (__DEV__ && window.ShadowRoot && container instanceof window.ShadowRoot && container.mode === 'closed') {
console.warn(`mounting on a ShadowRoot with \`{mode: "closed"}\` may lead to unpredictable bugs`)
} */
return container as any
}

export const { createApp } = createRenderer(nodeOps)

export const createTres = (...args) => {
const app = createApp(...args)
/* const { mount } = app
app.mount = (container: Element | string) => {} */
export const createTres = slots => {
const app = createApp(internalFnComponent)
function internalFnComponent() {
return slots.default()
}

const { mount } = app
app.mount = containerOrSelector => {
const container = normalizeContainer(containerOrSelector)
if (!container) return
if (container instanceof Element) {
container.removeAttribute('v-cloak')
container.setAttribute('data-v-app', '')
}
mount(container, false, false)
}

return app
}
Expand Down
2 changes: 2 additions & 0 deletions packages/tres/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ import './style.css'
export const app = createApp(App)

app.mount('#app')

console.log(app)
1 change: 1 addition & 0 deletions packages/tres/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default defineConfig({
resolve: {
alias: {
'/@': resolve(__dirname, './src'),
vue: 'vue/dist/vue.esm-bundler.js',
},
dedupe: ['@tresjs/cientos'],
},
Expand Down

0 comments on commit e19da3a

Please sign in to comment.