Bundle less CDN example?
Is there a possibility for adding a vanila/bundleless/CDN example?
search for @pmndrs/vanilla instead of drei-vanilla then it should come up
https://unpkg.com/browse/@pmndrs/vanilla/
https://www.jsdelivr.com/package/npm/@pmndrs/vanilla
was not able to setup the imports correctly ,will look at it again later
Somehow didn't work. Anyways, I will be keen to see an example without bundling. Thanks for replying.
check this fiddle , is this what you meant ?
https://jsfiddle.net/f7o0zsrg/15/
Thank you for the example. All seems to work except for the "Outlines" helper.
Simply executing the Outlines() function causes a different typeerror for different browsers.
I'm wondering if my implementation is incorrect.
I've forked your fiddle and imported the Outlines helper, example here: https://jsfiddle.net/jnitafan/2mokvrty/8/
Ok, i've managed to get the Outlines() function on a CDN only instance working by fixing all the issues that propped up.
Originally I imported vanilla-drei only for the outlines feature since no other implementation is good as react-three-drei's. Since that was the case I decided to just extract the code I needed.
The application I was dealing with required VanillaJS so I had to transpile it with Babel. It should not return "TypeError: Right side of assignment cannot be destructured" anymore.
If anyone else comes across this searching for a super hyperspecific scenario of where you are only allowed CDNs, VanillaJS and want some nice outlines, here are the snippets.
Heres the HTML:
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three/build/three.module.js",
"three/examples/jsm/": "https://unpkg.com/three/examples/jsm/",
}
}
</script>
<script type="module" src="./loadObject.js"></script>
And the Javascript:
loadObject.js
import * as THREE from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
import { toCreasedNormals } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
let renderer, scene, camera, controls
init()
animate()
function init() {
// renderer
renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
// scene
scene = new THREE.Scene()
// camera
camera = new THREE.PerspectiveCamera(
40,
window.innerWidth / window.innerHeight,
1,
10000,
)
camera.position.set(20, 20, 20)
// controls
controls = new OrbitControls(camera, renderer.domElement)
// ambient
scene.add(new THREE.AmbientLight(0x222222))
scene.background = new THREE.Color('grey')
// light
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(20, 20, 0)
scene.add(light)
// axes
scene.add(new THREE.AxesHelper(20))
const geometry = new THREE.BoxGeometry(15, 32, 32)
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 0x00ff00 }))
mesh.scale.set(0.25, 0.25, 0.25)
const outlines = Outlines({ color: new THREE.Color("blue"), thickness: 10 });
mesh.add(outlines.group)
outlines.generate()
scene.add(mesh)
}
function animate(time) {
requestAnimationFrame(animate)
renderer.render(scene, camera)
}
function Outlines({
color = new THREE.Color("black"),
opacity = 1,
transparent = false,
screenspace = false,
toneMapped = true,
polygonOffset = false,
polygonOffsetFactor = 0,
renderOrder = 0,
thickness = 0.05,
angle = Math.PI,
gl
} = {}) {
const OutlinesMaterial = shaderMaterial(
{
screenspace: false,
color: new THREE.Color('black'),
opacity: 1,
thickness: 100.0,
size: new THREE.Vector2(),
},
/* glsl */ `
#include <common>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
uniform float thickness;
uniform float screenspace;
uniform vec2 size;
void main() {
#if defined (USE_SKINNING)
#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <skinbase_vertex>
#include <skinnormal_vertex>
#include <defaultnormal_vertex>
#endif
#include <begin_vertex>
#include <morphtarget_vertex>
#include <skinning_vertex>
#include <project_vertex>
vec4 tNormal = vec4(normal, 0.0);
vec4 tPosition = vec4(transformed, 1.0);
#ifdef USE_INSTANCING
tNormal = instanceMatrix * tNormal;
tPosition = instanceMatrix * tPosition;
#endif
if (screenspace == 0.0) {
vec3 newPosition = tPosition.xyz + tNormal.xyz * thickness;
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
} else {
vec4 clipPosition = projectionMatrix * modelViewMatrix * tPosition;
vec4 clipNormal = projectionMatrix * modelViewMatrix * tNormal;
vec2 offset = normalize(clipNormal.xy) * thickness / size * clipPosition.w * 2.0;
clipPosition.xy += offset;
gl_Position = clipPosition;
}
}`,
/* glsl */ `
uniform vec3 color;
uniform float opacity;
void main(){
gl_FragColor = vec4(color, opacity);
#include <tonemapping_fragment>
#include <${parseInt(THREE.REVISION.replace(/\D+/g, '')) >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>
}`
)
const group = new THREE.Group()
let shapeProps = {
color,
opacity,
transparent,
screenspace,
toneMapped,
polygonOffset,
polygonOffsetFactor,
renderOrder,
thickness,
angle
}
function updateMesh(angle) {
const parent = group.parent
group.clear()
if (parent && parent.geometry) {
let mesh
const material = new OutlinesMaterial({ side: THREE.BackSide });
if (parent.skeleton) {
mesh = new THREE.SkinnedMesh()
mesh.material = material
mesh.bind(parent.skeleton, parent.bindMatrix)
group.add(mesh)
} else if (parent.isInstancedMesh) {
mesh = new THREE.InstancedMesh(parent.geometry, material, parent.count)
mesh.instanceMatrix = parent.instanceMatrix
group.add(mesh)
} else {
mesh = new THREE.Mesh()
mesh.material = material
group.add(mesh)
}
mesh.geometry = angle
? toCreasedNormals(parent.geometry, angle)
: parent.geometry
}
}
function updateProps(newProps) {
shapeProps = { ...shapeProps, ...newProps }
const mesh = group.children[0]
if (mesh) {
const {
transparent,
thickness,
color,
opacity,
screenspace,
toneMapped,
polygonOffset,
polygonOffsetFactor,
renderOrder
} = shapeProps
const contextSize = new THREE.Vector2()
if (!gl && shapeProps.screenspace) {
console.warn(
'Outlines: "screenspace" requires a WebGLRenderer instance to calculate the outline size'
)
}
if (gl) gl.getSize(contextSize)
Object.assign(mesh.material, {
transparent,
thickness,
color,
opacity,
size: contextSize,
screenspace,
toneMapped,
polygonOffset,
polygonOffsetFactor
})
if (renderOrder !== undefined) mesh.renderOrder = renderOrder
}
}
return {
group,
updateProps(props) {
const angle = props.angle ?? shapeProps.angle
if (angle !== shapeProps.angle) {
updateMesh(angle)
}
updateProps(props)
},
generate() {
updateMesh(shapeProps.angle)
updateProps(shapeProps)
}
}
}
export function shaderMaterial(uniforms, vertexShader, fragmentShader, onInit) {
const entries = Object.entries(uniforms)
class Material extends THREE.ShaderMaterial {
static key = THREE.MathUtils.generateUUID()
constructor(parameters) {
super({
uniforms: entries.reduce((acc, [name, value]) => {
const uniform = THREE.UniformsUtils.clone({ [name]: { value } })
return {
...acc,
...uniform
}
}, {}),
vertexShader,
fragmentShader
})
for (const [name] of entries) {
Object.defineProperty(this, name, {
get: () => this.uniforms[name].value,
set: v => (this.uniforms[name].value = v)
})
}
Object.assign(this, parameters)
onInit?.(this)
}
}
return Material
}
Hope this helps someone else.
On jsfiddle i was able to get outline working without any changes. so the problem might be specific to your build environment, Thanks for sharing
https://jsfiddle.net/jq8dpnoy/
Thanks for checking, I was able to reproduce the destructured TypeError by going on jsfiddle by doing:
const outlines = Outlines()
instead of:
const outlines = Outlines({})
Putting in an placeholder object inside the Outlines() function properties fixes the issue.
I'm assuming this isn't intended and can be fixed by going into Outlines.ts and editing line 101:
}: Partial<OutlinesProps> = {}): OutlinesType { to include a = {}
I should of tested more with some placeholder values 😭
cool, will add that in the upcoming updates