| <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> |