mylingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
151 lines (122 loc) • 4.2 kB
text/typescript
import { LinearToneMapping, NoToneMapping } from "three"
import { getExposure } from "../../states/useExposure"
import { getResolution, setResolution } from "../../states/useResolution"
import { createEffect, createNestedEffect } from "@lincode/reactivity"
import { getWebXR } from "../../states/useWebXR"
import { getRenderer } from "../../states/useRenderer"
import { getPBR } from "../../states/usePBR"
import { getSecondaryCamera } from "../../states/useSecondaryCamera"
import { VRButton } from "./VRButton"
import { getAutoMount } from "../../states/useAutoMount"
import { debounce } from "@lincode/utils"
import { getEditorMounted } from "../../states/useEditorMounted"
import { getPixelRatio } from "../../states/usePixelRatio"
const rootContainer = document.createElement("div")
Object.assign(rootContainer.style, {
position: "absolute",
left: "0px",
top: "0px",
width: "100%",
height: "100%"
})
export const container = document.createElement("div")
Object.assign(container.style, {
position: "absolute",
left: "0px",
top: "0px",
width: "100%"
})
rootContainer.appendChild(container)
getSecondaryCamera((cam) => (container.style.height = cam ? "50%" : "100%"))
export const containerBounds = [container.getBoundingClientRect()]
const useResize = (el: Element) => {
createNestedEffect(() => {
const handleResize = () => {
containerBounds[0] = container.getBoundingClientRect()
setResolution(
el === document.body
? [window.innerWidth, window.innerHeight]
: [el.clientWidth, el.clientHeight]
)
}
handleResize()
const handleResizeDebounced = debounce(handleResize, 100, "both")
window.addEventListener("resize", handleResizeDebounced)
const handle = getEditorMounted(handleResizeDebounced)
return () => {
window.removeEventListener("resize", handleResize)
handle.cancel()
}
}, [el])
}
createEffect(() => {
const autoMount = getAutoMount()
if (!autoMount) return
if (typeof autoMount === "string") {
const el = document.querySelector(autoMount)
if (!el) return
el.prepend(rootContainer)
useResize(el)
return () => {
el.removeChild(rootContainer)
}
}
if (autoMount === true) {
document.body.prepend(rootContainer)
useResize(document.body)
return () => {
document.body.removeChild(rootContainer)
}
}
autoMount.prepend(rootContainer)
useResize(autoMount)
return () => {
autoMount.removeChild(rootContainer)
}
}, [getAutoMount])
createEffect(() => {
const renderer = getRenderer()
if (!renderer) return
const canvas = renderer.domElement
rootContainer.prepend(canvas)
Object.assign(canvas.style, {
position: "absolute",
left: "0px",
top: "0px"
})
return () => {
rootContainer.removeChild(canvas)
}
}, [getRenderer])
createEffect(() => {
const renderer = getRenderer()
if (!renderer) return
const [w, h] = getResolution()
renderer.setSize(w, h)
renderer.setPixelRatio(Math.min(getPixelRatio(), devicePixelRatio))
}, [getRenderer, getResolution, getPixelRatio])
createEffect(() => {
const renderer = getRenderer()
if (!renderer) return
renderer.physicallyCorrectLights = getPBR()
}, [getRenderer, getPBR])
createEffect(() => {
const renderer = getRenderer()
if (!renderer) return
const exposure = getExposure()
renderer.toneMapping = exposure !== 1 ? LinearToneMapping : NoToneMapping
renderer.toneMappingExposure = exposure
}, [getExposure, getRenderer])
createEffect(() => {
if (!getWebXR()) return
const renderer = getRenderer()
if (!renderer) return
renderer.xr.enabled = true
const button = VRButton.createButton(renderer)
container.appendChild(button)
button.ontouchstart = () => button.onclick?.(new MouseEvent("click"))
return () => {
renderer.xr.enabled = false
container.removeChild(button)
}
}, [getWebXR, getRenderer])