| <canvas id="canvas" style="width:960; height:540" width=1920 height=1080></canvas> | |
| <script src="rive.js"></script> | |
| <script src="webgpu_player.js"></script> | |
| <script> | |
| async function loadRiveAsset(args) | |
| { | |
| const file = RiveLoadFile(await download(args.url)); | |
| let artboard; | |
| if (args.artboard) { | |
| artboard = file.artboardNamed(args.artboard); | |
| } else { | |
| artboard = file.artboardDefault(); | |
| } | |
| let asset; | |
| if (args.stateMachine) { | |
| asset = artboard.stateMachineNamed(args.stateMachine); | |
| } else if (args.animation) { | |
| asset = artboard.animationNamed(args.animation); | |
| } else { | |
| asset = artboard.defaultStateMachine(); | |
| } | |
| asset.artboard = artboard; | |
| asset.file = file; | |
| return asset; | |
| } | |
| const data = {}; | |
| const assets = {}; | |
| async function wgpuDataInitialize(wgpu, canvas, adapter, device) | |
| { | |
| data.device = device; | |
| data.queue = device.queue; | |
| data.width = canvas.width; | |
| data.height = canvas.height; | |
| data.wgpu = wgpu; | |
| // Rive has to be initialized before loading assets. | |
| const invertY = false | |
| RiveInitialize(data.device, data.queue, data.width, data.height, invertY, RIVE_PLS_TYPE_NONE); | |
| assets.skills = await loadRiveAsset({url:"skills_demov1.riv", stateMachine:"Motion"}); | |
| assets.santa = await loadRiveAsset({url:"Santa_Claus.riv"}); | |
| assets.coffee = await loadRiveAsset({url:"Coffee_Cup.riv", animation:"Timeline 1"}); | |
| assets.skull = await loadRiveAsset({url:"skull_404.riv"}); | |
| assets.octopus = await loadRiveAsset({url:"octopus_loop.riv"}); | |
| assets.mercury = await loadRiveAsset({url:"planets.riv", artboard:"Mercury", | |
| animation:"MercuryIdle"}); | |
| assets.timer = await loadRiveAsset({url:"Timer.riv", animation:"Esample"}); | |
| assets.adventureTime = await loadRiveAsset({url:"adventuretime_marceline-pb.riv", | |
| artboard:"AdventureTime_FtMarcelinePB", | |
| stateMachine:"State Machine 1"}); | |
| setInterval(function() { | |
| // Simulate a mouse click on assets.adventureTime every 2.5 seconds. | |
| assets.adventureTime.pointerDown(200, 200); | |
| assets.adventureTime.pointerUp(200, 200); | |
| }, 2500); | |
| assets.towers = await loadRiveAsset({url:"towersDemo.riv"}); | |
| } | |
| const fps = new function() { | |
| this.frames = 0; | |
| this.seconds = 0; | |
| this.tick = function(elapsed) { | |
| ++this.frames; | |
| this.seconds += elapsed; | |
| if (this.seconds >= 2) { | |
| console.log((this.frames / this.seconds).toFixed(2) + " FPS"); | |
| this.frames = 0; | |
| this.seconds = 0; | |
| } | |
| }; | |
| }; | |
| let lastTimestamp = 0; | |
| let totalElapsed = 0; | |
| async function wgpuRender(args) | |
| { | |
| const elapsed = lastTimestamp ? (args.timestamp - lastTimestamp) / 1000 : 0; | |
| lastTimestamp = args.timestamp; | |
| for (asset in assets) { | |
| assets[asset].advanceAndApply(elapsed); | |
| } | |
| const targetTextureView = data.wgpu.getCurrentTexture().createView(); | |
| const renderer = RiveBeginRendering(targetTextureView, RIVE_LOAD_ACTION_CLEAR, 0xff8030ff); | |
| const middleFrame = [0, data.height * .25, data.width, data.height * .67]; | |
| const dock = [0, data.height * .75, data.width, data.height * .97]; | |
| renderer.save(); | |
| assets.skills.artboard.align(renderer, middleFrame, RIVE_FIT_FIT_HEIGHT, [.38, 0]); | |
| renderer.transform(1.5, 0, 0, 1.5, 0, -250); | |
| assets.skills.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.santa.artboard.align(renderer, middleFrame, RIVE_FIT_FIT_HEIGHT, [-.5, 0]); | |
| assets.santa.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.coffee.artboard.align(renderer, dock, RIVE_FIT_FIT_HEIGHT, RIVE_ALIGNMENT_BOTTOM_CENTER); | |
| renderer.transform(2, 0, 0, 2, -40, -600); | |
| assets.coffee.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.skull.artboard.align(renderer, dock, RIVE_FIT_FIT_HEIGHT, [.565, 1]); | |
| assets.skull.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.octopus.artboard.align(renderer, dock, RIVE_FIT_FIT_HEIGHT, RIVE_ALIGNMENT_BOTTOM_LEFT); | |
| renderer.transform(1.7, 0, 0, 1.7, 90, -240); | |
| assets.octopus.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.mercury.artboard.align(renderer, dock, RIVE_FIT_FIT_HEIGHT, [-.62, 1]); | |
| const mercuryCenterX = assets.mercury.artboard.width() / 2; | |
| const mercuryCenterY = assets.mercury.artboard.height() / 2; | |
| renderer.translate(mercuryCenterX, mercuryCenterY); | |
| const cx = Math.cos(totalElapsed) * .7; | |
| const sx = Math.sin(totalElapsed) * .7; | |
| renderer.transform(cx, sx, -sx, cx, 0, 0); | |
| renderer.translate(-mercuryCenterX, -mercuryCenterY); | |
| assets.mercury.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.timer.artboard.align(renderer, dock, RIVE_FIT_FIT_HEIGHT, [.31, 1]); | |
| renderer.translate(0, -assets.timer.artboard.height() * .125); | |
| assets.timer.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.adventureTime.artboard.align( | |
| renderer, dock, RIVE_FIT_FIT_HEIGHT, RIVE_ALIGNMENT_BOTTOM_RIGHT); | |
| renderer.translate(-160, 0); | |
| assets.adventureTime.draw(renderer); | |
| renderer.restore(); | |
| renderer.save(); | |
| assets.towers.artboard.align(renderer, dock, RIVE_FIT_FIT_HEIGHT, [-.28, 1]); | |
| renderer.transform(.85, 0, 0, .85, 0, 20); | |
| assets.towers.draw(renderer); | |
| renderer.restore(); | |
| RiveFlushRendering(); | |
| totalElapsed += elapsed; | |
| fps.tick(elapsed); | |
| } | |
| function download(url) | |
| { | |
| return new Promise(function (resolve, reject) { | |
| let xhr = new XMLHttpRequest(); | |
| xhr.open("GET", url); | |
| xhr.responseType = "arraybuffer"; | |
| xhr.onload = function () { | |
| if (this.status >= 200 && this.status < 300) { | |
| resolve(new Uint8Array(xhr.response)); | |
| } else { | |
| reject({ | |
| status: this.status, | |
| statusText: xhr.statusText | |
| }); | |
| } | |
| }; | |
| xhr.onerror = function () { | |
| reject({ | |
| status: this.status, | |
| statusText: xhr.statusText | |
| }); | |
| }; | |
| xhr.send(); | |
| }); | |
| } | |
| async function main() | |
| { | |
| const canvas = document.getElementById("canvas"); | |
| const wgpu = canvas.getContext("webgpu"); | |
| const adapter = await navigator.gpu.requestAdapter(); | |
| if (!adapter) { | |
| throw new Error("No appropriate GPUAdapter found."); | |
| } | |
| const device = await adapter.requestDevice(); | |
| const canvasFormat = navigator.gpu.getPreferredCanvasFormat(); | |
| wgpu.configure({ | |
| device: device, | |
| format: canvasFormat, | |
| }); | |
| await wgpuDataInitialize(wgpu, canvas, adapter, device); | |
| const mainLoop = async () => { | |
| await wgpuRender({timestamp:performance.now()}); | |
| requestAnimationFrame(mainLoop); | |
| }; | |
| requestAnimationFrame(mainLoop); | |
| } | |
| if (Module.calledRun) { | |
| main(); | |
| } else { | |
| Module.onRuntimeInitialized = main; | |
| } | |
| </script> |