Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundle less CDN example? #68

Open
jikkuatwork opened this issue Jul 18, 2024 · 8 comments
Open

Bundle less CDN example? #68

jikkuatwork opened this issue Jul 18, 2024 · 8 comments
Labels
enhancement New feature or request

Comments

@jikkuatwork
Copy link

Is there a possibility for adding a vanila/bundleless/CDN example?

@jikkuatwork jikkuatwork added the enhancement New feature or request label Jul 18, 2024
@vis-prime
Copy link
Collaborator

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

@jikkuatwork
Copy link
Author

Somehow didn't work. Anyways, I will be keen to see an example without bundling. Thanks for replying.

@vis-prime
Copy link
Collaborator

vis-prime commented Nov 19, 2024

check this fiddle , is this what you meant ?

https://jsfiddle.net/f7o0zsrg/15/

@jnitafan
Copy link

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/

@jnitafan
Copy link

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.

@vis-prime
Copy link
Collaborator

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/

image

@jnitafan
Copy link

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({})

Screenshot 2024-11-25 at 3 53 04 PM

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 😭

@vis-prime
Copy link
Collaborator

cool, will add that in the upcoming updates

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants