From 00601168358e68f8e5b22a272920103d62e0e6e3 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sat, 5 Sep 2026 18:38:46 -0500 Subject: [PATCH] Start of 3D support. --- CHANGELOG | 12 + CMakeLists.txt | 6 + LICENSES | 1 + docs/Manual.adoc | 1290 +++++++ src/main.c | 29 +- src/math3d.c | 499 +++ src/math3d.h | 90 + src/model.c | 1018 +++++ src/model.h | 50 + src/scene.c | 2266 ++++++++++++ src/scene.h | 130 + src/shaders/build.sh | 61 + src/shaders/scene.hlsl | 166 + src/shaders/sceneShaders.h | 2992 +++++++++++++++ src/singe.c | 1210 +++++- src/singe.h | 2 +- thirdparty/cgltf/LICENSE | 7 + thirdparty/cgltf/cgltf.h | 7175 ++++++++++++++++++++++++++++++++++++ 18 files changed, 16996 insertions(+), 8 deletions(-) create mode 100644 src/math3d.c create mode 100644 src/math3d.h create mode 100644 src/model.c create mode 100644 src/model.h create mode 100644 src/scene.c create mode 100644 src/scene.h create mode 100755 src/shaders/build.sh create mode 100644 src/shaders/scene.hlsl create mode 100644 src/shaders/sceneShaders.h create mode 100644 thirdparty/cgltf/LICENSE create mode 100644 thirdparty/cgltf/cgltf.h diff --git a/CHANGELOG b/CHANGELOG index fb91b3557..d48243b93 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,18 @@ API Changes database), and lfs.mkdir and lfs.rmdir act on its data overlay, so frameworks that list or create their own directories work packed. +- 3D scenes. A game can draw a 3D scene between the disc video and the + overlay: primitive meshes and script-built geometry, materials with + colour, textures from sprites, the disc or a loaded video, metallic and + roughness, up to eight lights, any node as the camera, and glTF 2.0 + models (.glb, self-contained) with node animation and skinning, placed + any number of times. Everything is a node in one tree. New calls: + scene*, node*, mesh*, material*, light*, camera*, model*, animation*, + and the LIGHT_* constants; see the 3D Scenes chapter of the manual. + Needs a GPU with Vulkan, Direct3D 12 or Metal (Raspberry Pi 4 or + later); 2D games run as before without one. The renderer now runs on + SDL's GPU device where there is one. + - A game can be one file: singe --pack DIRECTORY GAME.game writes the game's scripts, art, sounds, fonts, and video into an SQLite database, and singe GAME.game (or the menu, which lists every .game file beside the game diff --git a/CMakeLists.txt b/CMakeLists.txt index 389973ad6..2d8b8e40d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,6 +273,12 @@ set(SINGE_SOURCE src/main.h src/pack.c src/pack.h + src/model.c + src/model.h + src/math3d.c + src/math3d.h + src/scene.c + src/scene.h src/singe.c src/singe.h src/stddclmr.h diff --git a/LICENSES b/LICENSES index 2d6006c5f..bb9c7f384 100644 --- a/LICENSES +++ b/LICENSES @@ -5,6 +5,7 @@ these tools, Singe would not exist. arg_parser BSD-2-Clause http://savannah.nongnu.org/projects/arg-parser binaryheap.lua MIT http://tieske.github.io/binaryheap.lua +cgltf MIT https://github.com/jkuhlmann/cgltf copas MIT https://lunarmodules.github.io/copas ffmpeg LGPL-2.1 https://ffmpeg.org freetype FTL https://freetype.org diff --git a/docs/Manual.adoc b/docs/Manual.adoc index ffde49f1d..24e182119 100644 --- a/docs/Manual.adoc +++ b/docs/Manual.adoc @@ -680,6 +680,120 @@ overlay, sprites, fonts, sounds, extra videos through `videoLoad`, input, and both programming models -- is identical. The global `SINGE_DISC` tells a script which kind of game it is running as. +[#scenes3d] +=== 3D Scenes + +Singe 3 can draw a 3D scene between the laserdisc video and the 2D overlay. +Everything you already know still applies: sprites, text, and the overlay +sit on top of the scene, and the video shows through wherever the scene's +background is transparent. A game that never calls a 3D function is +unaffected. + +Two ways to fill a scene, freely mixed: + +* *Build it from script* with primitive meshes (`meshBox`, `meshSphere`, + `meshPlane`, `meshCylinder`, `meshCone`, `meshTorus`) or your own vertex + data (`meshNew`), materials, lights, and a camera. +* *Load models* in glTF 2.0 binary form (`.glb`) with `modelLoad`, then place + them with `modelInstance`, as many times as you like. Blender, Maya, 3ds + Max, and every major engine export `.glb`. The file must be self-contained: + meshes, skins, animations, and PNG/JPEG textures all inside the one file + (Blender's default). A model referring to a `.bin` or image file beside it + is refused with a message naming the file, for the same reason a packed + game has to be self-contained. + +Everything in a scene is a *node* in one tree, and node `0` is the root. A +node has a position, a rotation, and a scale relative to its parent, and may +carry a mesh with a material, or a light. A loaded model becomes a subtree +of nodes under one root node, so the same calls (`nodeSetPosition`, +`nodeRotate`, `nodeSetParent`, `nodeFind`, ...) move a modelled character and +a scripted cube alike. + +.Coordinates and units +The scene is right-handed with +Y up and -Z forward, like glTF and Blender's +export. Units are whatever your models use; the primitives take the same +units. Rotations are given in degrees as three Euler angles (applied yaw, +pitch, roll), or as a quaternion for values that came from a model. A node's +"forward" is its own -Z: the camera looks down it, directional and spot lights +shine down it, and `nodeLookAt` points it at a world position. + +.A minimal scene +[source,lua] +---- +sceneEnable(true) +sceneSetBackground(0, 0, 0, 0) -- Transparent: the disc shows behind the scene. + +local red = materialNew() +materialSetColor(red, 220, 40, 40) + +local cube = nodeNew() +nodeSetMesh(cube, meshBox(1, 1, 1), red) + +local sun = lightNew(LIGHT_DIRECTIONAL) +nodeSetPosition(sun, 2, 4, 3) +nodeLookAt(sun, 0, 0, 0) + +local camera = nodeNew() +nodeSetPosition(camera, 0, 1.5, 5) +nodeLookAt(camera, 0, 0, 0) +cameraSet(camera) + +function onOverlayUpdate() + nodeRotate(cube, 0, 1, 0) -- One degree a frame about its own Y. + return OVERLAY_UPDATED +end +---- + +.Materials +A material is a base colour (with alpha), an optional texture, and the +glTF metallic-roughness look: `materialSetMetallic` and +`materialSetRoughness` from `0` to `1`, `materialSetEmissive` for glowing +parts, `materialSetUnlit` to show the colour as is, `materialSetDoubleSided` +for open shapes, and `materialSetBlend` for translucency. The texture can be +any sprite's image (`materialSetTexture`), or the laserdisc itself or a +loaded video (`materialSetVideo`): a modelled cabinet can play the disc on +its own screen. + +.Lights and camera +Up to eight lights shine at once: `LIGHT_DIRECTIONAL` (a sun, position +irrelevant), `LIGHT_POINT` (a bulb, fading with distance, optionally out to +a range), and `LIGHT_SPOT` (a cone with inner and outer angles). Lights are +nodes, so they parent and animate like anything else. `sceneSetAmbient` +lights everything a little from everywhere. Any node can be the camera +(`cameraSet`); without one, the scene is viewed from `(0, 0, 5)` looking at +the origin. Perspective is the default; `cameraSetOrthographic` is there +for diagrams and HUD-like scenes. + +.Animation +A model's animations are listed by `modelGetAnimations` and driven per +instance with `animationPlay(node, nameOrIndex, loop, speed)` on the +instance's root node, plus pause, resume, stop, and seek calls. Node +animation (things moving) and skinning (characters deforming) both work; +morph targets do not yet. Animations advance with real time and hold while +the game is paused. + +.From scene to overlay and back +`sceneProject(x, y, z)` turns a world point into overlay coordinates, so +you can draw a name tag or a health bar with `fontPrint` above a 3D object. +`sceneUnproject(sx, sy, distance)` goes the other way: the world point that +far along the ray through an overlay pixel, which is what mouse picking and +placing things "where the player clicked" need. + +.Performance +The scene needs a GPU that speaks Vulkan (Linux, Raspberry Pi 4 and later), +Direct3D 12 (Windows 10 and later), or Metal (macOS). On a machine without +one, 2D games run exactly as before and the first 3D call raises an error; +the Raspberry Pi 3 has no Vulkan driver and therefore no 3D. Antialiasing +(4x multisampling) is on wherever the GPU offers it; `sceneSetAntialias(false)` +buys back speed on a Pi. Keep an eye on triangle counts and texture sizes on +small boards, and load models once and instance them rather than loading +twice. + +.Packaging +Put `.glb` files in the game directory like any other asset; `modelLoad` +takes the same game-relative names as `spriteLoad`, and a packed `.game` +carries them along. + [#migrating] === Migrating from Singe 2.10 @@ -747,6 +861,7 @@ available to `controls.cfg` and to `Framework.singe` alike: | `OVERLAY_NOT_UPDATED`, `OVERLAY_UPDATED` | Return values for `onOverlayUpdate`. | `RENDER_PIXELATED`, `RENDER_SMOOTH` | Arguments for `spriteQuality` / `videoQuality`. | `DISC_STOPPED`, `DISC_PLAYING`, `DISC_PAUSED`, `DISC_EJECTED` | Return values of `discGetState`. +| `LIGHT_DIRECTIONAL`, `LIGHT_POINT`, `LIGHT_SPOT` | Arguments for `lightNew`. | `SOUND_ERROR_INVALID`, `SOUND_REMOVE_HANDLE` | `-1`, what `soundPlay` returns when no channel is free. | `SINGE_VERSION_MAJOR`, `SINGE_VERSION_MINOR`, `SINGE_VERSION_STRING`, `SINGE_FRAMEWORK_VERSION` | The engine version, as integers, as a string (`"v3.00"`), and as the number `singeVersion()` returns. | `SINGE_DEAD_ZONE` | The `DEAD_ZONE` from `controls.cfg`. @@ -861,6 +976,189 @@ A few rules apply across the whole API: * *Invalid arguments abort the script.* If a function is called with the wrong number of arguments, wrong types, or a handle that has already been unloaded, Singe terminates the running script with an error -- it does not return `nil` or a failure code. Validate user input in your script before handing it to the API. * *Drawing must happen from `onOverlayUpdate`.* Calls to any `overlay*`, `spriteDraw`, `videoDraw`, or `fontPrint` function from other callbacks will not be reflected on screen and may corrupt the overlay. This is the one rule that catches every new Singe developer. +[#animation] +=== Animation + +These calls drive the animations a model carries, per instance. Every one +takes the *root node* returned by `modelInstance`; other nodes raise an +error. Animations are numbered from `1` in the order `modelGetAnimations` +lists them, or named. Time advances with the wall clock and holds while the +game is paused. Node animation and skinning are supported; morph targets are +not. + +[#animationgettime] +==== animationGetTime + +[source,text] +---- +seconds = animationGetTime(node) +---- + +The current time into the playing (or paused) animation. + +*Returns:* Seconds as a number; `0` when nothing was played. + +*Since:* 3.00. +*See also:* <> + +[#animationisplaying] +==== animationIsPlaying + +[source,text] +---- +playing = animationIsPlaying(node) +---- + +Whether an animation is advancing on this instance. A non-looping animation stops playing when it reaches its end. + +*Returns:* Boolean. + +*Since:* 3.00. +*See also:* <> + +[#animationpause] +==== animationPause + +[source,text] +---- +animationPause(node) +---- + +Freezes the animation at its current time; `animationResume` continues it. + +*Since:* 3.00. +*See also:* <> + +[#animationplay] +==== animationPlay + +[source,text] +---- +animationPlay(node, nameOrIndex) +animationPlay(node, nameOrIndex, loop) +animationPlay(node, nameOrIndex, loop, speed) +---- + +Starts one of the model's animations from the beginning on this instance, replacing whatever was playing. + +* `node` -- the instance's root node from `modelInstance`. +* `nameOrIndex` -- an animation name as the file stores it, or its number from `1`. +* `loop` -- boolean, default `true`. When `false` the animation stops at its last keyframe. +* `speed` -- playback rate, default `1`. `2` is twice as fast; `0.5` is half. + +*Since:* 3.00. +*See also:* <>, <>, <> + +.Example +[source,lua] +---- +-- A guard that idles until the player is spotted, then runs. +guardModel = modelLoad(DIR .. "Models/Guard.glb") +guard = modelInstance(guardModel) +animationPlay(guard, "Idle") + +function onSpotted() + animationPlay(guard, "Run", true, 1.3) +end +---- + +[#animationresume] +==== animationResume + +[source,text] +---- +animationResume(node) +---- + +Continues an animation paused by `animationPause`. + +*Since:* 3.00. +*See also:* <> + +[#animationsettime] +==== animationSetTime + +[source,text] +---- +animationSetTime(node, seconds) +---- + +Jumps to a time in the current animation. The pose updates at once, whether or not the animation is playing, so a paused instance can be posed exactly. + +*Since:* 3.00. +*See also:* <>, <> + +[#animationstop] +==== animationStop + +[source,text] +---- +animationStop(node) +---- + +Stops and forgets the animation. The nodes keep their last pose. + +*Since:* 3.00. +*See also:* <> + +[#camera] +=== Camera + +The camera is a node like any other: place it with `nodeSetPosition`, aim it +with `nodeLookAt`, parent it to a moving object for a chase view. It looks +down its own -Z. Without a camera node the scene is viewed from `(0, 0, 5)` +looking at the origin, with a 60 degree perspective. + +[#cameraset] +==== cameraSet + +[source,text] +---- +cameraSet(node) +cameraSet(-1) +---- + +Makes a node the camera. `-1` restores the default view. + +*Since:* 3.00. +*See also:* <>, <> + +.Example +[source,lua] +---- +-- A camera that follows the player's car from behind. +camera = nodeNew(car) +nodeSetPosition(camera, 0, 2, 6) +nodeLookAt(camera, nodeGetWorldPosition(car)) +cameraSet(camera) +---- + +[#camerasetorthographic] +==== cameraSetOrthographic + +[source,text] +---- +cameraSetOrthographic(height, near, far) +---- + +A parallel projection showing `height` world units top to bottom (the width follows the layer's aspect ratio), from `near` to `far` in front of the camera. + +*Since:* 3.00. +*See also:* <> + +[#camerasetperspective] +==== cameraSetPerspective + +[source,text] +---- +cameraSetPerspective(fov, near, far) +---- + +The usual projection. `fov` is the vertical field of view in degrees (default `60`); `near` and `far` (defaults `0.1` and `1000`) bound what is drawn. Keep `near` as large as the scene allows for a precise depth buffer. + +*Since:* 3.00. +*See also:* <> + [#color] === Color @@ -1749,6 +2047,527 @@ collectInitials() keyboardSetMode(MODE_NORMAL) ---- +[#light] +=== Light + +Lights are nodes carrying a light. Up to eight visible lights shine in a +frame, in node order; `sceneSetAmbient` adds light from everywhere. +Directional and spot lights shine down the node's -Z, so aim them with +`nodeLookAt` or `nodeSetRotation`. + +[#lightnew] +==== lightNew + +[source,text] +---- +node = lightNew(type) +node = lightNew(type, parent) +---- + +A new node carrying a light, white at intensity `1`. + +* `type` -- `LIGHT_DIRECTIONAL` (a sun; only its direction matters), `LIGHT_POINT` (a bulb fading with distance), or `LIGHT_SPOT` (a cone). +* `parent` -- optional parent node; the root when omitted. + +*Returns:* The light's node handle. + +*Since:* 3.00. +*See also:* <>, <>, <>, <>, <> + +.Example +[source,lua] +---- +-- Warm key light from the upper left, a cool fill from the right. +key = lightNew(LIGHT_DIRECTIONAL) +nodeSetPosition(key, -3, 4, 3) +nodeLookAt(key, 0, 0, 0) +lightSetColor(key, 255, 235, 200) +lightSetIntensity(key, 1.4) + +fill = lightNew(LIGHT_POINT) +nodeSetPosition(fill, 3, 1, 2) +lightSetColor(fill, 150, 190, 255) +lightSetIntensity(fill, 4) +lightSetRange(fill, 10) +---- + +[#lightsetcolor] +==== lightSetColor + +[source,text] +---- +lightSetColor(node, r, g, b) +---- + +The light's colour, `0` to `255` per channel. + +*Since:* 3.00. +*See also:* <> + +[#lightsetcone] +==== lightSetCone + +[source,text] +---- +lightSetCone(node, innerDegrees, outerDegrees) +---- + +For spot lights: full brightness inside `innerDegrees` from the axis, fading to nothing at `outerDegrees`. Defaults `20` and `30`. + +*Since:* 3.00. +*See also:* <> + +[#lightsetintensity] +==== lightSetIntensity + +[source,text] +---- +lightSetIntensity(node, intensity) +---- + +Brightness multiplier, default `1`. Point and spot lights fade with the square of the distance, so they usually want values well above `1`. + +*Since:* 3.00. +*See also:* <> + +[#lightsetrange] +==== lightSetRange + +[source,text] +---- +lightSetRange(node, range) +---- + +How far a point or spot light reaches before fading to nothing, in world units; `0` (the default) means no limit. + +*Since:* 3.00. +*See also:* <> + +[#material] +=== Material + +A material is how a mesh looks: a base colour (with alpha), an optional +texture from a sprite or a video, and the glTF metallic-roughness +parameters. Materials are shared: change one and every mesh using it +changes. A node without a material draws white and half rough. Loaded +models bring their own materials. + +[#materialnew] +==== materialNew + +[source,text] +---- +material = materialNew() +---- + +A new material: white, opaque, roughness `0.5`, no texture. + +*Returns:* The material handle. + +*Since:* 3.00. +*See also:* <>, <>, <> + +.Example +[source,lua] +---- +-- Brushed gold for a trophy, translucent blue glass for its case. +gold = materialNew() +materialSetColor(gold, 255, 200, 60) +materialSetMetallic(gold, 1) +materialSetRoughness(gold, 0.35) + +glass = materialNew() +materialSetColor(glass, 80, 160, 255, 90) +materialSetBlend(glass, true) +materialSetRoughness(glass, 0.1) +---- + +[#materialdelete] +==== materialDelete + +[source,text] +---- +materialDelete(material) +---- + +Frees the material. Nodes that used it fall back to the default look. + +*Since:* 3.00. +*See also:* <> + +[#materialsetblend] +==== materialSetBlend + +[source,text] +---- +materialSetBlend(material, blend) +---- + +When `true` the material is drawn translucent using the base colour's alpha (and the texture's), after every opaque mesh, farthest first. Leave it `false` for solid surfaces: blended meshes do not write depth. + +*Since:* 3.00. +*See also:* <> + +[#materialsetcolor] +==== materialSetColor + +[source,text] +---- +materialSetColor(material, r, g, b) +materialSetColor(material, r, g, b, a) +---- + +The base colour, `0` to `255` per channel; `a` defaults to `255`. Multiplies the texture when there is one. + +*Since:* 3.00. +*See also:* <>, <> + +[#materialsetdoublesided] +==== materialSetDoubleSided + +[source,text] +---- +materialSetDoubleSided(material, doubleSided) +---- + +Draws both faces of every triangle. Needed for open shapes such as a single `meshPlane` seen from below; costs fill rate on closed ones. + +*Since:* 3.00. +*See also:* <> + +[#materialsetemissive] +==== materialSetEmissive + +[source,text] +---- +materialSetEmissive(material, r, g, b) +---- + +Light the surface gives off regardless of lighting, `0` to `255` per channel: screens, lamps, neon. + +*Since:* 3.00. +*See also:* <> + +[#materialsetmetallic] +==== materialSetMetallic + +[source,text] +---- +materialSetMetallic(material, metallic) +---- + +`0` for dielectrics (paint, plastic, wood) to `1` for metals, which reflect their own colour. + +*Since:* 3.00. +*See also:* <> + +[#materialsetroughness] +==== materialSetRoughness + +[source,text] +---- +materialSetRoughness(material, roughness) +---- + +`0` for a mirror-tight highlight to `1` for matte. Default `0.5`. + +*Since:* 3.00. +*See also:* <> + +[#materialsettexture] +==== materialSetTexture + +[source,text] +---- +materialSetTexture(material, sprite) +materialSetTexture(material) +---- + +Uses a sprite's image as the base colour texture (a copy is made, so the sprite may be unloaded afterwards). Without a sprite the texture is removed. Texture coordinates come from the mesh; the primitives map an image once across each face. + +*Since:* 3.00. +*See also:* <>, <> + +.Example +[source,lua] +---- +-- A crate textured from a sprite the game already loads for the menu. +crateArt = spriteLoad(DIR .. "Art/crate.png") +crateLook = materialNew() +materialSetTexture(crateLook, crateArt) +crate = nodeNew() +nodeSetMesh(crate, meshBox(1, 1, 1), crateLook) +---- + +[#materialsetunlit] +==== materialSetUnlit + +[source,text] +---- +materialSetUnlit(material, unlit) +---- + +Shows the base colour and texture exactly as they are, ignoring lights: for video screens, skyboxes, and flat-shaded looks. + +*Since:* 3.00. +*See also:* <>, <> + +[#materialsetvideo] +==== materialSetVideo + +[source,text] +---- +materialSetVideo(material) +materialSetVideo(material, video) +---- + +Uses the laserdisc (no `video`) or a video loaded with `videoLoad` as the base colour texture, updated every frame. Usually paired with `materialSetUnlit` so the picture is not tinted by the lights. The video keeps playing whether or not it is also drawn on the overlay. + +*Notes:* The disc form raises an error in a game without a disc. + +*Since:* 3.00. +*See also:* <>, <>, <> + +.Example +[source,lua] +---- +-- A modelled cabinet whose screen shows the laserdisc. +cabinet = modelInstance(modelLoad(DIR .. "Models/Cabinet.glb")) +screen = materialNew() +materialSetVideo(screen) +materialSetUnlit(screen, true) +nodeSetMesh(nodeFind("Screen", cabinet), meshPlane(0.6, 0.45), screen) +---- + +[#mesh] +=== Mesh + +A mesh is geometry on the GPU: primitives built by the engine or vertex +data from your script. A mesh can be shared by any number of nodes. Meshes +are centred on the origin unless stated otherwise; the node's transform +places them. Texture coordinates put an image once across each primitive +face. + +[#meshbox] +==== meshBox + +[source,text] +---- +mesh = meshBox(width, height, depth) +---- + +A box with flat-shaded faces, `width` along X, `height` along Y, `depth` along Z. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <> + +[#meshcone] +==== meshCone + +[source,text] +---- +mesh = meshCone(radius, height) +mesh = meshCone(radius, height, segments) +---- + +A cone standing on Y, its base at `-height / 2` and apex at `+height / 2`. `segments` (default `24`) sets how round it is. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <> + +[#meshcylinder] +==== meshCylinder + +[source,text] +---- +mesh = meshCylinder(radius, height) +mesh = meshCylinder(radius, height, segments) +---- + +A closed cylinder along Y. `segments` (default `24`) sets how round it is. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <> + +[#meshdelete] +==== meshDelete + +[source,text] +---- +meshDelete(mesh) +---- + +Frees the mesh; nodes that used it draw nothing until given another. + +*Since:* 3.00. +*See also:* <> + +[#meshnew] +==== meshNew + +[source,text] +---- +mesh = meshNew(positions, normals, uvs, indices) +---- + +Geometry from tables of numbers. + +* `positions` -- `x, y, z` for every vertex, one after another. +* `normals` -- `x, y, z` per vertex, or `nil` to have smooth normals computed from the triangles. +* `uvs` -- `u, v` per vertex (`0` to `1`, `v` down the image), or `nil` for none. +* `indices` -- three vertex numbers per triangle, counted from `1` as the vertices appear in `positions`, counter-clockwise when seen from outside. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <>, <> + +.Example +[source,lua] +---- +-- A flat arrow pointing along +X, seen from above (double sided so it works from below too). +arrow = meshNew( + { 0, 0, -0.5, 1, 0, 0, 0, 0, 0.5, -0.6, 0, -0.5, -0.6, 0, 0.5 }, + nil, + nil, + { 1, 2, 3, 4, 1, 3, 4, 3, 5 }) +---- + +[#meshplane] +==== meshPlane + +[source,text] +---- +mesh = meshPlane(width, depth) +---- + +A flat rectangle in the XZ plane facing +Y, `width` along X and `depth` along Z. Rotate the node `90` degrees about X to stand it up facing the camera. Single sided unless its material is double sided. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <>, <> + +[#meshsphere] +==== meshSphere + +[source,text] +---- +mesh = meshSphere(radius) +mesh = meshSphere(radius, segments) +---- + +A sphere; `segments` (default `32`) around the equator, half as many pole to pole. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <> + +[#meshtorus] +==== meshTorus + +[source,text] +---- +mesh = meshTorus(radius, tubeRadius) +mesh = meshTorus(radius, tubeRadius, segments) +---- + +A ring lying in the XZ plane: `radius` to the middle of the tube, `tubeRadius` the tube's own. `segments` (default `32`) around the ring, half as many around the tube. + +*Returns:* The mesh handle. + +*Since:* 3.00. +*See also:* <> + +[#model] +=== Model + +Models are glTF 2.0 binary files (`.glb`) loaded once and placed any number +of times. A model must be self-contained (buffers and images inside the +file); one referring to a file beside it is refused. Loading gives a handle; +`modelInstance` builds the model's nodes into the scene and returns their +root, which every `node*` and `animation*` call then takes. + +[#modelload] +==== modelLoad + +[source,text] +---- +model = modelLoad(name) +---- + +Loads a `.glb` through the same lookup as every other asset (game directory, data directory, packed database). Its meshes and materials go to the GPU once; its node tree, skins, and animations are kept for instancing. + +* `name` -- game-relative path, for example `DIR .. "Models/Ship.glb"`. + +*Returns:* The model handle. Fails with a message naming the problem: not a glTF file, an external buffer or image, a primitive that could not be uploaded. + +*Since:* 3.00. +*See also:* <>, <>, <> + +.Example +[source,lua] +---- +-- Load once, place three times. +treeModel = modelLoad(DIR .. "Models/Tree.glb") +for i = 1, 3 do + local tree = modelInstance(treeModel) + nodeSetPosition(tree, i * 3 - 6, 0, -4) + nodeSetRotation(tree, 0, i * 70, 0) +end +---- + +[#modeldelete] +==== modelDelete + +[source,text] +---- +modelDelete(model) +---- + +Frees the model's meshes and materials. Existing instances keep their nodes but draw nothing, and their animations stop. + +*Since:* 3.00. +*See also:* <> + +[#modelgetanimations] +==== modelGetAnimations + +[source,text] +---- +names = modelGetAnimations(model) +---- + +The model's animation names in file order, as a table. Unnamed animations give empty strings; they can still be played by number. + +*Returns:* A table of strings. + +*Since:* 3.00. +*See also:* <> + +[#modelinstance] +==== modelInstance + +[source,text] +---- +node = modelInstance(model) +node = modelInstance(model, parent) +---- + +Builds the model's node tree under a new root node (under `parent`, or the scene root). The root is a plain node: position, rotate, scale, or parent it like any other. The model's own nodes keep their glTF names (or their mesh's name when unnamed) for `nodeFind`; its lights become scene lights; its skins are attached. A mesh with several materials becomes one child node per part. + +*Returns:* The instance's root node handle. + +*Since:* 3.00. +*See also:* <>, <>, <> + [#mouse] === Mouse @@ -1880,6 +2699,339 @@ Switch to `MANY_MOUSE` when you need to tell two lightguns or two mice apart -- *Since:* 1.18 (RDG) *See also:* <>, the `onMouseMoved` callback +[#node] +=== Node + +Every object in the scene is a node in one tree. A node has a position, +rotation, and scale relative to its parent, may carry a mesh with a +material or a light, and can be hidden with everything under it. Node +`0` is the root and cannot be deleted or re-parented. Handles are integers; +a deleted node's handle is invalid (and may be reused). + +[#nodenew] +==== nodeNew + +[source,text] +---- +node = nodeNew() +node = nodeNew(parent) +---- + +A new empty node at its parent's origin, unrotated, scale `1`. Empty nodes are useful as pivots and groups. + +*Returns:* The node handle. + +*Since:* 3.00. +*See also:* <>, <>, <> + +.Example +[source,lua] +---- +-- A turret whose barrel pivots on the turret, which turns on the base. +base = nodeNew() +turret = nodeNew(base) +barrel = nodeNew(turret) +nodeSetMesh(base, meshCylinder(1, 0.3), steel) +nodeSetMesh(turret, meshSphere(0.6), steel) +nodeSetMesh(barrel, meshCylinder(0.1, 1.2), steel) +nodeSetPosition(barrel, 0, 0, -0.8) +nodeSetRotation(barrel, 90, 0, 0) +---- + +[#nodedelete] +==== nodeDelete + +[source,text] +---- +nodeDelete(node) +---- + +Removes the node and everything under it. Meshes and materials it used are untouched (they may be shared). + +*Since:* 3.00. +*See also:* <> + +[#nodefind] +==== nodeFind + +[source,text] +---- +node = nodeFind(name) +node = nodeFind(name, root) +---- + +The first node named `name` below `root` (the scene root when omitted), depth first; `nil` when there is none. Model nodes carry their glTF names. + +*Returns:* A node handle or `nil`. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodegetchildren] +==== nodeGetChildren + +[source,text] +---- +children = nodeGetChildren(node) +---- + +The node's direct children as a table of handles, in the order they were added. + +*Returns:* A table. + +*Since:* 3.00. +*See also:* <> + +[#nodegetname] +==== nodeGetName + +[source,text] +---- +name = nodeGetName(node) +---- + +The node's name, or an empty string. + +*Returns:* A string. + +*Since:* 3.00. +*See also:* <> + +[#nodegetparent] +==== nodeGetParent + +[source,text] +---- +parent = nodeGetParent(node) +---- + +The parent's handle, or `nil` for the root. + +*Returns:* A node handle or `nil`. + +*Since:* 3.00. +*See also:* <> + +[#nodegetposition] +==== nodeGetPosition + +[source,text] +---- +x, y, z = nodeGetPosition(node) +---- + +The node's position relative to its parent. + +*Returns:* Three numbers. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodegetquaternion] +==== nodeGetQuaternion + +[source,text] +---- +x, y, z, w = nodeGetQuaternion(node) +---- + +The node's rotation as a quaternion, the form models store. + +*Returns:* Four numbers. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodegetrotation] +==== nodeGetRotation + +[source,text] +---- +x, y, z = nodeGetRotation(node) +---- + +The node's rotation as Euler angles in degrees, relative to its parent. + +*Returns:* Three numbers. + +*Since:* 3.00. +*See also:* <> + +[#nodegetscale] +==== nodeGetScale + +[source,text] +---- +x, y, z = nodeGetScale(node) +---- + +The node's scale per axis. + +*Returns:* Three numbers. + +*Since:* 3.00. +*See also:* <> + +[#nodegetworldposition] +==== nodeGetWorldPosition + +[source,text] +---- +x, y, z = nodeGetWorldPosition(node) +---- + +Where the node's origin is in world space, as of the last rendered frame (changes made this frame show after the next render). + +*Returns:* Three numbers. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodelookat] +==== nodeLookAt + +[source,text] +---- +nodeLookAt(node, x, y, z) +---- + +Rotates the node so its -Z points at a world position, keeping +Y up as far as possible. What cameras and lights want; also handy for a turret tracking a target. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodemove] +==== nodeMove + +[source,text] +---- +nodeMove(node, dx, dy, dz) +---- + +Moves the node along its own axes: `nodeMove(ship, 0, 0, -0.1)` drives it forward wherever it is pointing. + +*Since:* 3.00. +*See also:* <>, <> + +[#noderotate] +==== nodeRotate + +[source,text] +---- +nodeRotate(node, dx, dy, dz) +---- + +Turns the node by the given degrees about its own axes. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodesetmesh] +==== nodeSetMesh + +[source,text] +---- +nodeSetMesh(node, mesh) +nodeSetMesh(node, mesh, material) +---- + +Gives the node a mesh to draw, optionally with a material (default look otherwise). Any node can draw, including a model's nodes. + +*Since:* 3.00. +*See also:* <>, <>, <> + +[#nodesetname] +==== nodeSetName + +[source,text] +---- +nodeSetName(node, name) +---- + +Names the node for `nodeFind`. + +*Since:* 3.00. +*See also:* <> + +[#nodesetparent] +==== nodeSetParent + +[source,text] +---- +nodeSetParent(node, parent) +---- + +Moves the node (and its subtree) under another parent, keeping its local position, rotation, and scale, so it now moves with the new parent. A node cannot go under itself or its own descendants. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodesetposition] +==== nodeSetPosition + +[source,text] +---- +nodeSetPosition(node, x, y, z) +---- + +Places the node relative to its parent. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodesetquaternion] +==== nodeSetQuaternion + +[source,text] +---- +nodeSetQuaternion(node, x, y, z, w) +---- + +Sets the rotation as a quaternion, for values that came from a model or your own maths. + +*Since:* 3.00. +*See also:* <> + +[#nodesetrotation] +==== nodeSetRotation + +[source,text] +---- +nodeSetRotation(node, x, y, z) +---- + +Sets the rotation as Euler angles in degrees: turned about Y first, then tilted about X, then rolled about Z. + +*Since:* 3.00. +*See also:* <>, <>, <> + +[#nodesetscale] +==== nodeSetScale + +[source,text] +---- +nodeSetScale(node, scale) +nodeSetScale(node, x, y, z) +---- + +Scales the node and everything under it, uniformly or per axis. Loaded models are often in centimetres or metres; scale their root to fit your scene. + +*Since:* 3.00. +*See also:* <> + +[#nodesetvisible] +==== nodeSetVisible + +[source,text] +---- +nodeSetVisible(node, visible) +---- + +Hides or shows the node and everything under it. Hidden lights stop shining. + +*Since:* 3.00. +*See also:* <> + [#overlay] === Overlay @@ -2084,6 +3236,144 @@ Higher resolutions give finer control over sprite placement and sharper text at overlaySetResolution(discGetWidth(), discGetHeight()) ---- +[#scene] +=== Scene + +The scene is the 3D layer drawn between the disc video and the overlay. It +is off until `sceneEnable(true)`, is sized like the overlay, and clears to +`sceneSetBackground` every frame. These calls also bridge world space and +overlay coordinates. + +[#sceneenable] +==== sceneEnable + +[source,text] +---- +sceneEnable(enabled) +---- + +Turns the 3D layer on or off. Raises an error on a machine with no usable GPU (see <>); check for that in `onShutdown`-safe code paths if you want to fall back to a 2D presentation. + +*Since:* 3.00. +*See also:* <> + +.Example +[source,lua] +---- +-- Fall back to the plain overlay when 3D is unavailable. +has3d = pcall(sceneEnable, true) +if not has3d then + debugPrint("No 3D here; using the 2D attract mode.") +end +---- + +[#scenegetsize] +==== sceneGetSize + +[source,text] +---- +width, height = sceneGetSize() +---- + +The layer's size, which is the overlay's resolution: the space `sceneProject` and `sceneUnproject` work in. + +*Returns:* Two integers. + +*Since:* 3.00. +*See also:* <> + +[#sceneproject] +==== sceneProject + +[source,text] +---- +x, y, depth, inFront = sceneProject(wx, wy, wz) +---- + +Where a world point falls in overlay coordinates, using the camera of the last rendered frame. `depth` is `0` at the near plane and `1` at the far plane; `inFront` is `false` when the point is behind the camera (and `x`, `y` meaningless). + +*Returns:* Two numbers, a number, and a boolean. + +*Since:* 3.00. +*See also:* <>, <> + +.Example +[source,lua] +---- +-- A name floating above a character. +function onOverlayUpdate() + local x, y, depth, visible = sceneProject(nodeGetWorldPosition(hero)) + if visible then + fontPrint(x - 30, y - 80, "DIRK") + end + return OVERLAY_UPDATED +end +---- + +[#sceneunproject] +==== sceneUnproject + +[source,text] +---- +x, y, z = sceneUnproject(sx, sy, distance) +---- + +The world point `distance` units along the ray through an overlay pixel, using the camera of the last rendered frame. Two distances give a ray for picking; one places an object "where the player clicked". + +*Returns:* Three numbers. + +*Since:* 3.00. +*See also:* <>, <> + +.Example +[source,lua] +---- +-- Drop a marker 8 units into the scene under the mouse. +local mx, my = mouseGetPosition(0) +local x, y, z = sceneUnproject(mx, my, 8) +nodeSetPosition(marker, x, y, z) +---- + +[#scenesetambient] +==== sceneSetAmbient + +[source,text] +---- +sceneSetAmbient(r, g, b) +---- + +Light from everywhere, `0` to `255` per channel (default a dim grey). Lifts shadowed sides so they are not black. + +*Since:* 3.00. +*See also:* <> + +[#scenesetantialias] +==== sceneSetAntialias + +[source,text] +---- +sceneSetAntialias(antialias) +---- + +4x multisampling, on by default wherever the GPU offers it. Turn it off to buy speed on a Raspberry Pi. + +*Since:* 3.00. +*See also:* <> + +[#scenesetbackground] +==== sceneSetBackground + +[source,text] +---- +sceneSetBackground(r, g, b) +sceneSetBackground(r, g, b, a) +---- + +The colour the layer clears to each frame. `a` below `255` lets the video show through; `0` (the usual choice for a game with a disc) makes the background fully transparent. Default black, transparent. + +*Since:* 3.00. +*See also:* <> + [#script] === Script diff --git a/src/main.c b/src/main.c index dd70bb8b0..b4a76a3c4 100644 --- a/src/main.c +++ b/src/main.c @@ -298,6 +298,7 @@ static void _launcher(const char *exeName, ConfigT *conf) { float bestRatio = HUGE_VALF; SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; + SDL_GPUDevice *device = NULL; SDL_Surface *icon = NULL; MIX_Mixer *mixer = NULL; const SDL_DisplayMode *mode = NULL; @@ -402,12 +403,27 @@ static void _launcher(const char *exeName, ConfigT *conf) { SDL_SyncWindow(window); } - // Create a renderer. SDL prefers accelerated drivers but can fall back to software. + // Create a renderer. On a GPU device when the platform has one, so the 3D scene can share it; + // otherwise SDL's ordinary renderer, which prefers accelerated drivers but can fall back to software. _mainTrace(conf, "Creating renderer"); - renderer = SDL_CreateRenderer(window, NULL); - if (renderer == NULL) { - utilDie("%s", SDL_GetError()); + device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, false, NULL); + if (device == NULL) { + _mainTrace(conf, "No GPU device (%s); 3D is unavailable", SDL_GetError()); + } else { + renderer = SDL_CreateGPURenderer(device, window); + if (renderer == NULL) { + _mainTrace(conf, "GPU renderer failed (%s); 3D is unavailable", SDL_GetError()); + SDL_DestroyGPUDevice(device); + device = NULL; + } } + if (renderer == NULL) { + renderer = SDL_CreateRenderer(window, NULL); + if (renderer == NULL) { + utilDie("%s", SDL_GetError()); + } + } + _mainTrace(conf, "Renderer: %s", SDL_GetRendererName(renderer)); // Clear screen with black SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); @@ -435,7 +451,7 @@ static void _launcher(const char *exeName, ConfigT *conf) { // Run Singe! _mainTrace(conf, "Starting Singe"); - singe(window, renderer, conf); + singe(window, renderer, device, conf); // Shutdown - framefiles own video handles, so they go first. _mainTrace(conf, "Shutting down laserdisc framefile handler"); @@ -446,6 +462,9 @@ static void _launcher(const char *exeName, ConfigT *conf) { MIX_DestroyMixer(mixer); _mainTrace(conf, "Destroying renderer"); SDL_DestroyRenderer(renderer); + if (device != NULL) { + SDL_DestroyGPUDevice(device); + } _mainTrace(conf, "Destroying window"); SDL_DestroyWindow(window); _mainTrace(conf, "Re-enabling screen saver"); diff --git a/src/math3d.c b/src/math3d.c new file mode 100644 index 000000000..5cc7658fa --- /dev/null +++ b/src/math3d.c @@ -0,0 +1,499 @@ +/* + * + * Singe 3 + * Copyright (C) 2006-2026 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +// Vector, quaternion and matrix arithmetic for the 3D scene. Small on purpose: only what a scene +// graph, a camera and glTF animation need. + +#include +#include +#include "math3d.h" + + +#define PI 3.14159265358979323846f +#define DEGREES_TO_RADIANS(d) ((d) * (PI / 180.0f)) +#define RADIANS_TO_DEGREES(r) ((r) * (180.0f / PI)) +#define EPSILON 1e-6f + + +// Translation * rotation * scale, the glTF node transform. +Mat4T mat4Compose(Vec3T translation, QuatT rotation, Vec3T scale) { + Mat4T out; + float xx = rotation.x * rotation.x; + float yy = rotation.y * rotation.y; + float zz = rotation.z * rotation.z; + float xy = rotation.x * rotation.y; + float xz = rotation.x * rotation.z; + float yz = rotation.y * rotation.z; + float wx = rotation.w * rotation.x; + float wy = rotation.w * rotation.y; + float wz = rotation.w * rotation.z; + + out.m[0] = (1.0f - 2.0f * (yy + zz)) * scale.x; + out.m[1] = (2.0f * (xy + wz)) * scale.x; + out.m[2] = (2.0f * (xz - wy)) * scale.x; + out.m[3] = 0.0f; + out.m[4] = (2.0f * (xy - wz)) * scale.y; + out.m[5] = (1.0f - 2.0f * (xx + zz)) * scale.y; + out.m[6] = (2.0f * (yz + wx)) * scale.y; + out.m[7] = 0.0f; + out.m[8] = (2.0f * (xz + wy)) * scale.z; + out.m[9] = (2.0f * (yz - wx)) * scale.z; + out.m[10] = (1.0f - 2.0f * (xx + yy)) * scale.z; + out.m[11] = 0.0f; + out.m[12] = translation.x; + out.m[13] = translation.y; + out.m[14] = translation.z; + out.m[15] = 1.0f; + return out; +} + + +// Splits a TRS matrix back into its parts (glTF nodes may carry a matrix instead of TRS). +void mat4Decompose(Mat4T a, Vec3T *translation, QuatT *rotation, Vec3T *scale) { + Vec3T x = vec3(a.m[0], a.m[1], a.m[2]); + Vec3T y = vec3(a.m[4], a.m[5], a.m[6]); + Vec3T z = vec3(a.m[8], a.m[9], a.m[10]); + Mat4T r = mat4Identity(); + + *translation = vec3(a.m[12], a.m[13], a.m[14]); + *scale = vec3(vec3Length(x), vec3Length(y), vec3Length(z)); + // A negative determinant means one axis is mirrored; put the flip on X. + if (vec3Dot(vec3Cross(x, y), z) < 0.0f) { + scale->x = -scale->x; + } + x = vec3Scale(x, (scale->x != 0.0f) ? 1.0f / scale->x : 0.0f); + y = vec3Scale(y, (scale->y != 0.0f) ? 1.0f / scale->y : 0.0f); + z = vec3Scale(z, (scale->z != 0.0f) ? 1.0f / scale->z : 0.0f); + r.m[0] = x.x; + r.m[1] = x.y; + r.m[2] = x.z; + r.m[4] = y.x; + r.m[5] = y.y; + r.m[6] = y.z; + r.m[8] = z.x; + r.m[9] = z.y; + r.m[10] = z.z; + *rotation = quatFromMat4(r); +} + + +Mat4T mat4Identity(void) { + Mat4T out; + + memset(&out, 0, sizeof(out)); + out.m[0] = 1.0f; + out.m[5] = 1.0f; + out.m[10] = 1.0f; + out.m[15] = 1.0f; + return out; +} + + +// General 4x4 inverse by cofactors. Returns false for a singular matrix (out untouched). +bool mat4Invert(Mat4T a, Mat4T *out) { + float inv[16]; + float det; + int32_t x; + const float *m = a.m; + + inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; + inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; + inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; + inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; + inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; + inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; + inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; + inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; + inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; + inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; + inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; + inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; + inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; + inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; + inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; + inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; + det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; + if (fabsf(det) < EPSILON) { + return false; + } + det = 1.0f / det; + for (x = 0; x < 16; x++) { + out->m[x] = inv[x] * det; + } + return true; +} + + +// A view matrix: the camera at eye looking at target. +Mat4T mat4LookAt(Vec3T eye, Vec3T target, Vec3T up) { + Mat4T out; + Vec3T f = vec3Normalize(vec3Subtract(target, eye)); + Vec3T s = vec3Normalize(vec3Cross(f, up)); + Vec3T u = vec3Cross(s, f); + + out.m[0] = s.x; + out.m[1] = u.x; + out.m[2] = -f.x; + out.m[3] = 0.0f; + out.m[4] = s.y; + out.m[5] = u.y; + out.m[6] = -f.y; + out.m[7] = 0.0f; + out.m[8] = s.z; + out.m[9] = u.z; + out.m[10] = -f.z; + out.m[11] = 0.0f; + out.m[12] = -vec3Dot(s, eye); + out.m[13] = -vec3Dot(u, eye); + out.m[14] = vec3Dot(f, eye); + out.m[15] = 1.0f; + return out; +} + + +// a * b: applies b first, then a. +Mat4T mat4Multiply(Mat4T a, Mat4T b) { + Mat4T out; + int32_t column; + int32_t row; + int32_t k; + float sum; + + for (column = 0; column < 4; column++) { + for (row = 0; row < 4; row++) { + sum = 0.0f; + for (k = 0; k < 4; k++) { + sum += a.m[k * 4 + row] * b.m[column * 4 + k]; + } + out.m[column * 4 + row] = sum; + } + } + return out; +} + + +// Depth maps to 0..1 (what SDL_GPU expects on every backend). +Mat4T mat4Orthographic(float width, float height, float near, float far) { + Mat4T out; + + memset(&out, 0, sizeof(out)); + out.m[0] = 2.0f / width; + out.m[5] = 2.0f / height; + out.m[10] = -1.0f / (far - near); + out.m[14] = -near / (far - near); + out.m[15] = 1.0f; + return out; +} + + +Mat4T mat4Perspective(float fovDegrees, float aspect, float near, float far) { + Mat4T out; + float f = 1.0f / tanf(DEGREES_TO_RADIANS(fovDegrees) / 2.0f); + + memset(&out, 0, sizeof(out)); + out.m[0] = f / aspect; + out.m[5] = f; + out.m[10] = far / (near - far); + out.m[11] = -1.0f; + out.m[14] = (near * far) / (near - far); + return out; +} + + +Vec3T mat4TransformPoint(Mat4T a, Vec3T p) { + Vec3T out; + float w = a.m[3] * p.x + a.m[7] * p.y + a.m[11] * p.z + a.m[15]; + + out.x = a.m[0] * p.x + a.m[4] * p.y + a.m[8] * p.z + a.m[12]; + out.y = a.m[1] * p.x + a.m[5] * p.y + a.m[9] * p.z + a.m[13]; + out.z = a.m[2] * p.x + a.m[6] * p.y + a.m[10] * p.z + a.m[14]; + if (fabsf(w) > EPSILON) { + out.x /= w; + out.y /= w; + out.z /= w; + } + return out; +} + + +// Directions ignore translation. +Vec3T mat4TransformVector(Mat4T a, Vec3T v) { + Vec3T out; + + out.x = a.m[0] * v.x + a.m[4] * v.y + a.m[8] * v.z; + out.y = a.m[1] * v.x + a.m[5] * v.y + a.m[9] * v.z; + out.z = a.m[2] * v.x + a.m[6] * v.y + a.m[10] * v.z; + return out; +} + + +Mat4T mat4Transpose(Mat4T a) { + Mat4T out; + int32_t column; + int32_t row; + + for (column = 0; column < 4; column++) { + for (row = 0; row < 4; row++) { + out.m[column * 4 + row] = a.m[row * 4 + column]; + } + } + return out; +} + + +QuatT quatFromAxisAngle(Vec3T axis, float degrees) { + QuatT out; + float half = DEGREES_TO_RADIANS(degrees) / 2.0f; + float s = sinf(half); + Vec3T n = vec3Normalize(axis); + + out.x = n.x * s; + out.y = n.y * s; + out.z = n.z * s; + out.w = cosf(half); + return out; +} + + +// Intrinsic rotations applied in the order Y (yaw), X (pitch), Z (roll), matching what a script +// means by "turn, then tilt, then bank". +QuatT quatFromEuler(float xDegrees, float yDegrees, float zDegrees) { + QuatT qx = quatFromAxisAngle(vec3(1.0f, 0.0f, 0.0f), xDegrees); + QuatT qy = quatFromAxisAngle(vec3(0.0f, 1.0f, 0.0f), yDegrees); + QuatT qz = quatFromAxisAngle(vec3(0.0f, 0.0f, 1.0f), zDegrees); + + return quatMultiply(quatMultiply(qy, qx), qz); +} + + +// The rotation part of a matrix, assuming no scale. +QuatT quatFromMat4(Mat4T a) { + QuatT out; + float trace = a.m[0] + a.m[5] + a.m[10]; + float s; + + if (trace > 0.0f) { + s = sqrtf(trace + 1.0f) * 2.0f; + out.w = 0.25f * s; + out.x = (a.m[6] - a.m[9]) / s; + out.y = (a.m[8] - a.m[2]) / s; + out.z = (a.m[1] - a.m[4]) / s; + } else if ((a.m[0] > a.m[5]) && (a.m[0] > a.m[10])) { + s = sqrtf(1.0f + a.m[0] - a.m[5] - a.m[10]) * 2.0f; + out.w = (a.m[6] - a.m[9]) / s; + out.x = 0.25f * s; + out.y = (a.m[4] + a.m[1]) / s; + out.z = (a.m[8] + a.m[2]) / s; + } else if (a.m[5] > a.m[10]) { + s = sqrtf(1.0f + a.m[5] - a.m[0] - a.m[10]) * 2.0f; + out.w = (a.m[8] - a.m[2]) / s; + out.x = (a.m[4] + a.m[1]) / s; + out.y = 0.25f * s; + out.z = (a.m[9] + a.m[6]) / s; + } else { + s = sqrtf(1.0f + a.m[10] - a.m[0] - a.m[5]) * 2.0f; + out.w = (a.m[1] - a.m[4]) / s; + out.x = (a.m[8] + a.m[2]) / s; + out.y = (a.m[9] + a.m[6]) / s; + out.z = 0.25f * s; + } + return quatNormalize(out); +} + + +QuatT quatIdentity(void) { + QuatT out = { 0.0f, 0.0f, 0.0f, 1.0f }; + + return out; +} + + +// The rotation that points -Z along forward with +Y near up. +QuatT quatLookRotation(Vec3T forward, Vec3T up) { + Mat4T m; + Vec3T f = vec3Normalize(forward); + Vec3T s; + Vec3T u; + + if (vec3Length(vec3Cross(f, up)) < EPSILON) { + // Looking straight along up: pick any perpendicular. + up = (fabsf(f.y) < 0.9f) ? vec3(0.0f, 1.0f, 0.0f) : vec3(0.0f, 0.0f, 1.0f); + } + s = vec3Normalize(vec3Cross(f, up)); + u = vec3Cross(s, f); + m = mat4Identity(); + m.m[0] = s.x; + m.m[1] = s.y; + m.m[2] = s.z; + m.m[4] = u.x; + m.m[5] = u.y; + m.m[6] = u.z; + m.m[8] = -f.x; + m.m[9] = -f.y; + m.m[10] = -f.z; + return quatFromMat4(m); +} + + +// a * b: applies b first, then a. +QuatT quatMultiply(QuatT a, QuatT b) { + QuatT out; + + out.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; + out.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; + out.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; + out.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + return out; +} + + +QuatT quatNormalize(QuatT q) { + float length = sqrtf(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + + if (length < EPSILON) { + return quatIdentity(); + } + q.x /= length; + q.y /= length; + q.z /= length; + q.w /= length; + return q; +} + + +Vec3T quatRotate(QuatT q, Vec3T v) { + Vec3T u = vec3(q.x, q.y, q.z); + Vec3T t = vec3Scale(vec3Cross(u, v), 2.0f); + + return vec3Add(vec3Add(v, vec3Scale(t, q.w)), vec3Cross(u, t)); +} + + +QuatT quatSlerp(QuatT a, QuatT b, float t) { + QuatT out; + float cosTheta = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + float theta; + float sinTheta; + float wa; + float wb; + + // Take the short way round. + if (cosTheta < 0.0f) { + b.x = -b.x; + b.y = -b.y; + b.z = -b.z; + b.w = -b.w; + cosTheta = -cosTheta; + } + if (cosTheta > 1.0f - EPSILON) { + // Nearly parallel: lerp is accurate and avoids the division. + out.x = a.x + (b.x - a.x) * t; + out.y = a.y + (b.y - a.y) * t; + out.z = a.z + (b.z - a.z) * t; + out.w = a.w + (b.w - a.w) * t; + return quatNormalize(out); + } + theta = acosf(cosTheta); + sinTheta = sinf(theta); + wa = sinf((1.0f - t) * theta) / sinTheta; + wb = sinf(t * theta) / sinTheta; + out.x = a.x * wa + b.x * wb; + out.y = a.y * wa + b.y * wb; + out.z = a.z * wa + b.z * wb; + out.w = a.w * wa + b.w * wb; + return out; +} + + +// The inverse of quatFromEuler (Y, then X, then Z). +void quatToEuler(QuatT q, float *xDegrees, float *yDegrees, float *zDegrees) { + Mat4T m = mat4Compose(vec3(0.0f, 0.0f, 0.0f), q, vec3(1.0f, 1.0f, 1.0f)); + float sinX = -m.m[9]; + + if (sinX > 1.0f) { + sinX = 1.0f; + } + if (sinX < -1.0f) { + sinX = -1.0f; + } + *xDegrees = RADIANS_TO_DEGREES(asinf(sinX)); + if (fabsf(sinX) < 1.0f - EPSILON) { + *yDegrees = RADIANS_TO_DEGREES(atan2f(m.m[8], m.m[10])); + *zDegrees = RADIANS_TO_DEGREES(atan2f(m.m[1], m.m[5])); + } else { + // Gimbal lock: give all the twist to Y. + *yDegrees = RADIANS_TO_DEGREES(atan2f(-m.m[2], m.m[0])); + *zDegrees = 0.0f; + } +} + + +Vec3T vec3(float x, float y, float z) { + Vec3T out = { x, y, z }; + + return out; +} + + +Vec3T vec3Add(Vec3T a, Vec3T b) { + return vec3(a.x + b.x, a.y + b.y, a.z + b.z); +} + + +Vec3T vec3Cross(Vec3T a, Vec3T b) { + return vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + + +float vec3Dot(Vec3T a, Vec3T b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + + +float vec3Length(Vec3T a) { + return sqrtf(vec3Dot(a, a)); +} + + +Vec3T vec3Lerp(Vec3T a, Vec3T b, float t) { + return vec3Add(a, vec3Scale(vec3Subtract(b, a), t)); +} + + +Vec3T vec3Normalize(Vec3T a) { + float length = vec3Length(a); + + if (length < EPSILON) { + return vec3(0.0f, 0.0f, 0.0f); + } + return vec3Scale(a, 1.0f / length); +} + + +Vec3T vec3Scale(Vec3T a, float s) { + return vec3(a.x * s, a.y * s, a.z * s); +} + + +Vec3T vec3Subtract(Vec3T a, Vec3T b) { + return vec3(a.x - b.x, a.y - b.y, a.z - b.z); +} diff --git a/src/math3d.h b/src/math3d.h new file mode 100644 index 000000000..a560c883f --- /dev/null +++ b/src/math3d.h @@ -0,0 +1,90 @@ +/* + * + * Singe 3 + * Copyright (C) 2006-2026 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef MATH3D_H +#define MATH3D_H + +#include +#include + + +// Right-handed, +Y up, -Z forward (glTF's convention). Matrices are column major, as the GPU +// expects them; m[column * 4 + row]. + +typedef struct Vec3S { + float x; + float y; + float z; +} Vec3T; + +typedef struct Vec4S { + float x; + float y; + float z; + float w; +} Vec4T; + +typedef struct QuatS { + float x; + float y; + float z; + float w; +} QuatT; + +typedef struct Mat4S { + float m[16]; +} Mat4T; + + +Mat4T mat4Compose(Vec3T translation, QuatT rotation, Vec3T scale); +void mat4Decompose(Mat4T a, Vec3T *translation, QuatT *rotation, Vec3T *scale); +Mat4T mat4Identity(void); +bool mat4Invert(Mat4T a, Mat4T *out); +Mat4T mat4LookAt(Vec3T eye, Vec3T target, Vec3T up); +Mat4T mat4Multiply(Mat4T a, Mat4T b); +Mat4T mat4Orthographic(float width, float height, float near, float far); +Mat4T mat4Perspective(float fovDegrees, float aspect, float near, float far); +Vec3T mat4TransformPoint(Mat4T a, Vec3T p); +Vec3T mat4TransformVector(Mat4T a, Vec3T v); +Mat4T mat4Transpose(Mat4T a); +QuatT quatFromAxisAngle(Vec3T axis, float degrees); +QuatT quatFromEuler(float xDegrees, float yDegrees, float zDegrees); +QuatT quatFromMat4(Mat4T a); +QuatT quatIdentity(void); +QuatT quatLookRotation(Vec3T forward, Vec3T up); +QuatT quatMultiply(QuatT a, QuatT b); +QuatT quatNormalize(QuatT q); +Vec3T quatRotate(QuatT q, Vec3T v); +QuatT quatSlerp(QuatT a, QuatT b, float t); +void quatToEuler(QuatT q, float *xDegrees, float *yDegrees, float *zDegrees); +Vec3T vec3(float x, float y, float z); +Vec3T vec3Add(Vec3T a, Vec3T b); +Vec3T vec3Cross(Vec3T a, Vec3T b); +float vec3Dot(Vec3T a, Vec3T b); +float vec3Length(Vec3T a); +Vec3T vec3Lerp(Vec3T a, Vec3T b, float t); +Vec3T vec3Normalize(Vec3T a); +Vec3T vec3Scale(Vec3T a, float s); +Vec3T vec3Subtract(Vec3T a, Vec3T b); + + +#endif diff --git a/src/model.c b/src/model.c new file mode 100644 index 000000000..8211d79b8 --- /dev/null +++ b/src/model.c @@ -0,0 +1,1018 @@ +/* + * + * Singe 3 + * Copyright (C) 2006-2026 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +// glTF 2.0 models for the 3D scene. A model is a template loaded once from a self-contained .glb +// (buffers and images embedded; anything referring to a file beside it is refused, the same rule a +// packed game lives by): its meshes and materials are uploaded once, its node tree, skins and +// animations are kept as parsed. Instantiating a model builds its node tree under a scene node, +// so one model can appear many times. Parsing is cgltf's (thirdparty/cgltf, MIT). + +#include +#include +#include +#include +#include +#include +#include +#define CGLTF_IMPLEMENTATION +#include "../thirdparty/cgltf/cgltf.h" +#include "util.h" +#include "vfs.h" +#include "scene.h" +#include "model.h" + + +#define NO_HANDLE -1 +#define ERROR_LENGTH 256 +#define DEGREES(r) ((r) * (180.0f / 3.14159265358979323846f)) + + +typedef struct PrimitiveS { + int32_t mesh; + int32_t material; +} PrimitiveT; + +typedef struct ModelMeshS { + PrimitiveT *primitives; + int32_t count; +} ModelMeshT; + +typedef struct ModelS { + cgltf_data *data; + int32_t *materials; // Scene material per glTF material, NO_HANDLE when it failed + ModelMeshT *meshes; // Per glTF mesh + bool used; +} ModelT; + +// A model placed in the scene: which scene node stands for each glTF node, and which of the +// model's animations is driving those nodes. +typedef struct InstanceS { + int32_t model; + int32_t root; + uint32_t rootGeneration; + int32_t *nodes; // Scene node per glTF node, NO_HANDLE where none was made + int32_t animation; // NO_HANDLE when none is playing + double time; // Seconds into the animation + float speed; + bool loop; + bool playing; + bool used; +} InstanceT; + + +static int32_t _allocInstance(void); +static int32_t _allocModel(void); +static void _attachSkins(ModelT *model, InstanceT *instance); +static double _animationLength(const cgltf_animation *animation); +static void _applyAnimation(InstanceT *instance); +static void _buildNode(ModelT *model, InstanceT *instance, const cgltf_node *node, int32_t parent); +static InstanceT *_findInstance(int32_t root); +static void _fail(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +static void _freeInstance(InstanceT *instance); +static void _freeModel(ModelT *model); +static void _freeStaleInstances(void); +static SDL_Surface *_loadImage(const cgltf_image *image); +static int32_t _loadMaterial(const cgltf_material *material); +static bool _loadMeshes(ModelT *model); +static int32_t _loadPrimitive(const cgltf_primitive *primitive); +static bool _readAttribute(const cgltf_accessor *accessor, float *out, int32_t components, int32_t count); +static void _sampleChannel(const cgltf_animation_channel *channel, double time, float *out, int32_t components); + + +static ModelT *_models; +static int32_t _modelCount; +static InstanceT *_instances; +static int32_t _instanceCount; +static char _error[ERROR_LENGTH]; +static uint64_t _lastTick; + + +// ===== Internal helpers ===== + +static int32_t _allocInstance(void) { + int32_t x; + + for (x = 0; x < _instanceCount; x++) { + if (!_instances[x].used) { + break; + } + } + if (x == _instanceCount) { + _instances = SDL_realloc(_instances, sizeof(InstanceT) * (size_t)(_instanceCount + 1)); + if (_instances == NULL) { + utilDie("Out of memory allocating a model instance."); + } + _instanceCount++; + } + memset(&_instances[x], 0, sizeof(InstanceT)); + _instances[x].used = true; + return x; +} + + +static int32_t _allocModel(void) { + int32_t x; + + for (x = 0; x < _modelCount; x++) { + if (!_models[x].used) { + break; + } + } + if (x == _modelCount) { + _models = SDL_realloc(_models, sizeof(ModelT) * (size_t)(_modelCount + 1)); + if (_models == NULL) { + utilDie("Out of memory allocating a model."); + } + _modelCount++; + } + memset(&_models[x], 0, sizeof(ModelT)); + _models[x].used = true; + return x; +} + + +// The last keyframe time over every channel. +static double _animationLength(const cgltf_animation *animation) { + double length = 0.0; + int32_t x; + + for (x = 0; x < (int32_t)animation->channels_count; x++) { + const cgltf_accessor *input = animation->channels[x].sampler->input; + + if (input->has_max && (input->max[0] > length)) { + length = input->max[0]; + } + } + return length; +} + + +// Writes the instance's animation at its current time into its scene nodes. +static void _applyAnimation(InstanceT *instance) { + const cgltf_data *data = _models[instance->model].data; + const cgltf_animation *animation; + int32_t x; + float values[4]; + + if ((instance->animation < 0) || (instance->animation >= (int32_t)data->animations_count)) { + return; + } + animation = &data->animations[instance->animation]; + for (x = 0; x < (int32_t)animation->channels_count; x++) { + const cgltf_animation_channel *channel = &animation->channels[x]; + int32_t node; + + if (channel->target_node == NULL) { + continue; + } + node = instance->nodes[channel->target_node - data->nodes]; + if (node == NO_HANDLE) { + continue; + } + if (channel->target_path == cgltf_animation_path_type_translation) { + _sampleChannel(channel, instance->time, values, 3); + nodeSetPosition(node, vec3(values[0], values[1], values[2])); + } else if (channel->target_path == cgltf_animation_path_type_rotation) { + QuatT q; + + _sampleChannel(channel, instance->time, values, 4); + q.x = values[0]; + q.y = values[1]; + q.z = values[2]; + q.w = values[3]; + nodeSetRotation(node, q); + } else if (channel->target_path == cgltf_animation_path_type_scale) { + _sampleChannel(channel, instance->time, values, 3); + nodeSetScale(node, vec3(values[0], values[1], values[2])); + } + } +} + + +// Once every node exists: each skinned glTF node's mesh node(s) get the skin's joints, resolved +// to this instance's scene nodes, with the inverse bind matrices (identity when the file has none). +static void _attachSkins(ModelT *model, InstanceT *instance) { + cgltf_data *data = model->data; + int32_t x; + int32_t y; + + for (x = 0; x < (int32_t)data->nodes_count; x++) { + const cgltf_node *node = &data->nodes[x]; + const cgltf_skin *skin = node->skin; + int32_t *joints; + Mat4T *inverseBind; + int32_t count; + int32_t handle = instance->nodes[x]; + + if ((skin == NULL) || (node->mesh == NULL) || (handle == NO_HANDLE)) { + continue; + } + count = (int32_t)SDL_min(skin->joints_count, 128); + joints = SDL_malloc(sizeof(int32_t) * (size_t)SDL_max(count, 1)); + inverseBind = SDL_malloc(sizeof(Mat4T) * (size_t)SDL_max(count, 1)); + if ((joints == NULL) || (inverseBind == NULL)) { + utilDie("Out of memory attaching a skin."); + } + for (y = 0; y < count; y++) { + joints[y] = instance->nodes[skin->joints[y] - data->nodes]; + inverseBind[y] = mat4Identity(); + if (skin->inverse_bind_matrices != NULL) { + cgltf_accessor_read_float(skin->inverse_bind_matrices, (cgltf_size)y, inverseBind[y].m, 16); + } + } + // A multi-primitive mesh lives in child nodes; each gets the skin. + if (model->meshes[node->mesh - data->meshes].count == 1) { + nodeSetSkin(handle, joints, inverseBind, count); + } else { + for (y = 0; y < nodeGetChildCount(handle); y++) { + nodeSetSkin(nodeGetChild(handle, y), joints, inverseBind, count); + } + } + SDL_free(joints); + SDL_free(inverseBind); + } +} + + +// A scene node for one glTF node and, recursively, its children. A mesh with several primitives +// becomes child nodes, one per primitive, so each can have its own material. +static void _buildNode(ModelT *model, InstanceT *instance, const cgltf_node *node, int32_t parent) { + int32_t handle = nodeNew(parent); + int32_t index = (int32_t)(node - model->data->nodes); + Vec3T translation; + QuatT rotation; + Vec3T scale; + Mat4T matrix; + ModelMeshT *mesh; + int32_t x; + + if (handle == NO_HANDLE) { + return; + } + instance->nodes[index] = handle; + // Exporters often leave nodes unnamed but name the mesh; scripts find nodes by name. + nodeSetName(handle, (node->name != NULL) ? node->name : ((node->mesh != NULL) ? node->mesh->name : NULL)); + if (node->has_matrix) { + memcpy(matrix.m, node->matrix, sizeof(matrix.m)); + mat4Decompose(matrix, &translation, &rotation, &scale); + } else { + translation = node->has_translation ? vec3(node->translation[0], node->translation[1], node->translation[2]) : vec3(0.0f, 0.0f, 0.0f); + scale = node->has_scale ? vec3(node->scale[0], node->scale[1], node->scale[2]) : vec3(1.0f, 1.0f, 1.0f); + rotation = quatIdentity(); + if (node->has_rotation) { + rotation.x = node->rotation[0]; + rotation.y = node->rotation[1]; + rotation.z = node->rotation[2]; + rotation.w = node->rotation[3]; + } + } + nodeSetPosition(handle, translation); + nodeSetRotation(handle, rotation); + nodeSetScale(handle, scale); + if (node->mesh != NULL) { + mesh = &model->meshes[node->mesh - model->data->meshes]; + if (mesh->count == 1) { + nodeSetMesh(handle, mesh->primitives[0].mesh, mesh->primitives[0].material); + } else { + for (x = 0; x < mesh->count; x++) { + int32_t child = nodeNew(handle); + + if (child != NO_HANDLE) { + nodeSetMesh(child, mesh->primitives[x].mesh, mesh->primitives[x].material); + } + } + } + } + if (node->light != NULL) { + LightTypeE type = LIGHT_POINT; + + if (node->light->type == cgltf_light_type_directional) { + type = LIGHT_DIRECTIONAL; + } else if (node->light->type == cgltf_light_type_spot) { + type = LIGHT_SPOT; + } + lightAttach(handle, type); + lightSetColor(handle, (uint8_t)(node->light->color[0] * 255.0f), (uint8_t)(node->light->color[1] * 255.0f), (uint8_t)(node->light->color[2] * 255.0f)); + lightSetIntensity(handle, node->light->intensity); + lightSetRange(handle, node->light->range); + if (type == LIGHT_SPOT) { + lightSetCone(handle, DEGREES(node->light->spot_inner_cone_angle), DEGREES(node->light->spot_outer_cone_angle)); + } + } + for (x = 0; x < (int32_t)node->children_count; x++) { + _buildNode(model, instance, node->children[x], handle); + } +} + + +// The instance whose root is this node, or NULL (a reused handle is not a match). +static InstanceT *_findInstance(int32_t root) { + int32_t x; + + for (x = 0; x < _instanceCount; x++) { + if (_instances[x].used && (_instances[x].root == root) && nodeValid(root) && (nodeGetGeneration(root) == _instances[x].rootGeneration)) { + return &_instances[x]; + } + } + return NULL; +} + + +// Remembers why the last load failed, for the script's error message. +static void _fail(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vsnprintf(_error, sizeof(_error), fmt, args); + va_end(args); + utilTrace("Model: %s", _error); +} + + +static void _freeInstance(InstanceT *instance) { + SDL_free(instance->nodes); + memset(instance, 0, sizeof(*instance)); +} + + +// Releases the scene's copies of the model's meshes and materials, then the parsed data. +static void _freeModel(ModelT *model) { + int32_t x; + int32_t y; + + if (model->data != NULL) { + for (x = 0; x < (int32_t)model->data->materials_count; x++) { + if (model->materials[x] != NO_HANDLE) { + materialDelete(model->materials[x]); + } + } + for (x = 0; x < (int32_t)model->data->meshes_count; x++) { + for (y = 0; y < model->meshes[x].count; y++) { + meshDelete(model->meshes[x].primitives[y].mesh); + } + SDL_free(model->meshes[x].primitives); + } + cgltf_free(model->data); + } + SDL_free(model->materials); + SDL_free(model->meshes); + memset(model, 0, sizeof(*model)); +} + + +// Drops instances whose root node the script deleted. +static void _freeStaleInstances(void) { + int32_t x; + + for (x = 0; x < _instanceCount; x++) { + if (_instances[x].used && (!nodeValid(_instances[x].root) || (nodeGetGeneration(_instances[x].root) != _instances[x].rootGeneration))) { + _freeInstance(&_instances[x]); + } + } +} + + +// Decodes an embedded image (a buffer view, or a base64 data URI) with SDL_image. +static SDL_Surface *_loadImage(const cgltf_image *image) { + const uint8_t *bytes = NULL; + size_t size = 0; + void *decoded = NULL; + const char *comma; + SDL_Surface *surface; + cgltf_options options; + + if (image->buffer_view != NULL) { + bytes = (const uint8_t *)image->buffer_view->buffer->data + image->buffer_view->offset; + size = image->buffer_view->size; + } else if ((image->uri != NULL) && (strncmp(image->uri, "data:", 5) == 0) && ((comma = strchr(image->uri, ',')) != NULL)) { + size_t encoded = strlen(comma + 1); + + size = encoded / 4 * 3; + if ((encoded >= 1) && (comma[encoded] == '=')) { + size--; + } + if ((encoded >= 2) && (comma[encoded - 1] == '=')) { + size--; + } + memset(&options, 0, sizeof(options)); + if (cgltf_load_buffer_base64(&options, size, comma + 1, &decoded) != cgltf_result_success) { + _fail("Unable to decode an embedded image."); + return NULL; + } + bytes = decoded; + } else { + _fail("Image %s is not embedded in the model.", image->uri ? image->uri : "(unnamed)"); + return NULL; + } + surface = IMG_Load_IO(SDL_IOFromConstMem(bytes, size), true); + if (surface == NULL) { + _fail("Unable to decode an embedded image: %s", SDL_GetError()); + } + free(decoded); + return surface; +} + + +// One scene material from a glTF material; returns NO_HANDLE on failure. +static int32_t _loadMaterial(const cgltf_material *material) { + int32_t handle = materialNew(); + const float *color; + SDL_Surface *image; + + if (handle == NO_HANDLE) { + return NO_HANDLE; + } + if (material->has_pbr_metallic_roughness) { + color = material->pbr_metallic_roughness.base_color_factor; + materialSetColor(handle, (uint8_t)(color[0] * 255.0f), (uint8_t)(color[1] * 255.0f), (uint8_t)(color[2] * 255.0f), (uint8_t)(color[3] * 255.0f)); + materialSetMetallic(handle, material->pbr_metallic_roughness.metallic_factor); + materialSetRoughness(handle, material->pbr_metallic_roughness.roughness_factor); + if ((material->pbr_metallic_roughness.base_color_texture.texture != NULL) && (material->pbr_metallic_roughness.base_color_texture.texture->image != NULL)) { + image = _loadImage(material->pbr_metallic_roughness.base_color_texture.texture->image); + if (image != NULL) { + materialSetTexture(handle, image); + SDL_DestroySurface(image); + } + } + } + materialSetEmissive(handle, (uint8_t)(material->emissive_factor[0] * 255.0f), (uint8_t)(material->emissive_factor[1] * 255.0f), (uint8_t)(material->emissive_factor[2] * 255.0f)); + materialSetBlend(handle, material->alpha_mode == cgltf_alpha_mode_blend); + materialSetDoubleSided(handle, material->double_sided); + materialSetUnlit(handle, material->unlit); + return handle; +} + + +// Every material, then every primitive of every mesh. +static bool _loadMeshes(ModelT *model) { + cgltf_data *data = model->data; + int32_t x; + int32_t y; + + model->materials = SDL_calloc(SDL_max(data->materials_count, 1), sizeof(int32_t)); + model->meshes = SDL_calloc(SDL_max(data->meshes_count, 1), sizeof(ModelMeshT)); + if ((model->materials == NULL) || (model->meshes == NULL)) { + utilDie("Out of memory loading a model."); + } + for (x = 0; x < (int32_t)data->materials_count; x++) { + model->materials[x] = _loadMaterial(&data->materials[x]); + } + for (x = 0; x < (int32_t)data->meshes_count; x++) { + model->meshes[x].primitives = SDL_calloc(SDL_max(data->meshes[x].primitives_count, 1), sizeof(PrimitiveT)); + if (model->meshes[x].primitives == NULL) { + utilDie("Out of memory loading a model."); + } + for (y = 0; y < (int32_t)data->meshes[x].primitives_count; y++) { + const cgltf_primitive *primitive = &data->meshes[x].primitives[y]; + int32_t mesh; + + if (primitive->type != cgltf_primitive_type_triangles) { + utilTrace("Model: mesh %s primitive %d is not triangles; skipped.", data->meshes[x].name ? data->meshes[x].name : "(unnamed)", y); + continue; + } + mesh = _loadPrimitive(primitive); + if (mesh == NO_HANDLE) { + return false; + } + model->meshes[x].primitives[model->meshes[x].count].mesh = mesh; + model->meshes[x].primitives[model->meshes[x].count].material = (primitive->material != NULL) ? model->materials[primitive->material - data->materials] : NO_HANDLE; + model->meshes[x].count++; + } + } + return true; +} + + +// One primitive as a scene mesh: positions, normals (computed when absent), texture +// coordinates, joints and weights when skinned, and indices (generated when absent). +static int32_t _loadPrimitive(const cgltf_primitive *primitive) { + const cgltf_accessor *positions = NULL; + const cgltf_accessor *normals = NULL; + const cgltf_accessor *uvs = NULL; + const cgltf_accessor *joints = NULL; + const cgltf_accessor *weights = NULL; + SceneVertexT *vertices; + uint32_t *indices; + float *values; + int32_t vertexCount; + int32_t indexCount; + int32_t x; + int32_t mesh; + + for (x = 0; x < (int32_t)primitive->attributes_count; x++) { + const cgltf_attribute *attribute = &primitive->attributes[x]; + + if ((attribute->type == cgltf_attribute_type_position) && (attribute->index == 0)) { + positions = attribute->data; + } else if ((attribute->type == cgltf_attribute_type_normal) && (attribute->index == 0)) { + normals = attribute->data; + } else if ((attribute->type == cgltf_attribute_type_texcoord) && (attribute->index == 0)) { + uvs = attribute->data; + } else if ((attribute->type == cgltf_attribute_type_joints) && (attribute->index == 0)) { + joints = attribute->data; + } else if ((attribute->type == cgltf_attribute_type_weights) && (attribute->index == 0)) { + weights = attribute->data; + } + } + if (positions == NULL) { + _fail("A primitive has no positions."); + return NO_HANDLE; + } + vertexCount = (int32_t)positions->count; + indexCount = (primitive->indices != NULL) ? (int32_t)primitive->indices->count : vertexCount; + vertices = SDL_calloc((size_t)vertexCount, sizeof(SceneVertexT)); + indices = SDL_calloc((size_t)indexCount, sizeof(uint32_t)); + values = SDL_calloc((size_t)vertexCount * 4, sizeof(float)); + if ((vertices == NULL) || (indices == NULL) || (values == NULL)) { + utilDie("Out of memory loading a model."); + } + _readAttribute(positions, values, 3, vertexCount); + for (x = 0; x < vertexCount; x++) { + vertices[x].position[0] = values[x * 3]; + vertices[x].position[1] = values[x * 3 + 1]; + vertices[x].position[2] = values[x * 3 + 2]; + vertices[x].weights[0] = 1.0f; + } + if ((normals != NULL) && _readAttribute(normals, values, 3, vertexCount)) { + for (x = 0; x < vertexCount; x++) { + vertices[x].normal[0] = values[x * 3]; + vertices[x].normal[1] = values[x * 3 + 1]; + vertices[x].normal[2] = values[x * 3 + 2]; + } + } + if ((uvs != NULL) && _readAttribute(uvs, values, 2, vertexCount)) { + for (x = 0; x < vertexCount; x++) { + vertices[x].uv[0] = values[x * 2]; + vertices[x].uv[1] = values[x * 2 + 1]; + } + } + if ((joints != NULL) && (weights != NULL) && _readAttribute(weights, values, 4, vertexCount)) { + for (x = 0; x < vertexCount; x++) { + cgltf_uint ids[4] = { 0, 0, 0, 0 }; + + cgltf_accessor_read_uint(joints, (cgltf_size)x, ids, 4); + vertices[x].joints[0] = (uint8_t)ids[0]; + vertices[x].joints[1] = (uint8_t)ids[1]; + vertices[x].joints[2] = (uint8_t)ids[2]; + vertices[x].joints[3] = (uint8_t)ids[3]; + vertices[x].weights[0] = values[x * 4]; + vertices[x].weights[1] = values[x * 4 + 1]; + vertices[x].weights[2] = values[x * 4 + 2]; + vertices[x].weights[3] = values[x * 4 + 3]; + } + } + if (primitive->indices != NULL) { + for (x = 0; x < indexCount; x++) { + indices[x] = (uint32_t)cgltf_accessor_read_index(primitive->indices, (cgltf_size)x); + } + } else { + for (x = 0; x < indexCount; x++) { + indices[x] = (uint32_t)x; + } + } + if (normals == NULL) { + sceneComputeNormals(vertices, vertexCount, indices, indexCount); + } + mesh = meshNewVertices(vertices, vertexCount, indices, indexCount, (joints != NULL) && (weights != NULL)); + if (mesh == NO_HANDLE) { + _fail("Unable to upload a primitive."); + } + SDL_free(values); + SDL_free(indices); + SDL_free(vertices); + return mesh; +} + + +// Reads count elements of the accessor as floats, components per element. +static bool _readAttribute(const cgltf_accessor *accessor, float *out, int32_t components, int32_t count) { + int32_t x; + + if ((int32_t)accessor->count < count) { + return false; + } + for (x = 0; x < count; x++) { + if (!cgltf_accessor_read_float(accessor, (cgltf_size)x, out + x * components, (cgltf_size)components)) { + return false; + } + } + return true; +} + + +// Evaluates one channel at a time (seconds): step, linear (slerp for rotations) or cubic +// spline, clamped to the first and last keyframes. +static void _sampleChannel(const cgltf_animation_channel *channel, double time, float *out, int32_t components) { + const cgltf_accessor *input = channel->sampler->input; + const cgltf_accessor *output = channel->sampler->output; + int32_t count = (int32_t)input->count; + int32_t k; + int32_t next; + float t0; + float t1; + float t; + float a[4]; + float b[4]; + int32_t c; + + if (count == 0) { + return; + } + // The keyframe pair around time; before the first or after the last just holds. + for (k = 0; k < count - 1; k++) { + cgltf_accessor_read_float(input, (cgltf_size)(k + 1), &t1, 1); + if (time < t1) { + break; + } + } + next = SDL_min(k + 1, count - 1); + cgltf_accessor_read_float(input, (cgltf_size)k, &t0, 1); + cgltf_accessor_read_float(input, (cgltf_size)next, &t1, 1); + t = (t1 > t0) ? (float)((time - t0) / (t1 - t0)) : 0.0f; + t = SDL_clamp(t, 0.0f, 1.0f); + if (channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline) { + // Three vectors per keyframe: in-tangent, value, out-tangent. Hermite basis. + float dt = t1 - t0; + float p0[4]; + float m0[4]; + float p1[4]; + float m1[4]; + float t2 = t * t; + float t3 = t2 * t; + + cgltf_accessor_read_float(output, (cgltf_size)(k * 3 + 1), p0, (cgltf_size)components); + cgltf_accessor_read_float(output, (cgltf_size)(k * 3 + 2), m0, (cgltf_size)components); + cgltf_accessor_read_float(output, (cgltf_size)(next * 3), m1, (cgltf_size)components); + cgltf_accessor_read_float(output, (cgltf_size)(next * 3 + 1), p1, (cgltf_size)components); + for (c = 0; c < components; c++) { + out[c] = (2.0f * t3 - 3.0f * t2 + 1.0f) * p0[c] + dt * (t3 - 2.0f * t2 + t) * m0[c] + (-2.0f * t3 + 3.0f * t2) * p1[c] + dt * (t3 - t2) * m1[c]; + } + if (components == 4) { + QuatT q = { out[0], out[1], out[2], out[3] }; + + q = quatNormalize(q); + out[0] = q.x; + out[1] = q.y; + out[2] = q.z; + out[3] = q.w; + } + return; + } + cgltf_accessor_read_float(output, (cgltf_size)k, a, (cgltf_size)components); + if ((channel->sampler->interpolation == cgltf_interpolation_type_step) || (next == k)) { + memcpy(out, a, sizeof(float) * (size_t)components); + return; + } + cgltf_accessor_read_float(output, (cgltf_size)next, b, (cgltf_size)components); + if (components == 4) { + QuatT qa = { a[0], a[1], a[2], a[3] }; + QuatT qb = { b[0], b[1], b[2], b[3] }; + QuatT q = quatSlerp(qa, qb, t); + + out[0] = q.x; + out[1] = q.y; + out[2] = q.z; + out[3] = q.w; + return; + } + for (c = 0; c < components; c++) { + out[c] = a[c] + (b[c] - a[c]) * t; + } +} + + +// ===== Animation (on a model instance's root node) ===== + +double animationGetTime(int32_t root) { + InstanceT *instance = _findInstance(root); + + return (instance != NULL) ? instance->time : 0.0; +} + + +bool animationIsPlaying(int32_t root) { + InstanceT *instance = _findInstance(root); + + return (instance != NULL) && instance->playing; +} + + +bool animationPause(int32_t root) { + InstanceT *instance = _findInstance(root); + + if (instance == NULL) { + return false; + } + instance->playing = false; + return true; +} + + +// Starts the animation from the beginning. +bool animationPlay(int32_t root, int32_t index, bool loop, float speed) { + InstanceT *instance = _findInstance(root); + + if ((instance == NULL) || (index < 0) || (index >= modelGetAnimationCount(instance->model))) { + return false; + } + instance->animation = index; + instance->time = 0.0; + instance->speed = speed; + instance->loop = loop; + instance->playing = true; + _applyAnimation(instance); + return true; +} + + +bool animationResume(int32_t root) { + InstanceT *instance = _findInstance(root); + + if ((instance == NULL) || (instance->animation == NO_HANDLE)) { + return false; + } + instance->playing = true; + return true; +} + + +// Jumps to a time; the pose updates whether or not the animation is playing. +bool animationSetTime(int32_t root, double seconds) { + InstanceT *instance = _findInstance(root); + + if ((instance == NULL) || (instance->animation == NO_HANDLE)) { + return false; + } + instance->time = SDL_max(seconds, 0.0); + _applyAnimation(instance); + return true; +} + + +// Stops and forgets the animation; the nodes keep their last pose. +bool animationStop(int32_t root) { + InstanceT *instance = _findInstance(root); + + if (instance == NULL) { + return false; + } + instance->animation = NO_HANDLE; + instance->playing = false; + return true; +} + + +// ===== Models ===== + +// The index of an animation by name, or NO_HANDLE. +int32_t modelAnimationIndex(int32_t model, const char *name) { + int32_t x; + + if (!modelValid(model) || (name == NULL)) { + return NO_HANDLE; + } + for (x = 0; x < (int32_t)_models[model].data->animations_count; x++) { + if ((_models[model].data->animations[x].name != NULL) && (strcmp(_models[model].data->animations[x].name, name) == 0)) { + return x; + } + } + return NO_HANDLE; +} + +// Frees the model's meshes and materials; nodes already instanced stay, without their meshes. +bool modelDelete(int32_t model) { + int32_t x; + + if (!modelValid(model)) { + return false; + } + for (x = 0; x < _instanceCount; x++) { + if (_instances[x].used && (_instances[x].model == model)) { + _freeInstance(&_instances[x]); + } + } + _freeModel(&_models[model]); + return true; +} + + +int32_t modelGetAnimationCount(int32_t model) { + if (!modelValid(model)) { + return 0; + } + return (int32_t)_models[model].data->animations_count; +} + + +const char *modelGetAnimationName(int32_t model, int32_t index) { + if (!modelValid(model) || (index < 0) || (index >= (int32_t)_models[model].data->animations_count)) { + return ""; + } + return _models[model].data->animations[index].name ? _models[model].data->animations[index].name : ""; +} + + +// Builds the model's node tree under parent and returns its root node (a plain node the scene's +// own calls move like any other). The default scene is used, or every root node when the file +// names none. +int32_t modelInstance(int32_t model, int32_t parent) { + ModelT *m; + InstanceT *instance; + int32_t index; + int32_t root; + int32_t x; + cgltf_data *data; + + if (!modelValid(model)) { + return NO_HANDLE; + } + m = &_models[model]; + data = m->data; + root = nodeNew(parent); + if (root == NO_HANDLE) { + return NO_HANDLE; + } + index = _allocInstance(); + instance = &_instances[index]; + instance->model = model; + instance->root = root; + instance->rootGeneration = nodeGetGeneration(root); + instance->animation = NO_HANDLE; + instance->speed = 1.0f; + instance->nodes = SDL_malloc(sizeof(int32_t) * SDL_max(data->nodes_count, 1)); + if (instance->nodes == NULL) { + utilDie("Out of memory instancing a model."); + } + for (x = 0; x < (int32_t)data->nodes_count; x++) { + instance->nodes[x] = NO_HANDLE; + } + if (data->scene != NULL) { + for (x = 0; x < (int32_t)data->scene->nodes_count; x++) { + _buildNode(m, instance, data->scene->nodes[x], root); + } + } else { + for (x = 0; x < (int32_t)data->nodes_count; x++) { + if (data->nodes[x].parent == NULL) { + _buildNode(m, instance, &data->nodes[x], root); + } + } + } + _attachSkins(m, instance); + return root; +} + + +const char *modelLastError(void) { + return _error; +} + + +// Parses a .glb through the vfs and uploads its meshes and materials. NO_HANDLE on failure, +// with modelLastError saying why. +int32_t modelLoad(const char *name) { + char *bytes; + size_t size; + cgltf_options options; + cgltf_data *data = NULL; + cgltf_result result; + int32_t handle; + int32_t x; + + _error[0] = '\0'; + if (!sceneAvailable()) { + _fail("3D is not available on this machine."); + return NO_HANDLE; + } + bytes = vfsRead(name, &size); + if (bytes == NULL) { + _fail("Unable to read %s.", name); + return NO_HANDLE; + } + memset(&options, 0, sizeof(options)); + result = cgltf_parse(&options, bytes, size, &data); + if (result != cgltf_result_success) { + _fail("%s is not a glTF model (cgltf result %d).", name, (int32_t)result); + free(bytes); + return NO_HANDLE; + } + // Every buffer must be the GLB binary chunk or a data URI; a file beside the model is refused. + for (x = 0; x < (int32_t)data->buffers_count; x++) { + if ((data->buffers[x].uri != NULL) && (strncmp(data->buffers[x].uri, "data:", 5) != 0)) { + _fail("%s refers to the external file %s; pack everything into the .glb.", name, data->buffers[x].uri); + cgltf_free(data); + free(bytes); + return NO_HANDLE; + } + } + result = cgltf_load_buffers(&options, data, NULL); + if (result != cgltf_result_success) { + _fail("Unable to load the buffers of %s (cgltf result %d).", name, (int32_t)result); + cgltf_free(data); + free(bytes); + return NO_HANDLE; + } + if (cgltf_validate(data) != cgltf_result_success) { + _fail("%s failed validation.", name); + cgltf_free(data); + free(bytes); + return NO_HANDLE; + } + handle = _allocModel(); + _models[handle].data = data; + if (!_loadMeshes(&_models[handle])) { + _freeModel(&_models[handle]); + free(bytes); + return NO_HANDLE; + } + // cgltf keeps the GLB's binary chunk pointing into bytes, so the file stays with the model. + _models[handle].data->file_data = bytes; + utilTrace("Model %d: %s, %d meshes, %d materials, %d nodes, %d animations", handle, name, (int32_t)data->meshes_count, (int32_t)data->materials_count, (int32_t)data->nodes_count, (int32_t)data->animations_count); + return handle; +} + + +void modelQuit(void) { + int32_t x; + + for (x = 0; x < _modelCount; x++) { + if (_models[x].used) { + _freeModel(&_models[x]); + } + } + for (x = 0; x < _instanceCount; x++) { + if (_instances[x].used) { + _freeInstance(&_instances[x]); + } + } + SDL_free(_models); + SDL_free(_instances); + _models = NULL; + _instances = NULL; + _modelCount = 0; + _instanceCount = 0; +} + + +// Whether the node is the root of a model instance (what the animation calls want). +int32_t modelRootOf(int32_t root) { + InstanceT *instance = _findInstance(root); + + return (instance != NULL) ? instance->model : NO_HANDLE; +} + + +// Once per frame: advances every playing animation by the wall clock (not at all while the game +// is paused) and poses the nodes. +void modelUpdate(bool advance) { + uint64_t now = SDL_GetTicksNS(); + double delta = (_lastTick != 0) ? (double)(now - _lastTick) / 1e9 : 0.0; + int32_t x; + + _lastTick = now; + _freeStaleInstances(); + for (x = 0; x < _instanceCount; x++) { + InstanceT *instance = &_instances[x]; + double length; + + if (!instance->used || !instance->playing || (instance->animation == NO_HANDLE)) { + continue; + } + if (advance) { + length = _animationLength(&_models[instance->model].data->animations[instance->animation]); + instance->time += delta * instance->speed; + if (instance->time > length) { + if (instance->loop && (length > 0.0)) { + instance->time = fmod(instance->time, length); + } else { + instance->time = length; + instance->playing = false; + } + } + } + _applyAnimation(instance); + } +} + + +bool modelValid(int32_t model) { + return (model >= 0) && (model < _modelCount) && _models[model].used; +} diff --git a/src/model.h b/src/model.h new file mode 100644 index 000000000..6f3d10401 --- /dev/null +++ b/src/model.h @@ -0,0 +1,50 @@ +/* + * + * Singe 3 + * Copyright (C) 2006-2026 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef MODEL_H +#define MODEL_H + +#include +#include + + +double animationGetTime(int32_t root); +bool animationIsPlaying(int32_t root); +bool animationPause(int32_t root); +bool animationPlay(int32_t root, int32_t index, bool loop, float speed); +bool animationResume(int32_t root); +bool animationSetTime(int32_t root, double seconds); +bool animationStop(int32_t root); +int32_t modelAnimationIndex(int32_t model, const char *name); +bool modelDelete(int32_t model); +int32_t modelGetAnimationCount(int32_t model); +const char *modelGetAnimationName(int32_t model, int32_t index); +int32_t modelInstance(int32_t model, int32_t parent); +const char *modelLastError(void); +int32_t modelLoad(const char *name); +void modelQuit(void); +int32_t modelRootOf(int32_t root); +void modelUpdate(bool advance); +bool modelValid(int32_t model); + + +#endif diff --git a/src/scene.c b/src/scene.c new file mode 100644 index 000000000..767566d1a --- /dev/null +++ b/src/scene.c @@ -0,0 +1,2266 @@ +/* + * + * Singe 3 + * Copyright (C) 2006-2026 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +// The 3D scene: a layer drawn between the disc video and the 2D overlay. It renders on the +// SDL_GPU device the 2D renderer was created on, into its own colour and depth textures; the colour +// texture is wrapped as an SDL_Texture so the frame loop composites it like any other layer. +// +// Everything in the scene is a node in one tree (node 0 is the root): a node has a transform, and +// optionally a mesh with a material, or a light. Meshes, materials and nodes are addressed from +// Lua by integer handles that index the arrays below; a freed slot is reused. Shaders come +// precompiled from sceneShaders.h (see src/shaders/build.sh). + +#include +#include +#include +#include "util.h" +#include "scene.h" +#include "shaders/sceneShaders.h" + + +#define COLOUR_MAX 255.0f +#define MAX_LIGHTS 8 +#define MAX_JOINTS 128 +#define INITIAL_CAPACITY 64 +#define PIPELINE_COUNT 8 // skinned x blend x double sided +#define PIPELINE_SKINNED 1 +#define PIPELINE_BLEND 2 +#define PIPELINE_TWO_SIDED 4 +#define DEFAULT_FOV 60.0f +#define DEFAULT_NEAR 0.1f +#define DEFAULT_FAR 1000.0f +#define DEFAULT_EYE_Z 5.0f +#define MIN_SEGMENTS 3 +#define PI 3.14159265358979323846f +#define NO_HANDLE -1 + + +// Matches DrawUniforms in scene.hlsl. +typedef struct DrawUniformsS { + Mat4T modelViewProjection; + Mat4T model; + Mat4T normalMatrix; +} DrawUniformsT; + +// Matches Light and FragmentUniforms in scene.hlsl. +typedef struct LightUniformS { + float positionType[4]; + float directionRange[4]; + float color[4]; + float cone[4]; +} LightUniformT; + +typedef struct FragmentUniformsS { + float cameraPosition[4]; + float ambient[4]; + float baseColor[4]; + float emissive[4]; + float material[4]; + float counts[4]; + LightUniformT lights[MAX_LIGHTS]; +} FragmentUniformsT; + +typedef struct MeshS { + SDL_GPUBuffer *vertexBuffer; + SDL_GPUBuffer *indexBuffer; + uint32_t indexCount; + bool skinned; + bool used; +} MeshT; + +// A video player's frames as a texture: the 2D renderer copies the player's (YUV) texture into +// an RGBA target every frame, and materials sample that. +typedef struct FeedS { + int32_t player; + SDL_Texture *target; // Owned; created once the source's size is known + SDL_GPUTexture *gpu; // The target as the scene samples it (not owned) + bool used; +} FeedT; + +typedef struct MaterialS { + Vec4T baseColor; + Vec3T emissive; + float metallic; + float roughness; + SDL_GPUTexture *texture; // Owned; NULL means untextured + int32_t feed; // A video feed instead of the texture, NO_HANDLE for none + bool unlit; + bool doubleSided; + bool blend; + bool used; +} MaterialT; + +typedef struct LightS { + LightTypeE type; + Vec3T color; + float intensity; + float range; + float innerDegrees; + float outerDegrees; +} LightT; + +typedef struct NodeS { + char *name; + int32_t parent; + int32_t firstChild; + int32_t nextSibling; + Vec3T translation; + QuatT rotation; + Vec3T scale; + Mat4T world; // Rebuilt every frame + int32_t mesh; + int32_t material; + LightT light; + int32_t *skinJoints; // Nodes whose world matrices drive a skinned mesh + Mat4T *skinInverseBind; + int32_t skinCount; + uint32_t generation; // Counts reuses of this slot, so stale handles can be told apart + bool hasLight; + bool visible; + bool worldVisible; // Own flag and every ancestor's + bool used; +} NodeT; + +typedef struct DrawS { + int32_t node; + float depth; // View-space distance, for sorting blended draws +} DrawT; + +// Matches SkinUniforms in scene.hlsl. +typedef struct SkinUniformsS { + Mat4T joints[MAX_JOINTS]; +} SkinUniformsT; + +typedef struct SceneS { + SDL_GPUDevice *device; + SDL_Renderer *renderer; + SDL_GPUTexture *colour; // What the 2D renderer samples (resolved when multisampled) + SDL_GPUTexture *multisampled; // The colour target while antialiasing, resolved into colour + SDL_GPUTexture *depth; + SDL_GPUTextureFormat depthFormat; + SDL_GPUSampleCount sampleCount; + bool antialias; // Wanted; sampleCount says what the device gave + SDL_Texture *composite; // The colour target as the 2D renderer sees it + SDL_GPUShader *vertexStatic; + SDL_GPUShader *vertexSkinned; + SDL_GPUShader *fragment; + SDL_GPUGraphicsPipeline *pipelines[PIPELINE_COUNT]; + SDL_GPUSampler *sampler; + SDL_GPUTexture *white; // 1x1 stand-in for untextured materials + SDL_FColor background; + Vec3T ambient; + NodeT *nodes; + int32_t nodeCount; + MeshT *meshes; + int32_t meshCount; + MaterialT *materials; + int32_t materialCount; + FeedT *feeds; + int32_t feedCount; + DrawT *draws; + int32_t drawCapacity; + int32_t cameraNode; // NO_HANDLE for the built-in default view + bool perspective; + float fov; + float orthoHeight; + float near; + float far; + Mat4T viewProjection; // Of the last rendered frame, for sceneProject + int32_t width; + int32_t height; + bool enabled; +} SceneT; + + +static int32_t _addMesh(const SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount, bool skinned); +static int32_t _allocFeed(int32_t player); +static int32_t _allocMaterial(void); +static int32_t _allocNode(void); +static void _attach(int32_t node, int32_t parent); +static int32_t _compareDraws(const void *a, const void *b); +static bool _createPipeline(int32_t variant); +static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms); +static bool _createShaders(void); +static SDL_GPUTextureFormat _depthFormat(void); +static void _destroyTargets(void); +static void _detach(int32_t node); +static void _destroyPipelines(void); +static void _fillLights(FragmentUniformsT *uniforms); +static void _fillSkin(const NodeT *node, SkinUniformsT *uniforms); +static void _freeFeed(FeedT *feed); +static void _freeSkin(NodeT *node); +static void _freeMaterialTexture(MaterialT *material); +static void _lathe(SceneVertexT **vertices, int32_t *vertexCount, uint32_t **indices, int32_t *indexCount, float bottomRadius, float topRadius, float height, int32_t segments); +static int32_t _lookupPipeline(int32_t node); +static Mat4T _projection(void); +static void _updateWorld(int32_t node, const Mat4T *parentWorld, bool parentVisible); +static SDL_GPUBuffer *_uploadBuffer(SDL_GPUBufferUsageFlags usage, const void *data, uint32_t size); +static SDL_GPUTexture *_materialTexture(const MaterialT *material); +static SDL_GPUTexture *_uploadTexture(SDL_Surface *image); +static SceneVertexT _vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v); +static Mat4T _view(void); +static Vec3T _viewEye(void); + + +static SceneT _scene; + + +// ===== Internal helpers ===== + +static int32_t _addMesh(const SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount, bool skinned) { + int32_t x; + MeshT *mesh; + + if ((_scene.device == NULL) || (vertexCount <= 0) || (indexCount <= 0)) { + return NO_HANDLE; + } + for (x = 0; x < _scene.meshCount; x++) { + if (!_scene.meshes[x].used) { + break; + } + } + if (x == _scene.meshCount) { + _scene.meshes = SDL_realloc(_scene.meshes, sizeof(MeshT) * (size_t)(_scene.meshCount + 1)); + if (_scene.meshes == NULL) { + utilDie("Out of memory allocating a mesh."); + } + _scene.meshCount++; + } + mesh = &_scene.meshes[x]; + memset(mesh, 0, sizeof(*mesh)); + mesh->vertexBuffer = _uploadBuffer(SDL_GPU_BUFFERUSAGE_VERTEX, vertices, (uint32_t)(sizeof(SceneVertexT) * (size_t)vertexCount)); + mesh->indexBuffer = _uploadBuffer(SDL_GPU_BUFFERUSAGE_INDEX, indices, (uint32_t)(sizeof(uint32_t) * (size_t)indexCount)); + if ((mesh->vertexBuffer == NULL) || (mesh->indexBuffer == NULL)) { + mesh->used = true; + meshDelete(x); + return NO_HANDLE; + } + mesh->indexCount = (uint32_t)indexCount; + mesh->skinned = skinned; + mesh->used = true; + return x; +} + + +// One feed per player, shared by every material showing it. +static int32_t _allocFeed(int32_t player) { + int32_t x; + + for (x = 0; x < _scene.feedCount; x++) { + if (_scene.feeds[x].used && (_scene.feeds[x].player == player)) { + return x; + } + } + for (x = 0; x < _scene.feedCount; x++) { + if (!_scene.feeds[x].used) { + break; + } + } + if (x == _scene.feedCount) { + _scene.feeds = SDL_realloc(_scene.feeds, sizeof(FeedT) * (size_t)(_scene.feedCount + 1)); + if (_scene.feeds == NULL) { + utilDie("Out of memory allocating a video feed."); + } + _scene.feedCount++; + } + memset(&_scene.feeds[x], 0, sizeof(FeedT)); + _scene.feeds[x].player = player; + _scene.feeds[x].used = true; + return x; +} + + +static int32_t _allocMaterial(void) { + int32_t x; + MaterialT *material; + + for (x = 0; x < _scene.materialCount; x++) { + if (!_scene.materials[x].used) { + break; + } + } + if (x == _scene.materialCount) { + _scene.materials = SDL_realloc(_scene.materials, sizeof(MaterialT) * (size_t)(_scene.materialCount + 1)); + if (_scene.materials == NULL) { + utilDie("Out of memory allocating a material."); + } + _scene.materialCount++; + } + material = &_scene.materials[x]; + memset(material, 0, sizeof(*material)); + material->baseColor.x = 1.0f; + material->baseColor.y = 1.0f; + material->baseColor.z = 1.0f; + material->baseColor.w = 1.0f; + material->roughness = 0.5f; + material->feed = NO_HANDLE; + material->used = true; + return x; +} + + +// A fresh node, detached, at the origin. +static int32_t _allocNode(void) { + int32_t x; + uint32_t generation; + NodeT *node; + + for (x = 0; x < _scene.nodeCount; x++) { + if (!_scene.nodes[x].used) { + break; + } + } + if (x == _scene.nodeCount) { + _scene.nodes = SDL_realloc(_scene.nodes, sizeof(NodeT) * (size_t)(_scene.nodeCount + 1)); + if (_scene.nodes == NULL) { + utilDie("Out of memory allocating a scene node."); + } + _scene.nodeCount++; + } + node = &_scene.nodes[x]; + generation = node->generation + 1; + memset(node, 0, sizeof(*node)); + node->generation = generation; + node->parent = NO_HANDLE; + node->firstChild = NO_HANDLE; + node->nextSibling = NO_HANDLE; + node->rotation = quatIdentity(); + node->scale = vec3(1.0f, 1.0f, 1.0f); + node->world = mat4Identity(); + node->mesh = NO_HANDLE; + node->material = NO_HANDLE; + node->visible = true; + node->used = true; + return x; +} + + +// Links node under parent as its last child. +static void _attach(int32_t node, int32_t parent) { + int32_t last; + + _scene.nodes[node].parent = parent; + _scene.nodes[node].nextSibling = NO_HANDLE; + if (_scene.nodes[parent].firstChild == NO_HANDLE) { + _scene.nodes[parent].firstChild = node; + return; + } + last = _scene.nodes[parent].firstChild; + while (_scene.nodes[last].nextSibling != NO_HANDLE) { + last = _scene.nodes[last].nextSibling; + } + _scene.nodes[last].nextSibling = node; +} + + +// Blended draws go back to front; opaque ones keep their order. +static int32_t _compareDraws(const void *a, const void *b) { + const DrawT *da = a; + const DrawT *db = b; + + if (da->depth > db->depth) { + return -1; + } + if (da->depth < db->depth) { + return 1; + } + return 0; +} + + +static bool _createPipeline(int32_t variant) { + SDL_GPUGraphicsPipelineCreateInfo info; + SDL_GPUVertexBufferDescription buffers[1]; + SDL_GPUVertexAttribute attributes[5]; + SDL_GPUColorTargetDescription colour; + + memset(&info, 0, sizeof(info)); + memset(buffers, 0, sizeof(buffers)); + memset(attributes, 0, sizeof(attributes)); + memset(&colour, 0, sizeof(colour)); + buffers[0].slot = 0; + buffers[0].pitch = sizeof(SceneVertexT); + buffers[0].input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; + attributes[0].location = 0; + attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; + attributes[0].offset = offsetof(SceneVertexT, position); + attributes[1].location = 1; + attributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; + attributes[1].offset = offsetof(SceneVertexT, normal); + attributes[2].location = 2; + attributes[2].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; + attributes[2].offset = offsetof(SceneVertexT, uv); + attributes[3].location = 3; + attributes[3].format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4; + attributes[3].offset = offsetof(SceneVertexT, joints); + attributes[4].location = 4; + attributes[4].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; + attributes[4].offset = offsetof(SceneVertexT, weights); + colour.format = SDL_GetGPUTextureFormatFromPixelFormat(SDL_PIXELFORMAT_BGRA32); + if (variant & PIPELINE_BLEND) { + colour.blend_state.enable_blend = true; + colour.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; + colour.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + colour.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD; + colour.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE; + colour.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + colour.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD; + } + info.vertex_shader = (variant & PIPELINE_SKINNED) ? _scene.vertexSkinned : _scene.vertexStatic; + info.fragment_shader = _scene.fragment; + info.vertex_input_state.vertex_buffer_descriptions = buffers; + info.vertex_input_state.num_vertex_buffers = 1; + info.vertex_input_state.vertex_attributes = attributes; + info.vertex_input_state.num_vertex_attributes = 5; + info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; + info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL; + info.rasterizer_state.cull_mode = (variant & PIPELINE_TWO_SIDED) ? SDL_GPU_CULLMODE_NONE : SDL_GPU_CULLMODE_BACK; + info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE; + info.multisample_state.sample_count = _scene.sampleCount; + info.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL; + info.depth_stencil_state.enable_depth_test = true; + info.depth_stencil_state.enable_depth_write = (variant & PIPELINE_BLEND) ? false : true; + info.target_info.color_target_descriptions = &colour; + info.target_info.num_color_targets = 1; + info.target_info.depth_stencil_format = _scene.depthFormat; + info.target_info.has_depth_stencil_target = true; + _scene.pipelines[variant] = SDL_CreateGPUGraphicsPipeline(_scene.device, &info); + if (_scene.pipelines[variant] == NULL) { + utilTrace("Scene: pipeline %d: %s", variant, SDL_GetError()); + return false; + } + return true; +} + + +// Picks the blob for the format the device accepts. +static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms) { + SDL_GPUShaderCreateInfo info; + SDL_GPUShaderFormat formats = SDL_GetGPUShaderFormats(_scene.device); + SDL_GPUShader *result; + + memset(&info, 0, sizeof(info)); + if (formats & SDL_GPU_SHADERFORMAT_SPIRV) { + info.code = shader->spirv; + info.code_size = shader->spirvSize; + info.format = SDL_GPU_SHADERFORMAT_SPIRV; + } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) { + info.code = shader->dxil; + info.code_size = shader->dxilSize; + info.format = SDL_GPU_SHADERFORMAT_DXIL; + } else if (formats & SDL_GPU_SHADERFORMAT_MSL) { + info.code = shader->msl; + info.code_size = shader->mslSize; + info.format = SDL_GPU_SHADERFORMAT_MSL; + } else { + utilTrace("Scene: the GPU device accepts none of SPIR-V, DXIL or MSL."); + return NULL; + } + info.entrypoint = shader->entryPoint; + info.stage = stage; + info.num_samplers = samplers; + info.num_uniform_buffers = uniforms; + result = SDL_CreateGPUShader(_scene.device, &info); + if (result == NULL) { + utilTrace("Scene: shader %s: %s", shader->entryPoint, SDL_GetError()); + } + return result; +} + + +static bool _createShaders(void) { + _scene.vertexStatic = _createShader(&sceneShaderVertexStatic, SDL_GPU_SHADERSTAGE_VERTEX, 0, 1); + _scene.vertexSkinned = _createShader(&sceneShaderVertexSkinned, SDL_GPU_SHADERSTAGE_VERTEX, 0, 2); + _scene.fragment = _createShader(&sceneShaderFragmentMain, SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1); + return (_scene.vertexStatic != NULL) && (_scene.vertexSkinned != NULL) && (_scene.fragment != NULL); +} + + +// Picks the best depth format the device offers. +static SDL_GPUTextureFormat _depthFormat(void) { + SDL_GPUTextureFormat wanted[] = { SDL_GPU_TEXTUREFORMAT_D32_FLOAT, SDL_GPU_TEXTUREFORMAT_D24_UNORM, SDL_GPU_TEXTUREFORMAT_D16_UNORM }; + int32_t x; + + for (x = 0; x < (int32_t)SDL_arraysize(wanted); x++) { + if (SDL_GPUTextureSupportsFormat(_scene.device, wanted[x], SDL_GPU_TEXTURETYPE_2D, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) { + return wanted[x]; + } + } + return SDL_GPU_TEXTUREFORMAT_D16_UNORM; +} + + +// Pipelines bake in the sample count, so a change in antialiasing drops them; they come back on +// first use. +static void _destroyPipelines(void) { + int32_t x; + + for (x = 0; x < PIPELINE_COUNT; x++) { + if (_scene.pipelines[x] != NULL) { + SDL_ReleaseGPUGraphicsPipeline(_scene.device, _scene.pipelines[x]); + _scene.pipelines[x] = NULL; + } + } +} + + +static void _destroyTargets(void) { + // The SDL_Texture only wraps the colour target (it was created from it), so it goes first. + if (_scene.composite != NULL) { + SDL_DestroyTexture(_scene.composite); + _scene.composite = NULL; + } + if (_scene.colour != NULL) { + SDL_ReleaseGPUTexture(_scene.device, _scene.colour); + _scene.colour = NULL; + } + if (_scene.multisampled != NULL) { + SDL_ReleaseGPUTexture(_scene.device, _scene.multisampled); + _scene.multisampled = NULL; + } + if (_scene.depth != NULL) { + SDL_ReleaseGPUTexture(_scene.device, _scene.depth); + _scene.depth = NULL; + } + _scene.width = 0; + _scene.height = 0; +} + + +// Unlinks node from its parent's child list. +static void _detach(int32_t node) { + int32_t parent = _scene.nodes[node].parent; + int32_t child; + + if (parent == NO_HANDLE) { + return; + } + if (_scene.nodes[parent].firstChild == node) { + _scene.nodes[parent].firstChild = _scene.nodes[node].nextSibling; + } else { + child = _scene.nodes[parent].firstChild; + while ((child != NO_HANDLE) && (_scene.nodes[child].nextSibling != node)) { + child = _scene.nodes[child].nextSibling; + } + if (child != NO_HANDLE) { + _scene.nodes[child].nextSibling = _scene.nodes[node].nextSibling; + } + } + _scene.nodes[node].parent = NO_HANDLE; + _scene.nodes[node].nextSibling = NO_HANDLE; +} + + +// The first MAX_LIGHTS visible lights, in world space. +static void _fillLights(FragmentUniformsT *uniforms) { + int32_t x; + int32_t count = 0; + NodeT *node; + LightUniformT *light; + Vec3T position; + Vec3T direction; + + for (x = 0; (x < _scene.nodeCount) && (count < MAX_LIGHTS); x++) { + node = &_scene.nodes[x]; + if (!node->used || !node->hasLight || !node->worldVisible) { + continue; + } + light = &uniforms->lights[count]; + position = mat4TransformPoint(node->world, vec3(0.0f, 0.0f, 0.0f)); + direction = vec3Normalize(mat4TransformVector(node->world, vec3(0.0f, 0.0f, -1.0f))); + light->positionType[0] = position.x; + light->positionType[1] = position.y; + light->positionType[2] = position.z; + light->positionType[3] = (float)node->light.type; + light->directionRange[0] = direction.x; + light->directionRange[1] = direction.y; + light->directionRange[2] = direction.z; + light->directionRange[3] = node->light.range; + light->color[0] = node->light.color.x * node->light.intensity; + light->color[1] = node->light.color.y * node->light.intensity; + light->color[2] = node->light.color.z * node->light.intensity; + light->color[3] = 1.0f; + light->cone[0] = cosf(node->light.innerDegrees * PI / 180.0f); + light->cone[1] = cosf(node->light.outerDegrees * PI / 180.0f); + light->cone[2] = 0.0f; + light->cone[3] = 0.0f; + count++; + } + uniforms->counts[0] = (float)count; +} + + +// Joint matrices for one skinned draw: the mesh node's own transform cancels out (glTF says a +// skinned mesh ignores it), so each joint is world * inverseBind brought into the mesh's space. +static void _fillSkin(const NodeT *node, SkinUniformsT *uniforms) { + Mat4T meshInverse; + int32_t x; + + if (!mat4Invert(node->world, &meshInverse)) { + meshInverse = mat4Identity(); + } + for (x = 0; (x < node->skinCount) && (x < MAX_JOINTS); x++) { + int32_t joint = node->skinJoints[x]; + + if (nodeValid(joint)) { + uniforms->joints[x] = mat4Multiply(meshInverse, mat4Multiply(_scene.nodes[joint].world, node->skinInverseBind[x])); + } else { + uniforms->joints[x] = mat4Identity(); + } + } + for (; x < MAX_JOINTS; x++) { + uniforms->joints[x] = mat4Identity(); + } +} + + +static void _freeFeed(FeedT *feed) { + if (feed->target != NULL) { + SDL_DestroyTexture(feed->target); + } + memset(feed, 0, sizeof(*feed)); +} + + +static void _freeMaterialTexture(MaterialT *material) { + if (material->texture != NULL) { + SDL_ReleaseGPUTexture(_scene.device, material->texture); + material->texture = NULL; + } +} + + +static void _freeSkin(NodeT *node) { + SDL_free(node->skinJoints); + SDL_free(node->skinInverseBind); + node->skinJoints = NULL; + node->skinInverseBind = NULL; + node->skinCount = 0; +} + + +// A surface of revolution around Y with flat caps: cylinders and cones. Sides get their own +// vertices so the caps can have flat normals. +static void _lathe(SceneVertexT **vertices, int32_t *vertexCount, uint32_t **indices, int32_t *indexCount, float bottomRadius, float topRadius, float height, int32_t segments) { + int32_t x; + int32_t v = 0; + int32_t i = 0; + float angle; + float c; + float s; + float half = height / 2.0f; + float slope; + float slopeLength; + int32_t bottomCentre; + int32_t topCentre; + SceneVertexT *verts; + uint32_t *idx; + + // Sides: two rings of segments + 1 vertices (the seam is doubled for the uv wrap), then a + // centre and ring per cap. + *vertexCount = (segments + 1) * 2 + (segments + 1) * 2; + *indexCount = segments * 6 + segments * 3 * 2; + verts = SDL_calloc((size_t)*vertexCount, sizeof(SceneVertexT)); + idx = SDL_calloc((size_t)*indexCount, sizeof(uint32_t)); + if ((verts == NULL) || (idx == NULL)) { + utilDie("Out of memory building a mesh."); + } + slope = bottomRadius - topRadius; + slopeLength = sqrtf(slope * slope + height * height); + for (x = 0; x <= segments; x++) { + angle = (float)x / (float)segments * 2.0f * PI; + c = cosf(angle); + s = sinf(angle); + verts[v++] = _vertex(c * bottomRadius, -half, s * bottomRadius, c * height / slopeLength, slope / slopeLength, s * height / slopeLength, (float)x / (float)segments, 1.0f); + verts[v++] = _vertex(c * topRadius, half, s * topRadius, c * height / slopeLength, slope / slopeLength, s * height / slopeLength, (float)x / (float)segments, 0.0f); + } + for (x = 0; x < segments; x++) { + // Counter-clockwise seen from outside: the ring runs with the angle, +X toward +Z. + idx[i++] = (uint32_t)(x * 2); + idx[i++] = (uint32_t)(x * 2 + 2); + idx[i++] = (uint32_t)(x * 2 + 1); + idx[i++] = (uint32_t)(x * 2 + 1); + idx[i++] = (uint32_t)(x * 2 + 2); + idx[i++] = (uint32_t)(x * 2 + 3); + } + bottomCentre = v; + verts[v++] = _vertex(0.0f, -half, 0.0f, 0.0f, -1.0f, 0.0f, 0.5f, 0.5f); + for (x = 0; x < segments; x++) { + angle = (float)x / (float)segments * 2.0f * PI; + verts[v++] = _vertex(cosf(angle) * bottomRadius, -half, sinf(angle) * bottomRadius, 0.0f, -1.0f, 0.0f, 0.5f + cosf(angle) / 2.0f, 0.5f + sinf(angle) / 2.0f); + } + for (x = 0; x < segments; x++) { + idx[i++] = (uint32_t)bottomCentre; + idx[i++] = (uint32_t)(bottomCentre + 1 + x); + idx[i++] = (uint32_t)(bottomCentre + 1 + (x + 1) % segments); + } + topCentre = v; + verts[v++] = _vertex(0.0f, half, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f); + for (x = 0; x < segments; x++) { + angle = (float)x / (float)segments * 2.0f * PI; + verts[v++] = _vertex(cosf(angle) * topRadius, half, sinf(angle) * topRadius, 0.0f, 1.0f, 0.0f, 0.5f + cosf(angle) / 2.0f, 0.5f - sinf(angle) / 2.0f); + } + for (x = 0; x < segments; x++) { + idx[i++] = (uint32_t)topCentre; + idx[i++] = (uint32_t)(topCentre + 1 + (x + 1) % segments); + idx[i++] = (uint32_t)(topCentre + 1 + x); + } + *vertices = verts; + *indices = idx; +} + + +// What the fragment shader samples for a material: its video feed, its image, or NULL. +static SDL_GPUTexture *_materialTexture(const MaterialT *material) { + if ((material->feed != NO_HANDLE) && (material->feed < _scene.feedCount) && _scene.feeds[material->feed].used) { + return _scene.feeds[material->feed].gpu; + } + return material->texture; +} + + +// The pipeline variant a node's mesh and material call for, created on first use. +static int32_t _lookupPipeline(int32_t node) { + int32_t variant = 0; + MaterialT *material; + + if (_scene.meshes[_scene.nodes[node].mesh].skinned && (_scene.nodes[node].skinCount > 0)) { + variant |= PIPELINE_SKINNED; + } + if (_scene.nodes[node].material != NO_HANDLE) { + material = &_scene.materials[_scene.nodes[node].material]; + if (material->blend) { + variant |= PIPELINE_BLEND; + } + if (material->doubleSided) { + variant |= PIPELINE_TWO_SIDED; + } + } + if ((_scene.pipelines[variant] == NULL) && !_createPipeline(variant)) { + return NO_HANDLE; + } + return variant; +} + + +static Mat4T _projection(void) { + float aspect = (_scene.height > 0) ? (float)_scene.width / (float)_scene.height : 1.0f; + + if (_scene.perspective) { + return mat4Perspective(_scene.fov, aspect, _scene.near, _scene.far); + } + return mat4Orthographic(_scene.orthoHeight * aspect, _scene.orthoHeight, _scene.near, _scene.far); +} + + +// World matrices and inherited visibility, depth first. +static void _updateWorld(int32_t node, const Mat4T *parentWorld, bool parentVisible) { + NodeT *n = &_scene.nodes[node]; + int32_t child; + + n->world = mat4Multiply(*parentWorld, mat4Compose(n->translation, n->rotation, n->scale)); + n->worldVisible = parentVisible && n->visible; + for (child = n->firstChild; child != NO_HANDLE; child = _scene.nodes[child].nextSibling) { + _updateWorld(child, &n->world, n->worldVisible); + } +} + + +// Copies data into a new GPU buffer through a transfer buffer. +static SDL_GPUBuffer *_uploadBuffer(SDL_GPUBufferUsageFlags usage, const void *data, uint32_t size) { + SDL_GPUBufferCreateInfo info; + SDL_GPUTransferBufferCreateInfo transferInfo; + SDL_GPUTransferBufferLocation source; + SDL_GPUBufferRegion region; + SDL_GPUBuffer *buffer; + SDL_GPUTransferBuffer *transfer; + SDL_GPUCommandBuffer *commands; + SDL_GPUCopyPass *pass; + void *mapped; + + memset(&info, 0, sizeof(info)); + info.usage = usage; + info.size = size; + buffer = SDL_CreateGPUBuffer(_scene.device, &info); + if (buffer == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + return NULL; + } + memset(&transferInfo, 0, sizeof(transferInfo)); + transferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + transferInfo.size = size; + transfer = SDL_CreateGPUTransferBuffer(_scene.device, &transferInfo); + if (transfer == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + SDL_ReleaseGPUBuffer(_scene.device, buffer); + return NULL; + } + mapped = SDL_MapGPUTransferBuffer(_scene.device, transfer, false); + memcpy(mapped, data, size); + SDL_UnmapGPUTransferBuffer(_scene.device, transfer); + commands = SDL_AcquireGPUCommandBuffer(_scene.device); + pass = SDL_BeginGPUCopyPass(commands); + memset(&source, 0, sizeof(source)); + memset(®ion, 0, sizeof(region)); + source.transfer_buffer = transfer; + region.buffer = buffer; + region.size = size; + SDL_UploadToGPUBuffer(pass, &source, ®ion, false); + SDL_EndGPUCopyPass(pass); + SDL_SubmitGPUCommandBuffer(commands); + SDL_ReleaseGPUTransferBuffer(_scene.device, transfer); + return buffer; +} + + +// An RGBA sampler texture from any surface. +static SDL_GPUTexture *_uploadTexture(SDL_Surface *image) { + SDL_Surface *rgba; + SDL_GPUTextureCreateInfo info; + SDL_GPUTransferBufferCreateInfo transferInfo; + SDL_GPUTextureTransferInfo source; + SDL_GPUTextureRegion region; + SDL_GPUTexture *texture; + SDL_GPUTransferBuffer *transfer; + SDL_GPUCommandBuffer *commands; + SDL_GPUCopyPass *pass; + void *mapped; + uint32_t size; + + rgba = SDL_ConvertSurface(image, SDL_PIXELFORMAT_RGBA32); + if (rgba == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + return NULL; + } + size = (uint32_t)(rgba->w * rgba->h * 4); + memset(&info, 0, sizeof(info)); + info.type = SDL_GPU_TEXTURETYPE_2D; + info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; + info.width = (Uint32)rgba->w; + info.height = (Uint32)rgba->h; + info.layer_count_or_depth = 1; + info.num_levels = 1; + info.sample_count = SDL_GPU_SAMPLECOUNT_1; + texture = SDL_CreateGPUTexture(_scene.device, &info); + if (texture == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + SDL_DestroySurface(rgba); + return NULL; + } + memset(&transferInfo, 0, sizeof(transferInfo)); + transferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + transferInfo.size = size; + transfer = SDL_CreateGPUTransferBuffer(_scene.device, &transferInfo); + if (transfer == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + SDL_ReleaseGPUTexture(_scene.device, texture); + SDL_DestroySurface(rgba); + return NULL; + } + mapped = SDL_MapGPUTransferBuffer(_scene.device, transfer, false); + if (rgba->pitch == rgba->w * 4) { + memcpy(mapped, rgba->pixels, size); + } else { + int32_t y; + + for (y = 0; y < rgba->h; y++) { + memcpy((uint8_t *)mapped + y * rgba->w * 4, (uint8_t *)rgba->pixels + y * rgba->pitch, (size_t)rgba->w * 4); + } + } + SDL_UnmapGPUTransferBuffer(_scene.device, transfer); + commands = SDL_AcquireGPUCommandBuffer(_scene.device); + pass = SDL_BeginGPUCopyPass(commands); + memset(&source, 0, sizeof(source)); + memset(®ion, 0, sizeof(region)); + source.transfer_buffer = transfer; + region.texture = texture; + region.w = (Uint32)rgba->w; + region.h = (Uint32)rgba->h; + region.d = 1; + SDL_UploadToGPUTexture(pass, &source, ®ion, false); + SDL_EndGPUCopyPass(pass); + SDL_SubmitGPUCommandBuffer(commands); + SDL_ReleaseGPUTransferBuffer(_scene.device, transfer); + SDL_DestroySurface(rgba); + return texture; +} + + +static SceneVertexT _vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v) { + SceneVertexT out; + + memset(&out, 0, sizeof(out)); + out.position[0] = x; + out.position[1] = y; + out.position[2] = z; + out.normal[0] = nx; + out.normal[1] = ny; + out.normal[2] = nz; + out.uv[0] = u; + out.uv[1] = v; + out.weights[0] = 1.0f; + return out; +} + + +// The view matrix: the inverse of the camera node's world matrix, or the default view. +static Mat4T _view(void) { + Mat4T view; + + if ((_scene.cameraNode == NO_HANDLE) || !nodeValid(_scene.cameraNode)) { + return mat4LookAt(vec3(0.0f, 0.0f, DEFAULT_EYE_Z), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f)); + } + if (!mat4Invert(_scene.nodes[_scene.cameraNode].world, &view)) { + return mat4Identity(); + } + return view; +} + + +static Vec3T _viewEye(void) { + if ((_scene.cameraNode == NO_HANDLE) || !nodeValid(_scene.cameraNode)) { + return vec3(0.0f, 0.0f, DEFAULT_EYE_Z); + } + return mat4TransformPoint(_scene.nodes[_scene.cameraNode].world, vec3(0.0f, 0.0f, 0.0f)); +} + + +// ===== Camera ===== + +// Any node can be the camera: it looks down its own -Z. NO_HANDLE restores the default view. +bool cameraSet(int32_t node) { + if ((node != NO_HANDLE) && !nodeValid(node)) { + return false; + } + _scene.cameraNode = node; + return true; +} + + +void cameraSetOrthographic(float height, float near, float far) { + _scene.perspective = false; + _scene.orthoHeight = height; + _scene.near = near; + _scene.far = far; +} + + +void cameraSetPerspective(float fovDegrees, float near, float far) { + _scene.perspective = true; + _scene.fov = fovDegrees; + _scene.near = near; + _scene.far = far; +} + + +// ===== Lights ===== + +// Makes an existing node a light. Directional and spot lights shine down the node's -Z. +bool lightAttach(int32_t node, LightTypeE type) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].hasLight = true; + _scene.nodes[node].light.type = type; + _scene.nodes[node].light.color = vec3(1.0f, 1.0f, 1.0f); + _scene.nodes[node].light.intensity = 1.0f; + _scene.nodes[node].light.range = 0.0f; + _scene.nodes[node].light.innerDegrees = 20.0f; + _scene.nodes[node].light.outerDegrees = 30.0f; + return true; +} + + +// A new node carrying a light. +int32_t lightNew(LightTypeE type, int32_t parent) { + int32_t node = nodeNew(parent); + + if (node != NO_HANDLE) { + lightAttach(node, type); + } + return node; +} + + +bool lightSetColor(int32_t node, uint8_t r, uint8_t g, uint8_t b) { + if (!nodeValid(node) || !_scene.nodes[node].hasLight) { + return false; + } + _scene.nodes[node].light.color = vec3(r / COLOUR_MAX, g / COLOUR_MAX, b / COLOUR_MAX); + return true; +} + + +bool lightSetCone(int32_t node, float innerDegrees, float outerDegrees) { + if (!nodeValid(node) || !_scene.nodes[node].hasLight) { + return false; + } + _scene.nodes[node].light.innerDegrees = innerDegrees; + _scene.nodes[node].light.outerDegrees = outerDegrees; + return true; +} + + +bool lightSetIntensity(int32_t node, float intensity) { + if (!nodeValid(node) || !_scene.nodes[node].hasLight) { + return false; + } + _scene.nodes[node].light.intensity = intensity; + return true; +} + + +// 0 means no range limit. +bool lightSetRange(int32_t node, float range) { + if (!nodeValid(node) || !_scene.nodes[node].hasLight) { + return false; + } + _scene.nodes[node].light.range = range; + return true; +} + + +// ===== Materials ===== + +bool materialDelete(int32_t material) { + int32_t x; + + if (!materialValid(material)) { + return false; + } + _freeMaterialTexture(&_scene.materials[material]); + _scene.materials[material].used = false; + // Nodes that used it fall back to the default look. + for (x = 0; x < _scene.nodeCount; x++) { + if (_scene.nodes[x].used && (_scene.nodes[x].material == material)) { + _scene.nodes[x].material = NO_HANDLE; + } + } + return true; +} + + +// White, half rough, no texture. +int32_t materialNew(void) { + return _allocMaterial(); +} + + +bool materialSetBlend(int32_t material, bool blend) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].blend = blend; + return true; +} + + +bool materialSetColor(int32_t material, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].baseColor.x = r / COLOUR_MAX; + _scene.materials[material].baseColor.y = g / COLOUR_MAX; + _scene.materials[material].baseColor.z = b / COLOUR_MAX; + _scene.materials[material].baseColor.w = a / COLOUR_MAX; + return true; +} + + +bool materialSetDoubleSided(int32_t material, bool doubleSided) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].doubleSided = doubleSided; + return true; +} + + +bool materialSetEmissive(int32_t material, uint8_t r, uint8_t g, uint8_t b) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].emissive = vec3(r / COLOUR_MAX, g / COLOUR_MAX, b / COLOUR_MAX); + return true; +} + + +bool materialSetMetallic(int32_t material, float metallic) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].metallic = SDL_clamp(metallic, 0.0f, 1.0f); + return true; +} + + +bool materialSetRoughness(int32_t material, float roughness) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].roughness = SDL_clamp(roughness, 0.0f, 1.0f); + return true; +} + + +// Copies the image into a GPU texture; NULL removes the texture. +bool materialSetTexture(int32_t material, SDL_Surface *image) { + SDL_GPUTexture *texture = NULL; + + if (!materialValid(material)) { + return false; + } + if (image != NULL) { + texture = _uploadTexture(image); + if (texture == NULL) { + return false; + } + } + _freeMaterialTexture(&_scene.materials[material]); + _scene.materials[material].texture = texture; + _scene.materials[material].feed = NO_HANDLE; + return true; +} + + +bool materialSetUnlit(int32_t material, bool unlit) { + if (!materialValid(material)) { + return false; + } + _scene.materials[material].unlit = unlit; + return true; +} + + +// A video player's frames as the base colour texture; replaces any image. The frames arrive +// through sceneUpdateVideo each frame. +bool materialSetVideo(int32_t material, int32_t player) { + if (!materialValid(material)) { + return false; + } + _freeMaterialTexture(&_scene.materials[material]); + _scene.materials[material].feed = _allocFeed(player); + return true; +} + + +bool materialValid(int32_t material) { + return (material >= 0) && (material < _scene.materialCount) && _scene.materials[material].used; +} + + +// ===== Meshes ===== + +// Six faces with their own vertices so each has a flat normal. Centred on the origin. +int32_t meshBox(float width, float height, float depth) { + SceneVertexT vertices[24]; + uint32_t indices[36]; + float w = width / 2.0f; + float h = height / 2.0f; + float d = depth / 2.0f; + int32_t face; + int32_t v = 0; + int32_t i = 0; + // Per face: normal, then the four corners counter-clockwise seen from outside. + float faces[6][5][3] = { + { { 0.0f, 0.0f, 1.0f }, { -w, -h, d }, { w, -h, d }, { w, h, d }, { -w, h, d } }, // Front (+Z) + { { 0.0f, 0.0f, -1.0f }, { w, -h, -d }, { -w, -h, -d }, { -w, h, -d }, { w, h, -d } }, // Back (-Z) + { { 1.0f, 0.0f, 0.0f }, { w, -h, d }, { w, -h, -d }, { w, h, -d }, { w, h, d } }, // Right (+X) + { { -1.0f, 0.0f, 0.0f }, { -w, -h, -d }, { -w, -h, d }, { -w, h, d }, { -w, h, -d } }, // Left (-X) + { { 0.0f, 1.0f, 0.0f }, { -w, h, d }, { w, h, d }, { w, h, -d }, { -w, h, -d } }, // Top (+Y) + { { 0.0f, -1.0f, 0.0f }, { -w, -h, -d }, { w, -h, -d }, { w, -h, d }, { -w, -h, d } }, // Bottom (-Y) + }; + float uvs[4][2] = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } }; + + for (face = 0; face < 6; face++) { + int32_t corner; + + for (corner = 0; corner < 4; corner++) { + vertices[v++] = _vertex(faces[face][corner + 1][0], faces[face][corner + 1][1], faces[face][corner + 1][2], faces[face][0][0], faces[face][0][1], faces[face][0][2], uvs[corner][0], uvs[corner][1]); + } + indices[i++] = (uint32_t)(face * 4); + indices[i++] = (uint32_t)(face * 4 + 1); + indices[i++] = (uint32_t)(face * 4 + 2); + indices[i++] = (uint32_t)(face * 4); + indices[i++] = (uint32_t)(face * 4 + 2); + indices[i++] = (uint32_t)(face * 4 + 3); + } + return _addMesh(vertices, 24, indices, 36, false); +} + + +int32_t meshCone(float radius, float height, int32_t segments) { + SceneVertexT *vertices; + uint32_t *indices; + int32_t vertexCount; + int32_t indexCount; + int32_t mesh; + + _lathe(&vertices, &vertexCount, &indices, &indexCount, radius, 0.0f, height, SDL_max(segments, MIN_SEGMENTS)); + mesh = _addMesh(vertices, vertexCount, indices, indexCount, false); + SDL_free(vertices); + SDL_free(indices); + return mesh; +} + + +int32_t meshCylinder(float radius, float height, int32_t segments) { + SceneVertexT *vertices; + uint32_t *indices; + int32_t vertexCount; + int32_t indexCount; + int32_t mesh; + + _lathe(&vertices, &vertexCount, &indices, &indexCount, radius, radius, height, SDL_max(segments, MIN_SEGMENTS)); + mesh = _addMesh(vertices, vertexCount, indices, indexCount, false); + SDL_free(vertices); + SDL_free(indices); + return mesh; +} + + +bool meshDelete(int32_t mesh) { + int32_t x; + + if (!meshValid(mesh)) { + return false; + } + if (_scene.meshes[mesh].vertexBuffer != NULL) { + SDL_ReleaseGPUBuffer(_scene.device, _scene.meshes[mesh].vertexBuffer); + } + if (_scene.meshes[mesh].indexBuffer != NULL) { + SDL_ReleaseGPUBuffer(_scene.device, _scene.meshes[mesh].indexBuffer); + } + memset(&_scene.meshes[mesh], 0, sizeof(MeshT)); + for (x = 0; x < _scene.nodeCount; x++) { + if (_scene.nodes[x].used && (_scene.nodes[x].mesh == mesh)) { + _scene.nodes[x].mesh = NO_HANDLE; + } + } + return true; +} + + +// Raw geometry from a script: positions (3 per vertex), normals (3, may be NULL for flat +// shading computed here), uvs (2, may be NULL), and triangle indices. +int32_t meshNew(const float *positions, const float *normals, const float *uvs, int32_t vertexCount, const uint32_t *indices, int32_t indexCount) { + SceneVertexT *vertices; + int32_t x; + int32_t mesh; + + if ((positions == NULL) || (indices == NULL) || (vertexCount <= 0) || (indexCount < 3)) { + return NO_HANDLE; + } + for (x = 0; x < indexCount; x++) { + if (indices[x] >= (uint32_t)vertexCount) { + return NO_HANDLE; + } + } + vertices = SDL_calloc((size_t)vertexCount, sizeof(SceneVertexT)); + if (vertices == NULL) { + utilDie("Out of memory building a mesh."); + } + for (x = 0; x < vertexCount; x++) { + vertices[x] = _vertex(positions[x * 3], positions[x * 3 + 1], positions[x * 3 + 2], 0.0f, 0.0f, 0.0f, uvs ? uvs[x * 2] : 0.0f, uvs ? uvs[x * 2 + 1] : 0.0f); + if (normals != NULL) { + vertices[x].normal[0] = normals[x * 3]; + vertices[x].normal[1] = normals[x * 3 + 1]; + vertices[x].normal[2] = normals[x * 3 + 2]; + } + } + if (normals == NULL) { + sceneComputeNormals(vertices, vertexCount, indices, indexCount); + } + mesh = _addMesh(vertices, vertexCount, indices, indexCount, false); + SDL_free(vertices); + return mesh; +} + + +// Geometry a loader has already laid out in GPU form. +int32_t meshNewVertices(const SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount, bool skinned) { + int32_t x; + + if ((vertices == NULL) || (indices == NULL) || (vertexCount <= 0) || (indexCount < 3)) { + return NO_HANDLE; + } + for (x = 0; x < indexCount; x++) { + if (indices[x] >= (uint32_t)vertexCount) { + return NO_HANDLE; + } + } + return _addMesh(vertices, vertexCount, indices, indexCount, skinned); +} + + +// A quad in the XZ plane facing +Y. +int32_t meshPlane(float width, float depth) { + SceneVertexT vertices[4]; + uint32_t indices[6] = { 0, 1, 2, 0, 2, 3 }; + float w = width / 2.0f; + float d = depth / 2.0f; + + vertices[0] = _vertex(-w, 0.0f, d, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f); + vertices[1] = _vertex( w, 0.0f, d, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f); + vertices[2] = _vertex( w, 0.0f, -d, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); + vertices[3] = _vertex(-w, 0.0f, -d, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); + return _addMesh(vertices, 4, indices, 6, false); +} + + +// Latitude/longitude sphere; segments around, half as many from pole to pole. +int32_t meshSphere(float radius, int32_t segments) { + SceneVertexT *vertices; + uint32_t *indices; + int32_t rings; + int32_t ring; + int32_t seg; + int32_t v = 0; + int32_t i = 0; + int32_t vertexCount; + int32_t indexCount; + int32_t mesh; + + segments = SDL_max(segments, MIN_SEGMENTS); + rings = SDL_max(segments / 2, 2); + vertexCount = (rings + 1) * (segments + 1); + indexCount = rings * segments * 6; + vertices = SDL_calloc((size_t)vertexCount, sizeof(SceneVertexT)); + indices = SDL_calloc((size_t)indexCount, sizeof(uint32_t)); + if ((vertices == NULL) || (indices == NULL)) { + utilDie("Out of memory building a mesh."); + } + for (ring = 0; ring <= rings; ring++) { + float phi = (float)ring / (float)rings * PI; + + for (seg = 0; seg <= segments; seg++) { + float theta = (float)seg / (float)segments * 2.0f * PI; + float nx = sinf(phi) * cosf(theta); + float ny = cosf(phi); + float nz = sinf(phi) * sinf(theta); + + // u runs the other way so an image reads correctly from outside. + vertices[v++] = _vertex(nx * radius, ny * radius, nz * radius, nx, ny, nz, 1.0f - (float)seg / (float)segments, (float)ring / (float)rings); + } + } + for (ring = 0; ring < rings; ring++) { + for (seg = 0; seg < segments; seg++) { + uint32_t a = (uint32_t)(ring * (segments + 1) + seg); + uint32_t b = a + (uint32_t)segments + 1; + + indices[i++] = a; + indices[i++] = a + 1; + indices[i++] = b; + indices[i++] = a + 1; + indices[i++] = b + 1; + indices[i++] = b; + } + } + mesh = _addMesh(vertices, vertexCount, indices, indexCount, false); + SDL_free(vertices); + SDL_free(indices); + return mesh; +} + + +// A ring around Y. +int32_t meshTorus(float radius, float tubeRadius, int32_t segments) { + SceneVertexT *vertices; + uint32_t *indices; + int32_t tubeSegments; + int32_t x; + int32_t y; + int32_t v = 0; + int32_t i = 0; + int32_t vertexCount; + int32_t indexCount; + int32_t mesh; + + segments = SDL_max(segments, MIN_SEGMENTS); + tubeSegments = SDL_max(segments / 2, MIN_SEGMENTS); + vertexCount = (segments + 1) * (tubeSegments + 1); + indexCount = segments * tubeSegments * 6; + vertices = SDL_calloc((size_t)vertexCount, sizeof(SceneVertexT)); + indices = SDL_calloc((size_t)indexCount, sizeof(uint32_t)); + if ((vertices == NULL) || (indices == NULL)) { + utilDie("Out of memory building a mesh."); + } + for (x = 0; x <= segments; x++) { + float theta = (float)x / (float)segments * 2.0f * PI; + float cx = cosf(theta); + float sx = sinf(theta); + + for (y = 0; y <= tubeSegments; y++) { + float phi = (float)y / (float)tubeSegments * 2.0f * PI; + float cy = cosf(phi); + float sy = sinf(phi); + + vertices[v++] = _vertex(cx * (radius + cy * tubeRadius), sy * tubeRadius, sx * (radius + cy * tubeRadius), cx * cy, sy, sx * cy, (float)x / (float)segments, (float)y / (float)tubeSegments); + } + } + for (x = 0; x < segments; x++) { + for (y = 0; y < tubeSegments; y++) { + uint32_t a = (uint32_t)(x * (tubeSegments + 1) + y); + uint32_t b = a + (uint32_t)tubeSegments + 1; + + indices[i++] = a; + indices[i++] = b; + indices[i++] = a + 1; + indices[i++] = a + 1; + indices[i++] = b; + indices[i++] = b + 1; + } + } + mesh = _addMesh(vertices, vertexCount, indices, indexCount, false); + SDL_free(vertices); + SDL_free(indices); + return mesh; +} + + +bool meshValid(int32_t mesh) { + return (mesh >= 0) && (mesh < _scene.meshCount) && _scene.meshes[mesh].used; +} + + +// ===== Nodes ===== + +// Frees the node and everything under it. The root cannot be deleted. +bool nodeDelete(int32_t node) { + int32_t child; + int32_t next; + + if (!nodeValid(node) || (node == SCENE_ROOT_NODE)) { + return false; + } + for (child = _scene.nodes[node].firstChild; child != NO_HANDLE; child = next) { + next = _scene.nodes[child].nextSibling; + nodeDelete(child); + } + _detach(node); + if (_scene.cameraNode == node) { + _scene.cameraNode = NO_HANDLE; + } + SDL_free(_scene.nodes[node].name); + _freeSkin(&_scene.nodes[node]); + _scene.nodes[node].name = NULL; + _scene.nodes[node].used = false; + return true; +} + + +// Depth-first search below root (root itself included) for a node by name. +int32_t nodeFind(int32_t root, const char *name) { + int32_t child; + int32_t found; + + if (!nodeValid(root) || (name == NULL)) { + return NO_HANDLE; + } + if ((_scene.nodes[root].name != NULL) && (strcmp(_scene.nodes[root].name, name) == 0)) { + return root; + } + for (child = _scene.nodes[root].firstChild; child != NO_HANDLE; child = _scene.nodes[child].nextSibling) { + found = nodeFind(child, name); + if (found != NO_HANDLE) { + return found; + } + } + return NO_HANDLE; +} + + +int32_t nodeGetChild(int32_t node, int32_t index) { + int32_t child; + + if (!nodeValid(node) || (index < 0)) { + return NO_HANDLE; + } + child = _scene.nodes[node].firstChild; + while ((child != NO_HANDLE) && (index > 0)) { + child = _scene.nodes[child].nextSibling; + index--; + } + return child; +} + + +int32_t nodeGetChildCount(int32_t node) { + int32_t child; + int32_t count = 0; + + if (!nodeValid(node)) { + return 0; + } + for (child = _scene.nodes[node].firstChild; child != NO_HANDLE; child = _scene.nodes[child].nextSibling) { + count++; + } + return count; +} + + +// Changes every time the slot is reused, so a handle kept across a delete can be detected. +uint32_t nodeGetGeneration(int32_t node) { + if ((node < 0) || (node >= _scene.nodeCount)) { + return 0; + } + return _scene.nodes[node].generation; +} + + +const char *nodeGetName(int32_t node) { + if (!nodeValid(node) || (_scene.nodes[node].name == NULL)) { + return ""; + } + return _scene.nodes[node].name; +} + + +int32_t nodeGetParent(int32_t node) { + if (!nodeValid(node)) { + return NO_HANDLE; + } + return _scene.nodes[node].parent; +} + + +Vec3T nodeGetPosition(int32_t node) { + if (!nodeValid(node)) { + return vec3(0.0f, 0.0f, 0.0f); + } + return _scene.nodes[node].translation; +} + + +QuatT nodeGetRotation(int32_t node) { + if (!nodeValid(node)) { + return quatIdentity(); + } + return _scene.nodes[node].rotation; +} + + +Vec3T nodeGetScale(int32_t node) { + if (!nodeValid(node)) { + return vec3(1.0f, 1.0f, 1.0f); + } + return _scene.nodes[node].scale; +} + + +// From the last rendered frame's matrices (this frame's edits show after the next render). +Vec3T nodeGetWorldPosition(int32_t node) { + if (!nodeValid(node)) { + return vec3(0.0f, 0.0f, 0.0f); + } + return mat4TransformPoint(_scene.nodes[node].world, vec3(0.0f, 0.0f, 0.0f)); +} + + +// Points the node's -Z at a world-space target (the camera and lights look down -Z). Only the +// node's own rotation changes, in its parent's space. +bool nodeLookAt(int32_t node, Vec3T target) { + NodeT *n; + Mat4T parentInverse; + Vec3T localTarget; + Vec3T forward; + + if (!nodeValid(node)) { + return false; + } + n = &_scene.nodes[node]; + if ((n->parent != NO_HANDLE) && mat4Invert(_scene.nodes[n->parent].world, &parentInverse)) { + localTarget = mat4TransformPoint(parentInverse, target); + } else { + localTarget = target; + } + forward = vec3Subtract(localTarget, n->translation); + if (vec3Length(forward) < 1e-6f) { + return true; + } + n->rotation = quatLookRotation(forward, vec3(0.0f, 1.0f, 0.0f)); + return true; +} + + +// Moves along the node's own axes. +bool nodeMove(int32_t node, Vec3T delta) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].translation = vec3Add(_scene.nodes[node].translation, quatRotate(_scene.nodes[node].rotation, delta)); + return true; +} + + +int32_t nodeNew(int32_t parent) { + int32_t node; + + if (_scene.device == NULL) { + return NO_HANDLE; + } + if (parent == NO_HANDLE) { + parent = SCENE_ROOT_NODE; + } + if (!nodeValid(parent)) { + return NO_HANDLE; + } + node = _allocNode(); + _attach(node, parent); + return node; +} + + +// Rotates about the node's own axes. +bool nodeRotate(int32_t node, QuatT delta) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].rotation = quatNormalize(quatMultiply(_scene.nodes[node].rotation, delta)); + return true; +} + + +// mesh NO_HANDLE clears; material NO_HANDLE means the default look. +bool nodeSetMesh(int32_t node, int32_t mesh, int32_t material) { + if (!nodeValid(node)) { + return false; + } + if ((mesh != NO_HANDLE) && !meshValid(mesh)) { + return false; + } + if ((material != NO_HANDLE) && !materialValid(material)) { + return false; + } + _scene.nodes[node].mesh = mesh; + _scene.nodes[node].material = material; + return true; +} + + +bool nodeSetName(int32_t node, const char *name) { + if (!nodeValid(node)) { + return false; + } + SDL_free(_scene.nodes[node].name); + _scene.nodes[node].name = (name != NULL) ? SDL_strdup(name) : NULL; + return true; +} + + +// Re-parents, keeping the node's local transform (so it moves with the new parent). A node +// cannot be put under itself or its own descendants. +bool nodeSetParent(int32_t node, int32_t parent) { + int32_t ancestor; + + if (!nodeValid(node) || (node == SCENE_ROOT_NODE)) { + return false; + } + if (parent == NO_HANDLE) { + parent = SCENE_ROOT_NODE; + } + if (!nodeValid(parent)) { + return false; + } + for (ancestor = parent; ancestor != NO_HANDLE; ancestor = _scene.nodes[ancestor].parent) { + if (ancestor == node) { + return false; + } + } + _detach(node); + _attach(node, parent); + return true; +} + + +bool nodeSetPosition(int32_t node, Vec3T position) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].translation = position; + return true; +} + + +bool nodeSetRotation(int32_t node, QuatT rotation) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].rotation = quatNormalize(rotation); + return true; +} + + +bool nodeSetScale(int32_t node, Vec3T scale) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].scale = scale; + return true; +} + + +// Drives the node's skinned mesh from other nodes: joints (up to 128) and their inverse bind +// matrices, copied. count 0 removes the skin and the mesh draws unskinned. +bool nodeSetSkin(int32_t node, const int32_t *joints, const Mat4T *inverseBind, int32_t count) { + NodeT *n; + + if (!nodeValid(node) || (count < 0) || (count > MAX_JOINTS)) { + return false; + } + n = &_scene.nodes[node]; + _freeSkin(n); + if (count == 0) { + return true; + } + n->skinJoints = SDL_malloc(sizeof(int32_t) * (size_t)count); + n->skinInverseBind = SDL_malloc(sizeof(Mat4T) * (size_t)count); + if ((n->skinJoints == NULL) || (n->skinInverseBind == NULL)) { + utilDie("Out of memory attaching a skin."); + } + memcpy(n->skinJoints, joints, sizeof(int32_t) * (size_t)count); + memcpy(n->skinInverseBind, inverseBind, sizeof(Mat4T) * (size_t)count); + n->skinCount = count; + return true; +} + + +// Hides the node and everything under it. +bool nodeSetVisible(int32_t node, bool visible) { + if (!nodeValid(node)) { + return false; + } + _scene.nodes[node].visible = visible; + return true; +} + + +bool nodeValid(int32_t node) { + return (node >= 0) && (node < _scene.nodeCount) && _scene.nodes[node].used; +} + + +// ===== Scene ===== + +bool sceneAvailable(void) { + return _scene.device != NULL; +} + + +// Turns the layer on or off. Fails only when the machine has no GPU device. +bool sceneEnable(bool enabled) { + if (enabled && (_scene.device == NULL)) { + return false; + } + _scene.enabled = enabled; + return true; +} + + +void sceneGetSize(int32_t *width, int32_t *height) { + *width = _scene.width; + *height = _scene.height; +} + + +// device may be NULL (no GPU backend on this machine); the scene then refuses to be enabled. +// Creates the root node, the shaders, the sampler and the white stand-in texture. +bool sceneInit(SDL_GPUDevice *device, SDL_Renderer *renderer) { + SDL_GPUSamplerCreateInfo samplerInfo; + SDL_Surface *pixel; + + memset(&_scene, 0, sizeof(_scene)); + _scene.device = device; + _scene.renderer = renderer; + _scene.cameraNode = NO_HANDLE; + _scene.perspective = true; + _scene.fov = DEFAULT_FOV; + _scene.near = DEFAULT_NEAR; + _scene.far = DEFAULT_FAR; + _scene.orthoHeight = DEFAULT_EYE_Z; + _scene.ambient = vec3(0.1f, 0.1f, 0.1f); + _scene.antialias = true; + _scene.sampleCount = SDL_GPU_SAMPLECOUNT_1; + if (device == NULL) { + return false; + } + _scene.depthFormat = _depthFormat(); + _allocNode(); + nodeSetName(SCENE_ROOT_NODE, "root"); + if (!_createShaders()) { + sceneQuit(); + return false; + } + memset(&samplerInfo, 0, sizeof(samplerInfo)); + samplerInfo.min_filter = SDL_GPU_FILTER_LINEAR; + samplerInfo.mag_filter = SDL_GPU_FILTER_LINEAR; + samplerInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; + samplerInfo.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; + samplerInfo.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; + samplerInfo.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; + _scene.sampler = SDL_CreateGPUSampler(device, &samplerInfo); + pixel = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA32); + if (pixel != NULL) { + SDL_FillSurfaceRect(pixel, NULL, 0xFFFFFFFFu); + _scene.white = _uploadTexture(pixel); + SDL_DestroySurface(pixel); + } + if ((_scene.sampler == NULL) || (_scene.white == NULL)) { + utilTrace("Scene: %s", SDL_GetError()); + sceneQuit(); + return false; + } + return true; +} + + +bool sceneIsEnabled(void) { + return _scene.enabled; +} + + +// World point to overlay coordinates using the last rendered frame's camera. Returns false when +// the point is behind the camera (x and y are still filled in). +bool sceneProject(Vec3T world, float *x, float *y, float *depth) { + Vec3T clip; + float w = _scene.viewProjection.m[3] * world.x + _scene.viewProjection.m[7] * world.y + _scene.viewProjection.m[11] * world.z + _scene.viewProjection.m[15]; + + clip = mat4TransformPoint(_scene.viewProjection, world); + *x = (clip.x + 1.0f) / 2.0f * (float)_scene.width; + *y = (1.0f - clip.y) / 2.0f * (float)_scene.height; + *depth = clip.z; + return w > 0.0f; +} + + +void sceneQuit(void) { + int32_t x; + + if (_scene.device != NULL) { + for (x = 0; x < _scene.materialCount; x++) { + _freeMaterialTexture(&_scene.materials[x]); + } + for (x = 0; x < _scene.feedCount; x++) { + if (_scene.feeds[x].used) { + _freeFeed(&_scene.feeds[x]); + } + } + for (x = 0; x < _scene.meshCount; x++) { + if (_scene.meshes[x].used) { + meshDelete(x); + } + } + _destroyPipelines(); + if (_scene.vertexStatic != NULL) { + SDL_ReleaseGPUShader(_scene.device, _scene.vertexStatic); + } + if (_scene.vertexSkinned != NULL) { + SDL_ReleaseGPUShader(_scene.device, _scene.vertexSkinned); + } + if (_scene.fragment != NULL) { + SDL_ReleaseGPUShader(_scene.device, _scene.fragment); + } + if (_scene.sampler != NULL) { + SDL_ReleaseGPUSampler(_scene.device, _scene.sampler); + } + if (_scene.white != NULL) { + SDL_ReleaseGPUTexture(_scene.device, _scene.white); + } + _destroyTargets(); + } + for (x = 0; x < _scene.nodeCount; x++) { + SDL_free(_scene.nodes[x].name); + _freeSkin(&_scene.nodes[x]); + } + SDL_free(_scene.nodes); + SDL_free(_scene.meshes); + SDL_free(_scene.materials); + SDL_free(_scene.feeds); + SDL_free(_scene.draws); + memset(&_scene, 0, sizeof(_scene)); +} + + +// Draws the frame into the colour target and returns it as a texture for compositing, or NULL when +// the layer is off or has no target yet. The command buffer is submitted before the 2D renderer +// flushes its own, so the composite always samples this frame's result. +SDL_Texture *sceneRender(void) { + SDL_GPUColorTargetInfo colour; + SDL_GPUDepthStencilTargetInfo depth; + SDL_GPUCommandBuffer *commands; + SDL_GPURenderPass *pass; + SDL_GPUBufferBinding binding; + SDL_GPUTextureSamplerBinding samplerBinding; + DrawUniformsT drawUniforms; + SkinUniformsT *skinUniforms = NULL; + FragmentUniformsT fragmentUniforms; + Mat4T identity = mat4Identity(); + Mat4T view; + Mat4T inverse; + Vec3T eye; + int32_t x; + int32_t drawCount = 0; + int32_t opaqueCount = 0; + int32_t lastPipeline = NO_HANDLE; + int32_t variant; + NodeT *node; + MeshT *mesh; + MaterialT *material; + MaterialT defaultMaterial; + + if (!_scene.enabled || (_scene.colour == NULL)) { + return NULL; + } + // Transforms, camera and lights for this frame. + _updateWorld(SCENE_ROOT_NODE, &identity, true); + view = _view(); + eye = _viewEye(); + _scene.viewProjection = mat4Multiply(_projection(), view); + memset(&fragmentUniforms, 0, sizeof(fragmentUniforms)); + fragmentUniforms.cameraPosition[0] = eye.x; + fragmentUniforms.cameraPosition[1] = eye.y; + fragmentUniforms.cameraPosition[2] = eye.z; + fragmentUniforms.cameraPosition[3] = 1.0f; + fragmentUniforms.ambient[0] = _scene.ambient.x; + fragmentUniforms.ambient[1] = _scene.ambient.y; + fragmentUniforms.ambient[2] = _scene.ambient.z; + fragmentUniforms.ambient[3] = 1.0f; + _fillLights(&fragmentUniforms); + // Collect what to draw: opaque first in node order, then blended back to front. + if (_scene.drawCapacity < _scene.nodeCount) { + _scene.draws = SDL_realloc(_scene.draws, sizeof(DrawT) * (size_t)_scene.nodeCount); + if (_scene.draws == NULL) { + utilDie("Out of memory collecting scene draws."); + } + _scene.drawCapacity = _scene.nodeCount; + } + for (x = 0; x < _scene.nodeCount; x++) { + node = &_scene.nodes[x]; + if (!node->used || !node->worldVisible || (node->mesh == NO_HANDLE) || !meshValid(node->mesh)) { + continue; + } + if ((node->material == NO_HANDLE) || !_scene.materials[node->material].blend) { + _scene.draws[opaqueCount].node = x; + _scene.draws[opaqueCount].depth = 0.0f; + opaqueCount++; + } + } + drawCount = opaqueCount; + for (x = 0; x < _scene.nodeCount; x++) { + node = &_scene.nodes[x]; + if (!node->used || !node->worldVisible || (node->mesh == NO_HANDLE) || !meshValid(node->mesh)) { + continue; + } + if ((node->material != NO_HANDLE) && _scene.materials[node->material].blend) { + _scene.draws[drawCount].node = x; + _scene.draws[drawCount].depth = vec3Length(vec3Subtract(mat4TransformPoint(node->world, vec3(0.0f, 0.0f, 0.0f)), eye)); + drawCount++; + } + } + if (drawCount > opaqueCount) { + qsort(&_scene.draws[opaqueCount], (size_t)(drawCount - opaqueCount), sizeof(DrawT), _compareDraws); + } + // Record the pass. + commands = SDL_AcquireGPUCommandBuffer(_scene.device); + if (commands == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + return NULL; + } + memset(&colour, 0, sizeof(colour)); + colour.clear_color = _scene.background; + colour.load_op = SDL_GPU_LOADOP_CLEAR; + if (_scene.multisampled != NULL) { + colour.texture = _scene.multisampled; + colour.resolve_texture = _scene.colour; + colour.store_op = SDL_GPU_STOREOP_RESOLVE; + } else { + colour.texture = _scene.colour; + colour.store_op = SDL_GPU_STOREOP_STORE; + } + memset(&depth, 0, sizeof(depth)); + depth.texture = _scene.depth; + depth.clear_depth = 1.0f; + depth.load_op = SDL_GPU_LOADOP_CLEAR; + depth.store_op = SDL_GPU_STOREOP_DONT_CARE; + depth.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE; + depth.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE; + pass = SDL_BeginGPURenderPass(commands, &colour, 1, &depth); + memset(&defaultMaterial, 0, sizeof(defaultMaterial)); + defaultMaterial.feed = NO_HANDLE; + defaultMaterial.baseColor.x = 1.0f; + defaultMaterial.baseColor.y = 1.0f; + defaultMaterial.baseColor.z = 1.0f; + defaultMaterial.baseColor.w = 1.0f; + defaultMaterial.roughness = 0.5f; + for (x = 0; x < drawCount; x++) { + node = &_scene.nodes[_scene.draws[x].node]; + mesh = &_scene.meshes[node->mesh]; + material = (node->material != NO_HANDLE) ? &_scene.materials[node->material] : &defaultMaterial; + variant = _lookupPipeline(_scene.draws[x].node); + if (variant == NO_HANDLE) { + continue; + } + if (variant != lastPipeline) { + SDL_BindGPUGraphicsPipeline(pass, _scene.pipelines[variant]); + lastPipeline = variant; + } + drawUniforms.modelViewProjection = mat4Multiply(_scene.viewProjection, node->world); + drawUniforms.model = node->world; + if (mat4Invert(node->world, &inverse)) { + drawUniforms.normalMatrix = mat4Transpose(inverse); + } else { + drawUniforms.normalMatrix = identity; + } + SDL_PushGPUVertexUniformData(commands, 0, &drawUniforms, sizeof(drawUniforms)); + if (variant & PIPELINE_SKINNED) { + // 8 KB per skinned draw; allocated once per frame that needs it. + if (skinUniforms == NULL) { + skinUniforms = SDL_malloc(sizeof(SkinUniformsT)); + if (skinUniforms == NULL) { + utilDie("Out of memory posing a skin."); + } + } + _fillSkin(node, skinUniforms); + SDL_PushGPUVertexUniformData(commands, 1, skinUniforms, sizeof(SkinUniformsT)); + } + fragmentUniforms.baseColor[0] = material->baseColor.x; + fragmentUniforms.baseColor[1] = material->baseColor.y; + fragmentUniforms.baseColor[2] = material->baseColor.z; + fragmentUniforms.baseColor[3] = material->baseColor.w; + fragmentUniforms.emissive[0] = material->emissive.x; + fragmentUniforms.emissive[1] = material->emissive.y; + fragmentUniforms.emissive[2] = material->emissive.z; + fragmentUniforms.emissive[3] = 1.0f; + fragmentUniforms.material[0] = material->metallic; + fragmentUniforms.material[1] = material->roughness; + fragmentUniforms.material[2] = material->unlit ? 1.0f : 0.0f; + fragmentUniforms.material[3] = (_materialTexture(material) != NULL) ? 1.0f : 0.0f; + SDL_PushGPUFragmentUniformData(commands, 0, &fragmentUniforms, sizeof(fragmentUniforms)); + memset(&samplerBinding, 0, sizeof(samplerBinding)); + samplerBinding.texture = (_materialTexture(material) != NULL) ? _materialTexture(material) : _scene.white; + samplerBinding.sampler = _scene.sampler; + SDL_BindGPUFragmentSamplers(pass, 0, &samplerBinding, 1); + memset(&binding, 0, sizeof(binding)); + binding.buffer = mesh->vertexBuffer; + SDL_BindGPUVertexBuffers(pass, 0, &binding, 1); + binding.buffer = mesh->indexBuffer; + SDL_BindGPUIndexBuffer(pass, &binding, SDL_GPU_INDEXELEMENTSIZE_32BIT); + SDL_DrawGPUIndexedPrimitives(pass, mesh->indexCount, 1, 0, 0, 0); + } + SDL_EndGPURenderPass(pass); + SDL_SubmitGPUCommandBuffer(commands); + SDL_free(skinUniforms); + return _scene.composite; +} + + +// (Re)creates the render targets at the overlay's size. +bool sceneResize(int32_t width, int32_t height) { + SDL_GPUTextureCreateInfo info; + SDL_PropertiesID props; + + if (_scene.device == NULL) { + return false; + } + if ((width == _scene.width) && (height == _scene.height)) { + return true; + } + _destroyTargets(); + memset(&info, 0, sizeof(info)); + info.type = SDL_GPU_TEXTURETYPE_2D; + info.format = SDL_GetGPUTextureFormatFromPixelFormat(SDL_PIXELFORMAT_BGRA32); + info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER; + info.width = (Uint32)width; + info.height = (Uint32)height; + info.layer_count_or_depth = 1; + info.num_levels = 1; + info.sample_count = SDL_GPU_SAMPLECOUNT_1; + _scene.colour = SDL_CreateGPUTexture(_scene.device, &info); + if (_scene.colour == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + return false; + } + // 4x multisampling when wanted and offered: a multisampled colour target resolved into colour, + // and a multisampled depth target to match. + _scene.sampleCount = SDL_GPU_SAMPLECOUNT_1; + if (_scene.antialias && SDL_GPUTextureSupportsSampleCount(_scene.device, info.format, SDL_GPU_SAMPLECOUNT_4) && SDL_GPUTextureSupportsSampleCount(_scene.device, _scene.depthFormat, SDL_GPU_SAMPLECOUNT_4)) { + info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + info.sample_count = SDL_GPU_SAMPLECOUNT_4; + _scene.multisampled = SDL_CreateGPUTexture(_scene.device, &info); + if (_scene.multisampled != NULL) { + _scene.sampleCount = SDL_GPU_SAMPLECOUNT_4; + } else { + utilTrace("Scene: no multisampling: %s", SDL_GetError()); + info.sample_count = SDL_GPU_SAMPLECOUNT_1; + } + } + _destroyPipelines(); + info.format = _scene.depthFormat; + info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; + _scene.depth = SDL_CreateGPUTexture(_scene.device, &info); + if (_scene.depth == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + _destroyTargets(); + return false; + } + props = SDL_CreateProperties(); + SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER, _scene.colour); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, SDL_PIXELFORMAT_BGRA32); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, width); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, height); + _scene.composite = SDL_CreateTextureWithProperties(_scene.renderer, props); + SDL_DestroyProperties(props); + if (_scene.composite == NULL) { + utilTrace("Scene: %s", SDL_GetError()); + _destroyTargets(); + return false; + } + SDL_SetTextureBlendMode(_scene.composite, SDL_BLENDMODE_BLEND); + _scene.width = width; + _scene.height = height; + return true; +} + + +// Smooth normals from the triangles: face normals accumulated per vertex, then normalised. +void sceneComputeNormals(SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount) { + int32_t x; + + for (x = 0; x < vertexCount; x++) { + vertices[x].normal[0] = 0.0f; + vertices[x].normal[1] = 0.0f; + vertices[x].normal[2] = 0.0f; + } + for (x = 0; x + 2 < indexCount; x += 3) { + SceneVertexT *a = &vertices[indices[x]]; + SceneVertexT *b = &vertices[indices[x + 1]]; + SceneVertexT *c = &vertices[indices[x + 2]]; + SceneVertexT *corners[3] = { a, b, c }; + Vec3T pa = vec3(a->position[0], a->position[1], a->position[2]); + Vec3T pb = vec3(b->position[0], b->position[1], b->position[2]); + Vec3T pc = vec3(c->position[0], c->position[1], c->position[2]); + Vec3T n = vec3Cross(vec3Subtract(pb, pa), vec3Subtract(pc, pa)); + int32_t k; + + for (k = 0; k < 3; k++) { + corners[k]->normal[0] += n.x; + corners[k]->normal[1] += n.y; + corners[k]->normal[2] += n.z; + } + } + for (x = 0; x < vertexCount; x++) { + Vec3T n = vec3Normalize(vec3(vertices[x].normal[0], vertices[x].normal[1], vertices[x].normal[2])); + + vertices[x].normal[0] = n.x; + vertices[x].normal[1] = n.y; + vertices[x].normal[2] = n.z; + } +} + + +void sceneSetAmbient(uint8_t r, uint8_t g, uint8_t b) { + _scene.ambient = vec3(r / COLOUR_MAX, g / COLOUR_MAX, b / COLOUR_MAX); +} + + +// 4x multisampling on or off (on by default where the device offers it); takes effect on the +// next resize, so the targets are rebuilt here. +void sceneSetAntialias(bool antialias) { + int32_t width = _scene.width; + int32_t height = _scene.height; + + _scene.antialias = antialias; + if (width > 0) { + _destroyTargets(); + sceneResize(width, height); + } +} + + +void sceneSetBackground(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _scene.background.r = r / COLOUR_MAX; + _scene.background.g = g / COLOUR_MAX; + _scene.background.b = b / COLOUR_MAX; + _scene.background.a = a / COLOUR_MAX; +} + + +// Overlay coordinates back to a world point: the point on the ray through that pixel at the given +// distance from the camera's near plane, using the last rendered frame's camera. Two distances +// give a ray for picking. +Vec3T sceneUnproject(float x, float y, float distance) { + Mat4T inverse; + Vec3T ndc; + Vec3T near; + Vec3T far; + + if ((_scene.width <= 0) || !mat4Invert(_scene.viewProjection, &inverse)) { + return vec3(0.0f, 0.0f, 0.0f); + } + ndc = vec3(x / (float)_scene.width * 2.0f - 1.0f, 1.0f - y / (float)_scene.height * 2.0f, 0.0f); + near = mat4TransformPoint(inverse, ndc); + ndc.z = 1.0f; + far = mat4TransformPoint(inverse, ndc); + return vec3Add(near, vec3Scale(vec3Normalize(vec3Subtract(far, near)), distance)); +} + + +// Once per frame before sceneRender: copies every player a material shows into that feed's RGBA +// target with the 2D renderer (which converts YUV on the way), then flushes the renderer so the +// copies are queued ahead of the scene's own command buffer. +void sceneUpdateVideo(SceneVideoSourceFn source) { + int32_t x; + bool any = false; + SDL_Texture *frame; + float width; + float height; + + for (x = 0; x < _scene.feedCount; x++) { + FeedT *feed = &_scene.feeds[x]; + + if (!feed->used) { + continue; + } + frame = source(feed->player); + if (frame == NULL) { + continue; + } + if (feed->target == NULL) { + SDL_GetTextureSize(frame, &width, &height); + feed->target = SDL_CreateTexture(_scene.renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, (int32_t)width, (int32_t)height); + if (feed->target == NULL) { + utilTrace("Scene: video feed: %s", SDL_GetError()); + continue; + } + feed->gpu = SDL_GetPointerProperty(SDL_GetTextureProperties(feed->target), SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, NULL); + } + SDL_SetRenderTarget(_scene.renderer, feed->target); + SDL_RenderTexture(_scene.renderer, frame, NULL, NULL); + any = true; + } + if (any) { + SDL_SetRenderTarget(_scene.renderer, NULL); + SDL_FlushRenderer(_scene.renderer); + } +} diff --git a/src/scene.h b/src/scene.h new file mode 100644 index 000000000..bb5a09126 --- /dev/null +++ b/src/scene.h @@ -0,0 +1,130 @@ +/* + * + * Singe 3 + * Copyright (C) 2006-2026 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef SCENE_H +#define SCENE_H + +#include +#include +#include +#include "math3d.h" + + +#define SCENE_ROOT_NODE 0 + +// A vertex as the GPU sees it; model loaders fill these directly. +typedef struct SceneVertexS { + float position[3]; + float normal[3]; + float uv[2]; + uint8_t joints[4]; + float weights[4]; +} SceneVertexT; + +// Hands back a video player's current texture (NULL when it has none), for materialSetVideo. +typedef SDL_Texture *(*SceneVideoSourceFn)(int32_t player); + +typedef enum LightTypeE { + LIGHT_DIRECTIONAL = 0, + LIGHT_POINT = 1, + LIGHT_SPOT = 2 +} LightTypeE; + + +bool sceneAvailable(void); +bool sceneEnable(bool enabled); +void sceneGetSize(int32_t *width, int32_t *height); +bool sceneInit(SDL_GPUDevice *device, SDL_Renderer *renderer); +bool sceneIsEnabled(void); +bool sceneProject(Vec3T world, float *x, float *y, float *depth); +void sceneQuit(void); +SDL_Texture *sceneRender(void); +bool sceneResize(int32_t width, int32_t height); +void sceneSetAntialias(bool antialias); +void sceneSetAmbient(uint8_t r, uint8_t g, uint8_t b); +void sceneComputeNormals(SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount); +void sceneSetBackground(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +Vec3T sceneUnproject(float x, float y, float distance); +void sceneUpdateVideo(SceneVideoSourceFn source); + +bool cameraSet(int32_t node); +void cameraSetOrthographic(float height, float near, float far); +void cameraSetPerspective(float fovDegrees, float near, float far); + +bool lightAttach(int32_t node, LightTypeE type); +int32_t lightNew(LightTypeE type, int32_t parent); +bool lightSetColor(int32_t node, uint8_t r, uint8_t g, uint8_t b); +bool lightSetCone(int32_t node, float innerDegrees, float outerDegrees); +bool lightSetIntensity(int32_t node, float intensity); +bool lightSetRange(int32_t node, float range); + +bool materialDelete(int32_t material); +int32_t materialNew(void); +bool materialSetBlend(int32_t material, bool blend); +bool materialSetColor(int32_t material, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +bool materialSetDoubleSided(int32_t material, bool doubleSided); +bool materialSetEmissive(int32_t material, uint8_t r, uint8_t g, uint8_t b); +bool materialSetMetallic(int32_t material, float metallic); +bool materialSetRoughness(int32_t material, float roughness); +bool materialSetTexture(int32_t material, SDL_Surface *image); +bool materialSetUnlit(int32_t material, bool unlit); +bool materialSetVideo(int32_t material, int32_t player); +bool materialValid(int32_t material); + +int32_t meshBox(float width, float height, float depth); +int32_t meshCone(float radius, float height, int32_t segments); +int32_t meshCylinder(float radius, float height, int32_t segments); +bool meshDelete(int32_t mesh); +int32_t meshNew(const float *positions, const float *normals, const float *uvs, int32_t vertexCount, const uint32_t *indices, int32_t indexCount); +int32_t meshNewVertices(const SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount, bool skinned); +int32_t meshPlane(float width, float depth); +int32_t meshSphere(float radius, int32_t segments); +int32_t meshTorus(float radius, float tubeRadius, int32_t segments); +bool meshValid(int32_t mesh); + +bool nodeDelete(int32_t node); +int32_t nodeFind(int32_t root, const char *name); +int32_t nodeGetChild(int32_t node, int32_t index); +int32_t nodeGetChildCount(int32_t node); +uint32_t nodeGetGeneration(int32_t node); +const char *nodeGetName(int32_t node); +int32_t nodeGetParent(int32_t node); +Vec3T nodeGetPosition(int32_t node); +QuatT nodeGetRotation(int32_t node); +Vec3T nodeGetScale(int32_t node); +Vec3T nodeGetWorldPosition(int32_t node); +bool nodeLookAt(int32_t node, Vec3T target); +bool nodeMove(int32_t node, Vec3T delta); +int32_t nodeNew(int32_t parent); +bool nodeRotate(int32_t node, QuatT delta); +bool nodeSetMesh(int32_t node, int32_t mesh, int32_t material); +bool nodeSetName(int32_t node, const char *name); +bool nodeSetParent(int32_t node, int32_t parent); +bool nodeSetPosition(int32_t node, Vec3T position); +bool nodeSetRotation(int32_t node, QuatT rotation); +bool nodeSetScale(int32_t node, Vec3T scale); +bool nodeSetSkin(int32_t node, const int32_t *joints, const Mat4T *inverseBind, int32_t count); +bool nodeSetVisible(int32_t node, bool visible); +bool nodeValid(int32_t node); + + +#endif diff --git a/src/shaders/build.sh b/src/shaders/build.sh new file mode 100755 index 000000000..07718b33e --- /dev/null +++ b/src/shaders/build.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Compiles scene.hlsl into sceneShaders.h: SPIR-V (Vulkan), DXIL (Direct3D 12) and MSL (Metal) +# for each entry point, as C arrays. Needs SDL_shadercross's command line tool (built from +# https://github.com/libsdl-org/SDL_shadercross with SDLSHADERCROSS_DXC=ON); pass its path as the +# first argument or put it on PATH. The engine build never runs this: the output is checked in. +# +# Usage: src/shaders/build.sh [path/to/shadercross] + +set -euo pipefail +cd "$(dirname "$0")" +SHADERCROSS=${1:-shadercross} +OUT=sceneShaders.h +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +ENTRIES="vertexStatic:vertex vertexSkinned:vertex fragmentMain:fragment" + +emit() { + # emit NAME FILE: a C array from a binary file + local name=$1 + local file=$2 + echo "static const unsigned char ${name}[] = {" + od -An -v -tx1 "$file" | sed 's/ \([0-9a-f][0-9a-f]\)/0x\1,/g; s/^/\t/' + echo "};" +} + +{ + echo "// Generated by build.sh from scene.hlsl with SDL_shadercross; do not edit." + echo "// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, one set per entry point." + echo + echo "#ifndef SCENE_SHADERS_H" + echo "#define SCENE_SHADERS_H" + echo + echo "#include " + echo + echo "typedef struct SceneShaderS {" + echo " const char *entryPoint;" + echo " const unsigned char *spirv;" + echo " size_t spirvSize;" + echo " const unsigned char *dxil;" + echo " size_t dxilSize;" + echo " const unsigned char *msl;" + echo " size_t mslSize;" + echo "} SceneShaderT;" + echo + for entry in $ENTRIES; do + name=${entry%%:*} + stage=${entry##*:} + for format in SPIRV DXIL MSL; do + ext=$(echo "$format" | tr '[:upper:]' '[:lower:]') + "$SHADERCROSS" scene.hlsl -s HLSL -d "$format" -t "$stage" -e "$name" -o "$TMP/$name.$ext" >&2 + emit "_${name}${format}" "$TMP/$name.$ext" + echo + done + echo "static const SceneShaderT sceneShader$(echo "${name:0:1}" | tr '[:lower:]' '[:upper:]')${name:1} = { \"$name\", _${name}SPIRV, sizeof(_${name}SPIRV), _${name}DXIL, sizeof(_${name}DXIL), _${name}MSL, sizeof(_${name}MSL) };" + echo + done + echo "#endif" +} > "$OUT" +echo "Wrote $OUT" diff --git a/src/shaders/scene.hlsl b/src/shaders/scene.hlsl new file mode 100644 index 000000000..351adf96c --- /dev/null +++ b/src/shaders/scene.hlsl @@ -0,0 +1,166 @@ +// Singe 3 scene shaders: one vertex shader for static meshes, one for skinned meshes, one +// fragment shader for both. Compiled offline by src/shaders/build.sh (SDL_shadercross) into the +// SPIR-V, DXIL and MSL blobs in sceneShaders.h; the engine build never compiles shaders. +// +// Resource bindings follow SDL_GPU's HLSL convention: vertex uniforms in space1, fragment +// textures and samplers in space2, fragment uniforms in space3. + +#define MAX_LIGHTS 8 +#define MAX_JOINTS 128 + +#define LIGHT_DIRECTIONAL 0 +#define LIGHT_POINT 1 +#define LIGHT_SPOT 2 + + +// ----- Vertex ----- + +cbuffer DrawUniforms : register(b0, space1) { + float4x4 modelViewProjection; + float4x4 model; + float4x4 normalMatrix; // Inverse transpose of model, for normals under non-uniform scale +}; + +cbuffer SkinUniforms : register(b1, space1) { + float4x4 joints[MAX_JOINTS]; +}; + +struct VertexInput { + float3 position : TEXCOORD0; + float3 normal : TEXCOORD1; + float2 uv : TEXCOORD2; + uint4 joints : TEXCOORD3; + float4 weights : TEXCOORD4; +}; + +struct VertexOutput { + float4 position : SV_Position; + float3 worldPosition : TEXCOORD0; + float3 worldNormal : TEXCOORD1; + float2 uv : TEXCOORD2; +}; + + +VertexOutput vertexStatic(VertexInput input) { + VertexOutput output; + + output.position = mul(modelViewProjection, float4(input.position, 1.0)); + output.worldPosition = mul(model, float4(input.position, 1.0)).xyz; + output.worldNormal = normalize(mul((float3x3)normalMatrix, input.normal)); + output.uv = input.uv; + return output; +} + + +VertexOutput vertexSkinned(VertexInput input) { + VertexOutput output; + float4 position = float4(input.position, 1.0); + float4 skinned; + float3 skinnedNormal; + + // The weighted sum of the joint transforms, applied to the position and the normal. + skinned = mul(joints[input.joints.x], position) * input.weights.x + + mul(joints[input.joints.y], position) * input.weights.y + + mul(joints[input.joints.z], position) * input.weights.z + + mul(joints[input.joints.w], position) * input.weights.w; + skinnedNormal = mul((float3x3)joints[input.joints.x], input.normal) * input.weights.x + + mul((float3x3)joints[input.joints.y], input.normal) * input.weights.y + + mul((float3x3)joints[input.joints.z], input.normal) * input.weights.z + + mul((float3x3)joints[input.joints.w], input.normal) * input.weights.w; + output.position = mul(modelViewProjection, skinned); + output.worldPosition = mul(model, skinned).xyz; + output.worldNormal = normalize(mul((float3x3)normalMatrix, skinnedNormal)); + output.uv = input.uv; + return output; +} + + +// ----- Fragment ----- + +struct Light { + float4 positionType; // xyz position (point, spot) or unused; w = type + float4 directionRange; // xyz direction the light shines in (directional, spot); w = range (0 = infinite) + float4 color; // rgb already multiplied by intensity + float4 cone; // x = cos(inner), y = cos(outer) +}; + +cbuffer FragmentUniforms : register(b0, space3) { + float4 cameraPosition; + float4 ambient; + float4 baseColor; + float4 emissive; + float4 material; // x = metallic, y = roughness, z = unlit (1/0), w = textured (1/0) + float4 counts; // x = light count + Light lights[MAX_LIGHTS]; +}; + +Texture2D baseTexture : register(t0, space2); +SamplerState baseSampler : register(s0, space2); + + +// Named fragmentMain because "fragment" is a keyword in Metal and the MSL entry point keeps this name. +float4 fragmentMain(VertexOutput input) : SV_Target { + float4 albedo = baseColor; + float3 normal; + float3 view; + float3 diffuseColor; + float3 specularColor; + float3 result; + float shininess; + float metallic = material.x; + float roughness = material.y; + int lightCount; + int x; + + if (material.w > 0.5) { + albedo *= baseTexture.Sample(baseSampler, input.uv); + } + if (material.z > 0.5) { + return albedo; + } + // Metallic surfaces reflect their own colour and have little diffuse; dielectrics reflect a + // little white. Roughness sets how tight the highlight is. Without an environment to + // reflect, metals would go black away from highlights, so they keep some diffuse and get a + // stronger ambient term standing in for reflections. + normal = normalize(input.worldNormal); + view = normalize(cameraPosition.xyz - input.worldPosition); + diffuseColor = albedo.rgb * (1.0 - 0.6 * metallic); + specularColor = lerp(float3(0.04, 0.04, 0.04), albedo.rgb, metallic) * (1.0 + 2.0 * metallic); + shininess = lerp(256.0, 4.0, roughness * roughness); + result = ambient.rgb * albedo.rgb * (1.0 + 2.0 * metallic) + emissive.rgb; + lightCount = (int)counts.x; + for (x = 0; x < MAX_LIGHTS; x++) { + float3 toLight; + float attenuation = 1.0; + float distance; + float diffuse; + float specular; + float3 halfway; + + // A break here crashes DXC; continue costs nothing worth measuring. + if (x >= lightCount) { + continue; + } + if (lights[x].positionType.w == LIGHT_DIRECTIONAL) { + toLight = normalize(-lights[x].directionRange.xyz); + } else { + toLight = lights[x].positionType.xyz - input.worldPosition; + distance = length(toLight); + toLight = toLight / max(distance, 0.0001); + attenuation = 1.0 / (1.0 + distance * distance); + if (lights[x].directionRange.w > 0.0) { + // Fade to nothing at the range so lights do not pop. + attenuation *= saturate(1.0 - pow(distance / lights[x].directionRange.w, 4.0)); + } + if (lights[x].positionType.w == LIGHT_SPOT) { + float cosAngle = dot(-toLight, normalize(lights[x].directionRange.xyz)); + attenuation *= smoothstep(lights[x].cone.y, lights[x].cone.x, cosAngle); + } + } + diffuse = saturate(dot(normal, toLight)); + halfway = normalize(toLight + view); + specular = pow(saturate(dot(normal, halfway)), shininess) * (1.0 - roughness * 0.5); + result += (diffuseColor * diffuse + specularColor * specular * diffuse) * lights[x].color.rgb * attenuation; + } + return float4(result, albedo.a); +} diff --git a/src/shaders/sceneShaders.h b/src/shaders/sceneShaders.h new file mode 100644 index 000000000..54ea228a1 --- /dev/null +++ b/src/shaders/sceneShaders.h @@ -0,0 +1,2992 @@ +// Generated by build.sh from scene.hlsl with SDL_shadercross; do not edit. +// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, one set per entry point. + +#ifndef SCENE_SHADERS_H +#define SCENE_SHADERS_H + +#include + +typedef struct SceneShaderS { + const char *entryPoint; + const unsigned char *spirv; + size_t spirvSize; + const unsigned char *dxil; + size_t dxilSize; + const unsigned char *msl; + size_t mslSize; +} SceneShaderT; + +static const unsigned char _vertexStaticSPIRV[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x4c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74, + 0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00, + 0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00,0x0a,0x00,0x00,0x00,0x74,0x79,0x70,0x65, + 0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x08,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, + 0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x00, + 0x06,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, + 0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, + 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, + 0x05,0x00,0x08,0x00,0x0e,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x46,0x72,0x61, + 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x6d,0x65, + 0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x00, + 0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x62,0x61,0x73,0x65, + 0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65,0x00,0x00,0x00,0x00, + 0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x74,0x65, + 0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e,0x74,0x73,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x6c,0x69,0x67,0x68,0x74,0x73,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x4c,0x69,0x67,0x68,0x74,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00, + 0x0f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f, + 0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00,0x06,0x00,0x05,0x00,0x0f,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, + 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x11,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67, + 0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x62,0x61,0x73,0x65, + 0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00,0x05,0x00,0x06,0x00,0x13,0x00,0x00,0x00, + 0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70, + 0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, + 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x05,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x07,0x00,0x00,0x00, + 0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x30,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e, + 0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0f,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0f,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x16,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x19,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x2b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x18,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x1e,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1f,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x1e,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x1e,0x00,0x06,0x00,0x0f,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x16,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x1e,0x00,0x09,0x00,0x0e,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x24,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x09,0x00, + 0x11,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x1a,0x00,0x02,0x00,0x13,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x26,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x27,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x28,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x29,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2c,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2d,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x2e,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x2f,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x30,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x31,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x1f,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x24,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x25,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x26,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x28,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x28,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2a,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2b,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2c,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2d,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x2e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x32,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x27,0x00,0x00,0x00, + 0x33,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x27,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x29,0x00,0x00,0x00, + 0x35,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x30,0x00,0x00,0x00, + 0x36,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x1e,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x19,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x19,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x33,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x19,0x00,0x00,0x00,0x3a,0x00,0x00,0x00, + 0x33,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x1d,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x3a,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x1d,0x00,0x00,0x00,0x3c,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x30,0x00,0x00,0x00, + 0x3d,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x1e,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x1d,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x3e,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x27,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x3f,0x00,0x00,0x00, + 0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x42,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1d,0x00,0x00,0x00,0x43,0x00,0x00,0x00, + 0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x27,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1d,0x00,0x00,0x00, + 0x45,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, + 0x27,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x45,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x1d,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x27,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x50,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x44,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x27,0x00,0x00,0x00, + 0x4a,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x0c,0x00,0x06,0x00, + 0x27,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00, + 0x4a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x3c,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00, + 0x35,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +static const unsigned char _vertexStaticDXIL[] = { + 0x44,0x58,0x42,0x43,0x0d,0x39,0xe0,0xb8,0x20,0x37,0x03,0xc3,0xcf,0xb8,0x25,0xa1, + 0xee,0x9e,0xbb,0x0b,0x01,0x00,0x00,0x00,0x08,0x16,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x70,0x01,0x00,0x00, + 0xcc,0x02,0x00,0x00,0x48,0x0a,0x00,0x00,0x64,0x0a,0x00,0x00,0x53,0x46,0x49,0x30, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, + 0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x31,0xa0,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x50,0x53,0x56,0x30,0x54,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff,0x01,0x00,0x00,0x00,0x03,0x04,0x00,0x03,0x04,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45, + 0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x76,0x65,0x72,0x74,0x65, + 0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x02,0x00,0x00,0x25,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x02,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x03,0x44,0x03,0x03,0x04,0x00,0x00,0x07,0xf0,0x00,0x00, + 0x07,0xf0,0x00,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54, + 0x74,0x07,0x00,0x00,0x60,0x00,0x01,0x00,0xdd,0x01,0x00,0x00,0x44,0x58,0x49,0x4c, + 0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x5c,0x07,0x00,0x00,0x42,0x43,0xc0,0xde, + 0x21,0x0c,0x00,0x00,0xd4,0x01,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39, + 0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02, + 0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x52,0x88, + 0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90, + 0x91,0x22,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x29,0x46,0x06, + 0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07, + 0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff, + 0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, + 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, + 0x28,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, + 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, + 0x10,0x78,0x23,0x00,0x25,0x00,0x14,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00,0x29, + 0xc6,0x20,0x84,0x14,0x42,0xa6,0x18,0x80,0x10,0x52,0x06,0xa1,0x82,0x0c,0x32,0xc6, + 0x18,0x63,0x90,0x2a,0xc3,0x20,0x83,0xd8,0x51,0xc3,0xe5,0x4f,0xd8,0x43,0x48,0x3e, + 0xb7,0x51,0xc5,0x4a,0x4c,0x7e,0x71,0xdb,0x88,0x18,0x63,0x0c,0x2a,0xf7,0x0c,0x97, + 0x3f,0x61,0x0f,0x21,0xf9,0x21,0xd0,0x0c,0x0b,0x81,0x82,0x57,0x08,0x47,0x20,0xa1, + 0x58,0x8a,0x41,0xc6,0x18,0x34,0xe7,0x08,0x82,0x62,0x40,0x52,0x08,0xa9,0x64,0x07, + 0x02,0x86,0x11,0x88,0x21,0x09,0xf2,0x71,0x87,0x23,0x4d,0x0b,0x80,0x39,0xd4,0xe4, + 0x4f,0xd8,0x43,0xfc,0x5d,0x04,0x58,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x68,0xd0,0x5a, + 0xd3,0x4e,0x06,0x02,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79, + 0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d, + 0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0, + 0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e, + 0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10, + 0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28,0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60, + 0xc8,0x13,0x01,0x01,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02, + 0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x20,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x18,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, + 0x25,0x30,0x02,0x50,0x0c,0x05,0x38,0x50,0x04,0x85,0x50,0x06,0xe5,0x50,0x12,0x05, + 0x18,0x50,0x80,0x02,0xe5,0x51,0x60,0xa5,0x55,0x2c,0x45,0x2e,0x50,0x10,0x85,0x41, + 0xa5,0x24,0x46,0x00,0xca,0xa0,0x08,0x0a,0x81,0x6a,0x0d,0xd0,0x9e,0x01,0x20,0x3e, + 0x03,0x40,0x7d,0x2c,0x46,0x81,0x4e,0x90,0x0f,0x4e,0x90,0x0f,0x4e,0x90,0x0f,0x00, + 0x78,0x80,0x07,0x78,0x00,0x40,0x04,0x84,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0xbc,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c, + 0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25, + 0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10, + 0x08,0x64,0x82,0x40,0x24,0x1b,0x84,0x81,0xd8,0x20,0x10,0x04,0x05,0xbb,0xb9,0x09, + 0x02,0xa1,0x6c,0x18,0x0e,0x84,0x98,0x20,0x70,0x61,0xc0,0x88,0x2e,0x0f,0xae,0xec, + 0x8b,0x48,0x2e,0xec,0xae,0xca,0x2d,0xcd,0xec,0x4d,0xae,0x6d,0x6e,0x82,0x40,0x2c, + 0x1b,0x10,0x42,0x59,0x06,0x62,0x60,0x80,0x0d,0x41,0xb3,0x81,0x00,0x00,0x07,0x98, + 0x20,0x6c,0x60,0x40,0x30,0x20,0x92,0x0b,0xbb,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b, + 0x9b,0xfb,0x6a,0x7b,0x23,0x2b,0x63,0xb3,0x4a,0x2b,0xbb,0x83,0x92,0x7b,0x53,0x2b, + 0x1b,0xa3,0x4b,0x7b,0x73,0x9b,0x20,0x10,0xcc,0x04,0x81,0x68,0x36,0x0c,0xd3,0x24, + 0x4d,0x10,0x08,0x67,0x82,0x40,0x3c,0x13,0x04,0x02,0x9a,0x20,0x54,0xdf,0x06,0x05, + 0x89,0x24,0xaa,0x22,0xac,0xeb,0xc2,0x28,0x11,0xc9,0x85,0xdd,0x55,0xb9,0xa5,0x99, + 0xbd,0xc9,0xb5,0xcd,0x7d,0xb5,0xbd,0x91,0x95,0xb1,0x4d,0x10,0x88,0x68,0x03,0x82, + 0x68,0x12,0x55,0x6d,0xd6,0xc5,0x8c,0x48,0x2e,0xec,0xae,0xca,0x2d,0xcd,0xec,0x4d, + 0xae,0x6d,0xee,0xcb,0xed,0x4d,0xae,0x2d,0x8c,0xad,0x29,0x8c,0x4e,0x2e,0x0d,0x6f, + 0x82,0x40,0x48,0x1b,0x10,0xa4,0x93,0xa8,0xca,0xb3,0xae,0x0d,0x04,0x93,0x71,0xdf, + 0x86,0x81,0x80,0xc0,0x60,0x82,0x20,0x00,0x1b,0x80,0x0d,0x03,0x31,0x06,0x63,0xb0, + 0x21,0x20,0x83,0x0d,0xc3,0x20,0x06,0x65,0x30,0x41,0xe8,0xc4,0x60,0x43,0x70,0x06, + 0x64,0xec,0xca,0xe4,0xe8,0xca,0xf0,0xa6,0xe8,0xc2,0xe8,0xd2,0xc6,0x88,0x50,0x15, + 0x61,0x0d,0x3d,0x3d,0x49,0x11,0x4d,0x10,0x0a,0x6d,0x82,0x50,0x6c,0x1b,0x02,0x62, + 0x82,0x50,0x70,0x1b,0x84,0xca,0xda,0xb0,0x10,0x6a,0xb0,0x06,0x6c,0xd0,0x06,0x6c, + 0x30,0xb8,0x01,0xc1,0x06,0x6f,0xb0,0x21,0x18,0x36,0x2c,0x83,0x1a,0xac,0x01,0x1b, + 0xc4,0x01,0x1b,0x0c,0x6e,0x30,0xb0,0xc1,0x1b,0x6c,0x08,0xa4,0x09,0x42,0xd1,0x6d, + 0x10,0xaa,0x6a,0xc3,0x22,0xa9,0xc1,0x1a,0xb0,0xc1,0x1c,0xb0,0xc1,0x40,0x07,0x12, + 0x1b,0xd4,0xc1,0x86,0x01,0x0e,0xe4,0xc0,0x0e,0x36,0x2c,0x84,0x1a,0xac,0x01,0x1b, + 0xb4,0x01,0x1d,0x0c,0x6e,0x40,0xb0,0xc1,0x1b,0x6c,0x58,0x06,0x35,0x58,0x03,0x36, + 0x88,0x03,0x3a,0x18,0xdc,0x60,0x60,0x83,0x37,0xd8,0xb0,0x48,0x6a,0xb0,0x06,0x6c, + 0x30,0x07,0x74,0x30,0xd0,0x81,0xc4,0x06,0x75,0xc0,0x65,0xca,0xea,0x0b,0xea,0x6d, + 0x2e,0x8d,0x2e,0xed,0xcd,0x6d,0x82,0x50,0x78,0x13,0x04,0x62,0xda,0x20,0x54,0x7d, + 0xb0,0x61,0xa9,0xf6,0x60,0x0d,0xdc,0xa0,0x0d,0xf8,0x60,0xe0,0x83,0x8a,0x0d,0xfc, + 0x60,0x03,0x81,0x07,0x79,0xa0,0x07,0x7f,0xb0,0x61,0xb8,0x03,0x50,0x00,0x36,0x14, + 0x62,0x90,0x06,0xa1,0xf0,0x00,0x8c,0xc2,0xe4,0xe4,0xc2,0xf2,0xbe,0xd8,0xde,0xc6, + 0xc2,0xd8,0xbe,0xc4,0xf2,0xe8,0xca,0xe6,0x26,0x08,0x04,0x45,0x28,0x4c,0x4e,0x2e, + 0x2c,0xef,0x8b,0xed,0x6d,0x2c,0x8c,0xed,0x8b,0x8d,0x6c,0x8e,0x6e,0x82,0x40,0x54, + 0x34,0xcc,0xd8,0xde,0xc2,0xe8,0xe6,0x26,0x08,0x84,0xc5,0x22,0xcd,0x6d,0x8e,0x6e, + 0x6e,0x82,0x40,0x5c,0x34,0xe6,0xd2,0xce,0xbe,0xd8,0xc8,0x26,0x08,0x04,0x46,0x63, + 0x2e,0xed,0xec,0x6b,0x8e,0x6e,0x82,0x40,0x64,0x1b,0x98,0x51,0x20,0x85,0x52,0x30, + 0x85,0x53,0x40,0x85,0x54,0x50,0x85,0x55,0x60,0x85,0x56,0x70,0x85,0x2a,0x6c,0x6c, + 0x76,0x6d,0x2e,0x69,0x64,0x65,0x6e,0x74,0x53,0x82,0xa0,0x0a,0x19,0x9e,0x8b,0x5d, + 0x99,0xdc,0x5c,0xda,0x9b,0xdb,0x94,0x80,0x68,0x42,0x86,0xe7,0x62,0x17,0xc6,0x66, + 0x57,0x26,0x37,0x25,0x28,0xea,0x90,0xe1,0xb9,0xcc,0xa1,0x85,0x91,0x95,0xc9,0x35, + 0xbd,0x91,0x95,0xb1,0x4d,0x09,0x90,0x32,0x64,0x78,0x2e,0x72,0x65,0x73,0x6f,0x75, + 0x72,0x63,0x65,0x73,0x53,0x02,0xa7,0x12,0x19,0x9e,0x0b,0x5d,0x1e,0x5c,0x59,0x90, + 0x9b,0xdb,0x1b,0x5d,0x18,0x5d,0xda,0x9b,0xdb,0xdc,0x14,0x01,0x0c,0xca,0xa0,0x0e, + 0x19,0x9e,0x8b,0x5d,0x5a,0xd9,0x5d,0x12,0xd9,0x14,0x5d,0x18,0x5d,0xd9,0x94,0xe0, + 0x0c,0xea,0x90,0xe1,0xb9,0x94,0xb9,0xd1,0xc9,0xe5,0x41,0xbd,0xa5,0xb9,0xd1,0xcd, + 0x4d,0x09,0x42,0xa1,0x0b,0x19,0x9e,0xcb,0xd8,0x5b,0x9d,0x1b,0x5d,0x99,0xdc,0xdc, + 0x94,0xc0,0x15,0x00,0x79,0x18,0x00,0x00,0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b, + 0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4, + 0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x20,0x00,0x00,0x00,0x56,0xb0,0x0d,0x97,0xef,0x3c,0xbe,0x10,0x50,0x45,0x41,0x44, + 0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6,0x17,0xb7,0x6d,0x07,0xd2,0x70,0xf9,0xce, + 0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34,0xc3,0x42,0xd8,0x80,0x33,0x5c,0xbe,0xf3, + 0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16,0x30,0x0d,0x97,0xef,0x3c,0xfe,0xe2,0x00, + 0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d,0x02,0xd5,0x70,0xf9,0xce,0xe3,0x4b,0x93, + 0x13,0x11,0x28,0x35,0x3d,0xd4,0xe4,0x17,0xb7,0x6d,0x06,0xd2,0x70,0xf9,0xce,0xe3, + 0x4f,0x44,0x34,0x21,0x40,0x84,0xf9,0xc5,0x6d,0x1b,0xc1,0x33,0x5c,0xbe,0xf3,0xf8, + 0x54,0x03,0x44,0x98,0x5f,0xdc,0xb6,0x01,0x30,0x15,0x11,0x4d,0xc4,0xc5,0x4e,0xc0, + 0x84,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x96,0x06,0xb3,0xb4,0xf8,0xc3,0xba,0x4b,0xc2,0x7c,0x62,0x4c, + 0xf4,0xab,0x45,0x92,0x44,0x58,0x49,0x4c,0x9c,0x0b,0x00,0x00,0x60,0x00,0x01,0x00, + 0xe7,0x02,0x00,0x00,0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00, + 0x84,0x0b,0x00,0x00,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xde,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5, + 0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c, + 0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00, + 0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff, + 0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00, + 0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06, + 0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x2c,0x00,0x00,0x00,0x32,0x22,0x88,0x09, + 0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14, + 0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c,0x10,0x84,0xc1,0x08,0x40,0x09,0x00,0x0a, + 0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51, + 0x0c,0x80,0x20,0x88,0x62,0x20,0xa4,0x20,0x03,0x31,0x0c,0xc3,0x30,0x0c,0xa4,0x94, + 0x61,0x20,0x06,0x62,0x8e,0x1a,0x2e,0x7f,0xc2,0x1e,0x42,0xf2,0xb9,0x8d,0x2a,0x56, + 0x62,0xf2,0x8b,0xdb,0x46,0xc4,0x30,0x0c,0x03,0x15,0xf7,0x0c,0x97,0x3f,0x61,0x0f, + 0x21,0xf9,0x21,0xd0,0x0c,0x0b,0x81,0x82,0xa7,0x10,0x0e,0x01,0x11,0x14,0x95,0x62, + 0x20,0x86,0x61,0xa0,0x69,0x8e,0x20,0x28,0x06,0x44,0x14,0x04,0x51,0x91,0x35,0x10, + 0x30,0x8c,0x40,0x0c,0x49,0x90,0x1d,0x77,0x38,0xd2,0xb4,0x00,0x98,0x43,0x4d,0xfe, + 0x84,0x3d,0xc4,0xdf,0x45,0x80,0xe5,0x36,0x48,0xe1,0x44,0x8c,0x84,0x06,0x4d,0xd3, + 0x68,0x4b,0x06,0x22,0x09,0x06,0xf2,0x68,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0, + 0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50, + 0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28, + 0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xc8,0x13,0x01,0x01,0x30,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02,0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59, + 0x20,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x32,0x1e,0x98,0x14,0x19,0x11,0x4c,0x90, + 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22,0x4a,0x60,0x04,0xa0,0x24,0x8a,0xa1,0x00, + 0x07,0xca,0xa0,0x1c,0x8a,0xa0,0x3c,0xa8,0x28,0x89,0x11,0x80,0x32,0x28,0x82,0x42, + 0x20,0x6e,0x06,0x80,0xba,0xb1,0x18,0x05,0x3a,0x41,0x3e,0x38,0x41,0x3e,0x38,0x41, + 0x3e,0x00,0xe0,0x01,0x1e,0xe0,0x01,0x00,0x11,0x10,0x02,0x00,0x79,0x18,0x00,0x00, + 0x66,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c, + 0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25, + 0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10, + 0x08,0x64,0x82,0x40,0x24,0x1b,0x84,0x81,0x98,0x20,0x10,0xca,0x06,0x61,0x30,0x28, + 0xd8,0xcd,0x4d,0x10,0x88,0x65,0xc3,0x80,0x24,0xc4,0x04,0x81,0xb3,0x08,0x4c,0x10, + 0x08,0x66,0x03,0x42,0x2c,0xcc,0x40,0x0c,0x0d,0xb0,0x21,0x70,0x36,0x10,0x00,0xf0, + 0x00,0x13,0x84,0xee,0xda,0x10,0x44,0x13,0x04,0x01,0x20,0x63,0x57,0x26,0x47,0x57, + 0x86,0x37,0x45,0x17,0x46,0x97,0x36,0x46,0x84,0xaa,0x08,0x6b,0xe8,0xe9,0x49,0x8a, + 0x68,0x82,0x50,0x44,0x13,0x84,0x42,0xda,0x10,0x10,0x13,0x84,0x62,0x9a,0x20,0x10, + 0xcd,0x04,0x81,0x70,0x36,0x08,0xda,0xb6,0x61,0x21,0x2a,0xeb,0xc2,0xae,0x21,0x23, + 0x2e,0x6e,0x43,0x30,0x6c,0x58,0x86,0xca,0xba,0xbc,0x6b,0xc8,0x86,0x8b,0x9b,0x20, + 0x10,0xcf,0x86,0x00,0x0c,0x26,0x08,0x05,0xb5,0x41,0xd0,0xb4,0x0d,0x0b,0x18,0x54, + 0xd6,0x15,0x06,0xd7,0x20,0x06,0x60,0x70,0x8d,0xc1,0x86,0xa1,0xfb,0xc8,0x60,0xc3, + 0x42,0x54,0xd6,0x85,0x89,0xc1,0x90,0x11,0x17,0xb7,0x61,0x19,0x2a,0xeb,0xf2,0xc4, + 0x60,0xc8,0x86,0x8b,0xdb,0xb0,0x80,0x41,0x65,0x5d,0x61,0x20,0x06,0x83,0x18,0x80, + 0xc1,0x35,0x06,0x5c,0xa6,0xac,0xbe,0xa0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x26, + 0x08,0x45,0x35,0x41,0x20,0xa0,0x0d,0x82,0xb6,0x06,0x1b,0x16,0x2d,0x0d,0xac,0x0c, + 0x53,0x83,0x41,0x0d,0xb4,0x8b,0x0d,0x36,0x10,0x66,0x70,0x06,0x68,0xd0,0x06,0x1b, + 0x86,0x32,0x70,0x03,0x60,0x43,0x31,0x51,0x6f,0x00,0x01,0x55,0xd8,0xd8,0xec,0xda, + 0x5c,0xd2,0xc8,0xca,0xdc,0xe8,0xa6,0x04,0x41,0x15,0x32,0x3c,0x17,0xbb,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x29,0x01,0xd1,0x84,0x0c,0xcf,0xc5,0x2e,0x8c,0xcd,0xae,0x4c, + 0x6e,0x4a,0x60,0xd4,0x21,0xc3,0x73,0x99,0x43,0x0b,0x23,0x2b,0x93,0x6b,0x7a,0x23, + 0x2b,0x63,0x9b,0x12,0x24,0x65,0xc8,0xf0,0x5c,0xe4,0xca,0xe6,0xde,0xea,0xe4,0xc6, + 0xca,0xe6,0xa6,0x04,0x4f,0x1d,0x32,0x3c,0x17,0xbb,0xb4,0xb2,0xbb,0x24,0xb2,0x29, + 0xba,0x30,0xba,0xb2,0x29,0x41,0x54,0x87,0x0c,0xcf,0xa5,0xcc,0x8d,0x4e,0x2e,0x0f, + 0xea,0x2d,0xcd,0x8d,0x6e,0x6e,0x4a,0xf0,0x06,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, + 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, + 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, + 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, + 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, + 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, + 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, + 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, + 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, + 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, + 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, + 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, + 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, + 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70, + 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87, + 0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e, + 0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x56,0xb0,0x0d,0x97, + 0xef,0x3c,0xbe,0x10,0x50,0x45,0x41,0x44,0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6, + 0x17,0xb7,0x6d,0x07,0xd2,0x70,0xf9,0xce,0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34, + 0xc3,0x42,0xd8,0x80,0x33,0x5c,0xbe,0xf3,0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16, + 0x30,0x0d,0x97,0xef,0x3c,0xfe,0xe2,0x00,0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d, + 0x02,0xd5,0x70,0xf9,0xce,0xe3,0x4b,0x93,0x13,0x11,0x28,0x35,0x3d,0xd4,0xe4,0x17, + 0xb7,0x6d,0x06,0xd2,0x70,0xf9,0xce,0xe3,0x4f,0x44,0x34,0x21,0x40,0x84,0xf9,0xc5, + 0x6d,0x1b,0xc1,0x33,0x5c,0xbe,0xf3,0xf8,0x54,0x03,0x44,0x98,0x5f,0xdc,0xb6,0x01, + 0x30,0x15,0x11,0x4d,0xc4,0xc5,0x4e,0xc0,0x84,0x08,0x00,0x00,0x61,0x20,0x00,0x00, + 0x60,0x01,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x44,0x14,0x57,0x29,0x94,0x5d,0x21,0xcc,0x00,0x8c,0x32,0x00,0x09,0x32,0xca,0x00, + 0x74,0xc8,0x28,0x03,0x90,0x6c,0xa3,0x0c,0xc0,0x87,0x8c,0x32,0x00,0x60,0x80,0x8c, + 0x32,0x00,0xc9,0x19,0x8c,0x32,0x00,0x62,0x80,0x94,0xdc,0x28,0x03,0xe0,0x21,0xa3, + 0x0c,0x40,0x18,0x20,0x05,0x31,0xca,0x00,0x24,0xc9,0x28,0x03,0x30,0x06,0xc8,0x28, + 0x03,0x90,0x3c,0xe5,0x56,0x32,0x54,0x94,0x00,0x55,0x23,0x00,0x00,0x00,0x00,0x00, + 0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xa1,0xc1,0xb2,0x99,0x81,0x19,0x04,0x33,0x11, + 0x1e,0x81,0x8c,0xc2,0x4c,0x84,0x47,0x20,0xa3,0x30,0x13,0xe1,0x11,0xc8,0x28,0x8c, + 0x18,0x24,0x00,0x08,0x82,0x81,0x21,0x07,0x18,0x19,0xa8,0x41,0x18,0x5c,0x23,0x06, + 0x09,0x00,0x82,0x60,0x60,0xcc,0x41,0x56,0x06,0x6b,0x70,0x60,0x23,0x06,0x09,0x00, + 0x82,0x60,0x60,0xd0,0x81,0xd6,0x06,0x6c,0x30,0x06,0xd9,0x88,0x41,0x02,0x80,0x20, + 0x18,0x18,0x75,0xb0,0xb9,0x41,0x1b,0x24,0xda,0x88,0x41,0x02,0x80,0x20,0x18,0x18, + 0x76,0xc0,0xbd,0x81,0x1b,0x8c,0xc1,0x36,0x62,0x90,0x00,0x20,0x08,0x06,0xc6,0x1d, + 0x74,0x6f,0xf0,0x06,0x66,0xc0,0x8d,0x18,0x24,0x00,0x08,0x82,0x81,0x81,0x07,0x1e, + 0x1c,0xc0,0x01,0xd3,0x8d,0x18,0x24,0x00,0x08,0x82,0x81,0x91,0x07,0x5f,0x1c,0xc4, + 0x81,0x19,0x78,0x23,0x06,0x07,0x00,0x82,0x60,0x10,0xd9,0x41,0x18,0x30,0x72,0x30, + 0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x30,0x62,0x70, + 0x00,0x20,0x08,0x06,0xd1,0x1e,0x98,0x41,0x34,0x06,0xa3,0x09,0x01,0x30,0x9a,0x20, + 0x04,0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x23,0x06,0x07,0x00,0x82,0x60,0x10,0x81, + 0xc2,0x1a,0x58,0x67,0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3, + 0x09,0xc4,0x30,0x62,0x70,0x00,0x20,0x08,0x06,0x51,0x29,0xc0,0xc1,0x56,0x06,0xa3, + 0x09,0x01,0x30,0x9a,0x20,0x04,0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x36,0x5d,0xf2, + 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x49,0x15,0xf0,0xe0,0xb9,0x82,0x11,0x03,0x04, + 0x00,0x41,0x30,0x98,0x56,0x21,0x0f,0x96,0x2b,0xb0,0xe0,0x80,0x8e,0x59,0x9b,0x7c, + 0x46,0x0c,0x10,0x00,0x04,0xc1,0x60,0x72,0x05,0x3e,0x90,0xb6,0x60,0xc4,0x00,0x01, + 0x40,0x10,0x0c,0xa6,0x57,0xe8,0x03,0x67,0x0b,0x2c,0x50,0xa0,0x63,0xd9,0x27,0x9f, + 0x11,0x03,0x04,0x00,0x41,0x30,0x98,0x64,0x01,0x14,0xaa,0x2f,0x18,0x31,0x40,0x00, + 0x10,0x04,0x83,0x69,0x16,0x42,0x21,0xfa,0x02,0x0b,0x1a,0xe8,0x18,0x37,0x06,0xf2, + 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0xc9,0x16,0x48,0x01,0x1b,0x83,0x60,0xc4,0x00, + 0x01,0x40,0x10,0x0c,0xa6,0x5b,0x28,0x05,0x6a,0x0c,0x02,0x0b,0x20,0xe8,0x8c,0x18, + 0x1c,0x00,0x08,0x82,0x41,0xa4,0x0b,0xa5,0x00,0x07,0xa0,0x30,0x9a,0x10,0x00,0xa3, + 0x09,0x42,0x30,0x9a,0x30,0x08,0x23,0x06,0x07,0x00,0x82,0x60,0x10,0xf9,0x42,0x2a, + 0xd0,0xc1,0x28,0x8c,0x26,0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x88,0xc1, + 0x01,0x80,0x20,0x18,0x44,0xe2,0xd0,0x0a,0x78,0x60,0x0a,0xa3,0x09,0x01,0x30,0x9a, + 0x20,0x04,0xa3,0x09,0x83,0x30,0x62,0x70,0x00,0x20,0x08,0x06,0x91,0x39,0xc4,0x02, + 0x1f,0x94,0xc2,0x68,0x42,0x00,0x8c,0x26,0x08,0xc1,0x68,0xc2,0x20,0xd8,0x73,0x07, + 0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x49,0x1d,0x70,0x81,0xb9,0x83,0x60,0xc4, + 0x00,0x01,0x40,0x10,0x0c,0xa6,0x75,0xc8,0x85,0xe4,0x0e,0x02,0x0b,0x0c,0xe8,0x98, + 0xb4,0x07,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0xc9,0x1d,0x78,0xe1,0xd9,0x83, + 0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xa6,0x77,0xe8,0x05,0x66,0x0f,0x02,0x0b,0x12, + 0xe8,0x58,0xf5,0x07,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x49,0x1e,0xc0,0x41, + 0xfa,0x83,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xa6,0x79,0x08,0x87,0xe7,0x0f,0x02, + 0x0b,0x18,0xe8,0x8c,0x18,0x1c,0x00,0x08,0x82,0x41,0x64,0x0f,0xe1,0xc0,0x0a,0xb9, + 0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0xb0,0xbd, + 0x01,0x16,0xee,0xe1,0x1e,0x86,0x0d,0x88,0xa0,0x18,0x80,0xed,0x0d,0xb1,0x80,0x0f, + 0xf9,0x30,0x6c,0x40,0x04,0xc5,0x00,0x6c,0x6f,0x90,0x85,0x7c,0x98,0x87,0x61,0x03, + 0x22,0x28,0x06,0x60,0x7b,0xc3,0x2c,0xe8,0x83,0x3d,0x0c,0x1b,0x10,0x41,0x31,0x00, + 0xdb,0x1b,0x68,0x61,0x1f,0x78,0x81,0x82,0x61,0x8c,0x18,0x1c,0x00,0x08,0x82,0x41, + 0x14,0x12,0xec,0x70,0x0b,0xe3,0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30, + 0x08,0xa3,0x09,0xc4,0x30,0x6c,0x40,0x2c,0xc4,0x00,0x0c,0x1b,0x10,0xca,0x30,0x00, + 0xc3,0x06,0x44,0x22,0x0c,0xc0,0xb0,0x01,0x81,0x04,0x03,0x40,0xc7,0x30,0x46,0x0c, + 0x0e,0x00,0x04,0xc1,0x20,0x3a,0x09,0x79,0xe8,0x85,0x71,0x18,0x4d,0x08,0x80,0xd1, + 0x04,0x21,0x18,0x4d,0x18,0x84,0xd1,0x04,0x62,0x18,0x36,0x20,0x22,0x62,0x00,0x86, + 0x0d,0x08,0x68,0x18,0x80,0x61,0x03,0xe2,0x11,0x06,0x60,0xd8,0x80,0x70,0x82,0x01, + 0xa0,0x66,0x18,0xdb,0x1b,0xc2,0x21,0x25,0x52,0x62,0xd8,0x80,0x08,0xae,0x01,0xd8, + 0xde,0x20,0x0e,0x2a,0xb1,0x12,0xc3,0x06,0x44,0x70,0x0d,0xc0,0xf6,0x86,0x71,0x58, + 0x89,0x92,0x18,0x36,0x20,0x82,0x6b,0x00,0xb6,0x37,0x90,0x03,0x4b,0xa0,0xc4,0xb0, + 0x01,0x11,0x5c,0x03,0xb0,0xbd,0xa1,0x1c,0x5a,0x22,0x1f,0x28,0x18,0xc6,0xb0,0x01, + 0x61,0x44,0x03,0x30,0x6c,0x40,0x14,0xd0,0x00,0x0c,0x1b,0x10,0xc4,0x33,0x00,0xc3, + 0x06,0xc4,0xe0,0x0c,0x00,0x09,0xc3,0x18,0x36,0x20,0x0e,0x66,0x00,0x86,0x0d,0x08, + 0x63,0x19,0x80,0x61,0x03,0xa2,0x50,0x06,0x60,0xd8,0x80,0x20,0x92,0x01,0xa0,0x61, + 0x18,0xdb,0x1b,0xd0,0x21,0x26,0x62,0x62,0xd8,0x80,0x08,0xbe,0x01,0xd8,0xde,0x90, + 0x0e,0x32,0x31,0x13,0xc3,0x06,0x44,0xf0,0x0d,0xc0,0xf6,0x06,0x75,0x98,0x89,0x96, + 0x18,0x36,0x20,0x82,0x6f,0x00,0xb6,0x37,0xac,0x03,0x4d,0xc0,0xc4,0xb0,0x01,0x11, + 0x7c,0x03,0xb0,0xbd,0x81,0x1d,0x6a,0x82,0x1e,0x28,0x18,0xc6,0xb0,0x01,0x61,0x64, + 0x03,0x30,0x6c,0x40,0x14,0xd8,0x00,0x0c,0x1b,0x10,0xc4,0x35,0x00,0xc3,0x06,0xc4, + 0x60,0x0d,0x00,0x09,0xc3,0x18,0x36,0x20,0x0e,0x6a,0x00,0x86,0x0d,0x08,0x63,0x1a, + 0x80,0x61,0x03,0xa2,0x90,0x06,0x60,0xd8,0x80,0x20,0xa2,0x01,0xa0,0x61,0x18,0xd6, + 0xb5,0x83,0x7c,0x46,0x0c,0x10,0x00,0x04,0xc1,0x60,0xea,0x89,0x95,0xc0,0xda,0x21, + 0x18,0x31,0x40,0x00,0x10,0x04,0x83,0xc9,0x27,0x58,0x62,0x6a,0x87,0xc0,0x1c,0x78, + 0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x4c,0x60,0xe1,0x12,0x0e,0x3c,0x04,0x23, + 0x06,0x08,0x00,0x82,0x60,0x30,0x85,0xc5,0x4b,0x38,0xf0,0x10,0x58,0x32,0x0f,0xf2, + 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x69,0x2c,0x62,0x22,0x99,0x87,0x60,0xc4,0x00, + 0x01,0x40,0x10,0x0c,0x26,0xb2,0x90,0x89,0x64,0x1e,0x82,0x11,0x83,0x05,0x00,0x41, + 0x30,0x50,0xd0,0x02,0x24,0x0e,0x22,0x38,0x88,0x60,0xc4,0xc0,0x00,0x40,0x10,0x0c, + 0x18,0xb4,0x00,0x89,0xc0,0x82,0x44,0x3e,0x26,0x1c,0xf2,0xb1,0xa1,0x90,0xcf,0x88, + 0x41,0x02,0x80,0x20,0x18,0x20,0x6e,0x71,0x13,0x67,0x71,0x16,0x3e,0xe1,0x07,0x23, + 0x06,0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0x9c,0xc5,0x59,0x8c,0x84,0x1e,0x8c, + 0x18,0x24,0x00,0x08,0x82,0x01,0xe2,0x16,0x37,0x71,0x16,0x67,0xc1,0x13,0x76,0x30, + 0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x5b,0xdc,0x04,0x5a,0x9c,0x85,0x4f,0x0c,0x23, + 0x06,0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0xa0,0xc5,0x59,0x8c,0x84,0x30,0x62, + 0x90,0x00,0x20,0x08,0x06,0x88,0x5b,0xdc,0x04,0x5a,0x9c,0x05,0x4f,0x04,0x23,0x06, + 0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0x84,0xc5,0x59,0xf8,0x44,0x3f,0x8c,0x18, + 0x24,0x00,0x08,0x82,0x01,0xe2,0x16,0x37,0x11,0x16,0x67,0x31,0x12,0xfc,0x30,0x62, + 0x90,0x00,0x20,0x08,0x06,0x88,0x5b,0xdc,0xc4,0x58,0x9c,0x85,0x4f,0xf8,0xc2,0x88, + 0x41,0x02,0x80,0x20,0x18,0x20,0x6e,0x71,0x13,0x63,0x71,0x16,0x23,0xa1,0x0b,0x23, + 0x06,0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0x8c,0xc5,0x59,0xf0,0x84,0x2d,0x8c, + 0x18,0x24,0x00,0x08,0x82,0x01,0xe2,0x16,0x37,0x31,0x16,0x67,0xd1,0x13,0xb2,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static const unsigned char _vertexStaticMSL[] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, + 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, + 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, + 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, + 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, + 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, + 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, + 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72,0x74, + 0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61, + 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x75,0x73, + 0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72, + 0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, + 0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c, + 0x6f,0x63,0x6e,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72,0x74,0x65,0x78,0x53, + 0x74,0x61,0x74,0x69,0x63,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, + 0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x32,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32, + 0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20, + 0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x6f,0x75,0x74, + 0x20,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x28,0x76,0x65, + 0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x69,0x6e,0x20,0x69,0x6e, + 0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63, + 0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x44,0x72,0x61, + 0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x26,0x20,0x44,0x72,0x61,0x77,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72, + 0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x72, + 0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75, + 0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x20,0x5f,0x35,0x39,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28, + 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, + 0x52,0x44,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x6f, + 0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f, + 0x6e,0x20,0x2a,0x20,0x5f,0x35,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x30,0x20,0x3d,0x20,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x73,0x2e,0x6d,0x6f,0x64,0x65,0x6c,0x20,0x2a,0x20,0x5f,0x35,0x39,0x29,0x2e, + 0x78,0x79,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x6f,0x75,0x74, + 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x3d, + 0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x69,0x7a,0x65, + 0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x31,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78, + 0x5b,0x30,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69, + 0x78,0x5b,0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, + 0x69,0x78,0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74, + 0x72,0x69,0x78,0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78, + 0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69, + 0x78,0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, + 0x69,0x78,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74, + 0x72,0x69,0x78,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78, + 0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69, + 0x78,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, + 0x69,0x78,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74, + 0x72,0x69,0x78,0x5b,0x33,0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61, + 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x3d,0x20,0x69,0x6e, + 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75, + 0x74,0x3b,0x0a,0x7d,0x0a,0x0a, +}; + +static const SceneShaderT sceneShaderVertexStatic = { "vertexStatic", _vertexStaticSPIRV, sizeof(_vertexStaticSPIRV), _vertexStaticDXIL, sizeof(_vertexStaticDXIL), _vertexStaticMSL, sizeof(_vertexStaticMSL) }; + +static const unsigned char _vertexSkinnedSPIRV[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x9e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74, + 0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x08,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f, + 0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65,0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61, + 0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, + 0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x0e,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69, + 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00, + 0x05,0x00,0x06,0x00,0x0f,0x00,0x00,0x00,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x08,0x00,0x10,0x00,0x00,0x00, + 0x74,0x79,0x70,0x65,0x2e,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x00,0x00,0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73, + 0x73,0x69,0x76,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00, + 0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e, + 0x74,0x73,0x00,0x00,0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x6c,0x69,0x67,0x68,0x74,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x4c,0x69,0x67,0x68,0x74,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x11,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65, + 0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00, + 0x06,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x63,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x12,0x00,0x00,0x00, + 0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x13,0x00,0x00,0x00,0x74,0x79,0x70,0x65, + 0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x14,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00, + 0x05,0x00,0x06,0x00,0x15,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x16,0x00,0x00,0x00, + 0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00, + 0x03,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00, + 0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31, + 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x05,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, + 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x34,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x09,0x00,0x00,0x00, + 0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x30,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x0a,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e, + 0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x0b,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65, + 0x64,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x10,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x1b,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x2b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x18,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x1e,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x1e,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x25,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x1e,0x00,0x06,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x18,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x1e,0x00,0x09,0x00,0x10,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x27,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x19,0x00,0x09,0x00, + 0x13,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x1a,0x00,0x02,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x29,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x2c,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2d,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x2e,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2f,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x30,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x31,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x32,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x34,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x35,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x36,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x37,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x25,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x27,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x28,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x29,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2d,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x30,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x31,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x32,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x32,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x33,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x39,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x34,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x3a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x3c,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2c,0x00,0x00,0x00, + 0x3d,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x3b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x20,0x00,0x00,0x00,0x41,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x39,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x43,0x00,0x00,0x00, + 0x42,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x36,0x00,0x00,0x00,0x44,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x21,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x45,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x38,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x48,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x39,0x00,0x00,0x00, + 0x4a,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x41,0x00,0x06,0x00, + 0x36,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x4b,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x4d,0x00,0x00,0x00, + 0x4c,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x4e,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x38,0x00,0x00,0x00, + 0x4f,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x81,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x51,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x39,0x00,0x00,0x00,0x53,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x23,0x00,0x00,0x00, + 0x54,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x36,0x00,0x00,0x00, + 0x55,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x54,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x55,0x00,0x00,0x00, + 0x90,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x41,0x00,0x00,0x00, + 0x56,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x38,0x00,0x00,0x00,0x58,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x5a,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x81,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x5a,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x39,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x5d,0x00,0x00,0x00, + 0x5c,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x36,0x00,0x00,0x00,0x5e,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x5d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x21,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x5e,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x5f,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x38,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x45,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x68,0x00,0x00,0x00, + 0x67,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x69,0x00,0x00,0x00, + 0x45,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00, + 0x6a,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x37,0x00,0x00,0x00, + 0x6b,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x6a,0x00,0x00,0x00, + 0x90,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x3c,0x00,0x00,0x00, + 0x6b,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x6c,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x6e,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, + 0x2a,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x6e,0x00,0x00,0x00,0x6e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x70,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x4d,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0x72,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x37,0x00,0x00,0x00,0x74,0x00,0x00,0x00, + 0x6f,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x2a,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x74,0x00,0x00,0x00, + 0x8e,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x77,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x78,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, + 0x2a,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x7a,0x00,0x00,0x00, + 0x7a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x56,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x7d,0x00,0x00,0x00, + 0x7c,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x37,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x79,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x7d,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x2a,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x8e,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x7f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x81,0x00,0x00,0x00, + 0x77,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x82,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, + 0x2a,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x84,0x00,0x00,0x00, + 0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x5f,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x87,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x37,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x83,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x2a,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x8e,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x8a,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x8b,0x00,0x00,0x00, + 0x81,0x00,0x00,0x00,0x8a,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x36,0x00,0x00,0x00, + 0x8c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x21,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x36,0x00,0x00,0x00,0x8f,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x8f,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x91,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x36,0x00,0x00,0x00, + 0x93,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x21,0x00,0x00,0x00,0x94,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x95,0x00,0x00,0x00,0x94,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x95,0x00,0x00,0x00, + 0x95,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x94,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x97,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x99,0x00,0x00,0x00, + 0x94,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2a,0x00,0x00,0x00, + 0x9a,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x37,0x00,0x00,0x00, + 0x9b,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, + 0x90,0x00,0x05,0x00,0x2a,0x00,0x00,0x00,0x9c,0x00,0x00,0x00,0x8b,0x00,0x00,0x00, + 0x9b,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x2a,0x00,0x00,0x00,0x9d,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x9c,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x9d,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, +}; + +static const unsigned char _vertexSkinnedDXIL[] = { + 0x44,0x58,0x42,0x43,0x27,0xb4,0xc6,0xd9,0x69,0x41,0xbc,0x07,0x91,0x11,0x7e,0x85, + 0x11,0x6e,0x35,0x08,0x01,0x00,0x00,0x00,0x70,0x25,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0xb0,0x01,0x00,0x00, + 0x80,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x1c,0x0c,0x00,0x00,0x53,0x46,0x49,0x30, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, + 0xb4,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x31,0xa0,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x50,0x53,0x56,0x30,0xc8,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff,0x01,0x00,0x00,0x00,0x05,0x04,0x00,0x05,0x04,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f, + 0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e, + 0x65,0x64,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x03,0x44,0x00,0x01,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x04,0x44,0x00,0x03,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x02,0x00,0x00, + 0x37,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x02,0x00,0x00, + 0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x44,0x03,0x03,0x04,0x00,0x00, + 0x07,0xf0,0x00,0x00,0x07,0xf0,0x00,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00, + 0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00, + 0x53,0x54,0x41,0x54,0x78,0x08,0x00,0x00,0x60,0x00,0x01,0x00,0x1e,0x02,0x00,0x00, + 0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x60,0x08,0x00,0x00, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x15,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b, + 0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42, + 0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a, + 0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff, + 0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20, + 0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00, + 0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00, + 0x89,0x20,0x00,0x00,0x34,0x00,0x00,0x00,0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04, + 0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c, + 0x0b,0x84,0xc4,0x4c,0x10,0x8c,0xc1,0x08,0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08, + 0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51,0x0c,0x82,0x20,0x88, + 0x82,0x20,0xa4,0x18,0x00,0x41,0x10,0xc5,0x40,0x4a,0x41,0x06,0x62,0x18,0x86,0x61, + 0x18,0x88,0x29,0xc3,0x40,0x0c,0xe4,0x1c,0x35,0x5c,0xfe,0x84,0x3d,0x84,0xe4,0x73, + 0x1b,0x55,0xac,0xc4,0xe4,0x17,0xb7,0x8d,0x88,0x61,0x18,0x06,0x2a,0xee,0x19,0x2e, + 0x7f,0xc2,0x1e,0x42,0xf2,0x43,0xa0,0x19,0x16,0x02,0x05,0x51,0x21,0x20,0x42,0x22, + 0x68,0x2a,0xc5,0x40,0x0c,0xc3,0x40,0xd5,0x1c,0x41,0x50,0x0c,0x89,0x28,0x08,0xe2, + 0x22,0x6c,0x20,0x60,0x18,0x81,0x18,0x92,0x60,0x3b,0xee,0x70,0xa4,0x69,0x01,0x30, + 0x87,0x9a,0xfc,0x09,0x7b,0x88,0xbf,0x8b,0x00,0xcb,0x6d,0x90,0xc2,0x89,0x18,0x09, + 0x0d,0x1c,0xc7,0x51,0x97,0x80,0x01,0xdc,0x71,0x87,0x23,0x4d,0x0b,0x80,0x39,0xd4, + 0xe4,0x4f,0xd8,0x43,0xfc,0xac,0x82,0x34,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x28,0xf8, + 0x08,0x4c,0x0b,0x02,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87, + 0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e, + 0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20, + 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76, + 0x40,0x07,0x43,0x9e,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86, + 0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20, + 0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28,0x40,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x60,0xc8,0xe3,0x00,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x90,0x67,0x02,0x02,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x4f, + 0x05,0x04,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x1e,0x0c,0x08,0x80, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x21,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x18,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, + 0x4a,0x60,0x04,0xa0,0x18,0x0a,0x70,0xa0,0x00,0x01,0x03,0x8a,0xa0,0x10,0xca,0xa0, + 0x1c,0x4a,0xa2,0x00,0x03,0x0a,0x50,0xa0,0x3c,0x0a,0x7c,0xa0,0x84,0x07,0x0a,0x3b, + 0xa0,0xb0,0x15,0x0a,0xa4,0x30,0xa8,0x28,0x89,0x11,0x80,0x32,0x28,0x82,0x52,0x28, + 0x04,0xba,0x6a,0x80,0xba,0x19,0x00,0xf2,0x66,0x00,0x08,0x9c,0x01,0xa0,0x70,0x06, + 0x80,0xc4,0xb1,0x2c,0x0a,0x3a,0x41,0x3e,0x38,0x41,0x3e,0x38,0x41,0x3e,0x00,0xe0, + 0x01,0x1e,0xe0,0x01,0x00,0x11,0x10,0x02,0x80,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b, + 0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4, + 0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe0,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90, + 0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c, + 0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c, + 0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x64,0x82,0x40,0x28,0x1b,0x84,0x81, + 0xd8,0x20,0x10,0x04,0x05,0xbb,0xb9,0x09,0x02,0xb1,0x6c,0x18,0x0e,0x84,0x98,0x20, + 0x78,0x64,0xc0,0x88,0x2e,0x0f,0xae,0xec,0x8b,0x48,0x2e,0xec,0xae,0xca,0x2d,0xcd, + 0xec,0x4d,0xae,0x6d,0x6e,0x82,0x40,0x30,0x1b,0x10,0x42,0x59,0x06,0x62,0x60,0x80, + 0x09,0x42,0x18,0x98,0x01,0x23,0xba,0x3c,0xb8,0xb2,0xaf,0xa9,0xb5,0x34,0xb7,0x2a, + 0xb7,0x34,0xb3,0x37,0xb9,0xb6,0xb9,0x09,0x02,0xd1,0x6c,0x40,0x06,0xe7,0x19,0x86, + 0x01,0x02,0x36,0x08,0x4d,0xb4,0x81,0x00,0x00,0x09,0x98,0x20,0x74,0x63,0x40,0x30, + 0x20,0x92,0x0b,0xbb,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b,0x9b,0xfb,0x6a,0x7b,0x23, + 0x2b,0x63,0xb3,0x4a,0x2b,0xbb,0x83,0x92,0x7b,0x53,0x2b,0x1b,0xa3,0x4b,0x7b,0x73, + 0x9b,0x20,0x10,0xce,0x04,0x81,0x78,0x36,0x0c,0xd7,0x65,0x4d,0x10,0x08,0x68,0x82, + 0x40,0x44,0x13,0x04,0x42,0x9a,0x20,0x5c,0x62,0xb0,0x41,0x41,0x2a,0x0b,0xcb,0x08, + 0x6d,0xdb,0x38,0x4a,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x5f,0x6d,0x6f,0x64,0x65,0x6c,0x13,0x04,0x62,0xda,0x80,0x20,0x9e,0x85,0x65,0x9f, + 0xb6,0x31,0x23,0x92,0x0b,0xbb,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b,0x9b,0xfb,0x72, + 0x7b,0x93,0x6b,0x0b,0x63,0x6b,0x0a,0xa3,0x93,0x4b,0xc3,0x9b,0x20,0x10,0xd4,0x06, + 0x04,0x09,0x03,0x0b,0xcb,0xc4,0x40,0xdb,0x36,0x10,0x4c,0x07,0x06,0x63,0x30,0x41, + 0x00,0x83,0x32,0xe0,0x34,0xb5,0x96,0xe6,0x56,0xe5,0x96,0x66,0xf6,0x26,0xd7,0x36, + 0xf7,0xa5,0xf6,0x96,0xe6,0x46,0x37,0xb7,0x01,0x41,0xcc,0xc0,0xc2,0x32,0x42,0xdb, + 0x36,0x08,0xd0,0x19,0x6c,0x28,0x08,0x8a,0x0c,0xca,0x00,0x0d,0x26,0x08,0x02,0xb0, + 0x01,0xd8,0x30,0x10,0x6b,0xb0,0x06,0x1b,0x02,0x36,0xd8,0x30,0x0c,0x6a,0xd0,0x06, + 0x13,0x04,0x31,0x38,0x83,0x0d,0xc1,0x1b,0xb0,0xb1,0x2b,0x93,0xa3,0x2b,0xc3,0x9b, + 0x5a,0x4b,0x73,0x73,0x2b,0x23,0x23,0x42,0x55,0x84,0x35,0xf4,0xf4,0x24,0x45,0x34, + 0x41,0x28,0xb8,0x09,0x42,0xd1,0x6d,0x08,0x88,0x09,0x42,0xe1,0x6d,0x10,0x32,0x6d, + 0xc3,0x42,0xc8,0xc1,0x1c,0xd0,0x41,0x1d,0xd0,0xc1,0x60,0x07,0x04,0x1d,0xdc,0xc1, + 0x86,0x60,0xd8,0xb0,0x0c,0x72,0x30,0x07,0x74,0x90,0x07,0x74,0x30,0xd8,0xc1,0x40, + 0x07,0x77,0xb0,0x21,0xb0,0x26,0x08,0xc5,0xb7,0x41,0xc8,0xb2,0x0d,0x8b,0x25,0x07, + 0x73,0x40,0x07,0x7b,0x40,0x07,0x03,0x1f,0x58,0x74,0xd0,0x07,0x13,0x84,0x02,0x0c, + 0x36,0x04,0xd9,0x04,0xa1,0x08,0x83,0x09,0x02,0x51,0x6d,0x10,0x32,0x51,0xd8,0xb0, + 0x64,0x72,0xf0,0x07,0x74,0x00,0x0a,0x74,0x30,0x84,0x42,0x46,0x07,0xa3,0xb0,0x21, + 0xb8,0x36,0x2c,0x97,0x1c,0xcc,0x01,0x1d,0x94,0x02,0x1d,0x0c,0xa1,0x70,0xd1,0xc1, + 0x28,0x6c,0x28,0xf0,0x40,0x0f,0xfc,0x80,0x14,0x4c,0x61,0xc3,0x42,0xc8,0xc1,0x1c, + 0xd0,0x41,0x1d,0xf0,0xc1,0x60,0x07,0x04,0x1d,0xdc,0xc1,0x86,0x65,0x90,0x83,0x39, + 0xa0,0x83,0x3c,0xe0,0x83,0xc1,0x0e,0x06,0x3a,0xb8,0x83,0x0d,0x8b,0x25,0x07,0x73, + 0x40,0x07,0x7b,0xc0,0x07,0x03,0x1f,0x58,0x74,0xd0,0x07,0x5c,0xa6,0xac,0xbe,0xa0, + 0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x36,0x2c,0xd9,0x2a,0xcc,0x81,0x1d,0xd4,0x41, + 0x28,0x0c,0xa1,0x90,0xd1,0xc1,0x28,0x6c,0x20,0x50,0x21,0x15,0x54,0x81,0x15,0x36, + 0x0c,0xa7,0xd0,0x0a,0xc0,0x86,0x42,0x0d,0xe2,0xc0,0x15,0x26,0x80,0x51,0x98,0x9c, + 0x5c,0x58,0xde,0x17,0xdb,0xdb,0x58,0x18,0xdb,0x97,0x58,0x1e,0x5d,0xd9,0xdc,0x04, + 0x81,0xb0,0x08,0x85,0xc9,0xc9,0x85,0xe5,0x7d,0xb1,0xbd,0x8d,0x85,0xb1,0x7d,0xb1, + 0x91,0xcd,0xd1,0x4d,0x10,0x88,0x8b,0x86,0x19,0xdb,0x5b,0x18,0xdd,0xdc,0x04,0x81, + 0xc0,0x58,0xa4,0xb9,0xcd,0xd1,0xcd,0x4d,0x10,0x88,0x8c,0x44,0x9a,0x1b,0xdd,0x1c, + 0x8d,0xb9,0xb4,0xb3,0x2f,0x36,0xb2,0x09,0x02,0xa1,0xd1,0x98,0x4b,0x3b,0xfb,0x9a, + 0xa3,0x9b,0x20,0x10,0x1b,0x8b,0xba,0x34,0x37,0xba,0xb9,0x0d,0x10,0x2c,0xc4,0x82, + 0x2c,0xcc,0x02,0x2d,0xd4,0x82,0x2d,0xdc,0x02,0x2e,0x5c,0xb9,0xa0,0x0b,0xbb,0xc0, + 0x0b,0xbd,0xc0,0x0b,0x55,0xd8,0xd8,0xec,0xda,0x5c,0xd2,0xc8,0xca,0xdc,0xe8,0xa6, + 0x04,0x41,0x15,0x32,0x3c,0x17,0xbb,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x29,0x01,0xd1, + 0x84,0x0c,0xcf,0xc5,0x2e,0x8c,0xcd,0xae,0x4c,0x6e,0x4a,0x50,0xd4,0x21,0xc3,0x73, + 0x99,0x43,0x0b,0x23,0x2b,0x93,0x6b,0x7a,0x23,0x2b,0x63,0x9b,0x12,0x20,0x65,0xc8, + 0xf0,0x5c,0xe4,0xca,0xe6,0xde,0xea,0xe4,0xc6,0xca,0xe6,0xa6,0x04,0x52,0x25,0x32, + 0x3c,0x17,0xba,0x3c,0xb8,0xb2,0x20,0x37,0xb7,0x37,0xba,0x30,0xba,0xb4,0x37,0xb7, + 0xb9,0x29,0x02,0x1a,0xb4,0x41,0x1d,0x32,0x3c,0x17,0xbb,0xb4,0xb2,0xbb,0x24,0xb2, + 0x29,0xba,0x30,0xba,0xb2,0x29,0xc1,0x1b,0xd4,0x21,0xc3,0x73,0x29,0x73,0xa3,0x93, + 0xcb,0x83,0x7a,0x4b,0x73,0xa3,0x9b,0x9b,0x12,0xb8,0x42,0x17,0x32,0x3c,0x97,0xb1, + 0xb7,0x3a,0x37,0xba,0x32,0xb9,0xb9,0x29,0x41,0x2f,0x00,0x00,0x79,0x18,0x00,0x00, + 0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, + 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, + 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, + 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, + 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, + 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, + 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, + 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, + 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, + 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, + 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, + 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, + 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, + 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70, + 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87, + 0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e, + 0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x24,0x00,0x00,0x00,0x66,0xb0,0x0d,0x97, + 0xef,0x3c,0xbe,0x10,0x50,0x45,0x41,0x44,0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6, + 0x17,0xb7,0x6d,0x08,0xd2,0x70,0xf9,0xce,0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34, + 0xc3,0x42,0x18,0x81,0x33,0x5c,0xbe,0xf3,0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16, + 0x30,0x0d,0x97,0xef,0x3c,0xfe,0xe2,0x00,0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d, + 0x02,0xd3,0x70,0xf9,0xce,0xe3,0x2f,0x0e,0x30,0x88,0xcd,0x43,0x4d,0x3e,0x72,0xdb, + 0x36,0x50,0x0d,0x97,0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e, + 0x71,0xdb,0x76,0x20,0x0d,0x97,0xef,0x3c,0xfe,0x44,0x44,0x13,0x02,0x44,0x98,0x5f, + 0xdc,0xb6,0x15,0x3c,0xc3,0xe5,0x3b,0x8f,0x4f,0x35,0x40,0x84,0xf9,0xc5,0x6d,0x1b, + 0x40,0x53,0x11,0xd1,0x44,0x5c,0xac,0x82,0x34,0x0d,0x31,0x00,0x00,0x00,0x00,0x00, + 0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0xf2,0x97,0xd6, + 0xad,0x07,0x8f,0xd0,0x8b,0x74,0xf8,0xde,0xae,0x5e,0x77,0x85,0x44,0x58,0x49,0x4c, + 0x4c,0x19,0x00,0x00,0x60,0x00,0x01,0x00,0x53,0x06,0x00,0x00,0x44,0x58,0x49,0x4c, + 0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0x19,0x00,0x00,0x42,0x43,0xc0,0xde, + 0x21,0x0c,0x00,0x00,0x4a,0x06,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39, + 0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02, + 0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88, + 0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90, + 0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06, + 0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07, + 0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff, + 0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, + 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, + 0x35,0x00,0x00,0x00,0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84, + 0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c, + 0x10,0x98,0xc1,0x08,0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00, + 0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51,0x0c,0x82,0x20,0x88,0x82,0x20,0xa4,0x18, + 0x00,0x41,0x10,0xc5,0x40,0x4a,0x41,0x06,0x62,0x18,0x86,0x61,0x18,0x88,0x29,0xc3, + 0x40,0x0c,0xe4,0x1c,0x35,0x5c,0xfe,0x84,0x3d,0x84,0xe4,0x73,0x1b,0x55,0xac,0xc4, + 0xe4,0x17,0xb7,0x8d,0x88,0x61,0x18,0x06,0x2a,0xee,0x19,0x2e,0x7f,0xc2,0x1e,0x42, + 0xf2,0x43,0xa0,0x19,0x16,0x02,0x05,0x51,0x21,0x20,0x42,0x22,0x68,0x2a,0xc5,0x40, + 0x0c,0xc3,0x40,0xd5,0x1c,0x41,0x50,0x0c,0x89,0x28,0x08,0xe2,0x22,0x6c,0x20,0x60, + 0x18,0x81,0x18,0x92,0x60,0x3b,0xee,0x70,0xa4,0x69,0x01,0x30,0x87,0x9a,0xfc,0x09, + 0x7b,0x88,0xbf,0x8b,0x00,0xcb,0x6d,0x90,0xc2,0x89,0x18,0x09,0x0d,0x1c,0xc7,0x51, + 0x97,0x80,0x01,0xdc,0x71,0x87,0x23,0x4d,0x0b,0x80,0x39,0xd4,0xe4,0x4f,0xd8,0x43, + 0xfc,0xac,0x82,0x34,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x28,0xf8,0x08,0x4c,0x0b,0x22, + 0x09,0x06,0x1a,0x69,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87, + 0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e, + 0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20, + 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76, + 0x40,0x07,0x43,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86, + 0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20, + 0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28,0x40,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x60,0xc8,0xe3,0x00,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x90,0x67,0x02,0x02,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x4f, + 0x05,0x04,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x1e,0x0c,0x08,0x80, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x14,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, + 0x4a,0x60,0x04,0xa0,0x24,0x8a,0xa1,0x00,0x07,0x0a,0x10,0x30,0xa0,0x0c,0xca,0xa1, + 0x08,0xca,0xa3,0x10,0xa8,0x28,0x89,0x11,0x80,0x32,0x28,0x82,0x52,0x28,0x04,0xf2, + 0x66,0x00,0x28,0x9c,0x01,0x20,0x71,0x2c,0x8b,0x82,0x4e,0x90,0x0f,0x4e,0x90,0x0f, + 0x4e,0x90,0x0f,0x00,0x78,0x80,0x07,0x78,0x00,0x40,0x04,0x84,0x00,0xe0,0x1e,0xf9, + 0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0, + 0x1e,0xf9,0xe0,0x1e,0xf9,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x76,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd, + 0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66, + 0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x64,0x82,0x40, + 0x28,0x1b,0x84,0x81,0x98,0x20,0x10,0xcb,0x06,0x61,0x30,0x28,0xd8,0xcd,0x4d,0x10, + 0x08,0x66,0xc3,0x80,0x24,0xc4,0x04,0xc1,0xd3,0x08,0x4c,0x10,0x88,0x66,0x03,0x42, + 0x2c,0xcc,0x40,0x0c,0x0d,0x30,0x41,0x08,0x83,0x6d,0x82,0x40,0x38,0x1b,0x90,0xe1, + 0x61,0x86,0x61,0x80,0x80,0x0d,0x82,0x13,0x6d,0x20,0x00,0x40,0x02,0x26,0x08,0x62, + 0xc0,0x6d,0x08,0xa8,0x09,0x82,0x00,0xb0,0xb1,0x2b,0x93,0xa3,0x2b,0xc3,0x9b,0x5a, + 0x4b,0x73,0x73,0x2b,0x23,0x23,0x42,0x55,0x84,0x35,0xf4,0xf4,0x24,0x45,0x34,0x41, + 0x28,0xa8,0x09,0x42,0x51,0x6d,0x08,0x88,0x09,0x42,0x61,0x4d,0x10,0x88,0x67,0x82, + 0x40,0x40,0x1b,0x84,0xce,0xdb,0xb0,0x10,0x58,0xa6,0x6d,0xda,0xc0,0x11,0xda,0xb7, + 0x21,0x18,0x36,0x2c,0x03,0x96,0x69,0x61,0xa0,0x0d,0xdc,0xa0,0x7d,0x13,0x04,0x22, + 0xda,0x10,0x8c,0xc1,0x04,0xa1,0xb8,0x36,0x08,0x5d,0xb7,0x61,0x19,0x03,0x2c,0xd3, + 0xc8,0x40,0x1b,0xca,0x60,0x0c,0x34,0x33,0x98,0x20,0x14,0xd8,0x86,0xa0,0x9b,0x20, + 0x14,0xd9,0x04,0x81,0x90,0x36,0x08,0xdd,0x1a,0x6c,0x58,0x3a,0x0c,0x0d,0xb4,0x34, + 0xd0,0x06,0x35,0xe8,0x34,0x36,0x98,0x20,0x10,0xd3,0x86,0xc0,0x0d,0x36,0x2c,0x6e, + 0x80,0x65,0xda,0x1b,0x68,0x83,0x1a,0xb8,0x81,0xc6,0x06,0x1b,0x0a,0x30,0x10,0x83, + 0x33,0x68,0x03,0x38,0xd8,0xb0,0x10,0x58,0xa6,0x6d,0x65,0x30,0x70,0x84,0xf6,0x6d, + 0x58,0x06,0x2c,0xd3,0xc2,0xa0,0x0c,0x06,0x6e,0xd0,0xbe,0x0d,0xcb,0x18,0x60,0x99, + 0x46,0x06,0x65,0x30,0x94,0xc1,0x18,0x68,0x66,0xc0,0x65,0xca,0xea,0x0b,0xea,0x6d, + 0x2e,0x8d,0x2e,0xed,0xcd,0x6d,0xc3,0xd2,0xd5,0x41,0xc6,0x6d,0x6a,0x30,0xa8,0x41, + 0xa7,0xb1,0xc1,0x06,0x42,0x0e,0xe6,0x80,0x0e,0xec,0x60,0xc3,0x10,0x07,0x77,0x00, + 0x6c,0x28,0xac,0x0b,0x0f,0x26,0xa0,0x0a,0x1b,0x9b,0x5d,0x9b,0x4b,0x1a,0x59,0x99, + 0x1b,0xdd,0x94,0x20,0xa8,0x42,0x86,0xe7,0x62,0x57,0x26,0x37,0x97,0xf6,0xe6,0x36, + 0x25,0x20,0x9a,0x90,0xe1,0xb9,0xd8,0x85,0xb1,0xd9,0x95,0xc9,0x4d,0x09,0x8c,0x3a, + 0x64,0x78,0x2e,0x73,0x68,0x61,0x64,0x65,0x72,0x4d,0x6f,0x64,0x65,0x6c,0x53,0x82, + 0xa4,0x0c,0x19,0x9e,0x8b,0x5c,0xd9,0xdc,0x5b,0x9d,0xdc,0x58,0xd9,0xdc,0x94,0x40, + 0xaa,0x43,0x86,0xe7,0x62,0x97,0x56,0x76,0x97,0x44,0x36,0x45,0x17,0x46,0x57,0x36, + 0x25,0xa0,0xea,0x90,0xe1,0xb9,0x94,0xb9,0xd1,0xc9,0xe5,0x41,0xbd,0xa5,0xb9,0xd1, + 0xcd,0x4d,0x09,0xf0,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x4c,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87, + 0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a, + 0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x24,0x00,0x00,0x00,0x66,0xb0,0x0d,0x97,0xef,0x3c,0xbe,0x10, + 0x50,0x45,0x41,0x44,0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6,0x17,0xb7,0x6d,0x08, + 0xd2,0x70,0xf9,0xce,0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34,0xc3,0x42,0x18,0x81, + 0x33,0x5c,0xbe,0xf3,0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16,0x30,0x0d,0x97,0xef, + 0x3c,0xfe,0xe2,0x00,0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d,0x02,0xd3,0x70,0xf9, + 0xce,0xe3,0x2f,0x0e,0x30,0x88,0xcd,0x43,0x4d,0x3e,0x72,0xdb,0x36,0x50,0x0d,0x97, + 0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e,0x71,0xdb,0x76,0x20, + 0x0d,0x97,0xef,0x3c,0xfe,0x44,0x44,0x13,0x02,0x44,0x98,0x5f,0xdc,0xb6,0x15,0x3c, + 0xc3,0xe5,0x3b,0x8f,0x4f,0x35,0x40,0x84,0xf9,0xc5,0x6d,0x1b,0x40,0x53,0x11,0xd1, + 0x44,0x5c,0xac,0x82,0x34,0x0d,0x31,0x00,0x61,0x20,0x00,0x00,0xa3,0x04,0x00,0x00, + 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x44,0x14,0x57,0xd9, + 0xcd,0x00,0x94,0xc2,0x28,0x03,0xa0,0x24,0xa3,0x0c,0x80,0xa2,0x8c,0x32,0x00,0x4a, + 0x34,0xca,0xb0,0xb0,0x41,0x52,0x72,0xa3,0x0c,0x80,0xd2,0x06,0xa3,0x0c,0x80,0x19, + 0x24,0xa3,0x0c,0x40,0x18,0x24,0xa3,0x0c,0x80,0x32,0x8d,0x32,0x00,0x69,0x90,0x8c, + 0x32,0x00,0x6a,0x90,0x8c,0x32,0x00,0x49,0x52,0x10,0xa3,0x0c,0xc0,0x19,0x24,0xe5, + 0x56,0x32,0xa3,0x0c,0x00,0x1a,0x24,0xa3,0x0c,0xc0,0x1a,0x24,0x54,0x94,0x00,0x5d, + 0x23,0x00,0x00,0x00,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0xb5,0x01,0xe4,0xb1,0x01, + 0x1b,0x04,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0xb9,0x41,0xf4,0xb1,0x01,0x1b,0x08, + 0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63, + 0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24,0xa3, + 0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31, + 0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32, + 0x0a,0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11, + 0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24, + 0xa3,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x46,0x29,0xcc,0xc1,0x1c,0xf0,0x41,0x1c, + 0x9c,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xa6,0x40,0x07,0x74,0xd0,0x07,0x14, + 0x1a,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0x71,0x0a,0x75,0x50,0x07,0x7e,0x10,0x07, + 0x69,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x06,0x2a,0xd8,0x81,0x1d,0xfc,0xc1,0x1c, + 0xa8,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x20,0xa8,0x70,0x07,0x7b,0x00,0x0a,0x75, + 0xb0,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0xa4,0x02,0x1e,0xf0,0x41,0x28,0x60, + 0x6c,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x2a,0xe4,0x41,0x1f,0x88,0x42,0x1d, + 0xb4,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x20,0xab,0xa0,0x07,0x7e,0x30,0x0a,0x77, + 0xe0,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xb4,0xc2,0x1e,0xf4,0x01,0x29,0xe4, + 0xc1,0x1b,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xe1,0x0a,0x7c,0xe0,0x07,0xa5,0xc0, + 0xc1,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xaf,0xd0,0x07,0xa7,0x60,0x0a,0x7b, + 0x10,0x07,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xc0,0x82,0x1f,0xa0,0xc2,0x29,0x78, + 0x72,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x46,0x2c,0xfc,0x41,0x2a,0xa0,0xc2,0x1e, + 0xcc,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xb2,0x00,0x0a,0xa9,0x90,0x0a,0x7e, + 0x40,0x07,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xcc,0x42,0x28,0xa8,0x82,0x2a,0x84, + 0x41,0x1d,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0x41,0x0b,0xa2,0xb0,0x0a,0xab,0xe0, + 0x07,0x76,0x50,0x4c,0x29,0xdc,0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0xb1,0x90,0x07, + 0x62,0x10,0x8c,0x26,0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31, + 0x94,0x31,0x0b,0x3b,0x62,0x70,0x00,0x20,0x08,0x06,0xd3,0x2d,0xfc,0x01,0x1a,0x04, + 0xa3,0x09,0x01,0x30,0x9a,0x20,0x04,0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x66,0xe4, + 0x02,0x08,0x46,0x0c,0x0e,0x00,0x04,0xc1,0x60,0xea,0x85,0x52,0x70,0x83,0x60,0x34, + 0x21,0x00,0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0xa1,0x24,0x52,0xd8, + 0x11,0x83,0x03,0x00,0x41,0x30,0x98,0xc6,0x61,0x15,0xe8,0x20,0x18,0x4d,0x08,0x80, + 0xd1,0x04,0x21,0x18,0x4d,0x18,0x84,0xd1,0x04,0x62,0x30,0x6b,0x93,0xcf,0x88,0x01, + 0x02,0x80,0x20,0x18,0x54,0xe8,0x20,0x0b,0xd1,0x16,0x8c,0x18,0x20,0x00,0x08,0x82, + 0x41,0x95,0x0e,0xb3,0xc0,0x6c,0x81,0x05,0x07,0x74,0x2c,0xfb,0xe4,0x33,0x62,0x80, + 0x00,0x20,0x08,0x06,0x15,0x3b,0xd8,0x02,0xf5,0x05,0x23,0x06,0x08,0x00,0x82,0x60, + 0x50,0xb5,0xc3,0x2d,0x3c,0x5f,0x60,0x81,0x02,0x1d,0xe3,0xc6,0x40,0x3e,0x23,0x06, + 0x08,0x00,0x82,0x60,0x50,0xc1,0x83,0x2e,0x5c,0x63,0x10,0x8c,0x18,0x20,0x00,0x08, + 0x82,0x41,0x15,0x0f,0xbb,0x20,0x8d,0x41,0x60,0x41,0x03,0x1d,0xfb,0xce,0x40,0x3e, + 0x23,0x06,0x08,0x00,0x82,0x60,0x50,0xd1,0x83,0x2f,0x68,0x67,0x10,0x8c,0x18,0x20, + 0x00,0x08,0x82,0x41,0x55,0x0f,0xbf,0x50,0x9d,0x41,0x60,0x01,0x04,0x1d,0x6b,0xf0, + 0x40,0x3e,0xa6,0xe4,0x81,0x7c,0xec,0xd0,0x03,0xf9,0x18,0xb1,0x07,0xf2,0xa9,0x3b, + 0x88,0x87,0x1b,0x31,0x38,0x00,0x10,0x04,0x83,0xa9,0x1f,0xca,0xc1,0x15,0x82,0xd1, + 0x84,0x00,0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x86,0x32,0xfe,0x61, + 0x47,0x0c,0x0e,0x00,0x04,0xc1,0x60,0x1a,0x89,0x75,0xa0,0x85,0x60,0x34,0x21,0x00, + 0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0xc1,0x8c,0x92,0x00,0xc1,0x88, + 0xc1,0x01,0x80,0x20,0x18,0x4c,0x29,0x11,0x0f,0xba,0x10,0x8c,0x26,0x04,0xc0,0x68, + 0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x94,0x04,0x0f,0x3b,0x62,0x70,0x00, + 0x20,0x08,0x06,0xd3,0x4b,0xdc,0x03,0x38,0x04,0xa3,0x09,0x01,0x30,0x9a,0x20,0x04, + 0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x66,0x9d,0x82,0x7c,0x46,0x0c,0x10,0x00,0x04, + 0xc1,0xa0,0xa2,0x09,0x7f,0x88,0x4e,0x21,0x18,0x31,0x40,0x00,0x10,0x04,0x83,0xaa, + 0x26,0xfe,0x81,0x39,0x85,0xc0,0x82,0x03,0x3a,0x96,0xad,0x82,0x7c,0x46,0x0c,0x10, + 0x00,0x04,0xc1,0xa0,0xc2,0x09,0x91,0xa0,0x56,0x21,0x18,0x31,0x40,0x00,0x10,0x04, + 0x83,0x2a,0x27,0x46,0xe2,0x59,0x85,0xc0,0x02,0x05,0x3a,0xc6,0xbd,0x82,0x7c,0x46, + 0x0c,0x10,0x00,0x04,0xc1,0xa0,0xe2,0x09,0x93,0xb8,0x5e,0x21,0x18,0x31,0x40,0x00, + 0x10,0x04,0x83,0xaa,0x27,0x4e,0x42,0x7a,0x85,0xc0,0x82,0x06,0x3a,0xf6,0xcd,0x82, + 0x7c,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0x02,0x0b,0x95,0xd0,0x66,0x21,0x18,0x31, + 0x40,0x00,0x10,0x04,0x83,0x2a,0x2c,0x56,0xa2,0x9a,0x85,0xc0,0x02,0x08,0x3a,0xd6, + 0x8c,0x83,0x7c,0x4c,0x21,0x07,0xf9,0xd8,0x51,0x0e,0xf2,0x31,0xc2,0x1c,0xe4,0x63, + 0x04,0x1c,0xc0,0xc7,0x08,0x38,0x80,0x8f,0x11,0x70,0x00,0x1f,0x23,0xe0,0x00,0x3e, + 0x65,0x0e,0x61,0x71,0x23,0x06,0x07,0x00,0x82,0x60,0x30,0xb5,0x45,0x4d,0xf8,0x43, + 0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x50,0xc6, + 0x5b,0xec,0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0x73,0xb1,0x13,0x24,0x11,0x8c,0x26, + 0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x98,0x51,0x17,0x20, + 0x18,0x31,0x38,0x00,0x10,0x04,0x83,0x29,0x2f,0xc2,0x42,0x25,0x82,0xd1,0x84,0x00, + 0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x86,0x92,0xc0,0x62,0x47,0x0c, + 0x0e,0x00,0x04,0xc1,0x60,0xfa,0x8b,0xb3,0x80,0x89,0x60,0x34,0x21,0x00,0x46,0x13, + 0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0xc1,0xac,0x7b,0x90,0xcf,0x88,0x01,0x02, + 0x80,0x20,0x18,0x54,0xa4,0xe1,0x16,0xd1,0x3d,0x04,0x23,0x06,0x08,0x00,0x82,0x60, + 0x50,0x95,0xc6,0x5b,0x30,0xf7,0x10,0x58,0x70,0x40,0xc7,0xb2,0x7d,0x90,0xcf,0x88, + 0x01,0x02,0x80,0x20,0x18,0x54,0xa8,0x21,0x17,0xd4,0x3e,0x04,0x23,0x06,0x08,0x00, + 0x82,0x60,0x50,0xa5,0xc6,0x5c,0x3c,0xfb,0x10,0x58,0xa0,0x40,0xc7,0xb8,0x7f,0x90, + 0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xac,0x61,0x17,0xd7,0x3f,0x04,0x23,0x06, + 0x08,0x00,0x82,0x60,0x50,0xb5,0xc6,0x5d,0x48,0xff,0x10,0x58,0xd0,0x40,0xc7,0xbe, + 0x91,0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xb0,0xa1,0x17,0xda,0x48,0x04, + 0x23,0x06,0x08,0x00,0x82,0x60,0x50,0xc5,0xc6,0x5e,0x54,0x23,0x11,0x58,0x00,0x41, + 0xc7,0x1a,0x99,0x90,0x8f,0x29,0x33,0x21,0x1f,0x3b,0x68,0x42,0x3e,0x46,0xd4,0x84, + 0x7c,0x0c,0x0e,0x08,0xf8,0x18,0x1c,0x10,0xf0,0x31,0x38,0x20,0xe0,0x63,0x70,0x40, + 0xc0,0xa7,0x6a,0x22,0x36,0x6e,0xc4,0xe0,0x00,0x40,0x10,0x0c,0xa6,0xde,0x28,0x0d, + 0xb7,0x08,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06,0x61,0x34,0x81,0x18, + 0xca,0xf8,0x8d,0x1d,0x31,0x38,0x00,0x10,0x04,0x83,0x69,0x3c,0x56,0x83,0x2e,0x82, + 0xd1,0x84,0x00,0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x06,0x33,0xca, + 0x03,0x04,0x23,0x06,0x07,0x00,0x82,0x60,0x30,0xa5,0x47,0x6c,0xe8,0x45,0x30,0x9a, + 0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x50,0x12,0x6c,0xec, + 0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0xef,0x71,0x1b,0xa0,0x11,0x8c,0x26,0x04,0xc0, + 0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x98,0x75,0x16,0xf2,0x19,0x31, + 0x40,0x00,0x10,0x04,0x83,0x8a,0x3e,0x7c,0x23,0x3a,0x8b,0x60,0xc4,0x00,0x01,0x40, + 0x10,0x0c,0xaa,0xfa,0xf8,0x0d,0xe6,0x2c,0x02,0x0b,0x0e,0xe8,0x58,0xb6,0x16,0xf2, + 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x0a,0x3f,0xc4,0x83,0x5a,0x8b,0x60,0xc4,0x00, + 0x01,0x40,0x10,0x0c,0xaa,0xfc,0x18,0x8f,0x67,0x2d,0x02,0x0b,0x14,0xe8,0x18,0xf7, + 0x16,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x8a,0x3f,0xcc,0xe3,0x7a,0x8b,0x60, + 0xc4,0x00,0x01,0x40,0x10,0x0c,0xaa,0xfe,0x38,0x0f,0xe9,0x2d,0x02,0x0b,0x1a,0xe8, + 0xd8,0x37,0x17,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x0a,0x44,0xd4,0x43,0x9b, + 0x8b,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xaa,0x10,0x59,0x8f,0x6a,0x2e,0x02,0x0b, + 0x20,0xe8,0x58,0x13,0x1a,0xf2,0x31,0x45,0x34,0xe4,0x63,0xc7,0x68,0xc8,0xc7,0x08, + 0xd2,0x90,0x8f,0xc1,0x01,0x01,0x1f,0x83,0x03,0x02,0x3e,0x06,0x07,0x04,0x7c,0x0c, + 0x0e,0x08,0xf8,0xec,0x71,0xd8,0x0d,0x14,0x41,0x91,0x61,0x03,0x22,0xd8,0x8b,0x01, + 0xd8,0xe3,0xc0,0x1b,0x29,0xa2,0x22,0xc3,0x06,0x44,0xb0,0x17,0x03,0xb0,0xc7,0xa1, + 0x37,0x54,0x64,0x44,0x86,0x0d,0x88,0x60,0x2f,0x06,0x60,0x8f,0x83,0x6f,0xac,0x88, + 0x89,0x0c,0x1b,0x10,0xc1,0x5e,0x0c,0xc0,0x1e,0x87,0xdf,0x60,0x11,0xfa,0xa0,0x60, + 0x18,0xc3,0x06,0x84,0xa1,0x17,0x03,0x30,0x6c,0x40,0x14,0x79,0x31,0x00,0xc3,0x06, + 0x04,0x81,0x17,0x03,0x30,0x6c,0x40,0x0c,0x77,0x31,0x00,0x24,0x0c,0x63,0xd8,0x80, + 0x38,0xea,0x62,0x00,0x86,0x0d,0x08,0x83,0x2e,0x06,0x60,0xd8,0x80,0x28,0xe6,0x62, + 0x00,0x86,0x0d,0x08,0x42,0x2e,0x06,0x80,0x86,0x61,0xec,0x71,0x10,0x0f,0x18,0x81, + 0x91,0x61,0x03,0x22,0x18,0x8d,0x01,0xd8,0xe3,0x30,0x1e,0x31,0x22,0x23,0xc3,0x06, + 0x44,0x30,0x1a,0x03,0xb0,0xc7,0x81,0x3c,0x64,0x64,0x45,0x86,0x0d,0x88,0x60,0x34, + 0x06,0x60,0x8f,0x43,0x79,0xcc,0x88,0x8b,0x0c,0x1b,0x10,0xc1,0x68,0x0c,0xc0,0x1e, + 0x07,0xf3,0xa0,0x91,0xfe,0xa0,0x60,0x18,0xc3,0x06,0x84,0x21,0x1a,0x03,0x30,0x6c, + 0x40,0x14,0xa1,0x31,0x00,0xc3,0x06,0x04,0x01,0x1a,0x03,0x30,0x6c,0x40,0x0c,0x7f, + 0x31,0x00,0x24,0x0c,0x63,0xd8,0x80,0x38,0xfa,0x62,0x00,0x86,0x0d,0x08,0x83,0x2f, + 0x06,0x60,0xd8,0x80,0x28,0xf6,0x62,0x00,0x86,0x0d,0x08,0x42,0x2f,0x06,0x80,0x86, + 0x61,0xec,0x71,0x48,0x0f,0x1c,0xc1,0x91,0x61,0x03,0x22,0x58,0x8d,0x01,0xd8,0xe3, + 0xa0,0x1e,0x39,0xa2,0x23,0xc3,0x06,0x44,0xb0,0x1a,0x03,0xb0,0xc7,0x61,0x3d,0x74, + 0x64,0x46,0x86,0x0d,0x88,0x60,0x35,0x06,0x60,0x8f,0x03,0x7b,0xec,0x88,0x8d,0x0c, + 0x1b,0x10,0xc1,0x6a,0x0c,0xc0,0x1e,0x87,0xf6,0xe0,0x91,0x11,0xa1,0x60,0x18,0xc3, + 0x06,0x84,0xa1,0x1a,0x03,0x30,0x6c,0x40,0x14,0xa9,0x31,0x00,0xc3,0x06,0x04,0x81, + 0x1a,0x03,0x30,0x6c,0x40,0x0c,0xa7,0x31,0x00,0x24,0x0c,0x63,0xd8,0x80,0x38,0x4a, + 0x63,0x00,0x86,0x0d,0x08,0x83,0x34,0x06,0x60,0xd8,0x80,0x28,0x46,0x63,0x00,0x86, + 0x0d,0x08,0x42,0x34,0x06,0x80,0x86,0x61,0xd8,0xa4,0x1b,0xf2,0x19,0x31,0x40,0x00, + 0x10,0x04,0x83,0x8a,0x4c,0x5c,0x64,0xd2,0x8d,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c, + 0xaa,0x32,0x79,0x91,0x49,0x37,0x02,0x73,0x7a,0x43,0x3e,0x23,0x06,0x08,0x00,0x82, + 0x60,0x50,0x9d,0x49,0x8c,0x38,0xbd,0x11,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0x85, + 0x26,0x32,0xe2,0xf4,0x46,0x60,0x09,0x78,0xc8,0x67,0xc4,0x00,0x01,0x40,0x10,0x0c, + 0x2a,0x35,0xa1,0x91,0x04,0x3c,0x82,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0xd6,0xa4, + 0x46,0x12,0xf0,0x08,0xec,0x68,0x0f,0xf9,0x58,0xe1,0x1e,0xf2,0xb1,0xe1,0x3d,0xe4, + 0xb3,0xc7,0x81,0x3f,0xd8,0x84,0x4d,0x86,0x0d,0x88,0x60,0x2e,0x06,0x60,0x8f,0x43, + 0x7f,0xb4,0x89,0x9b,0x0c,0x1b,0x10,0xc1,0x5c,0x0c,0xc0,0x1e,0x07,0xff,0x70,0x93, + 0x33,0x19,0x36,0x20,0x82,0xb9,0x18,0x80,0x3d,0x0e,0xff,0xf1,0x26,0x6a,0x32,0x6c, + 0x40,0x04,0x73,0x31,0x00,0x7b,0x1c,0x40,0x04,0x4e,0x70,0x84,0x82,0x61,0x0c,0x1b, + 0x10,0x86,0x5c,0x0c,0xc0,0xb0,0x01,0x51,0xc4,0xc5,0x00,0x0c,0x1b,0x10,0x04,0x5c, + 0x0c,0xc0,0xb0,0x01,0x31,0xbc,0xc5,0x00,0x90,0x30,0x8c,0x61,0x03,0xe2,0x68,0x8b, + 0x01,0x18,0x36,0x20,0x0c,0xb6,0x18,0x80,0x61,0x03,0xa2,0x58,0x8b,0x01,0x18,0x36, + 0x20,0x08,0xb5,0x18,0x00,0x1a,0x86,0xb1,0xc7,0x61,0x44,0xe8,0x84,0x4e,0x86,0x0d, + 0x88,0x60,0x2f,0x06,0x60,0x8f,0x03,0x89,0xd4,0x89,0x9d,0x0c,0x1b,0x10,0xc1,0x5e, + 0x0c,0xc0,0x1e,0x87,0x12,0xb1,0x93,0x37,0x19,0x36,0x20,0x82,0xbd,0x18,0x80,0x3d, + 0x0e,0x26,0x72,0x27,0x72,0x32,0x6c,0x40,0x04,0x7b,0x31,0x00,0x7b,0x1c,0x4e,0x04, + 0x4f,0xc2,0x84,0x82,0x61,0x0c,0x1b,0x10,0x86,0x5e,0x0c,0xc0,0xb0,0x01,0x51,0xe4, + 0xc5,0x00,0x0c,0x1b,0x10,0x04,0x5e,0x0c,0xc0,0xb0,0x01,0x31,0xdc,0xc5,0x00,0x90, + 0x30,0x8c,0x61,0x03,0xe2,0xa8,0x8b,0x01,0x18,0x36,0x20,0x0c,0xba,0x18,0x80,0x61, + 0x03,0xa2,0x98,0x8b,0x01,0x18,0x36,0x20,0x08,0xb9,0x18,0x00,0x1a,0x86,0xb1,0xc7, + 0x41,0x45,0xf8,0x84,0x4f,0x86,0x0d,0x88,0x60,0x34,0x06,0x60,0x8f,0xc3,0x8a,0xf4, + 0x89,0x9f,0x0c,0x1b,0x10,0xc1,0x68,0x0c,0xc0,0x1e,0x07,0x16,0xf1,0x93,0x3b,0x19, + 0x36,0x20,0x82,0xd1,0x18,0x80,0x3d,0x0e,0x2d,0xf2,0x27,0x7a,0x32,0x6c,0x40,0x04, + 0xa3,0x31,0x00,0x7b,0x1c,0x5c,0x04,0x54,0xce,0x84,0x82,0x61,0x0c,0x1b,0x10,0x86, + 0x68,0x0c,0xc0,0xb0,0x01,0x51,0x84,0xc6,0x00,0x0c,0x1b,0x10,0x04,0x68,0x0c,0xc0, + 0xb0,0x01,0x31,0xfc,0xc5,0x00,0x90,0x30,0x8c,0x61,0x03,0xe2,0xe8,0x8b,0x01,0x18, + 0x36,0x20,0x0c,0xbe,0x18,0x80,0x61,0x03,0xa2,0xd8,0x8b,0x01,0x18,0x36,0x20,0x08, + 0xbd,0x18,0x00,0x1a,0x86,0x61,0x93,0x7f,0xc8,0x67,0xc4,0x00,0x01,0x40,0x10,0x0c, + 0x2a,0x54,0x91,0x93,0xc9,0x3f,0x82,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0x52,0x65, + 0x4e,0x26,0xff,0x08,0xcc,0x09,0x11,0xf9,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0xb5, + 0x2a,0x75,0xe2,0x84,0x48,0x30,0x62,0x80,0x00,0x20,0x08,0x06,0x15,0xab,0xd8,0x89, + 0x13,0x22,0x81,0x25,0x24,0x22,0x9f,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0x5c,0x05, + 0x4f,0x12,0x12,0x09,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0x7a,0x95,0x3c,0x49,0x48, + 0x24,0xb0,0x03,0x46,0xe4,0x63,0x45,0x8c,0xc8,0xc7,0x06,0x19,0x91,0x8f,0x0d,0x67, + 0x00,0x1f,0x1b,0xce,0x00,0x3e,0x36,0x9c,0x01,0x7c,0xf6,0x38,0x80,0xc9,0xac,0xcc, + 0xca,0xb0,0x01,0x11,0xa8,0xc5,0x00,0xec,0x71,0x08,0x13,0x5a,0xa9,0x95,0x61,0x03, + 0x22,0x50,0x8b,0x01,0xd8,0xe3,0x20,0x26,0xb5,0xe2,0x2a,0xc3,0x06,0x44,0xa0,0x16, + 0x03,0xb0,0xc7,0x61,0x4c,0x6c,0x25,0x56,0x86,0x0d,0x88,0x40,0x2d,0x06,0x60,0x8f, + 0x03,0x99,0xdc,0xca,0x9f,0x50,0x30,0x8c,0x61,0x03,0xc2,0x48,0x8b,0x01,0x18,0x36, + 0x20,0x0a,0xb4,0x18,0x80,0x61,0x03,0x82,0x38,0x8b,0x01,0x18,0x36,0x20,0x06,0xb3, + 0x18,0x00,0x12,0x86,0x31,0x6c,0x40,0x1c,0x64,0x31,0x00,0xc3,0x06,0x84,0x31,0x16, + 0x03,0x30,0x6c,0x40,0x14,0x62,0x31,0x00,0xc3,0x06,0x04,0x11,0x16,0x03,0x40,0xc3, + 0x30,0xf6,0x38,0x9c,0xc9,0xae,0xec,0xca,0xb0,0x01,0x11,0xc8,0xc5,0x00,0xec,0x71, + 0x40,0x13,0x5e,0xe9,0x95,0x61,0x03,0x22,0x90,0x8b,0x01,0xd8,0xe3,0x90,0x26,0xbd, + 0x62,0x2b,0xc3,0x06,0x44,0x20,0x17,0x03,0xb0,0xc7,0x41,0x4d,0x7c,0x25,0x57,0x86, + 0x0d,0x88,0x40,0x2e,0x06,0x60,0x8f,0xc3,0x9a,0xfc,0x0a,0xaa,0x50,0x30,0x8c,0x61, + 0x03,0xc2,0x88,0x8b,0x01,0x18,0x36,0x20,0x0a,0xb8,0x18,0x80,0x61,0x03,0x82,0x78, + 0x8b,0x01,0x18,0x36,0x20,0x06,0xb7,0x18,0x00,0x12,0x86,0x31,0x6c,0x40,0x1c,0x6c, + 0x31,0x00,0xc3,0x06,0x84,0xb1,0x16,0x03,0x30,0x6c,0x40,0x14,0x6a,0x31,0x00,0xc3, + 0x06,0x04,0x91,0x16,0x03,0x40,0xc3,0x30,0xf6,0x38,0xb8,0xc9,0xb8,0x8c,0xcb,0xb0, + 0x01,0x11,0xe8,0xc5,0x00,0xec,0x71,0x78,0x13,0x72,0x29,0x97,0x61,0x03,0x22,0xd0, + 0x8b,0x01,0xd8,0xe3,0x00,0x27,0xe5,0xe2,0x2b,0xc3,0x06,0x44,0xa0,0x17,0x03,0xb0, + 0xc7,0x21,0x4e,0xcc,0x25,0x5c,0x86,0x0d,0x88,0x40,0x2f,0x06,0x60,0x8f,0x83,0x9c, + 0x9c,0x8b,0xab,0x50,0x30,0x8c,0x61,0x03,0xc2,0xc8,0x8b,0x01,0x18,0x36,0x20,0x0a, + 0xbc,0x18,0x80,0x61,0x03,0x82,0xb8,0x8b,0x01,0x18,0x36,0x20,0x06,0xbb,0x18,0x00, + 0x12,0x86,0x31,0x6c,0x40,0x1c,0x74,0x31,0x00,0xc3,0x06,0x84,0x31,0x17,0x03,0x30, + 0x6c,0x40,0x14,0x72,0x31,0x00,0xc3,0x06,0x04,0x11,0x17,0x03,0x40,0xc3,0x30,0x6c, + 0x2a,0x13,0xf9,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0xf5,0x2e,0xb9,0x32,0x95,0x49, + 0x30,0x62,0x80,0x00,0x20,0x08,0x06,0x15,0xbc,0xe8,0xca,0x54,0x26,0x81,0x39,0x68, + 0x22,0x9f,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0xe4,0x85,0x57,0x1c,0x34,0x09,0x46, + 0x0c,0x10,0x00,0x04,0xc1,0xa0,0x9a,0x97,0x5e,0x71,0xd0,0x24,0xb0,0x64,0x4d,0xe4, + 0x33,0x62,0x80,0x00,0x20,0x08,0x06,0x55,0xbd,0xfc,0x4a,0xb2,0x26,0xc1,0x88,0x01, + 0x02,0x80,0x20,0x18,0x54,0xf6,0x02,0x2e,0xc9,0x9a,0x04,0x76,0xd8,0x89,0x7c,0xac, + 0xb8,0x13,0xf9,0xd8,0x80,0x27,0xf2,0xb1,0x33,0x18,0xe0,0x63,0x67,0x30,0xc0,0xc7, + 0xce,0x60,0x80,0xcf,0x1e,0x07,0x52,0xd1,0x17,0x7d,0x19,0x36,0x20,0x82,0xb0,0x18, + 0x80,0x3d,0x0e,0xa5,0xb2,0x2f,0xfc,0x32,0x6c,0x40,0x04,0x61,0x31,0x00,0x7b,0x1c, + 0x4c,0x85,0x5f,0xea,0x65,0xd8,0x80,0x08,0xc2,0x62,0x00,0xf6,0x38,0x9c,0x4a,0xbf, + 0xe0,0xcb,0xb0,0x01,0x11,0x84,0xc5,0x00,0xec,0x71,0x40,0x15,0x7f,0x31,0x17,0x0a, + 0x86,0x31,0x6c,0x40,0x18,0x60,0x31,0x00,0xc3,0x06,0x44,0xf1,0x13,0x03,0x30,0x6c, + 0x40,0x10,0x3e,0x31,0x00,0xc3,0x06,0xc4,0xd0,0x13,0x03,0x40,0xc2,0x30,0x86,0x0d, + 0x88,0x63,0x27,0x06,0x60,0xd8,0x80,0x30,0x74,0x62,0x00,0x86,0x0d,0x88,0x22,0x27, + 0x06,0x60,0xd8,0x80,0x20,0x70,0x62,0x00,0x68,0x18,0xc6,0x1e,0x87,0x55,0x11,0x19, + 0x91,0x19,0x36,0x20,0x82,0xb4,0x18,0x80,0x3d,0x0e,0xac,0x32,0x32,0x24,0x33,0x6c, + 0x40,0x04,0x69,0x31,0x00,0x7b,0x1c,0x5a,0x85,0x64,0xfa,0x65,0xd8,0x80,0x08,0xd2, + 0x62,0x00,0xf6,0x38,0xb8,0x4a,0xc9,0x80,0xcc,0xb0,0x01,0x11,0xa4,0xc5,0x00,0xec, + 0x71,0x78,0x15,0x93,0x79,0x17,0x0a,0x86,0x31,0x6c,0x40,0x18,0x68,0x31,0x00,0xc3, + 0x06,0x44,0x71,0x16,0x03,0x30,0x6c,0x40,0x10,0x66,0x31,0x00,0xc3,0x06,0xc4,0x50, + 0x16,0x03,0x40,0xc2,0x30,0x86,0x0d,0x88,0x63,0x2c,0x06,0x60,0xd8,0x80,0x30,0xc4, + 0x62,0x00,0x86,0x0d,0x88,0x22,0x2c,0x06,0x60,0xd8,0x80,0x20,0xc0,0x62,0x00,0x68, + 0x18,0xc6,0x1e,0x07,0x59,0x51,0x19,0x95,0x19,0x36,0x20,0x82,0xb8,0x18,0x80,0x3d, + 0x0e,0xb3,0xb2,0x32,0x2c,0x33,0x6c,0x40,0x04,0x71,0x31,0x00,0x7b,0x1c,0x68,0x85, + 0x65,0x4a,0x66,0xd8,0x80,0x08,0xe2,0x62,0x00,0xf6,0x38,0xd4,0x4a,0xcb,0xa0,0xcc, + 0xb0,0x01,0x11,0xc4,0xc5,0x00,0xec,0x71,0xb0,0x15,0x97,0xa9,0x17,0x0a,0x86,0x31, + 0x6c,0x40,0x18,0x70,0x31,0x00,0xc3,0x06,0x44,0xf1,0x16,0x03,0x30,0x6c,0x40,0x10, + 0x6e,0x31,0x00,0xc3,0x06,0xc4,0xd0,0x16,0x03,0x40,0xc2,0x30,0x86,0x0d,0x88,0x63, + 0x2d,0x06,0x60,0xd8,0x80,0x30,0xd4,0x62,0x00,0x86,0x0d,0x88,0x22,0x2d,0x06,0x60, + 0xd8,0x80,0x20,0xd0,0x62,0x00,0x68,0x18,0x86,0x4d,0xac,0x22,0x9f,0x11,0x03,0x04, + 0x00,0x41,0x30,0xa8,0x6c,0x06,0x64,0x26,0x56,0x09,0x46,0x0c,0x10,0x00,0x04,0xc1, + 0xa0,0xba,0x99,0x90,0x99,0x58,0x25,0x30,0xe7,0x55,0xe4,0x33,0x62,0x80,0x00,0x20, + 0x08,0x06,0x55,0xce,0x8c,0x8c,0xf3,0x2a,0xc1,0x88,0x01,0x02,0x80,0x20,0x18,0x54, + 0x3a,0x43,0x32,0xce,0xab,0x04,0x96,0xc8,0x8a,0x7c,0x46,0x0c,0x10,0x00,0x04,0xc1, + 0xa0,0xe2,0x19,0x93,0x49,0x64,0x25,0x18,0x31,0x40,0x00,0x10,0x04,0x83,0xaa,0x67, + 0x4e,0x26,0x91,0x95,0xc0,0x0e,0x5e,0x91,0x8f,0x15,0xbd,0x22,0x1f,0x1b,0x7c,0x45, + 0x3e,0x76,0x06,0x03,0x7c,0xec,0x0c,0x06,0xf8,0xd8,0x19,0x0c,0xf0,0x19,0x31,0x38, + 0x00,0x10,0x04,0x83,0xa9,0x6c,0x5a,0xa6,0x5e,0x4a,0x66,0x34,0x21,0x00,0x46,0x13, + 0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0x61,0x8f,0x43,0xbb,0x98,0x8d,0xd9,0x0c, + 0x1b,0x10,0x41,0x31,0x00,0x7b,0x1c,0xdc,0xe5,0x6c,0xd0,0x66,0xd8,0x80,0x08,0x8a, + 0x01,0xd8,0xe3,0xf0,0x2e,0x68,0x13,0x36,0xc3,0x06,0x44,0x50,0x0c,0xc0,0x1e,0x07, + 0x78,0x49,0x1b,0xb2,0x19,0x36,0x20,0x82,0x62,0x00,0xf6,0x38,0xc4,0x8b,0xda,0xc8, + 0x0c,0x05,0xc3,0x18,0x31,0x38,0x00,0x10,0x04,0x83,0x09,0x6e,0x70,0x06,0x64,0x5e, + 0x66,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0x61,0xd8, + 0x80,0x58,0x88,0x01,0x18,0x36,0x20,0x94,0x61,0x00,0x86,0x0d,0x88,0x44,0x18,0x80, + 0x61,0x03,0x02,0x09,0x06,0x80,0x8e,0x61,0x8c,0x18,0x1c,0x00,0x08,0x82,0xc1,0x64, + 0x37,0x3e,0x63,0x32,0x2e,0x33,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08, + 0xa3,0x09,0xc4,0x30,0x6c,0x40,0x44,0xc4,0x00,0x0c,0x1b,0x10,0xd0,0x30,0x00,0xc3, + 0x06,0xc4,0x23,0x0c,0xc0,0xb0,0x01,0xe1,0x04,0x03,0x40,0xcd,0x30,0xf6,0x38,0xf8, + 0x0b,0xde,0xe0,0xcd,0xb0,0x01,0x11,0x5c,0x03,0xb0,0xc7,0xe1,0x5f,0xf2,0x46,0x6f, + 0x86,0x0d,0x88,0xe0,0x1a,0x80,0x3d,0x0e,0x20,0xa3,0x37,0x73,0x33,0x6c,0x40,0x04, + 0xd7,0x00,0xec,0x71,0x08,0x99,0xbd,0xb1,0x9b,0x61,0x03,0x22,0xb8,0x06,0x60,0x8f, + 0x83,0xc8,0xf0,0x4d,0xd9,0x50,0x30,0x8c,0x61,0x03,0xc2,0x88,0x06,0x60,0xd8,0x80, + 0x28,0xa0,0x01,0x18,0x36,0x20,0x88,0x67,0x00,0x86,0x0d,0x88,0xc1,0x19,0x00,0x12, + 0x86,0x31,0x6c,0x40,0x1c,0xcc,0x00,0x0c,0x1b,0x10,0xc6,0x32,0x00,0xc3,0x06,0x44, + 0xa1,0x0c,0xc0,0xb0,0x01,0x41,0x24,0x03,0x40,0xc3,0x30,0xf6,0x38,0x94,0x0c,0xe8, + 0x80,0xce,0xb0,0x01,0x11,0x7c,0x03,0xb0,0xc7,0xc1,0x64,0x42,0x47,0x74,0x86,0x0d, + 0x88,0xe0,0x1b,0x80,0x3d,0x0e,0x27,0x23,0x3a,0x7b,0x33,0x6c,0x40,0x04,0xdf,0x00, + 0xec,0x71,0x40,0x99,0xd1,0xf1,0x9b,0x61,0x03,0x22,0xf8,0x06,0x60,0x8f,0x43,0xca, + 0x90,0xce,0xda,0x50,0x30,0x8c,0x61,0x03,0xc2,0xc8,0x06,0x60,0xd8,0x80,0x28,0xb0, + 0x01,0x18,0x36,0x20,0x88,0x6b,0x00,0x86,0x0d,0x88,0xc1,0x1a,0x00,0x12,0x86,0x31, + 0x6c,0x40,0x1c,0xd4,0x00,0x0c,0x1b,0x10,0xc6,0x34,0x00,0xc3,0x06,0x44,0x21,0x0d, + 0xc0,0xb0,0x01,0x41,0x44,0x03,0x40,0xc3,0x30,0xac,0x53,0x03,0xf9,0x8c,0x18,0x20, + 0x00,0x08,0x82,0x41,0xc5,0x3a,0x76,0x83,0xa9,0x41,0x30,0x62,0x80,0x00,0x20,0x08, + 0x06,0x55,0xeb,0xdc,0xcd,0xa4,0x06,0x81,0x39,0x6d,0x20,0x9f,0x11,0x03,0x04,0x00, + 0x41,0x30,0xa8,0x5e,0x27,0x6f,0x9c,0x36,0x08,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0, + 0x82,0x1d,0xbd,0x71,0xda,0x20,0xb0,0x04,0x0e,0xe4,0x33,0x62,0x80,0x00,0x20,0x08, + 0x06,0x95,0xec,0xf0,0x4d,0x02,0x07,0xc1,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xb3, + 0xd3,0x37,0x09,0x1c,0x04,0x23,0x06,0x0b,0x00,0x82,0x60,0xc0,0xdc,0x0e,0xdb,0x1c, + 0x44,0x70,0x10,0xc1,0x88,0x81,0x01,0x80,0x20,0x18,0x38,0xb7,0xc3,0x36,0x81,0x05, + 0x89,0x7c,0x4c,0x38,0xe4,0x63,0x43,0x21,0x9f,0x11,0x83,0x03,0x00,0x41,0x30,0x98, + 0x74,0x47,0x74,0xd4,0xc6,0x76,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06, + 0x61,0x34,0x81,0x18,0x46,0x0c,0x0e,0x00,0x04,0xc1,0x60,0xfa,0x9d,0xd3,0x79,0x1b, + 0xd2,0x19,0x4d,0x08,0x80,0xd1,0x04,0x21,0x18,0x4d,0x18,0x84,0xd1,0x04,0x62,0x18, + 0x31,0x38,0x00,0x10,0x04,0x83,0x89,0x7c,0x58,0x87,0x6e,0x44,0x67,0x34,0x21,0x00, + 0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0x61,0xc4,0xe0,0x00,0x40,0x10, + 0x0c,0xa6,0xf4,0x89,0x9d,0xbc,0xf1,0x9b,0xd1,0x84,0x00,0x18,0x4d,0x10,0x82,0xd1, + 0x84,0x41,0x18,0x4d,0x20,0x06,0x9b,0xcc,0x43,0x3e,0x23,0x06,0x08,0x00,0x82,0x60, + 0x50,0xb9,0x0f,0xee,0x3c,0xe6,0x11,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0xf5,0x3e, + 0xb9,0xb3,0x98,0x47,0x30,0x62,0x80,0x00,0x20,0x08,0x06,0x15,0xfc,0xe8,0xce,0x61, + 0x1e,0x81,0x59,0xea,0x21,0x9f,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0xe4,0x87,0x77, + 0x24,0xf5,0x08,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0x9a,0x9f,0xde,0x71,0xd4,0x23, + 0x18,0x31,0x40,0x00,0x10,0x04,0x83,0x8a,0x7e,0x7c,0x47,0x51,0x8f,0xc0,0x32,0xf7, + 0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xf6,0x03,0x3e,0x95,0x7b,0x04,0x23, + 0x06,0x08,0x00,0x82,0x60,0x50,0xdd,0x4f,0xf8,0x44,0xee,0x11,0x8c,0x18,0x20,0x00, + 0x08,0x82,0x41,0x85,0x3f,0xe2,0xd3,0xb8,0x47,0x60,0x9c,0x7c,0xc8,0x67,0xc4,0x00, + 0x01,0x40,0x10,0x0c,0x2a,0xfd,0x21,0x1f,0x4c,0x3e,0x82,0x11,0x03,0x04,0x00,0x41, + 0x30,0xa8,0xf6,0xa7,0x7c,0x28,0xf9,0x08,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0xe2, + 0x1f,0xf3,0x81,0xe4,0x23,0x18,0x31,0x38,0x00,0x10,0x04,0x83,0xc9,0x7f,0xcc,0xc7, + 0x75,0x76,0x67,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x0c,0x0e, + 0x00,0x04,0xc1,0x60,0x12,0x21,0xf5,0x91,0x1d,0xdf,0x19,0x4d,0x08,0x80,0xd1,0x04, + 0x21,0x18,0x4d,0x18,0x84,0x11,0x83,0x03,0x00,0x41,0x30,0x98,0x4c,0xc8,0x7d,0x6c, + 0x27,0x7c,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06,0x61,0xc4,0xe0,0x00, + 0x40,0x10,0x0c,0x26,0x15,0x92,0x1f,0xdd,0xf1,0x9d,0xd1,0x84,0x00,0x18,0x4d,0x10, + 0x82,0xd1,0x84,0x41,0xb0,0xc7,0x44,0xe4,0x33,0x62,0x80,0x00,0x20,0x08,0x06,0x95, + 0x0b,0xe1,0x0f,0x63,0x22,0xc1,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0x2f,0x94,0x3f, + 0x89,0x89,0x04,0x23,0x06,0x08,0x00,0x82,0x60,0x50,0xc1,0x90,0xfe,0x18,0x26,0x12, + 0x98,0xa4,0x22,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x4a,0x86,0xf8,0xe7,0x51, + 0x91,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xaa,0x19,0xea,0x1f,0x46,0x45,0x82,0x11, + 0x03,0x04,0x00,0x41,0x30,0xa8,0x68,0xc8,0x7f,0x12,0x15,0x09,0xac,0x72,0x11,0xf9, + 0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0x65,0x43,0x20,0x24,0xb9,0x48,0x30,0x62,0x80, + 0x00,0x20,0x08,0x06,0xd5,0x0d,0x85,0xd0,0xe3,0x22,0xc1,0x88,0x01,0x02,0x80,0x20, + 0x18,0x54,0x38,0x24,0x42,0x8c,0x8b,0x04,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4, + 0x10,0x08,0xd9,0x90,0x0d,0xad,0x50,0x32,0x62,0x90,0x00,0x20,0x08,0x06,0x4a,0x0f, + 0x81,0x90,0x0d,0xd9,0x50,0xfb,0x14,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4,0x10, + 0x08,0xd9,0x90,0x0d,0xa5,0x50,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x4a,0x0f,0x81, + 0xd0,0x0d,0xd9,0xd0,0x0a,0x8d,0xc2,0x88,0x41,0x02,0x80,0x20,0x18,0x28,0x3d,0x04, + 0x42,0x37,0x64,0x43,0xed,0x23,0x0a,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4,0x10, + 0x08,0xdd,0x90,0x0d,0xa5,0x50,0x28,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xd2,0x43, + 0x20,0xf4,0x42,0x36,0xb4,0x42,0xb2,0x33,0x62,0x90,0x00,0x20,0x08,0x06,0x4a,0x0f, + 0x81,0xd0,0x0b,0xd9,0x50,0xfb,0xc4,0xce,0x88,0x41,0x02,0x80,0x20,0x18,0x28,0x3d, + 0x04,0x42,0x31,0x64,0x43,0x2b,0x94,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4, + 0x10,0x08,0xc5,0x90,0x0d,0xb5,0x4f,0x19,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xd2, + 0x43,0x20,0x14,0x43,0x36,0x94,0x42,0x61,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x4a, + 0x0f,0x81,0x50,0x0c,0xd9,0x90,0x0a,0x75,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static const unsigned char _vertexSkinnedMSL[] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, + 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, + 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, + 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, + 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, + 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, + 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, + 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72,0x74, + 0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x75, + 0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x75,0x73,0x65, + 0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28, + 0x6c,0x6f,0x63,0x6e,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72,0x74,0x65,0x78, + 0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, + 0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x32,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, + 0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x69,0x6e,0x74,0x34, + 0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x33,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x33,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69, + 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x20, + 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x34,0x29,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x76,0x65,0x72, + 0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x6f,0x75,0x74,0x20,0x76, + 0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x28,0x76,0x65,0x72, + 0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x69,0x6e,0x20,0x69,0x6e, + 0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63, + 0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x44,0x72,0x61, + 0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x26,0x20,0x44,0x72,0x61,0x77,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72, + 0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20, + 0x74,0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x26,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20, + 0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x31,0x29,0x5d,0x5d,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e, + 0x65,0x64,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x5f,0x36,0x35,0x20, + 0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x2c,0x20,0x31,0x2e, + 0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x5f, + 0x31,0x30,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, + 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x33,0x2e,0x78,0x5d,0x20,0x2a,0x20,0x5f,0x36,0x35,0x29,0x20,0x2a,0x20,0x69,0x6e, + 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x34,0x2e,0x78,0x29,0x20,0x2b,0x20,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x79,0x5d,0x20,0x2a,0x20,0x5f,0x36,0x35,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34, + 0x2e,0x79,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x7a,0x5d,0x20,0x2a,0x20,0x5f,0x36,0x35,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34, + 0x2e,0x7a,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x77,0x5d,0x20,0x2a,0x20,0x5f,0x36,0x35,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34, + 0x2e,0x77,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20, + 0x5f,0x31,0x35,0x37,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72, + 0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28,0x28,0x28,0x28,0x28,0x28,0x69,0x6e,0x2e,0x69, + 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20, + 0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74, + 0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a, + 0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x30,0x5d,0x5b, + 0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, + 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x31, + 0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, + 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d, + 0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69, + 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e, + 0x78,0x5d,0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, + 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d, + 0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69, + 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e, + 0x78,0x5d,0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, + 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x33,0x2e,0x78,0x5d,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e, + 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b, + 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, + 0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79, + 0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, + 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x33,0x2e,0x78,0x5d,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e, + 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b, + 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, + 0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b, + 0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74, + 0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20, + 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69, + 0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, + 0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x33,0x5d,0x5b,0x32,0x5d, + 0x29,0x2e,0x78,0x79,0x7a,0x29,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f, + 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x2e,0x78,0x29, + 0x20,0x2b,0x20,0x28,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, + 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x33,0x2e,0x79,0x5d,0x5b,0x30,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69, + 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73, + 0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53, + 0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e, + 0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c, + 0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f, + 0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x33,0x5d,0x5b,0x30, + 0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53, + 0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e, + 0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c, + 0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f, + 0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x31,0x5d,0x5b,0x31, + 0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x32,0x5d, + 0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b, + 0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x30,0x5d, + 0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b, + 0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e, + 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79, + 0x5d,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x79,0x5d,0x5b,0x33,0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29, + 0x20,0x2a,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x34,0x2e,0x79,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x69, + 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x31,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b, + 0x30,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e, + 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a, + 0x5d,0x5b,0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x7a,0x5d,0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, + 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a, + 0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x7a,0x5d,0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, + 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69, + 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73, + 0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53, + 0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e, + 0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29, + 0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69, + 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73, + 0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53, + 0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e, + 0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c, + 0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f, + 0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x32,0x5d,0x5b,0x32, + 0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x33,0x5d, + 0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34, + 0x2e,0x7a,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x2a,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53, + 0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e, + 0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, + 0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x30,0x5d,0x5b,0x30,0x5d,0x2c, + 0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f, + 0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x31,0x5d,0x5b,0x30, + 0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x32,0x5d, + 0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b, + 0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x30,0x5d, + 0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b, + 0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e, + 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77, + 0x5d,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x77,0x5d,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e, + 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77, + 0x5d,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, + 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, + 0x2e,0x77,0x5d,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, + 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x33,0x2e,0x77,0x5d,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69, + 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73, + 0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x33,0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78, + 0x79,0x7a,0x29,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, + 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x2e,0x77,0x29,0x29,0x20,0x2a, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f, + 0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x30,0x5d,0x5b,0x30,0x5d, + 0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e, + 0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x31,0x5d,0x5b,0x30, + 0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x32,0x5d,0x5b, + 0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x33,0x5d, + 0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f, + 0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x30,0x5d,0x5b,0x31,0x5d, + 0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e, + 0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x31,0x5d,0x5b,0x31, + 0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x32,0x5d,0x5b, + 0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x33,0x5d, + 0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f, + 0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x30,0x5d,0x5b,0x32,0x5d, + 0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e, + 0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x31,0x5d,0x5b,0x32, + 0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x32,0x5d,0x5b, + 0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x33,0x5d, + 0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d, + 0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69, + 0x6f,0x6e,0x20,0x2a,0x20,0x5f,0x31,0x30,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x30,0x20,0x3d,0x20,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x6f,0x64,0x65,0x6c,0x20,0x2a,0x20,0x5f,0x31,0x30, + 0x30,0x29,0x2e,0x78,0x79,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e, + 0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x31,0x20,0x3d,0x20,0x5f,0x31,0x35,0x37,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75, + 0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, + 0x52,0x44,0x32,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72, + 0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a, +}; + +static const SceneShaderT sceneShaderVertexSkinned = { "vertexSkinned", _vertexSkinnedSPIRV, sizeof(_vertexSkinnedSPIRV), _vertexSkinnedDXIL, sizeof(_vertexSkinnedDXIL), _vertexSkinnedMSL, sizeof(_vertexSkinnedMSL) }; + +static const unsigned char _fragmentMainSPIRV[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0xd0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x0b,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x66,0x72,0x61,0x67, + 0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x10,0x00,0x03,0x00, + 0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00, + 0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x74,0x79,0x70,0x65, + 0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, + 0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x00, + 0x06,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, + 0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x08,0x00,0x00,0x00,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x09,0x00,0x00,0x00, + 0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x0a,0x00,0x00,0x00, + 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, + 0x05,0x00,0x08,0x00,0x0b,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x46,0x72,0x61, + 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x6d,0x65, + 0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x00, + 0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x62,0x61,0x73,0x65, + 0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65,0x00,0x00,0x00,0x00, + 0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x74,0x65, + 0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e,0x74,0x73,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x6c,0x69,0x67,0x68,0x74,0x73,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x4c,0x69,0x67,0x68,0x74,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f, + 0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00,0x06,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x0d,0x00,0x00,0x00,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, + 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0e,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67, + 0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0f,0x00,0x00,0x00,0x62,0x61,0x73,0x65, + 0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00,0x05,0x00,0x06,0x00,0x10,0x00,0x00,0x00, + 0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70, + 0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, + 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54, + 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x05,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, + 0x74,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x66,0x72,0x61,0x67, + 0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x12,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x64, + 0x2e,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x04,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x05,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x1b,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x3f,0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x9a,0x99,0x19,0x3f,0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x0a,0xd7,0x23,0x3d,0x17,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2c,0x00,0x06,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x2b,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x80,0x43,0x2b,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x2b,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x17,0xb7,0xd1,0x38,0x17,0x00,0x04,0x00, + 0x2a,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x80,0x00,0x00,0x00, + 0x1c,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x1e,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1e,0x00,0x06,0x00, + 0x0c,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00,0x1e,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x31,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0e,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x36,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x35,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x37,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x38,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x39,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3a,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x14,0x00,0x02,0x00,0x3c,0x00,0x00,0x00, + 0x1b,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x31,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x32,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x33,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x34,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x34,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x36,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x37,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x2d,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x38,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x39,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x3e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x20,0x00,0x00,0x00, + 0x3f,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x20,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x35,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x42,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfb,0x00,0x03,0x00,0x3d,0x00,0x00,0x00,0x43,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x43,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3a,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x2a,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x41,0x00,0x06,0x00, + 0x3b,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x3b,0x00,0x00,0x00,0x48,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x41,0x00,0x06,0x00, + 0x3b,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x4b,0x00,0x00,0x00, + 0x4a,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00, + 0x4b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x4d,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x4c,0x00,0x00,0x00,0x4e,0x00,0x00,0x00, + 0x4d,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x4e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x56,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x57,0x00,0x06,0x00,0x2a,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x51,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x2a,0x00,0x00,0x00, + 0x53,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, + 0x4d,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x4d,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, + 0x2a,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x43,0x00,0x00,0x00, + 0x53,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x3b,0x00,0x00,0x00, + 0x55,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x55,0x00,0x00,0x00, + 0xba,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x56,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfa,0x00,0x04,0x00,0x57,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x58,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x59,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x42,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x58,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x20,0x00,0x00,0x00, + 0x5a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x3a,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x5c,0x00,0x00,0x00, + 0x5b,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x5d,0x00,0x00,0x00, + 0x5c,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x5e,0x00,0x00,0x00, + 0x5d,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x20,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x5e,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x54,0x00,0x00,0x00, + 0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x50,0x00,0x06,0x00, + 0x20,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x0c,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0x66,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0x67,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x67,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x49,0x00,0x00,0x00,0x0c,0x00,0x08,0x00,0x1b,0x00,0x00,0x00,0x6a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x69,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3a,0x00,0x00,0x00,0x6b,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x6c,0x00,0x00,0x00,0x6b,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0x6e,0x00,0x00,0x00,0x6d,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x6e,0x00,0x00,0x00,0x67,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x3a,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x72,0x00,0x00,0x00, + 0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0x6f,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x3b,0x00,0x00,0x00, + 0x74,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x74,0x00,0x00,0x00, + 0x6e,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0xf9,0x00,0x02,0x00,0x77,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x77,0x00,0x00,0x00, + 0xf5,0x00,0x07,0x00,0x20,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, + 0x15,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x58,0x00,0x00,0x00, + 0x7c,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0xb1,0x00,0x05,0x00,0x3c,0x00,0x00,0x00, + 0x7d,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0xf6,0x00,0x04,0x00, + 0x7e,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00, + 0x7d,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, + 0x7f,0x00,0x00,0x00,0xaf,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0x80,0x00,0x00,0x00, + 0x7b,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x81,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x80,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x81,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x82,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, + 0x7a,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x81,0x00,0x00,0x00,0x41,0x00,0x08,0x00, + 0x3b,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x7b,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x1b,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0xb4,0x00,0x05,0x00, + 0x3c,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0xf7,0x00,0x03,0x00,0x86,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00, + 0x85,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, + 0x87,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x3a,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x8a,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x8b,0x00,0x00,0x00,0x8a,0x00,0x00,0x00, + 0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x7f,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0x8b,0x00,0x00,0x00, + 0x0c,0x00,0x06,0x00,0x20,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x86,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x88,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x3a,0x00,0x00,0x00, + 0x8e,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x8f,0x00,0x00,0x00, + 0x8e,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x8f,0x00,0x00,0x00,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x91,0x00,0x00,0x00, + 0x90,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x1b,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x91,0x00,0x00,0x00, + 0x0c,0x00,0x07,0x00,0x1b,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x50,0x00,0x06,0x00, + 0x20,0x00,0x00,0x00,0x94,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x93,0x00,0x00,0x00, + 0x93,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x95,0x00,0x00,0x00, + 0x91,0x00,0x00,0x00,0x94,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0x96,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x81,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x96,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x97,0x00,0x00,0x00,0x41,0x00,0x08,0x00,0x3b,0x00,0x00,0x00,0x99,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, + 0x99,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0x9b,0x00,0x00,0x00, + 0x9a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x9c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x9b,0x00,0x00,0x00,0x9d,0x00,0x00,0x00, + 0x9c,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x9d,0x00,0x00,0x00,0x88,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0x9e,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, + 0x0c,0x00,0x07,0x00,0x1b,0x00,0x00,0x00,0x9f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x9e,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x83,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, + 0x0c,0x00,0x08,0x00,0x1b,0x00,0x00,0x00,0xa1,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0xa2,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0xa1,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x9c,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, + 0x9c,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x1b,0x00,0x00,0x00,0xa3,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0xa2,0x00,0x00,0x00,0x9d,0x00,0x00,0x00, + 0xb4,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0xa4,0x00,0x00,0x00,0x84,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0xa5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfa,0x00,0x04,0x00,0xa4,0x00,0x00,0x00,0xa6,0x00,0x00,0x00,0xa5,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0xa6,0x00,0x00,0x00,0x7f,0x00,0x04,0x00,0x20,0x00,0x00,0x00, + 0xa7,0x00,0x00,0x00,0x95,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x3a,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0xa9,0x00,0x00,0x00, + 0xa8,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0xaa,0x00,0x00,0x00, + 0xa9,0x00,0x00,0x00,0xa9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x20,0x00,0x00,0x00,0xab,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0xaa,0x00,0x00,0x00,0x94,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0xac,0x00,0x00,0x00,0xa7,0x00,0x00,0x00,0xab,0x00,0x00,0x00, + 0x41,0x00,0x08,0x00,0x3b,0x00,0x00,0x00,0xad,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0xae,0x00,0x00,0x00,0xad,0x00,0x00,0x00, + 0x41,0x00,0x08,0x00,0x3b,0x00,0x00,0x00,0xaf,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,0xaf,0x00,0x00,0x00, + 0x0c,0x00,0x08,0x00,0x1b,0x00,0x00,0x00,0xb1,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0xae,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,0xac,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0xb2,0x00,0x00,0x00,0xa3,0x00,0x00,0x00, + 0xb1,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0xa5,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, + 0xa5,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x1b,0x00,0x00,0x00,0xb3,0x00,0x00,0x00, + 0xa3,0x00,0x00,0x00,0x9c,0x00,0x00,0x00,0xb2,0x00,0x00,0x00,0xa6,0x00,0x00,0x00, + 0xf9,0x00,0x02,0x00,0x86,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x86,0x00,0x00,0x00, + 0xf5,0x00,0x07,0x00,0x1b,0x00,0x00,0x00,0xb4,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x87,0x00,0x00,0x00,0xb3,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, + 0x20,0x00,0x00,0x00,0xb5,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0x87,0x00,0x00,0x00, + 0x95,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,0x94,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0xb6,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0xb5,0x00,0x00,0x00,0x0c,0x00,0x08,0x00, + 0x1b,0x00,0x00,0x00,0xb7,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0xb6,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x81,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0xb5,0x00,0x00,0x00,0x5f,0x00,0x00,0x00, + 0x0c,0x00,0x06,0x00,0x20,0x00,0x00,0x00,0xb9,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x94,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0xba,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0xb9,0x00,0x00,0x00,0x0c,0x00,0x08,0x00, + 0x1b,0x00,0x00,0x00,0xbb,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0xba,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0c,0x00,0x07,0x00, + 0x1b,0x00,0x00,0x00,0xbc,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0xbb,0x00,0x00,0x00,0x6a,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0xbd,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x83,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0xbe,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0xbd,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xbc,0x00,0x00,0x00, + 0xbe,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0xb7,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0xc1,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, + 0x20,0x00,0x00,0x00,0xc2,0x00,0x00,0x00,0xc1,0x00,0x00,0x00,0xb7,0x00,0x00,0x00, + 0x81,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0xc3,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xc2,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x3a,0x00,0x00,0x00,0xc4,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0xc5,0x00,0x00,0x00,0xc4,0x00,0x00,0x00, + 0x4f,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0xc6,0x00,0x00,0x00,0xc5,0x00,0x00,0x00, + 0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0xc7,0x00,0x00,0x00,0xc3,0x00,0x00,0x00, + 0xc6,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0xc8,0x00,0x00,0x00, + 0xc7,0x00,0x00,0x00,0xb4,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x20,0x00,0x00,0x00, + 0xc9,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, + 0x7a,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x7a,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, + 0x20,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0xc9,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x80,0x00,0x05,0x00,0x15,0x00,0x00,0x00, + 0x7c,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, + 0x77,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x7e,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x1b,0x00,0x00,0x00,0xca,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0xcb,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0xcc,0x00,0x00,0x00, + 0x78,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0xcd,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x2a,0x00,0x00,0x00,0xce,0x00,0x00,0x00,0xcb,0x00,0x00,0x00,0xcc,0x00,0x00,0x00, + 0xcd,0x00,0x00,0x00,0xca,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x42,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x42,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x2a,0x00,0x00,0x00, + 0xcf,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0xce,0x00,0x00,0x00, + 0x7e,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0xcf,0x00,0x00,0x00, + 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +static const unsigned char _fragmentMainDXIL[] = { + 0x44,0x58,0x42,0x43,0xf6,0x41,0x05,0xb7,0x25,0x43,0x62,0xf9,0x67,0x7c,0x07,0x6f, + 0x09,0xc1,0x3f,0x6d,0x01,0x00,0x00,0x00,0x98,0x19,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x04,0x01,0x00,0x00, + 0x48,0x02,0x00,0x00,0xd4,0x0b,0x00,0x00,0xf0,0x0b,0x00,0x00,0x53,0x46,0x49,0x30, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, + 0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x31,0x34,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, + 0x74,0x00,0x00,0x00,0x50,0x53,0x56,0x30,0x3c,0x01,0x00,0x00,0x34,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x03,0x01,0x00,0x03, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f, + 0x4f,0x52,0x44,0x00,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x43,0x00,0x03,0x02,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x01,0x43,0x00,0x03,0x02,0x00,0x00,0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x02,0x42,0x00,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x44,0x10,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54,0x84,0x09,0x00,0x00, + 0x60,0x00,0x00,0x00,0x61,0x02,0x00,0x00,0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00, + 0x10,0x00,0x00,0x00,0x6c,0x09,0x00,0x00,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00, + 0x58,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c, + 0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42, + 0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20, + 0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50, + 0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00, + 0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d, + 0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f, + 0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42, + 0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x5b,0x00,0x00,0x00, + 0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3, + 0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c,0x10,0x9c,0xc1,0x08, + 0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10, + 0x44,0x41,0x90,0x51,0x0c,0x80,0x20,0x88,0x62,0x20,0xe4,0xa6,0xe1,0xf2,0x27,0xec, + 0x21,0x24,0x7f,0x25,0xa4,0x95,0x98,0xfc,0xe2,0xb6,0x51,0x31,0x0c,0xc3,0x40,0x50, + 0x71,0xcf,0x70,0xf9,0x13,0xf6,0x10,0x92,0x1f,0x02,0xcd,0xb0,0x10,0x28,0x58,0x0a, + 0xa3,0x10,0x0c,0x33,0x0c,0xc3,0x40,0x10,0xc4,0x40,0x4d,0x41,0x06,0x62,0x18,0x86, + 0x61,0x18,0xe8,0x29,0xc3,0x40,0x0c,0x14,0x15,0x62,0x20,0x86,0x81,0xa6,0xa3,0x86, + 0xcb,0x9f,0xb0,0x87,0x90,0x7c,0x6e,0xa3,0x8a,0x95,0x98,0xfc,0xe2,0xb6,0x11,0x31, + 0x0c,0xc3,0x50,0x88,0x8a,0x60,0x08,0xb2,0xe6,0x08,0x82,0x62,0x30,0x44,0x41,0x10, + 0x18,0x65,0x03,0x01,0xc3,0x08,0xc4,0x30,0x53,0x1b,0x8c,0x03,0x3b,0x84,0xc3,0x3c, + 0xcc,0x83,0x1b,0xd0,0x42,0x39,0xe0,0x03,0x3d,0xd4,0x83,0x3c,0x94,0x83,0x1c,0x90, + 0x02,0x1f,0xd8,0x43,0x39,0x8c,0x03,0x3d,0xbc,0x83,0x3c,0xf0,0x81,0x39,0xb0,0xc3, + 0x3b,0x84,0x03,0x3d,0xb0,0x01,0x18,0xd0,0x81,0x1f,0x80,0x81,0x1f,0xe8,0x81,0x1e, + 0xb4,0x43,0x3a,0xc0,0xc3,0x3c,0xfc,0x02,0x3d,0xe4,0x03,0x3c,0x94,0x03,0x0a,0x88, + 0x99,0xc4,0x60,0x1c,0xd8,0x21,0x1c,0xe6,0x61,0x1e,0xdc,0x80,0x16,0xca,0x01,0x1f, + 0xe8,0xa1,0x1e,0xe4,0xa1,0x1c,0xe4,0x80,0x14,0xf8,0xc0,0x1e,0xca,0x61,0x1c,0xe8, + 0xe1,0x1d,0xe4,0x81,0x0f,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x81,0x0d,0xc0,0x80, + 0x0e,0xfc,0x00,0x0c,0xfc,0x00,0x09,0x5c,0x47,0xde,0x31,0xd2,0x14,0x51,0xc2,0xe4, + 0x97,0x88,0x71,0x4c,0x88,0xe0,0x38,0x8e,0x4b,0x04,0xf0,0xaa,0x09,0x7b,0x88,0xff, + 0x8b,0x00,0x83,0x21,0x9a,0xc9,0x6d,0x90,0xc2,0x89,0x18,0x09,0x1d,0x1c,0xc7,0x71, + 0x1c,0x17,0x92,0x78,0x93,0x34,0x45,0x94,0x30,0xf9,0x2c,0xc0,0x3c,0x0b,0x11,0xb1, + 0x13,0x30,0x11,0x28,0x20,0x88,0x4c,0x06,0x02,0x00,0x00,0x00,0x13,0x14,0x72,0xc0, + 0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50, + 0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x34, + 0x40,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x79,0x80,0x00,0x08, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xc8,0x13,0x01,0x01,0x10,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02,0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43, + 0x9e,0x0c,0x08,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00, + 0x18,0x00,0x00,0x00,0x32,0x1e,0x98,0x18,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47, + 0xc6,0x04,0x43,0x22,0x4a,0x60,0x04,0xa0,0x18,0x8a,0xa0,0x24,0xca,0xa0,0x80,0x13, + 0x0a,0x30,0xa0,0x1c,0x4a,0xa3,0x10,0x0a,0xa4,0x80,0x0a,0xac,0x40,0x03,0x0a,0x38, + 0xa0,0x3c,0x8a,0x52,0xa0,0xd8,0x07,0x0a,0x82,0x8a,0x92,0x18,0x01,0x28,0x82,0x32, + 0x28,0x90,0x42,0x20,0xac,0x06,0xe8,0x9b,0x01,0x20,0x70,0x06,0x80,0xc4,0x19,0x00, + 0x1a,0x67,0x00,0xa8,0x9c,0x01,0x20,0x73,0x2c,0x46,0x21,0x8e,0xe3,0x00,0x8e,0xe3, + 0x00,0x9e,0x07,0x00,0x79,0x18,0x00,0x00,0x01,0x01,0x00,0x00,0x1a,0x03,0x4c,0x90, + 0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c, + 0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c, + 0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x64,0x82,0x40,0x28,0x1b,0x84,0x81, + 0xd8,0x20,0x10,0x04,0x05,0xb8,0xb9,0x09,0x02,0xb1,0x6c,0x18,0x0e,0x84,0x98,0x20, + 0x7c,0x64,0xc0,0x45,0x2c,0x6c,0xae,0x8c,0xaa,0x0c,0x8f,0xae,0x4e,0xae,0x6c,0x82, + 0x40,0x30,0x13,0x04,0xa2,0xd9,0x20,0x10,0xcd,0x86,0x84,0x50,0x16,0x86,0x18,0x18, + 0xc2,0xd9,0x10,0x3c,0x13,0x84,0x31,0x38,0x03,0x56,0x74,0x79,0x70,0x65,0x5f,0x46, + 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x13, + 0x04,0xc2,0x99,0x20,0x10,0xcf,0x06,0x84,0x88,0xa4,0x89,0x18,0x28,0x60,0x43,0x50, + 0x4d,0x10,0xca,0x00,0x0d,0xb8,0x88,0x85,0xcd,0x95,0x4d,0x85,0xb5,0xc1,0xb1,0x95, + 0xc9,0x6d,0x40,0x88,0x0b,0x63,0x88,0x81,0x00,0x36,0x04,0xd9,0x06,0x02,0x02,0x2c, + 0x6d,0x82,0x00,0x06,0x65,0x30,0x41,0x20,0x20,0x32,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x54,0x79,0x70,0x65,0x13,0x04,0x22,0x9a,0x20,0x10,0xd2,0x04,0x81,0x98, + 0x36,0x20,0x88,0x37,0x11,0x5f,0x03,0x06,0x61,0x40,0x87,0x2c,0x4d,0xae,0x6c,0x8c, + 0x2e,0xed,0xcd,0x4d,0x2a,0xcc,0xed,0xac,0x6c,0x82,0x40,0x50,0x1b,0x10,0x64,0x0c, + 0x26,0x32,0xf8,0x1a,0x30,0x08,0x03,0x16,0x63,0x6f,0x6c,0x6f,0x72,0x13,0x04,0xa2, + 0xda,0x80,0x20,0x66,0x30,0x9d,0xc1,0xd7,0x80,0x41,0x18,0x90,0x18,0x7b,0x73,0x2b, + 0x9b,0x20,0x10,0xd6,0x06,0x04,0x49,0x83,0x49,0x0d,0xbe,0x06,0x0c,0xc2,0x60,0x43, + 0xd1,0x89,0x41,0x19,0xa0,0xc1,0x1a,0x4c,0x10,0xc4,0xc0,0x0c,0xf8,0x19,0xc9,0x85, + 0x9d,0xb5,0x95,0xb9,0xd1,0x55,0xb9,0xa5,0x99,0xbd,0xc9,0xb5,0xcd,0x7d,0x8d,0x85, + 0xb5,0x95,0xc9,0x85,0x41,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x4d,0x10,0xb0,0x31, + 0xd8,0xa0,0x20,0x6e,0x30,0x11,0x5f,0xd3,0xbc,0x01,0x18,0x84,0x01,0x31,0x23,0xb9, + 0xb0,0xb3,0xb6,0x32,0x37,0xba,0x2a,0xb7,0x34,0xb3,0x37,0xb9,0xb6,0xb9,0xaf,0xb0, + 0x36,0xb1,0xb4,0x32,0x37,0xba,0x0d,0x08,0x12,0x07,0x13,0x19,0x7c,0x0d,0x18,0x84, + 0x01,0x35,0x23,0xb9,0xb0,0xb3,0xb6,0x32,0x37,0xba,0x2a,0xb7,0x34,0xb3,0x37,0xb9, + 0xb6,0xb9,0x2f,0xb1,0xb0,0xb9,0xb2,0xa1,0x37,0xb6,0x37,0xb9,0x0d,0x08,0x32,0x07, + 0xd3,0x19,0x7c,0x0d,0x18,0x84,0x01,0x33,0x23,0xb9,0xb0,0xb3,0xb6,0x32,0x37,0xba, + 0x2a,0xb7,0x34,0xb3,0x37,0xb9,0xb6,0xb9,0xaf,0xb2,0xb6,0xb4,0xb9,0xb9,0x34,0xbb, + 0xb2,0x0d,0x08,0x52,0x07,0x93,0x1a,0x7c,0x0d,0x18,0x84,0x01,0x33,0x23,0xb9,0xb0, + 0xb3,0xb6,0x32,0x37,0xba,0x2a,0xb7,0x34,0xb3,0x37,0xb9,0xb6,0xb9,0xaf,0xb6,0x30, + 0xba,0x32,0xb9,0xb4,0x30,0xb6,0x0d,0x08,0x72,0x07,0x53,0xf7,0x35,0x60,0x10,0x06, + 0xbc,0x8c,0xe4,0xc2,0xce,0xda,0xca,0xdc,0xe8,0xaa,0xdc,0xd2,0xcc,0xde,0xe4,0xda, + 0xe6,0xbe,0xc6,0xde,0xea,0xdc,0xe8,0xe6,0x26,0x08,0xc4,0xb5,0x01,0x41,0xf2,0x60, + 0xd2,0x83,0xaf,0x01,0x83,0x30,0xe0,0x65,0x24,0x17,0x76,0xd6,0x56,0xe6,0x46,0x57, + 0xe5,0x96,0x66,0xf6,0x26,0xd7,0x36,0xf7,0xc5,0x96,0x76,0x86,0x46,0x37,0x37,0x41, + 0x20,0xb0,0x0d,0x04,0xc2,0x07,0x53,0x1f,0x6c,0x40,0x28,0x38,0x90,0x03,0x3a,0xb0, + 0x03,0x3c,0xd8,0x03,0x3f,0xd8,0x50,0x10,0x1c,0x1b,0xb4,0xc1,0x1f,0x4c,0x10,0x04, + 0x60,0x03,0xb0,0x61,0x20,0x44,0x41,0x14,0x36,0x04,0xa3,0xb0,0x61,0x18,0x42,0x81, + 0x14,0x26,0x08,0x66,0x90,0x06,0x1b,0x02,0x53,0x20,0x63,0x26,0x17,0x76,0xd6,0x56, + 0xe6,0x46,0xd7,0x14,0x96,0xe6,0x46,0x84,0xaa,0x08,0x6b,0xe8,0xe9,0x49,0x8a,0x68, + 0x82,0x50,0x74,0x13,0x84,0xc2,0xdb,0x10,0x10,0x13,0x84,0xe2,0x9b,0x20,0x14,0x60, + 0xb0,0x41,0x98,0xbe,0x0d,0x0b,0x91,0x0a,0xaa,0xb0,0x0a,0xac,0xd0,0x0a,0x83,0x2b, + 0x10,0xab,0xf0,0x0a,0x1b,0x82,0x61,0xc3,0x32,0xa4,0x82,0x2a,0xac,0x42,0x2c,0xb4, + 0xc2,0xe0,0x0a,0xc3,0x2a,0xbc,0xc2,0x86,0x80,0xd9,0x20,0x4c,0xd3,0x86,0x85,0x49, + 0x05,0x55,0x58,0x85,0x59,0x68,0x85,0xa1,0x15,0x98,0x55,0xa0,0x85,0x0d,0x03,0x2c, + 0xc8,0x42,0x2d,0x30,0x99,0xb2,0xfa,0xa2,0x0a,0x93,0x3b,0x2b,0xa3,0x9b,0x20,0x14, + 0x61,0x30,0x41,0x28,0xc4,0x60,0x82,0x40,0x64,0x1b,0x84,0x49,0x17,0x36,0x2c,0xc4, + 0x2d,0xa8,0x02,0x2e,0xb0,0xc2,0x2a,0x0c,0xb9,0x40,0xac,0xc2,0x2e,0x6c,0x08,0x78, + 0x61,0xc3,0x60,0x0b,0xbd,0x00,0x6c,0x28,0x42,0x01,0x15,0x7c,0x61,0x03,0x88,0x88, + 0xc9,0x85,0xb9,0x8d,0xa1,0x95,0xcd,0xd1,0x30,0x63,0x7b,0x0b,0xa3,0x9b,0x9b,0x20, + 0x10,0x1a,0x8b,0x34,0xb7,0x39,0xba,0xb9,0x09,0x02,0xb1,0x91,0x48,0x73,0xa3,0x9b, + 0xa3,0x31,0x97,0x76,0xf6,0xc5,0x46,0x36,0x41,0x20,0x38,0x1a,0x73,0x69,0x67,0x5f, + 0x73,0x74,0x44,0xe8,0xca,0xf0,0xbe,0xdc,0xde,0xe4,0xda,0x58,0xd4,0xa5,0xb9,0xd1, + 0xcd,0x6d,0x80,0xc0,0xe1,0x0b,0x07,0x71,0x18,0x07,0x72,0x28,0x87,0xcf,0x1c,0xce, + 0x01,0x1d,0xc2,0x20,0x1d,0x06,0x75,0x18,0xaa,0xb0,0xb1,0xd9,0xb5,0xb9,0xa4,0x91, + 0x95,0xb9,0xd1,0x4d,0x09,0x82,0x2a,0x64,0x78,0x2e,0x76,0x65,0x72,0x73,0x69,0x6f, + 0x6e,0x53,0x02,0xa2,0x09,0x19,0x9e,0x8b,0x5d,0x18,0x9b,0x5d,0x99,0xdc,0x94,0xa0, + 0xa8,0x43,0x86,0xe7,0x32,0x87,0x16,0x46,0x56,0x26,0xd7,0xf4,0x46,0x56,0xc6,0x36, + 0x25,0x40,0xca,0x90,0xe1,0xb9,0xc8,0x95,0xcd,0xbd,0xd5,0xc9,0x8d,0x95,0xcd,0x4d, + 0x09,0xb4,0x4a,0x64,0x78,0x2e,0x74,0x79,0x70,0x65,0x41,0x6e,0x6e,0x6f,0x74,0x61, + 0x74,0x69,0x6f,0x6e,0x73,0x53,0x84,0x3f,0x20,0x85,0x3a,0x64,0x78,0x2e,0x76,0x69, + 0x65,0x77,0x49,0x64,0x53,0x74,0x61,0x74,0x65,0x53,0x02,0x53,0xa8,0x43,0x86,0xe7, + 0x52,0xe6,0x46,0x27,0x97,0x07,0xf5,0x96,0xe6,0x46,0x37,0x37,0x25,0xf0,0x85,0x2e, + 0x64,0x78,0x2e,0x63,0x6f,0x75,0x6e,0x74,0x65,0x72,0x73,0x53,0x02,0x75,0x00,0x00, + 0x79,0x18,0x00,0x00,0x51,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66, + 0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73, + 0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e, + 0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b, + 0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b, + 0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20, + 0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0, + 0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61, + 0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83, + 0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87, + 0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76, + 0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98, + 0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30, + 0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61, + 0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43, + 0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b, + 0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8, + 0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60, + 0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40, + 0x0f,0xe5,0xd0,0x0e,0xf0,0x30,0x83,0x81,0xc8,0x01,0x1f,0xdc,0x40,0x1c,0xe4,0xa1, + 0x1c,0xc2,0x61,0x1d,0xdc,0x40,0x1c,0xe4,0x01,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x66,0x00,0x0d,0x97,0xef,0x3c,0x7e,0x80,0x34,0x40,0x84,0xf9, + 0xc5,0x6d,0xdb,0xc1,0x36,0x5c,0xbe,0xf3,0xf8,0x42,0x40,0x15,0x05,0x11,0x95,0x0e, + 0x30,0x94,0x84,0x01,0x08,0x98,0x5f,0xdc,0xb6,0x21,0x48,0xc3,0xe5,0x3b,0x8f,0x2f, + 0x44,0x04,0x30,0x11,0x21,0xd0,0x0c,0x0b,0x61,0x04,0xce,0x70,0xf9,0xce,0xe3,0x0f, + 0xce,0x74,0xfb,0xc5,0x6d,0x5b,0xc0,0x34,0x5c,0xbe,0xf3,0xf8,0x8b,0x03,0x0c,0x62, + 0xf3,0x50,0x93,0x5f,0xdc,0xb6,0x0d,0x40,0xc3,0xe5,0x3b,0x8f,0x2f,0x01,0xcc,0xb3, + 0x10,0x7e,0x71,0xdb,0x26,0x50,0x0d,0x97,0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52, + 0xd3,0x43,0x4d,0x7e,0x71,0xdb,0x56,0xf0,0x0c,0x97,0xef,0x3c,0x3e,0xd5,0x00,0x11, + 0xe6,0x17,0xb7,0x6d,0x00,0x4c,0x11,0x01,0x06,0x43,0x34,0x93,0x09,0x20,0x0d,0x00, + 0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa4,0x93,0xc4,0x36,0x20,0x29,0x62,0xc1,0x36,0x49,0x2b,0x91,0x3b,0x44,0xe1,0x9c, + 0x44,0x58,0x49,0x4c,0xa0,0x0d,0x00,0x00,0x60,0x00,0x00,0x00,0x68,0x03,0x00,0x00, + 0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x88,0x0d,0x00,0x00, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x5f,0x03,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b, + 0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42, + 0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a, + 0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff, + 0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20, + 0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00, + 0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00, + 0x89,0x20,0x00,0x00,0x5b,0x00,0x00,0x00,0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04, + 0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c, + 0x0b,0x84,0xc4,0x4c,0x10,0xa0,0xc1,0x08,0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08, + 0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51,0x0c,0x80,0x20,0x88, + 0x62,0x20,0xe4,0xa6,0xe1,0xf2,0x27,0xec,0x21,0x24,0x7f,0x25,0xa4,0x95,0x98,0xfc, + 0xe2,0xb6,0x51,0x31,0x0c,0xc3,0x40,0x50,0x71,0xcf,0x70,0xf9,0x13,0xf6,0x10,0x92, + 0x1f,0x02,0xcd,0xb0,0x10,0x28,0x58,0x0a,0xa3,0x10,0x0c,0x33,0x0c,0xc3,0x40,0x10, + 0xc4,0x40,0x4d,0x41,0x06,0x62,0x18,0x86,0x61,0x18,0xe8,0x29,0xc3,0x40,0x0c,0x14, + 0x15,0x62,0x20,0x86,0x81,0xa6,0xa3,0x86,0xcb,0x9f,0xb0,0x87,0x90,0x7c,0x6e,0xa3, + 0x8a,0x95,0x98,0xfc,0xe2,0xb6,0x11,0x31,0x0c,0xc3,0x50,0x88,0x8a,0x60,0x08,0xb2, + 0xe6,0x08,0x82,0x62,0x30,0x44,0x41,0x10,0x18,0x65,0x03,0x01,0xc3,0x08,0xc4,0x30, + 0x53,0x1b,0x8c,0x03,0x3b,0x84,0xc3,0x3c,0xcc,0x83,0x1b,0xd0,0x42,0x39,0xe0,0x03, + 0x3d,0xd4,0x83,0x3c,0x94,0x83,0x1c,0x90,0x02,0x1f,0xd8,0x43,0x39,0x8c,0x03,0x3d, + 0xbc,0x83,0x3c,0xf0,0x81,0x39,0xb0,0xc3,0x3b,0x84,0x03,0x3d,0xb0,0x01,0x18,0xd0, + 0x81,0x1f,0x80,0x81,0x1f,0xe8,0x81,0x1e,0xb4,0x43,0x3a,0xc0,0xc3,0x3c,0xfc,0x02, + 0x3d,0xe4,0x03,0x3c,0x94,0x03,0x0a,0x88,0x99,0xc4,0x60,0x1c,0xd8,0x21,0x1c,0xe6, + 0x61,0x1e,0xdc,0x80,0x16,0xca,0x01,0x1f,0xe8,0xa1,0x1e,0xe4,0xa1,0x1c,0xe4,0x80, + 0x14,0xf8,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0x81,0x0f,0xcc,0x81,0x1d, + 0xde,0x21,0x1c,0xe8,0x81,0x0d,0xc0,0x80,0x0e,0xfc,0x00,0x0c,0xfc,0x00,0x09,0x5c, + 0x47,0xde,0x31,0xd2,0x14,0x51,0xc2,0xe4,0x97,0x88,0x71,0x4c,0x88,0xe0,0x38,0x8e, + 0x4b,0x04,0xf0,0xaa,0x09,0x7b,0x88,0xff,0x8b,0x00,0x83,0x21,0x9a,0xc9,0x6d,0x90, + 0xc2,0x89,0x18,0x09,0x1d,0x1c,0xc7,0x71,0x1c,0x17,0x92,0x78,0x93,0x34,0x45,0x94, + 0x30,0xf9,0x2c,0xc0,0x3c,0x0b,0x11,0xb1,0x13,0x30,0x11,0x28,0x20,0x88,0x4c,0x06, + 0x62,0x0a,0x00,0x00,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79, + 0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d, + 0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0, + 0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e, + 0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10, + 0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x34,0x40,0x00,0x0c,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x30,0xe4,0x79,0x80,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60, + 0xc8,0x13,0x01,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02, + 0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x9e,0x0c,0x08,0x80,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x11,0x00,0x00,0x00,0x32,0x1e,0x98,0x14, + 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22,0x4a,0x60,0x04,0xa0, + 0x24,0x8a,0xa1,0x08,0xca,0xa0,0x80,0x13,0xca,0xa1,0x3c,0xa8,0x28,0x89,0x11,0x80, + 0x22,0x28,0x83,0x02,0x29,0x04,0xfa,0x66,0x00,0x68,0x9c,0x01,0xa0,0x72,0x06,0x80, + 0xcc,0xb1,0x18,0x85,0x38,0x8e,0x03,0x38,0x8e,0x03,0x78,0x1e,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0x68,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4, + 0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25, + 0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65, + 0x43,0x10,0x4c,0x10,0x88,0x64,0x82,0x40,0x28,0x1b,0x84,0x81,0x98,0x20,0x10,0xcb, + 0x06,0x61,0x30,0x28,0xc0,0xcd,0x4d,0x10,0x08,0x66,0xc3,0x80,0x24,0xc4,0x04,0xe1, + 0xc3,0x08,0x4c,0x10,0x88,0x66,0x83,0x40,0x18,0x1b,0x12,0x62,0x61,0x1a,0x62,0x68, + 0x08,0x67,0x43,0xf0,0x4c,0x10,0xc6,0x20,0x9b,0x20,0x10,0xce,0x04,0x81,0x78,0x36, + 0x20,0x44,0xc4,0x48,0xc4,0x30,0x01,0x1b,0x02,0x6a,0x82,0x50,0x06,0xda,0x06,0x84, + 0xb0,0x98,0x86,0x18,0x08,0x60,0x43,0x70,0x6d,0x20,0x20,0xa0,0xc2,0x26,0x08,0x66, + 0xb0,0x6d,0x08,0xb4,0x09,0x82,0x00,0x90,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3, + 0x6b,0x0a,0x4b,0x73,0x23,0x42,0x55,0x84,0x35,0xf4,0xf4,0x24,0x45,0x34,0x41,0x28, + 0xa4,0x09,0x42,0x31,0x6d,0x08,0x88,0x09,0x42,0x41,0x4d,0x10,0x8a,0x6a,0x82,0x40, + 0x40,0x1b,0x04,0x89,0x0c,0x36,0x2c,0x84,0xf7,0x81,0x41,0x18,0x88,0xc1,0x30,0x06, + 0x04,0x18,0x94,0xc1,0x86,0x60,0xd8,0xb0,0x0c,0xde,0x07,0x06,0x67,0x20,0x06,0xc3, + 0x18,0x0c,0x60,0x50,0x06,0x1b,0x82,0x66,0x83,0x20,0x49,0x1b,0x96,0xc6,0xfb,0xc0, + 0x20,0x0d,0xc4,0x60,0x10,0x83,0x06,0x0c,0xd4,0x60,0xc3,0x60,0x06,0x68,0xb0,0x06, + 0x4c,0xa6,0xac,0xbe,0xa8,0xc2,0xe4,0xce,0xca,0xe8,0x26,0x08,0x85,0x35,0x41,0x28, + 0xae,0x09,0x02,0x11,0x6d,0x10,0x24,0x38,0xd8,0xb0,0x10,0x6d,0xf0,0xb9,0x41,0x18, + 0x80,0xc1,0xf0,0x06,0x04,0x18,0xc4,0xc1,0x86,0x40,0x0e,0x36,0x0c,0x6c,0x30,0x07, + 0xc0,0x86,0x82,0xeb,0xe8,0x20,0x03,0xaa,0xb0,0xb1,0xd9,0xb5,0xb9,0xa4,0x91,0x95, + 0xb9,0xd1,0x4d,0x09,0x82,0x2a,0x64,0x78,0x2e,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e, + 0x53,0x02,0xa2,0x09,0x19,0x9e,0x8b,0x5d,0x18,0x9b,0x5d,0x99,0xdc,0x94,0xc0,0xa8, + 0x43,0x86,0xe7,0x32,0x87,0x16,0x46,0x56,0x26,0xd7,0xf4,0x46,0x56,0xc6,0x36,0x25, + 0x48,0xca,0x90,0xe1,0xb9,0xc8,0x95,0xcd,0xbd,0xd5,0xc9,0x8d,0x95,0xcd,0x4d,0x09, + 0xb0,0x3a,0x64,0x78,0x2e,0x76,0x69,0x65,0x77,0x49,0x64,0x53,0x74,0x61,0x74,0x65, + 0x53,0x02,0xad,0x0e,0x19,0x9e,0x4b,0x99,0x1b,0x9d,0x5c,0x1e,0xd4,0x5b,0x9a,0x1b, + 0xdd,0xdc,0x94,0x80,0x0e,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x51,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87, + 0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a, + 0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x30,0x83,0x81, + 0xc8,0x01,0x1f,0xdc,0x40,0x1c,0xe4,0xa1,0x1c,0xc2,0x61,0x1d,0xdc,0x40,0x1c,0xe4, + 0x01,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x66,0x00,0x0d,0x97, + 0xef,0x3c,0x7e,0x80,0x34,0x40,0x84,0xf9,0xc5,0x6d,0xdb,0xc1,0x36,0x5c,0xbe,0xf3, + 0xf8,0x42,0x40,0x15,0x05,0x11,0x95,0x0e,0x30,0x94,0x84,0x01,0x08,0x98,0x5f,0xdc, + 0xb6,0x21,0x48,0xc3,0xe5,0x3b,0x8f,0x2f,0x44,0x04,0x30,0x11,0x21,0xd0,0x0c,0x0b, + 0x61,0x04,0xce,0x70,0xf9,0xce,0xe3,0x0f,0xce,0x74,0xfb,0xc5,0x6d,0x5b,0xc0,0x34, + 0x5c,0xbe,0xf3,0xf8,0x8b,0x03,0x0c,0x62,0xf3,0x50,0x93,0x5f,0xdc,0xb6,0x0d,0x40, + 0xc3,0xe5,0x3b,0x8f,0x2f,0x01,0xcc,0xb3,0x10,0x7e,0x71,0xdb,0x26,0x50,0x0d,0x97, + 0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e,0x71,0xdb,0x56,0xf0, + 0x0c,0x97,0xef,0x3c,0x3e,0xd5,0x00,0x11,0xe6,0x17,0xb7,0x6d,0x00,0x4c,0x11,0x01, + 0x06,0x43,0x34,0x93,0x09,0x20,0x0d,0x00,0x61,0x20,0x00,0x00,0xa5,0x01,0x00,0x00, + 0x13,0x04,0x4f,0x2c,0x10,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x84,0x8d,0x00,0x50, + 0x51,0x02,0x44,0x14,0xc4,0x28,0x03,0x00,0x07,0xcd,0x28,0x03,0x00,0x07,0x4e,0xc1, + 0x8c,0x32,0x00,0x70,0x90,0x8c,0x32,0x00,0x8a,0x33,0xca,0x00,0x28,0xc9,0x28,0x03, + 0xa0,0xbc,0x41,0xe1,0x8d,0x32,0x00,0x4a,0x53,0x2a,0xe5,0x52,0x76,0x85,0x30,0x03, + 0x50,0x6e,0x25,0x53,0x0a,0xa3,0x0c,0x80,0xc2,0x94,0x51,0xc9,0x15,0xd2,0x28,0x03, + 0xa0,0xac,0x01,0x0d,0x63,0x04,0x20,0x08,0x82,0x24,0x18,0x8c,0x11,0xdc,0x78,0x3b, + 0xb6,0xdc,0x28,0x43,0xa0,0x07,0x79,0x30,0x46,0xa0,0xb3,0xe6,0x1c,0x7f,0x63,0x04, + 0x20,0x08,0x82,0x78,0x18,0x8c,0x11,0x80,0x20,0x08,0xa2,0x60,0x30,0xca,0x10,0x84, + 0xc2,0x1f,0x8c,0x11,0x80,0x20,0x08,0x82,0x60,0x30,0x03,0x30,0x46,0x00,0x82,0x20, + 0x08,0x7f,0x63,0x04,0x2a,0x5e,0x9f,0xb2,0x37,0x02,0x30,0x46,0x00,0x82,0x20,0x08, + 0x82,0xc2,0x18,0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x23,0x06,0x09,0x00, + 0x82,0x60,0x90,0xed,0x41,0x04,0x07,0x79,0x90,0x07,0x67,0x30,0x62,0x90,0x00,0x20, + 0x08,0x06,0x19,0x1f,0x48,0x6f,0xa0,0x07,0x7a,0x80,0x06,0x23,0x06,0x09,0x00,0x82, + 0x60,0x90,0xf5,0xc1,0x14,0x07,0x7b,0xb0,0x07,0x69,0x30,0x62,0x90,0x00,0x20,0x08, + 0x06,0x46,0x29,0x6c,0x79,0xc0,0x07,0x73,0xa0,0x8d,0x18,0x24,0x00,0x08,0x82,0x81, + 0x61,0x0a,0x9c,0x1e,0xf4,0x81,0x1a,0x6c,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0x9c, + 0x42,0xf7,0x07,0x7e,0x50,0x07,0xdc,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xa8,0xe0, + 0x81,0xc2,0x1f,0xb0,0x41,0x37,0x62,0x90,0x00,0x20,0x08,0x06,0x46,0x2a,0x7c,0xa1, + 0x00,0x0a,0x76,0xe0,0x8d,0x18,0x24,0x00,0x08,0x82,0x81,0xa1,0x0a,0x60,0x10,0x0a, + 0xa1,0x80,0x07,0xdf,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xab,0x10,0x06,0xa2,0x20, + 0x0a,0x6f,0x00,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xb0,0x82,0x18,0x8c,0xc2, + 0x28,0xe4,0x41,0x18,0x8c,0x18,0x1c,0x00,0x08,0x82,0x81,0x75,0x0a,0x64,0x90,0x68, + 0xa3,0x09,0xc1,0x30,0x1c,0x11,0x48,0xc2,0x37,0x62,0x70,0x00,0x20,0x08,0x06,0x96, + 0x2a,0x9c,0x01,0xa3,0x06,0xa3,0x09,0x01,0x30,0x9a,0x20,0x04,0xa3,0x09,0x83,0x30, + 0x9a,0x40,0x0c,0xb3,0x0c,0x81,0x60,0x8c,0x18,0x3c,0x00,0x08,0x82,0x41,0x33,0x0b, + 0x70,0x30,0x49,0xd0,0x93,0x65,0xac,0xc0,0x0a,0x6a,0x90,0x8d,0x26,0x04,0xc0,0x68, + 0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x18,0x91,0xc8,0xc7,0x88,0x44,0x3e, + 0x46,0x24,0xf2,0x31,0x22,0x91,0xcf,0x2c,0x81,0x30,0x50,0x31,0x20,0x81,0x06,0x0c, + 0x54,0x0c,0x48,0xa0,0x01,0x03,0x15,0x03,0x12,0x68,0xc0,0x40,0xc5,0x80,0x04,0x1a, + 0x30,0x9a,0x50,0x09,0xc3,0x11,0x81,0x19,0x08,0xdf,0x2c,0x83,0x33,0x04,0x23,0x06, + 0x0b,0x00,0x82,0x60,0xf0,0x84,0x03,0x1e,0x74,0xdc,0xd6,0x71,0xdb,0x88,0x81,0x01, + 0x80,0x20,0x18,0x44,0xe1,0x80,0x07,0x81,0x05,0x9f,0x7c,0x4c,0xf8,0xe4,0x63,0xc3, + 0x27,0x9f,0x11,0x83,0x03,0x00,0x41,0x30,0xb0,0xc6,0x01,0x14,0xca,0x00,0x1c,0x46, + 0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06,0xc1,0x86,0x31,0x88,0x8f,0x0d,0x63, + 0x10,0x1f,0x1b,0xc6,0x20,0x3e,0x23,0x06,0x0b,0x00,0x82,0x60,0xf0,0xb4,0x03,0x29, + 0x0c,0x42,0x30,0x08,0xc1,0x88,0x81,0x01,0x80,0x20,0x18,0x44,0xed,0x40,0x0a,0x81, + 0x15,0x81,0x7c,0xac,0x10,0xe4,0x63,0xc5,0x20,0x9f,0xd1,0x04,0x34,0x00,0x2c,0xf8, + 0x03,0xf9,0x98,0x1d,0x04,0xf1,0x31,0x2d,0x90,0x8f,0x69,0x82,0x7c,0x4c,0x1b,0xe4, + 0x63,0x06,0x28,0xc8,0xc7,0x82,0x3d,0x80,0x8f,0x7d,0xa3,0x00,0x1f,0xfb,0x48,0x01, + 0x3e,0xf6,0x95,0x02,0x7c,0x6c,0x58,0xe4,0x63,0x03,0x23,0x1f,0x1b,0x1a,0xf9,0xd8, + 0x50,0x0a,0xf0,0xb1,0xc1,0x14,0xe0,0x63,0xc3,0x29,0xc0,0x67,0x34,0x21,0x0f,0x02, + 0x0b,0x02,0xf9,0x58,0x20,0x0b,0xf2,0xb1,0x20,0x16,0xe0,0x33,0x62,0x70,0x00,0x20, + 0x08,0x06,0x16,0x49,0x84,0x83,0x29,0x98,0xc3,0x68,0x42,0x00,0x5c,0x40,0xd0,0x88, + 0xc1,0x01,0x80,0x20,0x18,0x58,0x27,0x41,0x0e,0xa9,0xa0,0x0e,0xa3,0x09,0x01,0x30, + 0x9a,0x20,0x04,0xa3,0x09,0x83,0x60,0x73,0x50,0xc9,0xc7,0x02,0x42,0x3e,0x46,0x07, + 0x97,0x7c,0x2c,0x28,0xe4,0x63,0x75,0x90,0xc9,0xc7,0x02,0x43,0x3e,0x23,0x06,0x07, + 0x00,0x82,0x60,0x60,0xc5,0x84,0x3b,0xcc,0x42,0x3d,0x8c,0x26,0x04,0xc0,0x68,0x82, + 0x10,0x8c,0x26,0x0c,0x82,0x25,0x03,0x7c,0x0c,0x19,0xe0,0x63,0xc7,0x00,0x9f,0x59, + 0x02,0x62,0xa0,0x62,0x30,0x86,0xff,0x60,0x06,0x2a,0x06,0x63,0xf8,0x0f,0x66,0xa0, + 0x62,0x30,0x86,0xff,0x60,0x06,0x2a,0x08,0x38,0x19,0xfe,0x83,0x19,0x6e,0x08,0x2c, + 0x34,0x98,0x65,0x28,0x98,0xa0,0x84,0x9b,0x38,0x0b,0xea,0x01,0x06,0x23,0x06,0x07, + 0x00,0x82,0x60,0x60,0xfd,0x04,0x3f,0x84,0x43,0x30,0x9a,0x10,0x0c,0xc3,0x11,0x01, + 0x3a,0x04,0x9f,0x15,0x27,0x01,0x83,0x11,0x83,0x03,0x00,0x41,0x30,0xb0,0xc6,0x02, + 0x24,0xca,0x21,0x98,0x65,0x30,0x8e,0x61,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34, + 0x61,0x10,0xac,0x1d,0x86,0xf8,0x98,0x3b,0x0c,0xf1,0xb1,0x77,0x18,0xe2,0x33,0x62, + 0xb0,0x00,0x20,0x08,0x06,0x4f,0x5b,0x90,0xc4,0x20,0x04,0x83,0x10,0x8c,0x18,0x18, + 0x00,0x08,0x82,0x41,0xd4,0x16,0x24,0x11,0x58,0x50,0xc8,0xc7,0x84,0x42,0x3e,0x36, + 0x14,0xf2,0x99,0x25,0x58,0x46,0x13,0x20,0x60,0x34,0x21,0x0a,0x46,0x13,0x24,0xc1, + 0x06,0x77,0x88,0x8f,0x0d,0xee,0x10,0x1f,0x1b,0xdc,0x21,0x3e,0x36,0x0c,0xf2,0xb1, + 0x61,0x90,0x8f,0x09,0x01,0x7c,0x8c,0x20,0xe4,0x63,0x42,0x00,0x9f,0x11,0x03,0x03, + 0x00,0x41,0x30,0x88,0xf8,0x02,0x2c,0x82,0x11,0x83,0x03,0x00,0x41,0x30,0x98,0xf8, + 0x22,0x26,0x02,0x96,0x30,0x25,0xa0,0x8f,0x29,0x02,0x7d,0x4c,0x19,0xe8,0x63,0x45, + 0x21,0x1f,0x0b,0x4a,0x02,0x3e,0x66,0x12,0x01,0x7d,0x46,0x13,0xbe,0x61,0x38,0x22, + 0x50,0x09,0xe1,0x9b,0x65,0x40,0x92,0xc0,0x14,0x81,0x3e,0x23,0x06,0x06,0x00,0x82, + 0x60,0x10,0x9d,0xc6,0x58,0x04,0x16,0xcc,0x84,0x7c,0x46,0x0c,0x0c,0x00,0x04,0xc1, + 0x20,0x4a,0x0d,0xb3,0x08,0xac,0x25,0x82,0xf8,0x8c,0x18,0x1c,0x00,0x08,0x82,0xc1, + 0xa4,0x1a,0x3f,0x11,0xc0,0xc4,0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0xab,0xe1,0x13, + 0xc1,0x4b,0x58,0xa0,0xc8,0x67,0x96,0x20,0x19,0xa8,0x18,0x04,0xc4,0x3a,0x86,0x23, + 0xda,0x00,0x27,0x82,0x6f,0x96,0x41,0x59,0x82,0xd1,0x84,0x35,0x00,0x46,0x13,0xd8, + 0x20,0x18,0x4d,0x68,0x03,0x61,0xc4,0x60,0x01,0x40,0x10,0x0c,0x1e,0xda,0x58,0x8b, + 0x41,0x08,0x06,0x21,0x18,0x31,0x30,0x00,0x10,0x04,0x83,0x88,0x36,0xd6,0x22,0xb0, + 0xa0,0x90,0x8f,0x09,0x85,0x7c,0x6c,0x28,0xe4,0x63,0x3c,0xa1,0xc5,0xc7,0x7a,0x42, + 0x8b,0x8f,0xf9,0x84,0x16,0x9f,0x11,0x83,0x05,0x00,0x41,0x30,0x78,0x78,0x63,0x2e, + 0x06,0x21,0x30,0x0a,0xc2,0xfa,0x20,0x34,0x60,0x30,0x62,0x70,0x00,0x20,0x08,0x06, + 0xd6,0x6e,0xe0,0x45,0x4f,0x04,0xa3,0x09,0x01,0x30,0x9a,0x20,0x04,0x26,0x04,0xf1, + 0x31,0x43,0x88,0x8f,0x05,0x02,0x7d,0x46,0x0c,0x0c,0x00,0x04,0xc1,0x20,0x1a,0x0f, + 0xdc,0x08,0x2c,0x68,0x0b,0xf9,0x58,0x5d,0x04,0xf1,0xb1,0x61,0x90,0x8f,0x05,0x99, + 0x7c,0x2c,0x18,0xe4,0x33,0x4b,0xb0,0x0c,0x74,0x0c,0xb8,0x62,0x08,0x8a,0x1d,0x24, + 0x03,0x1d,0x03,0x3e,0x18,0xb8,0xa0,0xe0,0x42,0x32,0xd0,0x31,0xe0,0x83,0x81,0x0b, + 0x0a,0x2e,0x24,0x03,0x1d,0x03,0x3e,0x18,0xb8,0xa0,0xe0,0x42,0x32,0x62,0xb0,0x00, + 0x20,0x08,0x06,0x8f,0x7b,0x94,0xc6,0x4a,0xa8,0x44,0x4a,0x0c,0x42,0x30,0x62,0x70, + 0x00,0x20,0x08,0x06,0x53,0x7b,0x88,0x46,0x30,0x17,0x23,0x06,0x07,0x00,0x82,0x60, + 0x30,0xb9,0x47,0x68,0x04,0x72,0x61,0x86,0x48,0xc0,0xc7,0x0c,0x91,0x80,0x8f,0x19, + 0x22,0x01,0x9f,0x11,0x83,0x05,0x00,0x41,0x30,0x78,0xe8,0x63,0x35,0x06,0x21,0x18, + 0x84,0x60,0xc4,0xc0,0x00,0x40,0x10,0x0c,0x22,0xfa,0x58,0x8d,0xc0,0x82,0x42,0x3e, + 0x26,0x14,0xf2,0xb1,0xa1,0x90,0xcf,0x88,0xc1,0x02,0x80,0x20,0x18,0x3c,0xf9,0x01, + 0x1b,0x36,0x51,0x13,0x34,0x31,0x08,0xc1,0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0xf8, + 0xd1,0x1a,0x81,0x5f,0x8c,0x18,0x1c,0x00,0x08,0x82,0xc1,0x94,0x1f,0xac,0x11,0xf4, + 0xc5,0x88,0x81,0x01,0x80,0x20,0x18,0x44,0xfb,0x71,0x1b,0x81,0x05,0xf7,0x20,0x9f, + 0x11,0x03,0x03,0x00,0x41,0x30,0x88,0xfa,0x43,0x37,0x82,0x11,0x83,0x03,0x00,0x41, + 0x30,0xb0,0xf8,0x23,0x37,0xfc,0xe2,0x35,0x46,0x13,0x82,0xc0,0x82,0xd3,0x90,0x8f, + 0x91,0x46,0x10,0x1f,0x2b,0x54,0x42,0x3e,0x16,0x08,0xf2,0xb1,0xa0,0x24,0xe4,0x63, + 0x42,0x49,0xc8,0xc7,0x86,0x92,0x90,0x8f,0x0d,0x33,0x01,0x1f,0x1b,0x66,0x02,0x3e, + 0x36,0xcc,0x04,0x7c,0x6c,0x1d,0xe0,0x03,0x06,0x23,0x06,0x07,0x00,0x82,0x60,0x60, + 0xa5,0x88,0x79,0xac,0x46,0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08, + 0x06,0x06,0x66,0x20,0x1f,0x23,0x02,0xf9,0x58,0xa0,0xc8,0xc7,0x8a,0x41,0x3e,0x16, + 0x2c,0xf2,0x31,0xa3,0x90,0x8f,0x05,0x8c,0x7c,0xac,0xe0,0x07,0xf8,0x18,0xc1,0x0f, + 0xf0,0xb1,0x81,0x1f,0xe0,0x33,0x4b,0xc0,0x0c,0x54,0x0c,0xc6,0xe2,0x1f,0xc4,0x40, + 0xc5,0x60,0x2c,0xfe,0x41,0x0c,0x54,0x0c,0xc6,0xe2,0x1f,0x84,0xfd,0x03,0x8e,0xc0, + 0x60,0xb8,0x21,0x20,0x11,0x30,0x98,0x65,0x68,0x88,0x60,0x96,0xc0,0x19,0xa8,0x18, + 0x50,0x46,0x50,0x9a,0x81,0x8a,0x01,0x65,0x04,0xa5,0x19,0xa8,0x18,0x50,0x46,0x50, + 0x9a,0x11,0x83,0x04,0x00,0x41,0x30,0x40,0xc8,0xe4,0x3e,0x78,0x84,0x47,0x66,0x64, + 0x18,0x31,0x48,0x00,0x10,0x04,0x03,0x84,0x4c,0xee,0x83,0x47,0x78,0x24,0x45,0x84, + 0x11,0x83,0x04,0x00,0x41,0x30,0x40,0xc8,0xe4,0x3e,0x78,0x84,0x47,0x64,0x24,0x18, + 0x31,0x48,0x00,0x10,0x04,0x03,0x84,0x4c,0xee,0x83,0x47,0x78,0x24,0x46,0x48,0x03, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static const unsigned char _fragmentMainMSL[] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, + 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, + 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, + 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, + 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, + 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, + 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, + 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, + 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x66,0x72,0x61,0x67, + 0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61, + 0x72,0x5f,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x20,0x5b,0x5b,0x63,0x6f, + 0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69, + 0x6e,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x30,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x69, + 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20, + 0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x75, + 0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x66,0x72,0x61,0x67,0x6d, + 0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x66,0x72,0x61,0x67, + 0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x28,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e, + 0x74,0x4d,0x61,0x69,0x6e,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74, + 0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61, + 0x6e,0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, + 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x26,0x20,0x46,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x62,0x61,0x73,0x65,0x54, + 0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x62, + 0x61,0x73,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x6f,0x75, + 0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x5f,0x32,0x30,0x37,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x64,0x6f,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x5f,0x38,0x34,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72, + 0x69,0x61,0x6c,0x2e,0x77,0x20,0x3e,0x20,0x30,0x2e,0x35,0x29,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x5f,0x38,0x34,0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, + 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x62,0x61,0x73,0x65,0x43,0x6f, + 0x6c,0x6f,0x72,0x20,0x2a,0x20,0x62,0x61,0x73,0x65,0x54,0x65,0x78,0x74,0x75,0x72, + 0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x61,0x73,0x65,0x53,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x2c,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c, + 0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x38,0x34,0x20,0x3d,0x20,0x46, + 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20, + 0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x7a,0x20,0x3e,0x20,0x30, + 0x2e,0x35,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x30,0x37,0x20,0x3d, + 0x20,0x5f,0x38,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x62,0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x33,0x20,0x5f,0x39,0x30,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f, + 0x72,0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, + 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x29,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x39,0x35, + 0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x69, + 0x7a,0x65,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x2e,0x78,0x79,0x7a,0x20,0x2d,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, + 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x29,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x39, + 0x39,0x20,0x3d,0x20,0x5f,0x38,0x34,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x28,0x31, + 0x2e,0x30,0x20,0x2d,0x20,0x28,0x30,0x2e,0x36,0x30,0x30,0x30,0x30,0x30,0x30,0x32, + 0x33,0x38,0x34,0x31,0x38,0x35,0x37,0x39,0x31,0x30,0x31,0x35,0x36,0x32,0x35,0x20, + 0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x78,0x29,0x29,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f, + 0x31,0x30,0x33,0x20,0x3d,0x20,0x31,0x2e,0x30,0x20,0x2b,0x20,0x28,0x32,0x2e,0x30, + 0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x78,0x29,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20, + 0x5f,0x31,0x30,0x34,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x66,0x6c,0x6f,0x61,0x74, + 0x33,0x28,0x30,0x2e,0x30,0x33,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x31,0x30,0x35, + 0x39,0x33,0x30,0x33,0x32,0x38,0x33,0x36,0x39,0x31,0x34,0x30,0x36,0x32,0x35,0x29, + 0x2c,0x20,0x5f,0x38,0x34,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x33,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x78,0x29,0x29,0x20, + 0x2a,0x20,0x5f,0x31,0x30,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x31,0x30,0x36,0x20,0x3d,0x20,0x6d,0x69,0x78, + 0x28,0x32,0x35,0x36,0x2e,0x30,0x2c,0x20,0x34,0x2e,0x30,0x2c,0x20,0x46,0x72,0x61, + 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61, + 0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x79,0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d, + 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65, + 0x72,0x69,0x61,0x6c,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x69,0x6e,0x74,0x20,0x5f,0x31,0x31,0x38,0x20,0x3d,0x20,0x69,0x6e,0x74,0x28, + 0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, + 0x2e,0x63,0x6f,0x75,0x6e,0x74,0x73,0x2e,0x78,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x32,0x30,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x30,0x20,0x3d,0x20, + 0x28,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x73,0x2e,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x2e,0x78,0x79,0x7a,0x20,0x2a, + 0x20,0x5f,0x38,0x34,0x2e,0x78,0x79,0x7a,0x29,0x20,0x2a,0x20,0x5f,0x31,0x30,0x33, + 0x29,0x20,0x2b,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x73,0x2e,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65,0x2e,0x78,0x79, + 0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x33,0x20,0x5f,0x31,0x32,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x5f,0x31,0x32,0x33,0x20,0x3d,0x20, + 0x30,0x3b,0x20,0x5f,0x31,0x32,0x33,0x20,0x3c,0x20,0x38,0x3b,0x20,0x5f,0x31,0x32, + 0x30,0x20,0x3d,0x20,0x5f,0x31,0x32,0x31,0x2c,0x20,0x5f,0x31,0x32,0x33,0x2b,0x2b, + 0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x31,0x32,0x33, + 0x20,0x3e,0x3d,0x20,0x5f,0x31,0x31,0x38,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x31,0x20,0x3d,0x20,0x5f, + 0x31,0x32,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f, + 0x31,0x38,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x38,0x31,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72, + 0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c, + 0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x32,0x33,0x5d,0x2e,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2e,0x77,0x20,0x3d,0x3d,0x20,0x30,0x2e, + 0x30,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x5f,0x31,0x38,0x30,0x20,0x3d,0x20,0x31,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x38, + 0x31,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c, + 0x69,0x7a,0x65,0x28,0x2d,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x32, + 0x33,0x5d,0x2e,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67, + 0x65,0x2e,0x78,0x79,0x7a,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x34,0x35, + 0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x32,0x33,0x5d, + 0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2e,0x78,0x79, + 0x7a,0x20,0x2d,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, + 0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f, + 0x31,0x34,0x36,0x20,0x3d,0x20,0x6c,0x65,0x6e,0x67,0x74,0x68,0x28,0x5f,0x31,0x34, + 0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x34,0x39,0x20, + 0x3d,0x20,0x5f,0x31,0x34,0x35,0x20,0x2f,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28, + 0x70,0x72,0x65,0x63,0x69,0x73,0x65,0x3a,0x3a,0x6d,0x61,0x78,0x28,0x5f,0x31,0x34, + 0x36,0x2c,0x20,0x39,0x2e,0x39,0x39,0x39,0x39,0x39,0x39,0x37,0x34,0x37,0x33,0x37, + 0x38,0x37,0x35,0x31,0x36,0x33,0x35,0x35,0x35,0x31,0x34,0x35,0x32,0x36,0x33,0x36, + 0x37,0x31,0x38,0x38,0x65,0x2d,0x30,0x35,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x20,0x5f,0x31,0x35,0x32,0x20,0x3d,0x20,0x31,0x2e,0x30,0x20,0x2f,0x20,0x28, + 0x31,0x2e,0x30,0x20,0x2b,0x20,0x28,0x5f,0x31,0x34,0x36,0x20,0x2a,0x20,0x5f,0x31, + 0x34,0x36,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x31,0x36,0x33, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31, + 0x32,0x33,0x5d,0x2e,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, + 0x67,0x65,0x2e,0x77,0x20,0x3e,0x20,0x30,0x2e,0x30,0x29,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x5f,0x31,0x36,0x33,0x20,0x3d,0x20,0x5f,0x31,0x35,0x32,0x20,0x2a,0x20, + 0x66,0x61,0x73,0x74,0x3a,0x3a,0x63,0x6c,0x61,0x6d,0x70,0x28,0x31,0x2e,0x30,0x20, + 0x2d,0x20,0x70,0x6f,0x77,0x72,0x28,0x5f,0x31,0x34,0x36,0x20,0x2f,0x20,0x46,0x72, + 0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c, + 0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x32,0x33,0x5d,0x2e,0x64,0x69,0x72,0x65, + 0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67,0x65,0x2e,0x77,0x2c,0x20,0x34,0x2e, + 0x30,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x36,0x33, + 0x20,0x3d,0x20,0x5f,0x31,0x35,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x20,0x5f,0x31,0x37,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72,0x61,0x67,0x6d, + 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68, + 0x74,0x73,0x5b,0x5f,0x31,0x32,0x33,0x5d,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x54,0x79,0x70,0x65,0x2e,0x77,0x20,0x3d,0x3d,0x20,0x32,0x2e,0x30,0x29,0x0a, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x37,0x39,0x20,0x3d,0x20,0x5f,0x31,0x36, + 0x33,0x20,0x2a,0x20,0x73,0x6d,0x6f,0x6f,0x74,0x68,0x73,0x74,0x65,0x70,0x28,0x46, + 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, + 0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x32,0x33,0x5d,0x2e,0x63,0x6f,0x6e, + 0x65,0x2e,0x79,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x32, + 0x33,0x5d,0x2e,0x63,0x6f,0x6e,0x65,0x2e,0x78,0x2c,0x20,0x64,0x6f,0x74,0x28,0x2d, + 0x5f,0x31,0x34,0x39,0x2c,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d, + 0x61,0x6c,0x69,0x7a,0x65,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31, + 0x32,0x33,0x5d,0x2e,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, + 0x67,0x65,0x2e,0x78,0x79,0x7a,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c,0x73, + 0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x37,0x39,0x20,0x3d,0x20,0x5f, + 0x31,0x36,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x38,0x30,0x20,0x3d,0x20,0x5f,0x31, + 0x37,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x5f,0x31,0x38,0x31,0x20,0x3d,0x20,0x5f,0x31,0x34,0x39,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x20,0x5f,0x31,0x38,0x33,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x63,0x6c, + 0x61,0x6d,0x70,0x28,0x64,0x6f,0x74,0x28,0x5f,0x39,0x30,0x2c,0x20,0x5f,0x31,0x38, + 0x31,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x31,0x20, + 0x3d,0x20,0x5f,0x31,0x32,0x30,0x20,0x2b,0x20,0x28,0x28,0x28,0x28,0x5f,0x39,0x39, + 0x20,0x2a,0x20,0x5f,0x31,0x38,0x33,0x29,0x20,0x2b,0x20,0x28,0x28,0x5f,0x31,0x30, + 0x34,0x20,0x2a,0x20,0x28,0x70,0x6f,0x77,0x72,0x28,0x66,0x61,0x73,0x74,0x3a,0x3a, + 0x63,0x6c,0x61,0x6d,0x70,0x28,0x64,0x6f,0x74,0x28,0x5f,0x39,0x30,0x2c,0x20,0x66, + 0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28,0x5f, + 0x31,0x38,0x31,0x20,0x2b,0x20,0x5f,0x39,0x35,0x29,0x29,0x2c,0x20,0x30,0x2e,0x30, + 0x2c,0x20,0x31,0x2e,0x30,0x29,0x2c,0x20,0x5f,0x31,0x30,0x36,0x29,0x20,0x2a,0x20, + 0x28,0x31,0x2e,0x30,0x20,0x2d,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, + 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61, + 0x6c,0x2e,0x79,0x20,0x2a,0x20,0x30,0x2e,0x35,0x29,0x29,0x29,0x29,0x20,0x2a,0x20, + 0x5f,0x31,0x38,0x33,0x29,0x29,0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, + 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73, + 0x5b,0x5f,0x31,0x32,0x33,0x5d,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x2e,0x78,0x79,0x7a, + 0x29,0x20,0x2a,0x20,0x5f,0x31,0x38,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x30, + 0x37,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x5f,0x31,0x32,0x30,0x2c, + 0x20,0x5f,0x38,0x34,0x2e,0x77,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x62,0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x20,0x77,0x68, + 0x69,0x6c,0x65,0x28,0x66,0x61,0x6c,0x73,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x6f,0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x20,0x3d,0x20,0x5f,0x32,0x30,0x37,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a, + 0x0a, +}; + +static const SceneShaderT sceneShaderFragmentMain = { "fragmentMain", _fragmentMainSPIRV, sizeof(_fragmentMainSPIRV), _fragmentMainDXIL, sizeof(_fragmentMainDXIL), _fragmentMainMSL, sizeof(_fragmentMainMSL) }; + +#endif diff --git a/src/singe.c b/src/singe.c index ce3cb23d0..408f55a07 100644 --- a/src/singe.c +++ b/src/singe.c @@ -58,6 +58,8 @@ LSEC_API int luaopen_ssl_config(lua_State *L); #include "util.h" #include "frameFile.h" #include "vfs.h" +#include "scene.h" +#include "model.h" #include "videoPlayer.h" #include "singe.h" @@ -431,13 +433,19 @@ static const int32_t _sdlMouseButtonToCode[] = { 0, 0, 2, 1, 3, 4 }; static int32_t _apiUnimplemented(lua_State *L, const char *method); static bool _argBoolean(lua_State *L, const char *method, int32_t index); static void _argCheck(lua_State *L, const char *method, int32_t minimum, int32_t maximum); +static QuatT _argEuler(lua_State *L, const char *method, int32_t index); +static float *_argFloatTable(lua_State *L, const char *method, int32_t index, int32_t *count); static FontT *_argFont(lua_State *L, const char *method, int32_t index); static int32_t _argInteger(lua_State *L, const char *method, int32_t index); +static int32_t _argMaterial(lua_State *L, const char *method, int32_t index); +static int32_t _argMesh(lua_State *L, const char *method, int32_t index); +static int32_t _argNode(lua_State *L, const char *method, int32_t index); static int64_t _argInteger64(lua_State *L, const char *method, int32_t index); static double _argNumber(lua_State *L, const char *method, int32_t index); static SoundT *_argSound(lua_State *L, const char *method, int32_t index); static SpriteT *_argSprite(lua_State *L, const char *method, int32_t index); static const char *_argString(lua_State *L, const char *method, int32_t index); +static Vec3T _argVec3(lua_State *L, const char *method, int32_t index); static VideoT *_argVideo(lua_State *L, const char *method, int32_t index); static ConfigT *_buildConfFromTable(lua_State *L, const ConfigT *base); static void _callLua(const char *func, const char *sig, ...); @@ -485,6 +493,8 @@ static void _overlayTouched(void); static void _pauseAllVideos(bool pause); static void _processKey(bool down, int32_t keysym, int32_t scancode); static void _progTrace(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +static int32_t _pushVec3(lua_State *L, Vec3T v); +static SDL_Texture *_sceneVideoSource(int32_t player); static void _pushConstants(lua_State *L); static void _putPixel(int32_t x, int32_t y, uint32_t pixel); static void _readColor(lua_State *L, const char *method, SDL_Color *color, uint8_t defaultAlpha); @@ -509,6 +519,16 @@ static void _takeScreenshot(void); static void _updatePauseState(void); static void _videoDestroy(VideoT *video); +static int32_t apiAnimationGetTime(lua_State *L); +static int32_t apiAnimationIsPlaying(lua_State *L); +static int32_t apiAnimationPause(lua_State *L); +static int32_t apiAnimationPlay(lua_State *L); +static int32_t apiAnimationResume(lua_State *L); +static int32_t apiAnimationSetTime(lua_State *L); +static int32_t apiAnimationStop(lua_State *L); +static int32_t apiCameraSet(lua_State *L); +static int32_t apiCameraSetOrthographic(lua_State *L); +static int32_t apiCameraSetPerspective(lua_State *L); static int32_t apiColorBackground(lua_State *L); static int32_t apiColorForeground(lua_State *L); static int32_t apiControllerGetAxis(lua_State *L); @@ -548,11 +568,61 @@ static int32_t apiKeyboardGetMode(lua_State *L); static int32_t apiKeyboardGetModifiers(lua_State *L); static int32_t apiKeyboardIsDown(lua_State *L); static int32_t apiKeyboardSetMode(lua_State *L); +static int32_t apiLightNew(lua_State *L); +static int32_t apiLightSetColor(lua_State *L); +static int32_t apiLightSetCone(lua_State *L); +static int32_t apiLightSetIntensity(lua_State *L); +static int32_t apiLightSetRange(lua_State *L); +static int32_t apiMaterialDelete(lua_State *L); +static int32_t apiMaterialNew(lua_State *L); +static int32_t apiMaterialSetBlend(lua_State *L); +static int32_t apiMaterialSetColor(lua_State *L); +static int32_t apiMaterialSetDoubleSided(lua_State *L); +static int32_t apiMaterialSetEmissive(lua_State *L); +static int32_t apiMaterialSetMetallic(lua_State *L); +static int32_t apiMaterialSetRoughness(lua_State *L); +static int32_t apiMaterialSetTexture(lua_State *L); +static int32_t apiMaterialSetUnlit(lua_State *L); +static int32_t apiMaterialSetVideo(lua_State *L); +static int32_t apiMeshBox(lua_State *L); +static int32_t apiMeshCone(lua_State *L); +static int32_t apiMeshCylinder(lua_State *L); +static int32_t apiMeshDelete(lua_State *L); +static int32_t apiMeshNew(lua_State *L); +static int32_t apiMeshPlane(lua_State *L); +static int32_t apiMeshSphere(lua_State *L); +static int32_t apiMeshTorus(lua_State *L); +static int32_t apiModelDelete(lua_State *L); +static int32_t apiModelGetAnimations(lua_State *L); +static int32_t apiModelInstance(lua_State *L); +static int32_t apiModelLoad(lua_State *L); static int32_t apiMouseGetPosition(lua_State *L); static int32_t apiMouseHowMany(lua_State *L); static int32_t apiMouseSetCaptured(lua_State *L); static int32_t apiMouseSetEnabled(lua_State *L); static int32_t apiMouseSetMode(lua_State *L); +static int32_t apiNodeDelete(lua_State *L); +static int32_t apiNodeFind(lua_State *L); +static int32_t apiNodeGetChildren(lua_State *L); +static int32_t apiNodeGetName(lua_State *L); +static int32_t apiNodeGetParent(lua_State *L); +static int32_t apiNodeGetPosition(lua_State *L); +static int32_t apiNodeGetQuaternion(lua_State *L); +static int32_t apiNodeGetRotation(lua_State *L); +static int32_t apiNodeGetScale(lua_State *L); +static int32_t apiNodeGetWorldPosition(lua_State *L); +static int32_t apiNodeLookAt(lua_State *L); +static int32_t apiNodeMove(lua_State *L); +static int32_t apiNodeNew(lua_State *L); +static int32_t apiNodeRotate(lua_State *L); +static int32_t apiNodeSetMesh(lua_State *L); +static int32_t apiNodeSetName(lua_State *L); +static int32_t apiNodeSetParent(lua_State *L); +static int32_t apiNodeSetPosition(lua_State *L); +static int32_t apiNodeSetQuaternion(lua_State *L); +static int32_t apiNodeSetRotation(lua_State *L); +static int32_t apiNodeSetScale(lua_State *L); +static int32_t apiNodeSetVisible(lua_State *L); static int32_t apiOsClock(lua_State *L); static int32_t apiOverlayBox(lua_State *L); static int32_t apiOverlayCircle(lua_State *L); @@ -564,6 +634,13 @@ static int32_t apiOverlayLine(lua_State *L); static int32_t apiOverlayPlot(lua_State *L); static int32_t apiOverlayPrint(lua_State *L); static int32_t apiOverlaySetResolution(lua_State *L); +static int32_t apiSceneEnable(lua_State *L); +static int32_t apiSceneGetSize(lua_State *L); +static int32_t apiSceneProject(lua_State *L); +static int32_t apiSceneSetAmbient(lua_State *L); +static int32_t apiSceneSetAntialias(lua_State *L); +static int32_t apiSceneSetBackground(lua_State *L); +static int32_t apiSceneUnproject(lua_State *L); static int32_t apiScriptExecute(lua_State *L); static int32_t apiScriptPush(lua_State *L); static int32_t apiSingeGetAudioCalibration(lua_State *L); @@ -681,11 +758,80 @@ static FontT *_argFont(lua_State *L, const char *method, int32_t index) { } +// The scene's rotation arguments: three Euler angles in degrees. +static QuatT _argEuler(lua_State *L, const char *method, int32_t index) { + return quatFromEuler((float)_argNumber(L, method, index), (float)_argNumber(L, method, index + 1), (float)_argNumber(L, method, index + 2)); +} + + +// A table of numbers as a float array (caller frees). A nil argument gives NULL and count 0. +static float *_argFloatTable(lua_State *L, const char *method, int32_t index, int32_t *count) { + float *values; + int32_t x; + + *count = 0; + if (lua_isnoneornil(L, index)) { + return NULL; + } + if (!lua_istable(L, index)) { + _luaDie(L, method, "Argument %d must be a table of numbers.", index); + } + *count = (int32_t)lua_rawlen(L, index); + values = SDL_calloc((size_t)SDL_max(*count, 1), sizeof(float)); + if (values == NULL) { + _luaDie(L, method, "Out of memory."); + } + for (x = 0; x < *count; x++) { + lua_rawgeti(L, index, x + 1); + if (!lua_isnumber(L, -1)) { + SDL_free(values); + _luaDie(L, method, "Argument %d, element %d is not a number.", index, x + 1); + } + values[x] = (float)lua_tonumber(L, -1); + lua_pop(L, 1); + } + return values; +} + + static int32_t _argInteger(lua_State *L, const char *method, int32_t index) { return (int32_t)_argNumber(L, method, index); } +// A material handle argument, checked. +static int32_t _argMaterial(lua_State *L, const char *method, int32_t index) { + int32_t material = _argInteger(L, method, index); + + if (!materialValid(material)) { + _luaDie(L, method, "No material %d.", material); + } + return material; +} + + +// A mesh handle argument, checked. +static int32_t _argMesh(lua_State *L, const char *method, int32_t index) { + int32_t mesh = _argInteger(L, method, index); + + if (!meshValid(mesh)) { + _luaDie(L, method, "No mesh %d.", mesh); + } + return mesh; +} + + +// A scene node handle argument, checked. +static int32_t _argNode(lua_State *L, const char *method, int32_t index) { + int32_t node = _argInteger(L, method, index); + + if (!nodeValid(node)) { + _luaDie(L, method, "No node %d.", node); + } + return node; +} + + static int64_t _argInteger64(lua_State *L, const char *method, int32_t index) { return (int64_t)_argNumber(L, method, index); } @@ -735,6 +881,12 @@ static const char *_argString(lua_State *L, const char *method, int32_t index) { } +// Three numbers as a vector. +static Vec3T _argVec3(lua_State *L, const char *method, int32_t index) { + return vec3((float)_argNumber(L, method, index), (float)_argNumber(L, method, index + 1), (float)_argNumber(L, method, index + 2)); +} + + static VideoT *_argVideo(lua_State *L, const char *method, int32_t index) { int32_t id = _argInteger(L, method, index); VideoT *video = NULL; @@ -1917,6 +2069,33 @@ static void _progTrace(const char *fmt, ...) { } +// Pushes a vector as three results. +static int32_t _pushVec3(lua_State *L, Vec3T v) { + lua_pushnumber(L, v.x); + lua_pushnumber(L, v.y); + lua_pushnumber(L, v.z); + return 3; +} + + +// A player's current frame for the 3D scene's video materials: the disc's texture is already +// updated for this frame; a loaded video is advanced here (drawing it on the overlay too is harmless). +static SDL_Texture *_sceneVideoSource(int32_t player) { + VideoT *video; + + if ((player == _global.videoHandle) && (player >= 0)) { + return _global.videoTexture; + } + for (video = _global.videoList; video != NULL; video = video->hh.next) { + if (video->handle == player) { + videoUpdate(video->handle, &video->texture); + return video->texture; + } + } + return NULL; +} + + // Constants every script (and controls.cfg) can rely on. These are the single source of truth. static void _pushConstants(lua_State *L) { int32_t x = 0; @@ -1965,6 +2144,13 @@ static void _pushConstants(lua_State *L) { lua_setglobal(L, "DISC_PAUSED"); lua_pushinteger(L, DISC_EJECTED); lua_setglobal(L, "DISC_EJECTED"); + // 3D light types + lua_pushinteger(L, LIGHT_DIRECTIONAL); + lua_setglobal(L, "LIGHT_DIRECTIONAL"); + lua_pushinteger(L, LIGHT_POINT); + lua_setglobal(L, "LIGHT_POINT"); + lua_pushinteger(L, LIGHT_SPOT); + lua_setglobal(L, "LIGHT_SPOT"); lua_pushinteger(L, -1); lua_setglobal(L, "SOUND_ERROR_INVALID"); @@ -2405,6 +2591,147 @@ static void _videoDestroy(VideoT *video) { // ===== Lua API ===== +// seconds = animationGetTime(node) +static int32_t apiAnimationGetTime(lua_State *L) { + int32_t root; + + _argCheck(L, "animationGetTime", 1, 1); + root = _argNode(L, "animationGetTime", 1); + if (modelRootOf(root) < 0) { + _luaDie(L, "animationGetTime", "Node %d is not a model instance.", root); + } + lua_pushnumber(L, animationGetTime(root)); + return 1; +} + + +// playing = animationIsPlaying(node) +static int32_t apiAnimationIsPlaying(lua_State *L) { + _argCheck(L, "animationIsPlaying", 1, 1); + lua_pushboolean(L, animationIsPlaying(_argNode(L, "animationIsPlaying", 1))); + return 1; +} + + +// animationPause(node) +static int32_t apiAnimationPause(lua_State *L) { + int32_t root; + + _argCheck(L, "animationPause", 1, 1); + root = _argNode(L, "animationPause", 1); + if (!animationPause(root)) { + _luaDie(L, "animationPause", "Node %d is not a model instance.", root); + } + return 0; +} + + +// animationPlay(node, nameOrIndex [, loop [, speed]]): on a model instance's root node +static int32_t apiAnimationPlay(lua_State *L) { + int32_t root; + int32_t model; + int32_t index; + bool loop = true; + float speed = 1.0f; + + _argCheck(L, "animationPlay", 2, 4); + root = _argNode(L, "animationPlay", 1); + model = modelRootOf(root); + if (model < 0) { + _luaDie(L, "animationPlay", "Node %d is not a model instance.", root); + } + if (lua_type(L, 2) == LUA_TSTRING) { + index = modelAnimationIndex(model, lua_tostring(L, 2)); + if (index < 0) { + _luaDie(L, "animationPlay", "Model %d has no animation named %s.", model, lua_tostring(L, 2)); + } + } else { + // Scripts number animations from 1, as modelGetAnimations lists them. + index = _argInteger(L, "animationPlay", 2) - 1; + } + if (lua_gettop(L) >= 3) { + loop = _argBoolean(L, "animationPlay", 3); + } + if (lua_gettop(L) >= 4) { + speed = (float)_argNumber(L, "animationPlay", 4); + } + if (!animationPlay(root, index, loop, speed)) { + _luaDie(L, "animationPlay", "Model %d has no animation %d.", model, index + 1); + } + _luaTrace(L, "animationPlay", "node %d animation %d%s x%.2f", root, index + 1, loop ? " looping" : "", speed); + return 0; +} + + +// animationResume(node) +static int32_t apiAnimationResume(lua_State *L) { + int32_t root; + + _argCheck(L, "animationResume", 1, 1); + root = _argNode(L, "animationResume", 1); + if (!animationResume(root)) { + _luaDie(L, "animationResume", "Node %d has no animation to resume.", root); + } + return 0; +} + + +// animationSetTime(node, seconds) +static int32_t apiAnimationSetTime(lua_State *L) { + int32_t root; + + _argCheck(L, "animationSetTime", 2, 2); + root = _argNode(L, "animationSetTime", 1); + if (!animationSetTime(root, _argNumber(L, "animationSetTime", 2))) { + _luaDie(L, "animationSetTime", "Node %d has no animation.", root); + } + return 0; +} + + +// animationStop(node) +static int32_t apiAnimationStop(lua_State *L) { + int32_t root; + + _argCheck(L, "animationStop", 1, 1); + root = _argNode(L, "animationStop", 1); + if (!animationStop(root)) { + _luaDie(L, "animationStop", "Node %d is not a model instance.", root); + } + return 0; +} + + +// Any node can be the camera (it looks down its own -Z); -1 restores the default view. +static int32_t apiCameraSet(lua_State *L) { + int32_t node; + + _argCheck(L, "cameraSet", 1, 1); + node = _argInteger(L, "cameraSet", 1); + if (!cameraSet(node)) { + _luaDie(L, "cameraSet", "No node %d.", node); + } + _luaTrace(L, "cameraSet", "%d", node); + return 0; +} + + +// cameraSetOrthographic(height, near, far) +static int32_t apiCameraSetOrthographic(lua_State *L) { + _argCheck(L, "cameraSetOrthographic", 3, 3); + cameraSetOrthographic((float)_argNumber(L, "cameraSetOrthographic", 1), (float)_argNumber(L, "cameraSetOrthographic", 2), (float)_argNumber(L, "cameraSetOrthographic", 3)); + return 0; +} + + +// cameraSetPerspective(fovDegrees, near, far) +static int32_t apiCameraSetPerspective(lua_State *L) { + _argCheck(L, "cameraSetPerspective", 3, 3); + cameraSetPerspective((float)_argNumber(L, "cameraSetPerspective", 1), (float)_argNumber(L, "cameraSetPerspective", 2), (float)_argNumber(L, "cameraSetPerspective", 3)); + return 0; +} + + // colorBackground(r, g, b[, a]) Default alpha is transparent so overlayPrint shows the video through. static int32_t apiColorBackground(lua_State *L) { _readColor(L, "colorBackground", &_global.colorBackground, SDL_ALPHA_TRANSPARENT); @@ -2998,6 +3325,455 @@ static int32_t apiKeyboardSetMode(lua_State *L) { } +// node = lightNew(type [, parent]): a node carrying a light +static int32_t apiLightNew(lua_State *L) { + int32_t type; + int32_t parent = SCENE_ROOT_NODE; + int32_t node; + + _argCheck(L, "lightNew", 1, 2); + type = _argInteger(L, "lightNew", 1); + if ((type < LIGHT_DIRECTIONAL) || (type > LIGHT_SPOT)) { + _luaDie(L, "lightNew", "Unknown light type %d.", type); + } + if (lua_gettop(L) >= 2) { + parent = _argNode(L, "lightNew", 2); + } + node = lightNew((LightTypeE)type, parent); + if (node < 0) { + _luaDie(L, "lightNew", "3D is not available on this machine."); + } + _luaTrace(L, "lightNew", "%d type %d under %d", node, type, parent); + lua_pushinteger(L, node); + return 1; +} + + +// lightSetColor(node, r, g, b) +static int32_t apiLightSetColor(lua_State *L) { + int32_t node; + + _argCheck(L, "lightSetColor", 4, 4); + node = _argNode(L, "lightSetColor", 1); + if (!lightSetColor(node, (uint8_t)_argInteger(L, "lightSetColor", 2), (uint8_t)_argInteger(L, "lightSetColor", 3), (uint8_t)_argInteger(L, "lightSetColor", 4))) { + _luaDie(L, "lightSetColor", "Node %d is not a light.", node); + } + return 0; +} + + +// lightSetCone(node, innerDegrees, outerDegrees) for spot lights +static int32_t apiLightSetCone(lua_State *L) { + int32_t node; + + _argCheck(L, "lightSetCone", 3, 3); + node = _argNode(L, "lightSetCone", 1); + if (!lightSetCone(node, (float)_argNumber(L, "lightSetCone", 2), (float)_argNumber(L, "lightSetCone", 3))) { + _luaDie(L, "lightSetCone", "Node %d is not a light.", node); + } + return 0; +} + + +// lightSetIntensity(node, intensity) +static int32_t apiLightSetIntensity(lua_State *L) { + int32_t node; + + _argCheck(L, "lightSetIntensity", 2, 2); + node = _argNode(L, "lightSetIntensity", 1); + if (!lightSetIntensity(node, (float)_argNumber(L, "lightSetIntensity", 2))) { + _luaDie(L, "lightSetIntensity", "Node %d is not a light.", node); + } + return 0; +} + + +// lightSetRange(node, range): 0 for unlimited +static int32_t apiLightSetRange(lua_State *L) { + int32_t node; + + _argCheck(L, "lightSetRange", 2, 2); + node = _argNode(L, "lightSetRange", 1); + if (!lightSetRange(node, (float)_argNumber(L, "lightSetRange", 2))) { + _luaDie(L, "lightSetRange", "Node %d is not a light.", node); + } + return 0; +} + + +// materialDelete(material) +static int32_t apiMaterialDelete(lua_State *L) { + _argCheck(L, "materialDelete", 1, 1); + materialDelete(_argMaterial(L, "materialDelete", 1)); + return 0; +} + + +// material = materialNew(): white, half rough +static int32_t apiMaterialNew(lua_State *L) { + int32_t material; + + _argCheck(L, "materialNew", 0, 0); + material = materialNew(); + if (material < 0) { + _luaDie(L, "materialNew", "3D is not available on this machine."); + } + _luaTrace(L, "materialNew", "%d", material); + lua_pushinteger(L, material); + return 1; +} + + +// materialSetBlend(material, bool): alpha blended instead of opaque +static int32_t apiMaterialSetBlend(lua_State *L) { + _argCheck(L, "materialSetBlend", 2, 2); + materialSetBlend(_argMaterial(L, "materialSetBlend", 1), _argBoolean(L, "materialSetBlend", 2)); + return 0; +} + + +// materialSetColor(material, r, g, b [, a]) +static int32_t apiMaterialSetColor(lua_State *L) { + int32_t material; + int32_t a = SDL_ALPHA_OPAQUE; + + _argCheck(L, "materialSetColor", 4, 5); + material = _argMaterial(L, "materialSetColor", 1); + if (lua_gettop(L) >= 5) { + a = _argInteger(L, "materialSetColor", 5); + } + materialSetColor(material, (uint8_t)_argInteger(L, "materialSetColor", 2), (uint8_t)_argInteger(L, "materialSetColor", 3), (uint8_t)_argInteger(L, "materialSetColor", 4), (uint8_t)a); + return 0; +} + + +// materialSetDoubleSided(material, bool) +static int32_t apiMaterialSetDoubleSided(lua_State *L) { + _argCheck(L, "materialSetDoubleSided", 2, 2); + materialSetDoubleSided(_argMaterial(L, "materialSetDoubleSided", 1), _argBoolean(L, "materialSetDoubleSided", 2)); + return 0; +} + + +// materialSetEmissive(material, r, g, b) +static int32_t apiMaterialSetEmissive(lua_State *L) { + _argCheck(L, "materialSetEmissive", 4, 4); + materialSetEmissive(_argMaterial(L, "materialSetEmissive", 1), (uint8_t)_argInteger(L, "materialSetEmissive", 2), (uint8_t)_argInteger(L, "materialSetEmissive", 3), (uint8_t)_argInteger(L, "materialSetEmissive", 4)); + return 0; +} + + +// materialSetMetallic(material, 0..1) +static int32_t apiMaterialSetMetallic(lua_State *L) { + _argCheck(L, "materialSetMetallic", 2, 2); + materialSetMetallic(_argMaterial(L, "materialSetMetallic", 1), (float)_argNumber(L, "materialSetMetallic", 2)); + return 0; +} + + +// materialSetRoughness(material, 0..1) +static int32_t apiMaterialSetRoughness(lua_State *L) { + _argCheck(L, "materialSetRoughness", 2, 2); + materialSetRoughness(_argMaterial(L, "materialSetRoughness", 1), (float)_argNumber(L, "materialSetRoughness", 2)); + return 0; +} + + +// materialSetTexture(material [, sprite]): a sprite's image as the base colour; none clears it +static int32_t apiMaterialSetTexture(lua_State *L) { + int32_t material; + SpriteT *sprite = NULL; + + _argCheck(L, "materialSetTexture", 1, 2); + material = _argMaterial(L, "materialSetTexture", 1); + if (lua_gettop(L) >= 2) { + sprite = _argSprite(L, "materialSetTexture", 2); + } + if (!materialSetTexture(material, sprite ? sprite->originalSurface : NULL)) { + _luaDie(L, "materialSetTexture", "%s", SDL_GetError()); + } + return 0; +} + + +// materialSetUnlit(material, bool): shows the base colour as is +static int32_t apiMaterialSetUnlit(lua_State *L) { + _argCheck(L, "materialSetUnlit", 2, 2); + materialSetUnlit(_argMaterial(L, "materialSetUnlit", 1), _argBoolean(L, "materialSetUnlit", 2)); + return 0; +} + + +// materialSetVideo(material [, video]): the disc (or a loaded video) as the base colour texture +static int32_t apiMaterialSetVideo(lua_State *L) { + int32_t material; + int32_t player; + VideoT *video; + + _argCheck(L, "materialSetVideo", 1, 2); + material = _argMaterial(L, "materialSetVideo", 1); + if (lua_gettop(L) >= 2) { + video = _argVideo(L, "materialSetVideo", 2); + player = video->handle; + } else { + if (_global.videoHandle < 0) { + _luaDie(L, "materialSetVideo", "This game has no disc."); + } + player = _global.videoHandle; + } + materialSetVideo(material, player); + return 0; +} + + +// mesh = meshBox(width, height, depth) +static int32_t apiMeshBox(lua_State *L) { + int32_t mesh; + + _argCheck(L, "meshBox", 3, 3); + mesh = meshBox((float)_argNumber(L, "meshBox", 1), (float)_argNumber(L, "meshBox", 2), (float)_argNumber(L, "meshBox", 3)); + if (mesh < 0) { + _luaDie(L, "meshBox", "Unable to create the mesh."); + } + _luaTrace(L, "meshBox", "%d", mesh); + lua_pushinteger(L, mesh); + return 1; +} + + +// mesh = meshCone(radius, height [, segments]) +static int32_t apiMeshCone(lua_State *L) { + int32_t mesh; + int32_t segments = 24; + + _argCheck(L, "meshCone", 2, 3); + if (lua_gettop(L) >= 3) { + segments = _argInteger(L, "meshCone", 3); + } + mesh = meshCone((float)_argNumber(L, "meshCone", 1), (float)_argNumber(L, "meshCone", 2), segments); + if (mesh < 0) { + _luaDie(L, "meshCone", "Unable to create the mesh."); + } + _luaTrace(L, "meshCone", "%d", mesh); + lua_pushinteger(L, mesh); + return 1; +} + + +// mesh = meshCylinder(radius, height [, segments]) +static int32_t apiMeshCylinder(lua_State *L) { + int32_t mesh; + int32_t segments = 24; + + _argCheck(L, "meshCylinder", 2, 3); + if (lua_gettop(L) >= 3) { + segments = _argInteger(L, "meshCylinder", 3); + } + mesh = meshCylinder((float)_argNumber(L, "meshCylinder", 1), (float)_argNumber(L, "meshCylinder", 2), segments); + if (mesh < 0) { + _luaDie(L, "meshCylinder", "Unable to create the mesh."); + } + _luaTrace(L, "meshCylinder", "%d", mesh); + lua_pushinteger(L, mesh); + return 1; +} + + +// meshDelete(mesh) +static int32_t apiMeshDelete(lua_State *L) { + _argCheck(L, "meshDelete", 1, 1); + meshDelete(_argMesh(L, "meshDelete", 1)); + return 0; +} + + +// mesh = meshNew(positions, normals, uvs, indices): tables of numbers; normals and uvs may be nil +static int32_t apiMeshNew(lua_State *L) { + float *positions; + float *normals; + float *uvs; + float *indexValues; + uint32_t *indices; + int32_t positionCount; + int32_t normalCount; + int32_t uvCount; + int32_t indexCount; + int32_t vertexCount; + int32_t x; + int32_t mesh; + + _argCheck(L, "meshNew", 4, 4); + positions = _argFloatTable(L, "meshNew", 1, &positionCount); + normals = _argFloatTable(L, "meshNew", 2, &normalCount); + uvs = _argFloatTable(L, "meshNew", 3, &uvCount); + indexValues = _argFloatTable(L, "meshNew", 4, &indexCount); + vertexCount = positionCount / 3; + if ((positionCount == 0) || (positionCount % 3 != 0)) { + _luaDie(L, "meshNew", "Positions must hold three numbers per vertex."); + } + if ((normals != NULL) && (normalCount != positionCount)) { + _luaDie(L, "meshNew", "Normals must hold three numbers per vertex, or be nil."); + } + if ((uvs != NULL) && (uvCount != vertexCount * 2)) { + _luaDie(L, "meshNew", "Texture coordinates must hold two numbers per vertex, or be nil."); + } + if ((indexCount < 3) || (indexCount % 3 != 0)) { + _luaDie(L, "meshNew", "Indices must hold three vertex numbers per triangle."); + } + indices = SDL_calloc((size_t)indexCount, sizeof(uint32_t)); + if (indices == NULL) { + _luaDie(L, "meshNew", "Out of memory."); + } + // Scripts number vertices from 1, as they appear in the positions table. + for (x = 0; x < indexCount; x++) { + if ((indexValues[x] < 1.0f) || (indexValues[x] > (float)vertexCount)) { + _luaDie(L, "meshNew", "Index %d refers to vertex %d; there are %d.", x + 1, (int32_t)indexValues[x], vertexCount); + } + indices[x] = (uint32_t)indexValues[x] - 1; + } + mesh = meshNew(positions, normals, uvs, vertexCount, indices, indexCount); + SDL_free(positions); + SDL_free(normals); + SDL_free(uvs); + SDL_free(indexValues); + SDL_free(indices); + if (mesh < 0) { + _luaDie(L, "meshNew", "Unable to create the mesh."); + } + _luaTrace(L, "meshNew", "%d (%d vertices, %d triangles)", mesh, vertexCount, indexCount / 3); + lua_pushinteger(L, mesh); + return 1; +} + + +// mesh = meshPlane(width, depth): flat in XZ, facing up +static int32_t apiMeshPlane(lua_State *L) { + int32_t mesh; + + _argCheck(L, "meshPlane", 2, 2); + mesh = meshPlane((float)_argNumber(L, "meshPlane", 1), (float)_argNumber(L, "meshPlane", 2)); + if (mesh < 0) { + _luaDie(L, "meshPlane", "Unable to create the mesh."); + } + _luaTrace(L, "meshPlane", "%d", mesh); + lua_pushinteger(L, mesh); + return 1; +} + + +// mesh = meshSphere(radius [, segments]) +static int32_t apiMeshSphere(lua_State *L) { + int32_t mesh; + int32_t segments = 32; + + _argCheck(L, "meshSphere", 1, 2); + if (lua_gettop(L) >= 2) { + segments = _argInteger(L, "meshSphere", 2); + } + mesh = meshSphere((float)_argNumber(L, "meshSphere", 1), segments); + if (mesh < 0) { + _luaDie(L, "meshSphere", "Unable to create the mesh."); + } + _luaTrace(L, "meshSphere", "%d", mesh); + lua_pushinteger(L, mesh); + return 1; +} + + +// mesh = meshTorus(radius, tubeRadius [, segments]) +static int32_t apiMeshTorus(lua_State *L) { + int32_t mesh; + int32_t segments = 32; + + _argCheck(L, "meshTorus", 2, 3); + if (lua_gettop(L) >= 3) { + segments = _argInteger(L, "meshTorus", 3); + } + mesh = meshTorus((float)_argNumber(L, "meshTorus", 1), (float)_argNumber(L, "meshTorus", 2), segments); + if (mesh < 0) { + _luaDie(L, "meshTorus", "Unable to create the mesh."); + } + _luaTrace(L, "meshTorus", "%d", mesh); + lua_pushinteger(L, mesh); + return 1; +} + + +// modelDelete(model): frees its meshes and materials; instances keep their nodes, bare +static int32_t apiModelDelete(lua_State *L) { + int32_t model; + + _argCheck(L, "modelDelete", 1, 1); + model = _argInteger(L, "modelDelete", 1); + if (!modelDelete(model)) { + _luaDie(L, "modelDelete", "No model %d.", model); + } + return 0; +} + + +// names = modelGetAnimations(model): a table of animation names, in file order +static int32_t apiModelGetAnimations(lua_State *L) { + int32_t model; + int32_t count; + int32_t x; + + _argCheck(L, "modelGetAnimations", 1, 1); + model = _argInteger(L, "modelGetAnimations", 1); + if (!modelValid(model)) { + _luaDie(L, "modelGetAnimations", "No model %d.", model); + } + count = modelGetAnimationCount(model); + lua_createtable(L, count, 0); + for (x = 0; x < count; x++) { + lua_pushstring(L, modelGetAnimationName(model, x)); + lua_rawseti(L, -2, x + 1); + } + return 1; +} + + +// node = modelInstance(model [, parent]): the model's node tree under parent; returns its root +static int32_t apiModelInstance(lua_State *L) { + int32_t model; + int32_t parent = SCENE_ROOT_NODE; + int32_t root; + + _argCheck(L, "modelInstance", 1, 2); + model = _argInteger(L, "modelInstance", 1); + if (!modelValid(model)) { + _luaDie(L, "modelInstance", "No model %d.", model); + } + if (lua_gettop(L) >= 2) { + parent = _argNode(L, "modelInstance", 2); + } + root = modelInstance(model, parent); + if (root < 0) { + _luaDie(L, "modelInstance", "Unable to instance model %d.", model); + } + _luaTrace(L, "modelInstance", "%d -> node %d under %d", model, root, parent); + lua_pushinteger(L, root); + return 1; +} + + +// model = modelLoad(name): a self-contained .glb through the vfs +static int32_t apiModelLoad(lua_State *L) { + const char *name; + int32_t model; + + _argCheck(L, "modelLoad", 1, 1); + name = _argString(L, "modelLoad", 1); + model = modelLoad(name); + if (model < 0) { + _luaDie(L, "modelLoad", "%s", modelLastError()); + } + _luaTrace(L, "modelLoad", "%d %s", model, name); + lua_pushinteger(L, model); + return 1; +} + + // x, y = mouseGetPosition(mouse) static int32_t apiMouseGetPosition(lua_State *L) { int32_t m = 0; @@ -3064,6 +3840,270 @@ static int32_t apiMouseSetMode(lua_State *L) { } +// nodeDelete(node): the node and everything under it +static int32_t apiNodeDelete(lua_State *L) { + int32_t node; + + _argCheck(L, "nodeDelete", 1, 1); + node = _argNode(L, "nodeDelete", 1); + if (!nodeDelete(node)) { + _luaDie(L, "nodeDelete", "The root node cannot be deleted."); + } + _luaTrace(L, "nodeDelete", "%d", node); + return 0; +} + + +// node = nodeFind(name [, root]): by name, below root; nil when absent +static int32_t apiNodeFind(lua_State *L) { + int32_t root = SCENE_ROOT_NODE; + int32_t found; + + _argCheck(L, "nodeFind", 1, 2); + if (lua_gettop(L) >= 2) { + root = _argNode(L, "nodeFind", 2); + } + found = nodeFind(root, _argString(L, "nodeFind", 1)); + if (found < 0) { + lua_pushnil(L); + } else { + lua_pushinteger(L, found); + } + return 1; +} + + +// children = nodeGetChildren(node): a table of handles +static int32_t apiNodeGetChildren(lua_State *L) { + int32_t node; + int32_t count; + int32_t x; + + _argCheck(L, "nodeGetChildren", 1, 1); + node = _argNode(L, "nodeGetChildren", 1); + count = nodeGetChildCount(node); + lua_createtable(L, count, 0); + for (x = 0; x < count; x++) { + lua_pushinteger(L, nodeGetChild(node, x)); + lua_rawseti(L, -2, x + 1); + } + return 1; +} + + +// name = nodeGetName(node) +static int32_t apiNodeGetName(lua_State *L) { + _argCheck(L, "nodeGetName", 1, 1); + lua_pushstring(L, nodeGetName(_argNode(L, "nodeGetName", 1))); + return 1; +} + + +// parent = nodeGetParent(node): nil for the root +static int32_t apiNodeGetParent(lua_State *L) { + int32_t parent; + + _argCheck(L, "nodeGetParent", 1, 1); + parent = nodeGetParent(_argNode(L, "nodeGetParent", 1)); + if (parent < 0) { + lua_pushnil(L); + } else { + lua_pushinteger(L, parent); + } + return 1; +} + + +// x, y, z = nodeGetPosition(node) +static int32_t apiNodeGetPosition(lua_State *L) { + _argCheck(L, "nodeGetPosition", 1, 1); + return _pushVec3(L, nodeGetPosition(_argNode(L, "nodeGetPosition", 1))); +} + + +// x, y, z, w = nodeGetQuaternion(node) +static int32_t apiNodeGetQuaternion(lua_State *L) { + QuatT q; + + _argCheck(L, "nodeGetQuaternion", 1, 1); + q = nodeGetRotation(_argNode(L, "nodeGetQuaternion", 1)); + lua_pushnumber(L, q.x); + lua_pushnumber(L, q.y); + lua_pushnumber(L, q.z); + lua_pushnumber(L, q.w); + return 4; +} + + +// x, y, z = nodeGetRotation(node): Euler degrees +static int32_t apiNodeGetRotation(lua_State *L) { + float x; + float y; + float z; + + _argCheck(L, "nodeGetRotation", 1, 1); + quatToEuler(nodeGetRotation(_argNode(L, "nodeGetRotation", 1)), &x, &y, &z); + lua_pushnumber(L, x); + lua_pushnumber(L, y); + lua_pushnumber(L, z); + return 3; +} + + +// x, y, z = nodeGetScale(node) +static int32_t apiNodeGetScale(lua_State *L) { + _argCheck(L, "nodeGetScale", 1, 1); + return _pushVec3(L, nodeGetScale(_argNode(L, "nodeGetScale", 1))); +} + + +// x, y, z = nodeGetWorldPosition(node): as of the last rendered frame +static int32_t apiNodeGetWorldPosition(lua_State *L) { + _argCheck(L, "nodeGetWorldPosition", 1, 1); + return _pushVec3(L, nodeGetWorldPosition(_argNode(L, "nodeGetWorldPosition", 1))); +} + + +// nodeLookAt(node, x, y, z): points the node's -Z at a world point +static int32_t apiNodeLookAt(lua_State *L) { + _argCheck(L, "nodeLookAt", 4, 4); + nodeLookAt(_argNode(L, "nodeLookAt", 1), _argVec3(L, "nodeLookAt", 2)); + return 0; +} + + +// nodeMove(node, dx, dy, dz): along the node's own axes +static int32_t apiNodeMove(lua_State *L) { + _argCheck(L, "nodeMove", 4, 4); + nodeMove(_argNode(L, "nodeMove", 1), _argVec3(L, "nodeMove", 2)); + return 0; +} + + +// node = nodeNew([parent]): a new node at its parent's origin +static int32_t apiNodeNew(lua_State *L) { + int32_t parent = SCENE_ROOT_NODE; + int32_t node; + + _argCheck(L, "nodeNew", 0, 1); + if (lua_gettop(L) >= 1) { + parent = _argNode(L, "nodeNew", 1); + } + node = nodeNew(parent); + if (node < 0) { + _luaDie(L, "nodeNew", "3D is not available on this machine."); + } + _luaTrace(L, "nodeNew", "%d under %d", node, parent); + lua_pushinteger(L, node); + return 1; +} + + +// nodeRotate(node, dx, dy, dz): degrees, about the node's own axes +static int32_t apiNodeRotate(lua_State *L) { + _argCheck(L, "nodeRotate", 4, 4); + nodeRotate(_argNode(L, "nodeRotate", 1), _argEuler(L, "nodeRotate", 2)); + return 0; +} + + +// nodeSetMesh(node, mesh [, material]) +static int32_t apiNodeSetMesh(lua_State *L) { + int32_t node; + int32_t mesh; + int32_t material = -1; + + _argCheck(L, "nodeSetMesh", 2, 3); + node = _argNode(L, "nodeSetMesh", 1); + mesh = _argMesh(L, "nodeSetMesh", 2); + if (lua_gettop(L) >= 3) { + material = _argMaterial(L, "nodeSetMesh", 3); + } + nodeSetMesh(node, mesh, material); + return 0; +} + + +// nodeSetName(node, name) +static int32_t apiNodeSetName(lua_State *L) { + _argCheck(L, "nodeSetName", 2, 2); + nodeSetName(_argNode(L, "nodeSetName", 1), _argString(L, "nodeSetName", 2)); + return 0; +} + + +// nodeSetParent(node, parent): keeps the local transform +static int32_t apiNodeSetParent(lua_State *L) { + int32_t node; + int32_t parent; + + _argCheck(L, "nodeSetParent", 2, 2); + node = _argNode(L, "nodeSetParent", 1); + parent = _argNode(L, "nodeSetParent", 2); + if (!nodeSetParent(node, parent)) { + _luaDie(L, "nodeSetParent", "Node %d cannot go under node %d.", node, parent); + } + return 0; +} + + +// nodeSetPosition(node, x, y, z) +static int32_t apiNodeSetPosition(lua_State *L) { + _argCheck(L, "nodeSetPosition", 4, 4); + nodeSetPosition(_argNode(L, "nodeSetPosition", 1), _argVec3(L, "nodeSetPosition", 2)); + return 0; +} + + +// nodeSetQuaternion(node, x, y, z, w) +static int32_t apiNodeSetQuaternion(lua_State *L) { + QuatT q; + + _argCheck(L, "nodeSetQuaternion", 5, 5); + q.x = (float)_argNumber(L, "nodeSetQuaternion", 2); + q.y = (float)_argNumber(L, "nodeSetQuaternion", 3); + q.z = (float)_argNumber(L, "nodeSetQuaternion", 4); + q.w = (float)_argNumber(L, "nodeSetQuaternion", 5); + nodeSetRotation(_argNode(L, "nodeSetQuaternion", 1), q); + return 0; +} + + +// nodeSetRotation(node, x, y, z): Euler degrees +static int32_t apiNodeSetRotation(lua_State *L) { + _argCheck(L, "nodeSetRotation", 4, 4); + nodeSetRotation(_argNode(L, "nodeSetRotation", 1), _argEuler(L, "nodeSetRotation", 2)); + return 0; +} + + +// nodeSetScale(node, s) or nodeSetScale(node, x, y, z) +static int32_t apiNodeSetScale(lua_State *L) { + int32_t node; + Vec3T scale; + + _argCheck(L, "nodeSetScale", 2, 4); + node = _argNode(L, "nodeSetScale", 1); + if (lua_gettop(L) == 2) { + scale.x = (float)_argNumber(L, "nodeSetScale", 2); + scale.y = scale.x; + scale.z = scale.x; + } else { + scale = _argVec3(L, "nodeSetScale", 2); + } + nodeSetScale(node, scale); + return 0; +} + + +// nodeSetVisible(node, bool): hides the node and its children +static int32_t apiNodeSetVisible(lua_State *L) { + _argCheck(L, "nodeSetVisible", 2, 2); + nodeSetVisible(_argNode(L, "nodeSetVisible", 1), _argBoolean(L, "nodeSetVisible", 2)); + return 0; +} + + // seconds = os.clock() Replaces Lua's processor-time clock with wall time since the engine started. static int32_t apiOsClock(lua_State *L) { lua_pushnumber(L, (lua_Number)SDL_GetTicks() / MS_PER_SECOND_NUMBER); @@ -3360,6 +4400,7 @@ static int32_t apiOverlaySetResolution(lua_State *L) { if (_global.overlayTexture == NULL) { utilDie("%s", SDL_GetError()); } + sceneResize(width, height); SDL_SetTextureBlendMode(_global.overlayTexture, SDL_BLENDMODE_BLEND); _global.overlayScaleX = (double)width / (double)_global.canvasWidth; _global.overlayScaleY = (double)height / (double)_global.canvasHeight; @@ -3371,6 +4412,92 @@ static int32_t apiOverlaySetResolution(lua_State *L) { // scriptExecute(config) Runs another script after this one ends. +// Turns the 3D layer on or off. +static int32_t apiSceneEnable(lua_State *L) { + bool enabled; + + _argCheck(L, "sceneEnable", 1, 1); + enabled = _argBoolean(L, "sceneEnable", 1); + if (!sceneEnable(enabled)) { + _luaDie(L, "sceneEnable", "3D is not available on this machine."); + } + _global.refreshDisplay = true; + _luaTrace(L, "sceneEnable", "%s", enabled ? "on" : "off"); + return 0; +} + + +// width, height = sceneGetSize(): the layer's size in overlay coordinates +static int32_t apiSceneGetSize(lua_State *L) { + int32_t width; + int32_t height; + + _argCheck(L, "sceneGetSize", 0, 0); + sceneGetSize(&width, &height); + lua_pushinteger(L, width); + lua_pushinteger(L, height); + return 2; +} + + +// x, y, depth, inFront = sceneProject(wx, wy, wz): a world point in overlay coordinates +static int32_t apiSceneProject(lua_State *L) { + float x; + float y; + float depth; + bool inFront; + + _argCheck(L, "sceneProject", 3, 3); + inFront = sceneProject(_argVec3(L, "sceneProject", 1), &x, &y, &depth); + lua_pushnumber(L, x); + lua_pushnumber(L, y); + lua_pushnumber(L, depth); + lua_pushboolean(L, inFront); + return 4; +} + + +// sceneSetAmbient(r, g, b): light from everywhere +static int32_t apiSceneSetAmbient(lua_State *L) { + _argCheck(L, "sceneSetAmbient", 3, 3); + sceneSetAmbient((uint8_t)_argInteger(L, "sceneSetAmbient", 1), (uint8_t)_argInteger(L, "sceneSetAmbient", 2), (uint8_t)_argInteger(L, "sceneSetAmbient", 3)); + return 0; +} + + +// sceneSetAntialias(bool): 4x multisampling, on by default where the GPU offers it +static int32_t apiSceneSetAntialias(lua_State *L) { + _argCheck(L, "sceneSetAntialias", 1, 1); + sceneSetAntialias(_argBoolean(L, "sceneSetAntialias", 1)); + return 0; +} + + +// The colour the scene clears to; alpha below 255 lets the video show through. +static int32_t apiSceneSetBackground(lua_State *L) { + int32_t r; + int32_t g; + int32_t b; + int32_t a; + + _argCheck(L, "sceneSetBackground", 3, 4); + r = _argInteger(L, "sceneSetBackground", 1); + g = _argInteger(L, "sceneSetBackground", 2); + b = _argInteger(L, "sceneSetBackground", 3); + a = (lua_gettop(L) >= 4) ? _argInteger(L, "sceneSetBackground", 4) : SDL_ALPHA_OPAQUE; + sceneSetBackground((uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a); + _global.refreshDisplay = true; + return 0; +} + + +// x, y, z = sceneUnproject(sx, sy, distance): the world point that far along the ray through an overlay point +static int32_t apiSceneUnproject(lua_State *L) { + _argCheck(L, "sceneUnproject", 3, 3); + return _pushVec3(L, sceneUnproject((float)_argNumber(L, "sceneUnproject", 1), (float)_argNumber(L, "sceneUnproject", 2), (float)_argNumber(L, "sceneUnproject", 3))); +} + + static int32_t apiScriptExecute(lua_State *L) { ConfigT *conf = NULL; @@ -4644,7 +5771,7 @@ ConfigT *confFromDatabase(const ConfigT *conf) { } -void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { +void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, ConfigT *conf) { int32_t x = 0; int32_t y = 0; int32_t xr = 0; @@ -4663,6 +5790,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { SDL_FRect windowTarget; SDL_FRect sindenWhite; SDL_FRect sindenBlack; + SDL_Texture *sceneTexture = NULL; SDL_Color sindenWhiteColor = { 255, 255, 255, SDL_ALPHA_OPAQUE }; SDL_Color sindenBlackColor = { 0, 0, 0, SDL_ALPHA_OPAQUE }; SpriteT *sprite = NULL; @@ -4700,6 +5828,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { _global.mouseEnabled = true; _global.window = window; _global.renderer = renderer; + sceneInit(device, renderer); // Local copy of config _global.conf = cloneConf(conf); @@ -4783,9 +5912,19 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { _startLuaContext(_global.luaContext); // Lua API for Singe. Comments give the version each call appeared in. + lua_register(_global.luaContext, "animationGetTime", apiAnimationGetTime); // 3.00 + lua_register(_global.luaContext, "animationIsPlaying", apiAnimationIsPlaying); // 3.00 + lua_register(_global.luaContext, "animationPause", apiAnimationPause); // 3.00 + lua_register(_global.luaContext, "animationPlay", apiAnimationPlay); // 3.00 + lua_register(_global.luaContext, "animationResume", apiAnimationResume); // 3.00 + lua_register(_global.luaContext, "animationSetTime", apiAnimationSetTime); // 3.00 + lua_register(_global.luaContext, "animationStop", apiAnimationStop); // 3.00 lua_register(_global.luaContext, "colorBackground", apiColorBackground); // 1.xx lua_register(_global.luaContext, "colorForeground", apiColorForeground); // 1.xx + lua_register(_global.luaContext, "cameraSet", apiCameraSet); // 3.00 + lua_register(_global.luaContext, "cameraSetOrthographic", apiCameraSetOrthographic); // 3.00 + lua_register(_global.luaContext, "cameraSetPerspective", apiCameraSetPerspective); // 3.00 lua_register(_global.luaContext, "controllerGetAxis", apiControllerGetAxis); // 2.00 lua_register(_global.luaContext, "controllerGetButton", apiControllerGetButton); // 2.10 @@ -4829,12 +5968,62 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { lua_register(_global.luaContext, "keyboardIsDown", apiKeyboardIsDown); // 2.10 lua_register(_global.luaContext, "keyboardSetMode", apiKeyboardSetMode); // 1.xx RDG + lua_register(_global.luaContext, "lightNew", apiLightNew); // 3.00 + lua_register(_global.luaContext, "lightSetColor", apiLightSetColor); // 3.00 + lua_register(_global.luaContext, "lightSetCone", apiLightSetCone); // 3.00 + lua_register(_global.luaContext, "lightSetIntensity", apiLightSetIntensity); // 3.00 + lua_register(_global.luaContext, "lightSetRange", apiLightSetRange); // 3.00 + lua_register(_global.luaContext, "materialDelete", apiMaterialDelete); // 3.00 + lua_register(_global.luaContext, "materialNew", apiMaterialNew); // 3.00 + lua_register(_global.luaContext, "materialSetBlend", apiMaterialSetBlend); // 3.00 + lua_register(_global.luaContext, "materialSetColor", apiMaterialSetColor); // 3.00 + lua_register(_global.luaContext, "materialSetDoubleSided", apiMaterialSetDoubleSided); // 3.00 + lua_register(_global.luaContext, "materialSetEmissive", apiMaterialSetEmissive); // 3.00 + lua_register(_global.luaContext, "materialSetMetallic", apiMaterialSetMetallic); // 3.00 + lua_register(_global.luaContext, "materialSetRoughness", apiMaterialSetRoughness); // 3.00 + lua_register(_global.luaContext, "materialSetTexture", apiMaterialSetTexture); // 3.00 + lua_register(_global.luaContext, "materialSetUnlit", apiMaterialSetUnlit); // 3.00 + lua_register(_global.luaContext, "materialSetVideo", apiMaterialSetVideo); // 3.00 + lua_register(_global.luaContext, "meshBox", apiMeshBox); // 3.00 + lua_register(_global.luaContext, "meshCone", apiMeshCone); // 3.00 + lua_register(_global.luaContext, "meshCylinder", apiMeshCylinder); // 3.00 + lua_register(_global.luaContext, "meshDelete", apiMeshDelete); // 3.00 + lua_register(_global.luaContext, "meshNew", apiMeshNew); // 3.00 + lua_register(_global.luaContext, "meshPlane", apiMeshPlane); // 3.00 + lua_register(_global.luaContext, "meshSphere", apiMeshSphere); // 3.00 + lua_register(_global.luaContext, "meshTorus", apiMeshTorus); // 3.00 + lua_register(_global.luaContext, "modelDelete", apiModelDelete); // 3.00 + lua_register(_global.luaContext, "modelGetAnimations", apiModelGetAnimations); // 3.00 + lua_register(_global.luaContext, "modelInstance", apiModelInstance); // 3.00 + lua_register(_global.luaContext, "modelLoad", apiModelLoad); // 3.00 lua_register(_global.luaContext, "mouseGetPosition", apiMouseGetPosition); // 2.00 lua_register(_global.luaContext, "mouseHowMany", apiMouseHowMany); // 1.18 RDG lua_register(_global.luaContext, "mouseSetCaptured", apiMouseSetCaptured); // 2.00 lua_register(_global.luaContext, "mouseSetEnabled", apiMouseSetEnabled); // 3.00 mouseEnable/mouseDisable are framework aliases. lua_register(_global.luaContext, "mouseSetMode", apiMouseSetMode); // 1.18 RDG + lua_register(_global.luaContext, "nodeDelete", apiNodeDelete); // 3.00 + lua_register(_global.luaContext, "nodeFind", apiNodeFind); // 3.00 + lua_register(_global.luaContext, "nodeGetChildren", apiNodeGetChildren); // 3.00 + lua_register(_global.luaContext, "nodeGetName", apiNodeGetName); // 3.00 + lua_register(_global.luaContext, "nodeGetParent", apiNodeGetParent); // 3.00 + lua_register(_global.luaContext, "nodeGetPosition", apiNodeGetPosition); // 3.00 + lua_register(_global.luaContext, "nodeGetQuaternion", apiNodeGetQuaternion); // 3.00 + lua_register(_global.luaContext, "nodeGetRotation", apiNodeGetRotation); // 3.00 + lua_register(_global.luaContext, "nodeGetScale", apiNodeGetScale); // 3.00 + lua_register(_global.luaContext, "nodeGetWorldPosition", apiNodeGetWorldPosition); // 3.00 + lua_register(_global.luaContext, "nodeLookAt", apiNodeLookAt); // 3.00 + lua_register(_global.luaContext, "nodeMove", apiNodeMove); // 3.00 + lua_register(_global.luaContext, "nodeNew", apiNodeNew); // 3.00 + lua_register(_global.luaContext, "nodeRotate", apiNodeRotate); // 3.00 + lua_register(_global.luaContext, "nodeSetMesh", apiNodeSetMesh); // 3.00 + lua_register(_global.luaContext, "nodeSetName", apiNodeSetName); // 3.00 + lua_register(_global.luaContext, "nodeSetParent", apiNodeSetParent); // 3.00 + lua_register(_global.luaContext, "nodeSetPosition", apiNodeSetPosition); // 3.00 + lua_register(_global.luaContext, "nodeSetQuaternion", apiNodeSetQuaternion); // 3.00 + lua_register(_global.luaContext, "nodeSetRotation", apiNodeSetRotation); // 3.00 + lua_register(_global.luaContext, "nodeSetScale", apiNodeSetScale); // 3.00 + lua_register(_global.luaContext, "nodeSetVisible", apiNodeSetVisible); // 3.00 lua_register(_global.luaContext, "overlayBox", apiOverlayBox); // 2.00 lua_register(_global.luaContext, "overlayCircle", apiOverlayCircle); // 2.00 lua_register(_global.luaContext, "overlayClear", apiOverlayClear); // 1.xx @@ -4846,6 +6035,13 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { lua_register(_global.luaContext, "overlayPrint", apiOverlayPrint); // 1.xx lua_register(_global.luaContext, "overlaySetResolution", apiOverlaySetResolution); // 2.00 + lua_register(_global.luaContext, "sceneEnable", apiSceneEnable); // 3.00 + lua_register(_global.luaContext, "sceneGetSize", apiSceneGetSize); // 3.00 + lua_register(_global.luaContext, "sceneProject", apiSceneProject); // 3.00 + lua_register(_global.luaContext, "sceneSetAmbient", apiSceneSetAmbient); // 3.00 + lua_register(_global.luaContext, "sceneSetAntialias", apiSceneSetAntialias); // 3.00 + lua_register(_global.luaContext, "sceneSetBackground", apiSceneSetBackground); // 3.00 + lua_register(_global.luaContext, "sceneUnproject", apiSceneUnproject); // 3.00 lua_register(_global.luaContext, "scriptExecute", apiScriptExecute); // 2.00 lua_register(_global.luaContext, "scriptPush", apiScriptPush); // 2.00 @@ -5058,6 +6254,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { if (_global.overlayTexture == NULL) { utilDie("%s", SDL_GetError()); } + sceneResize(x, y); SDL_SetTextureBlendMode(_global.overlayTexture, SDL_BLENDMODE_BLEND); _global.overlayDirty = true; @@ -5367,7 +6564,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { } // Update display - if (_global.refreshDisplay || _global.overlayDirty) { + if (_global.refreshDisplay || _global.overlayDirty || sceneIsEnabled()) { // Clear entire display to black SDL_SetRenderDrawColor(_global.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); SDL_RenderClear(_global.renderer); @@ -5390,6 +6587,13 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { SDL_RenderTexture(_global.renderer, _global.videoTexture, NULL, &windowTarget); } } + // 3D scene + modelUpdate(!_global.frozen); + sceneUpdateVideo(_sceneVideoSource); + sceneTexture = sceneRender(); + if (sceneTexture != NULL) { + SDL_RenderTexture(_global.renderer, sceneTexture, NULL, &windowTarget); + } // Overlay if (_global.overlayDirty) { SDL_UpdateTexture(_global.overlayTexture, NULL, _global.overlay->pixels, _global.overlay->pitch); @@ -5432,6 +6636,8 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { // Free overlay & overlay font _progTrace("Destroying overlay"); SDL_DestroyTexture(_global.pauseTexture); + modelQuit(); + sceneQuit(); SDL_DestroyTexture(_global.overlayTexture); SDL_DestroySurface(_global.overlay); _progTrace("Destroying console font"); diff --git a/src/singe.h b/src/singe.h index c17308dd1..208465551 100644 --- a/src/singe.h +++ b/src/singe.h @@ -93,7 +93,7 @@ typedef struct ConfigS { ConfigT *confFromDatabase(const ConfigT *conf); -void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf); +void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, ConfigT *conf); #endif // SINGE_H diff --git a/thirdparty/cgltf/LICENSE b/thirdparty/cgltf/LICENSE new file mode 100644 index 000000000..599d9341a --- /dev/null +++ b/thirdparty/cgltf/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2018-2021 Johannes Kuhlmann + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/cgltf/cgltf.h b/thirdparty/cgltf/cgltf.h new file mode 100644 index 000000000..78ea905f0 --- /dev/null +++ b/thirdparty/cgltf/cgltf.h @@ -0,0 +1,7175 @@ +/** + * cgltf - a single-file glTF 2.0 parser written in C99. + * + * Version: 1.15 + * + * Website: https://github.com/jkuhlmann/cgltf + * + * Distributed under the MIT License, see notice at the end of this file. + * + * Building: + * Include this file where you need the struct and function + * declarations. Have exactly one source file where you define + * `CGLTF_IMPLEMENTATION` before including this file to get the + * function definitions. + * + * Reference: + * `cgltf_result cgltf_parse(const cgltf_options*, const void*, + * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If + * this function returns `cgltf_result_success`, you have to call + * `cgltf_free()` on the created `cgltf_data*` variable. + * Note that contents of external files for buffers and images are not + * automatically loaded. You'll need to read these files yourself using + * URIs in the `cgltf_data` structure. + * + * `cgltf_options` is the struct passed to `cgltf_parse()` to control + * parts of the parsing process. You can use it to force the file type + * and provide memory allocation as well as file operation callbacks. + * Should be zero-initialized to trigger default behavior. + * + * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`. + * It generally mirrors the glTF format as described by the spec (see + * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). + * + * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data` + * variable. + * + * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, + * const char* gltf_path)` can be optionally called to open and read buffer + * files using the `FILE*` APIs. The `gltf_path` argument is the path to + * the original glTF file, which allows the parser to resolve the path to + * buffer files. + * + * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, + * cgltf_size size, const char* base64, void** out_data)` decodes + * base64-encoded data content. Used internally by `cgltf_load_buffers()`. + * This is useful when decoding data URIs in images. + * + * `cgltf_result cgltf_parse_file(const cgltf_options* options, const + * char* path, cgltf_data** out_data)` can be used to open the given + * file using `FILE*` APIs and parse the data using `cgltf_parse()`. + * + * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional + * checks to make sure the parsed glTF data is valid. + * + * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node + * into a mat4. + * + * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order + * to compute the root-to-node transformation. + * + * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any), + * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called. + * By passing null for the output pointer, users can find out how many floats are required in the + * output buffer. + * + * `cgltf_accessor_unpack_indices` reads in the index data from an accessor. Assumes that + * `cgltf_load_buffers` has already been called. By passing null for the output pointer, users can + * find out how many indices are required in the output buffer. Returns 0 if the accessor is + * sparse or if the output component size is less than the accessor's component size. + * + * `cgltf_num_components` is a tiny utility that tells you the dimensionality of + * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate + * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for + * similar purposes. + * + * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to + * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element + * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns + * false if the passed-in element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading + * vector types and does not support matrix types. The passed-in element size is the number of uints + * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in + * element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t + * and only works with single-component data types. + * + * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many + * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using + * cgltf_extras::data directly instead. You can parse this data using your own JSON parser + * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. + */ +#ifndef CGLTF_H_INCLUDED__ +#define CGLTF_H_INCLUDED__ + +#include +#include /* For uint8_t, uint32_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef size_t cgltf_size; +typedef long long int cgltf_ssize; +typedef float cgltf_float; +typedef int cgltf_int; +typedef unsigned int cgltf_uint; +typedef int cgltf_bool; + +typedef enum cgltf_file_type +{ + cgltf_file_type_invalid, + cgltf_file_type_gltf, + cgltf_file_type_glb, + cgltf_file_type_max_enum +} cgltf_file_type; + +typedef enum cgltf_result +{ + cgltf_result_success, + cgltf_result_data_too_short, + cgltf_result_unknown_format, + cgltf_result_invalid_json, + cgltf_result_invalid_gltf, + cgltf_result_invalid_options, + cgltf_result_file_not_found, + cgltf_result_io_error, + cgltf_result_out_of_memory, + cgltf_result_legacy_gltf, + cgltf_result_max_enum +} cgltf_result; + +typedef struct cgltf_memory_options +{ + void* (*alloc_func)(void* user, cgltf_size size); + void (*free_func) (void* user, void* ptr); + void* user_data; +} cgltf_memory_options; + +typedef struct cgltf_file_options +{ + cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); + void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); + void* user_data; +} cgltf_file_options; + +typedef struct cgltf_options +{ + cgltf_file_type type; /* invalid == auto detect */ + cgltf_size json_token_count; /* 0 == auto */ + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_options; + +typedef enum cgltf_buffer_view_type +{ + cgltf_buffer_view_type_invalid, + cgltf_buffer_view_type_indices, + cgltf_buffer_view_type_vertices, + cgltf_buffer_view_type_max_enum +} cgltf_buffer_view_type; + +typedef enum cgltf_attribute_type +{ + cgltf_attribute_type_invalid, + cgltf_attribute_type_position, + cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, + cgltf_attribute_type_texcoord, + cgltf_attribute_type_color, + cgltf_attribute_type_joints, + cgltf_attribute_type_weights, + cgltf_attribute_type_custom, + cgltf_attribute_type_max_enum +} cgltf_attribute_type; + +typedef enum cgltf_component_type +{ + cgltf_component_type_invalid, + cgltf_component_type_r_8, /* BYTE */ + cgltf_component_type_r_8u, /* UNSIGNED_BYTE */ + cgltf_component_type_r_16, /* SHORT */ + cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ + cgltf_component_type_r_32u, /* UNSIGNED_INT */ + cgltf_component_type_r_32f, /* FLOAT */ + cgltf_component_type_max_enum +} cgltf_component_type; + +typedef enum cgltf_type +{ + cgltf_type_invalid, + cgltf_type_scalar, + cgltf_type_vec2, + cgltf_type_vec3, + cgltf_type_vec4, + cgltf_type_mat2, + cgltf_type_mat3, + cgltf_type_mat4, + cgltf_type_max_enum +} cgltf_type; + +typedef enum cgltf_primitive_type +{ + cgltf_primitive_type_invalid, + cgltf_primitive_type_points, + cgltf_primitive_type_lines, + cgltf_primitive_type_line_loop, + cgltf_primitive_type_line_strip, + cgltf_primitive_type_triangles, + cgltf_primitive_type_triangle_strip, + cgltf_primitive_type_triangle_fan, + cgltf_primitive_type_max_enum +} cgltf_primitive_type; + +typedef enum cgltf_alpha_mode +{ + cgltf_alpha_mode_opaque, + cgltf_alpha_mode_mask, + cgltf_alpha_mode_blend, + cgltf_alpha_mode_max_enum +} cgltf_alpha_mode; + +typedef enum cgltf_animation_path_type { + cgltf_animation_path_type_invalid, + cgltf_animation_path_type_translation, + cgltf_animation_path_type_rotation, + cgltf_animation_path_type_scale, + cgltf_animation_path_type_weights, + cgltf_animation_path_type_max_enum +} cgltf_animation_path_type; + +typedef enum cgltf_interpolation_type { + cgltf_interpolation_type_linear, + cgltf_interpolation_type_step, + cgltf_interpolation_type_cubic_spline, + cgltf_interpolation_type_max_enum +} cgltf_interpolation_type; + +typedef enum cgltf_camera_type { + cgltf_camera_type_invalid, + cgltf_camera_type_perspective, + cgltf_camera_type_orthographic, + cgltf_camera_type_max_enum +} cgltf_camera_type; + +typedef enum cgltf_light_type { + cgltf_light_type_invalid, + cgltf_light_type_directional, + cgltf_light_type_point, + cgltf_light_type_spot, + cgltf_light_type_max_enum +} cgltf_light_type; + +typedef enum cgltf_data_free_method { + cgltf_data_free_method_none, + cgltf_data_free_method_file_release, + cgltf_data_free_method_memory_free, + cgltf_data_free_method_max_enum +} cgltf_data_free_method; + +typedef struct cgltf_extras { + cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */ + cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */ + + char* data; +} cgltf_extras; + +typedef struct cgltf_extension { + char* name; + char* data; +} cgltf_extension; + +typedef struct cgltf_buffer +{ + char* name; + cgltf_size size; + char* uri; + void* data; /* loaded by cgltf_load_buffers */ + cgltf_data_free_method data_free_method; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer; + +typedef enum cgltf_meshopt_compression_mode { + cgltf_meshopt_compression_mode_invalid, + cgltf_meshopt_compression_mode_attributes, + cgltf_meshopt_compression_mode_triangles, + cgltf_meshopt_compression_mode_indices, + cgltf_meshopt_compression_mode_max_enum +} cgltf_meshopt_compression_mode; + +typedef enum cgltf_meshopt_compression_filter { + cgltf_meshopt_compression_filter_none, + cgltf_meshopt_compression_filter_octahedral, + cgltf_meshopt_compression_filter_quaternion, + cgltf_meshopt_compression_filter_exponential, + cgltf_meshopt_compression_filter_max_enum +} cgltf_meshopt_compression_filter; + +typedef struct cgltf_meshopt_compression +{ + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; + cgltf_size count; + cgltf_meshopt_compression_mode mode; + cgltf_meshopt_compression_filter filter; +} cgltf_meshopt_compression; + +typedef struct cgltf_buffer_view +{ + char *name; + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; /* 0 == automatically determined by accessor */ + cgltf_buffer_view_type type; + void* data; /* overrides buffer->data if present, filled by extensions */ + cgltf_bool has_meshopt_compression; + cgltf_meshopt_compression meshopt_compression; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer_view; + +typedef struct cgltf_accessor_sparse +{ + cgltf_size count; + cgltf_buffer_view* indices_buffer_view; + cgltf_size indices_byte_offset; + cgltf_component_type indices_component_type; + cgltf_buffer_view* values_buffer_view; + cgltf_size values_byte_offset; +} cgltf_accessor_sparse; + +typedef struct cgltf_accessor +{ + char* name; + cgltf_component_type component_type; + cgltf_bool normalized; + cgltf_type type; + cgltf_size offset; + cgltf_size count; + cgltf_size stride; + cgltf_buffer_view* buffer_view; + cgltf_bool has_min; + cgltf_float min[16]; + cgltf_bool has_max; + cgltf_float max[16]; + cgltf_bool is_sparse; + cgltf_accessor_sparse sparse; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_accessor; + +typedef struct cgltf_attribute +{ + char* name; + cgltf_attribute_type type; + cgltf_int index; + cgltf_accessor* data; +} cgltf_attribute; + +typedef struct cgltf_image +{ + char* name; + char* uri; + cgltf_buffer_view* buffer_view; + char* mime_type; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_image; + +typedef enum cgltf_filter_type { + cgltf_filter_type_undefined = 0, + cgltf_filter_type_nearest = 9728, + cgltf_filter_type_linear = 9729, + cgltf_filter_type_nearest_mipmap_nearest = 9984, + cgltf_filter_type_linear_mipmap_nearest = 9985, + cgltf_filter_type_nearest_mipmap_linear = 9986, + cgltf_filter_type_linear_mipmap_linear = 9987 +} cgltf_filter_type; + +typedef enum cgltf_wrap_mode { + cgltf_wrap_mode_clamp_to_edge = 33071, + cgltf_wrap_mode_mirrored_repeat = 33648, + cgltf_wrap_mode_repeat = 10497 +} cgltf_wrap_mode; + +typedef struct cgltf_sampler +{ + char* name; + cgltf_filter_type mag_filter; + cgltf_filter_type min_filter; + cgltf_wrap_mode wrap_s; + cgltf_wrap_mode wrap_t; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_sampler; + +typedef struct cgltf_texture +{ + char* name; + cgltf_image* image; + cgltf_sampler* sampler; + cgltf_bool has_basisu; + cgltf_image* basisu_image; + cgltf_bool has_webp; + cgltf_image* webp_image; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_texture; + +typedef struct cgltf_texture_transform +{ + cgltf_float offset[2]; + cgltf_float rotation; + cgltf_float scale[2]; + cgltf_bool has_texcoord; + cgltf_int texcoord; +} cgltf_texture_transform; + +typedef struct cgltf_texture_view +{ + cgltf_texture* texture; + cgltf_int texcoord; + cgltf_float scale; /* equivalent to strength for occlusion_texture */ + cgltf_bool has_transform; + cgltf_texture_transform transform; +} cgltf_texture_view; + +typedef struct cgltf_pbr_metallic_roughness +{ + cgltf_texture_view base_color_texture; + cgltf_texture_view metallic_roughness_texture; + + cgltf_float base_color_factor[4]; + cgltf_float metallic_factor; + cgltf_float roughness_factor; +} cgltf_pbr_metallic_roughness; + +typedef struct cgltf_pbr_specular_glossiness +{ + cgltf_texture_view diffuse_texture; + cgltf_texture_view specular_glossiness_texture; + + cgltf_float diffuse_factor[4]; + cgltf_float specular_factor[3]; + cgltf_float glossiness_factor; +} cgltf_pbr_specular_glossiness; + +typedef struct cgltf_clearcoat +{ + cgltf_texture_view clearcoat_texture; + cgltf_texture_view clearcoat_roughness_texture; + cgltf_texture_view clearcoat_normal_texture; + + cgltf_float clearcoat_factor; + cgltf_float clearcoat_roughness_factor; +} cgltf_clearcoat; + +typedef struct cgltf_transmission +{ + cgltf_texture_view transmission_texture; + cgltf_float transmission_factor; +} cgltf_transmission; + +typedef struct cgltf_ior +{ + cgltf_float ior; +} cgltf_ior; + +typedef struct cgltf_specular +{ + cgltf_texture_view specular_texture; + cgltf_texture_view specular_color_texture; + cgltf_float specular_color_factor[3]; + cgltf_float specular_factor; +} cgltf_specular; + +typedef struct cgltf_volume +{ + cgltf_texture_view thickness_texture; + cgltf_float thickness_factor; + cgltf_float attenuation_color[3]; + cgltf_float attenuation_distance; +} cgltf_volume; + +typedef struct cgltf_sheen +{ + cgltf_texture_view sheen_color_texture; + cgltf_float sheen_color_factor[3]; + cgltf_texture_view sheen_roughness_texture; + cgltf_float sheen_roughness_factor; +} cgltf_sheen; + +typedef struct cgltf_emissive_strength +{ + cgltf_float emissive_strength; +} cgltf_emissive_strength; + +typedef struct cgltf_iridescence +{ + cgltf_float iridescence_factor; + cgltf_texture_view iridescence_texture; + cgltf_float iridescence_ior; + cgltf_float iridescence_thickness_min; + cgltf_float iridescence_thickness_max; + cgltf_texture_view iridescence_thickness_texture; +} cgltf_iridescence; + +typedef struct cgltf_diffuse_transmission +{ + cgltf_texture_view diffuse_transmission_texture; + cgltf_float diffuse_transmission_factor; + cgltf_float diffuse_transmission_color_factor[3]; + cgltf_texture_view diffuse_transmission_color_texture; +} cgltf_diffuse_transmission; + +typedef struct cgltf_anisotropy +{ + cgltf_float anisotropy_strength; + cgltf_float anisotropy_rotation; + cgltf_texture_view anisotropy_texture; +} cgltf_anisotropy; + +typedef struct cgltf_dispersion +{ + cgltf_float dispersion; +} cgltf_dispersion; + +typedef struct cgltf_material +{ + char* name; + cgltf_bool has_pbr_metallic_roughness; + cgltf_bool has_pbr_specular_glossiness; + cgltf_bool has_clearcoat; + cgltf_bool has_transmission; + cgltf_bool has_volume; + cgltf_bool has_ior; + cgltf_bool has_specular; + cgltf_bool has_sheen; + cgltf_bool has_emissive_strength; + cgltf_bool has_iridescence; + cgltf_bool has_diffuse_transmission; + cgltf_bool has_anisotropy; + cgltf_bool has_dispersion; + cgltf_pbr_metallic_roughness pbr_metallic_roughness; + cgltf_pbr_specular_glossiness pbr_specular_glossiness; + cgltf_clearcoat clearcoat; + cgltf_ior ior; + cgltf_specular specular; + cgltf_sheen sheen; + cgltf_transmission transmission; + cgltf_volume volume; + cgltf_emissive_strength emissive_strength; + cgltf_iridescence iridescence; + cgltf_diffuse_transmission diffuse_transmission; + cgltf_anisotropy anisotropy; + cgltf_dispersion dispersion; + cgltf_texture_view normal_texture; + cgltf_texture_view occlusion_texture; + cgltf_texture_view emissive_texture; + cgltf_float emissive_factor[3]; + cgltf_alpha_mode alpha_mode; + cgltf_float alpha_cutoff; + cgltf_bool double_sided; + cgltf_bool unlit; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_material; + +typedef struct cgltf_material_mapping +{ + cgltf_size variant; + cgltf_material* material; + cgltf_extras extras; +} cgltf_material_mapping; + +typedef struct cgltf_morph_target { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_morph_target; + +typedef struct cgltf_draco_mesh_compression { + cgltf_buffer_view* buffer_view; + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_draco_mesh_compression; + +typedef struct cgltf_mesh_gpu_instancing { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_mesh_gpu_instancing; + +typedef struct cgltf_primitive { + cgltf_primitive_type type; + cgltf_accessor* indices; + cgltf_material* material; + cgltf_attribute* attributes; + cgltf_size attributes_count; + cgltf_morph_target* targets; + cgltf_size targets_count; + cgltf_extras extras; + cgltf_bool has_draco_mesh_compression; + cgltf_draco_mesh_compression draco_mesh_compression; + cgltf_material_mapping* mappings; + cgltf_size mappings_count; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_primitive; + +typedef struct cgltf_mesh { + char* name; + cgltf_primitive* primitives; + cgltf_size primitives_count; + cgltf_float* weights; + cgltf_size weights_count; + char** target_names; + cgltf_size target_names_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_mesh; + +typedef struct cgltf_node cgltf_node; + +typedef struct cgltf_skin { + char* name; + cgltf_node** joints; + cgltf_size joints_count; + cgltf_node* skeleton; + cgltf_accessor* inverse_bind_matrices; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_skin; + +typedef struct cgltf_camera_perspective { + cgltf_bool has_aspect_ratio; + cgltf_float aspect_ratio; + cgltf_float yfov; + cgltf_bool has_zfar; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_perspective; + +typedef struct cgltf_camera_orthographic { + cgltf_float xmag; + cgltf_float ymag; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_orthographic; + +typedef struct cgltf_camera { + char* name; + cgltf_camera_type type; + union { + cgltf_camera_perspective perspective; + cgltf_camera_orthographic orthographic; + } data; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_camera; + +typedef struct cgltf_light { + char* name; + cgltf_float color[3]; + cgltf_float intensity; + cgltf_light_type type; + cgltf_float range; + cgltf_float spot_inner_cone_angle; + cgltf_float spot_outer_cone_angle; + cgltf_extras extras; +} cgltf_light; + +struct cgltf_node { + char* name; + cgltf_node* parent; + cgltf_node** children; + cgltf_size children_count; + cgltf_skin* skin; + cgltf_mesh* mesh; + cgltf_camera* camera; + cgltf_light* light; + cgltf_float* weights; + cgltf_size weights_count; + cgltf_bool has_translation; + cgltf_bool has_rotation; + cgltf_bool has_scale; + cgltf_bool has_matrix; + cgltf_float translation[3]; + cgltf_float rotation[4]; + cgltf_float scale[3]; + cgltf_float matrix[16]; + cgltf_extras extras; + cgltf_bool has_mesh_gpu_instancing; + cgltf_mesh_gpu_instancing mesh_gpu_instancing; + cgltf_size extensions_count; + cgltf_extension* extensions; +}; + +typedef struct cgltf_scene { + char* name; + cgltf_node** nodes; + cgltf_size nodes_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_scene; + +typedef struct cgltf_animation_sampler { + cgltf_accessor* input; + cgltf_accessor* output; + cgltf_interpolation_type interpolation; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_sampler; + +typedef struct cgltf_animation_channel { + cgltf_animation_sampler* sampler; + cgltf_node* target_node; + cgltf_animation_path_type target_path; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_channel; + +typedef struct cgltf_animation { + char* name; + cgltf_animation_sampler* samplers; + cgltf_size samplers_count; + cgltf_animation_channel* channels; + cgltf_size channels_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation; + +typedef struct cgltf_material_variant +{ + char* name; + cgltf_extras extras; +} cgltf_material_variant; + +typedef struct cgltf_asset { + char* copyright; + char* generator; + char* version; + char* min_version; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_asset; + +typedef struct cgltf_data +{ + cgltf_file_type file_type; + void* file_data; + + cgltf_asset asset; + + cgltf_mesh* meshes; + cgltf_size meshes_count; + + cgltf_material* materials; + cgltf_size materials_count; + + cgltf_accessor* accessors; + cgltf_size accessors_count; + + cgltf_buffer_view* buffer_views; + cgltf_size buffer_views_count; + + cgltf_buffer* buffers; + cgltf_size buffers_count; + + cgltf_image* images; + cgltf_size images_count; + + cgltf_texture* textures; + cgltf_size textures_count; + + cgltf_sampler* samplers; + cgltf_size samplers_count; + + cgltf_skin* skins; + cgltf_size skins_count; + + cgltf_camera* cameras; + cgltf_size cameras_count; + + cgltf_light* lights; + cgltf_size lights_count; + + cgltf_node* nodes; + cgltf_size nodes_count; + + cgltf_scene* scenes; + cgltf_size scenes_count; + + cgltf_scene* scene; + + cgltf_animation* animations; + cgltf_size animations_count; + + cgltf_material_variant* variants; + cgltf_size variants_count; + + cgltf_extras extras; + + cgltf_size data_extensions_count; + cgltf_extension* data_extensions; + + char** extensions_used; + cgltf_size extensions_used_count; + + char** extensions_required; + cgltf_size extensions_required_count; + + const char* json; + cgltf_size json_size; + + const void* bin; + cgltf_size bin_size; + + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_data; + +cgltf_result cgltf_parse( + const cgltf_options* options, + const void* data, + cgltf_size size, + cgltf_data** out_data); + +cgltf_result cgltf_parse_file( + const cgltf_options* options, + const char* path, + cgltf_data** out_data); + +cgltf_result cgltf_load_buffers( + const cgltf_options* options, + cgltf_data* data, + const char* gltf_path); + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); + +cgltf_size cgltf_decode_string(char* string); +cgltf_size cgltf_decode_uri(char* uri); + +cgltf_result cgltf_validate(cgltf_data* data); + +void cgltf_free(cgltf_data* data); + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); + +const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index); + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); + +cgltf_size cgltf_num_components(cgltf_type type); +cgltf_size cgltf_component_size(cgltf_component_type component_type); +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count); + +/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */ +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); + +cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object); +cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object); +cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object); +cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object); +cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object); +cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object); +cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object); +cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object); +cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object); +cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object); +cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object); +cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object); +cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object); +cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object); +cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object); +cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef CGLTF_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) +/* This makes MSVC/CLion intellisense work. */ +#define CGLTF_IMPLEMENTATION +#endif + +#ifdef CGLTF_IMPLEMENTATION + +#include /* For assert */ +#include /* For strncpy */ +#include /* For fopen */ +#include /* For UINT_MAX etc */ +#include /* For FLT_MAX */ + +#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL) +#include /* For malloc, free, atoi, atof */ +#endif + +/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ +#define JSMN_PARENT_LINKS + +/* JSMN_STRICT is necessary to reject invalid JSON documents */ +#define JSMN_STRICT + +/* + * -- jsmn.h start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; +typedef struct { + jsmntype_t type; + ptrdiff_t start; + ptrdiff_t end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; +typedef struct { + size_t pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; +static void jsmn_init(jsmn_parser *parser); +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); +/* + * -- jsmn.h end -- + */ + + +#ifndef CGLTF_CONSTS +#define GlbHeaderSize 12 +#define GlbChunkHeaderSize 8 +static const uint32_t GlbVersion = 2; +static const uint32_t GlbMagic = 0x46546C67; +static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; +static const uint32_t GlbMagicBinChunk = 0x004E4942; +#define CGLTF_CONSTS +#endif + +#ifndef CGLTF_MALLOC +#define CGLTF_MALLOC(size) malloc(size) +#endif +#ifndef CGLTF_FREE +#define CGLTF_FREE(ptr) free(ptr) +#endif +#ifndef CGLTF_ATOI +#define CGLTF_ATOI(str) atoi(str) +#endif +#ifndef CGLTF_ATOF +#define CGLTF_ATOF(str) atof(str) +#endif +#ifndef CGLTF_ATOLL +#define CGLTF_ATOLL(str) atoll(str) +#endif +#ifndef CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_VALIDATE_ENABLE_ASSERTS 0 +#endif + +static void* cgltf_default_alloc(void* user, cgltf_size size) +{ + (void)user; + return CGLTF_MALLOC(size); +} + +static void cgltf_default_free(void* user, void* ptr) +{ + (void)user; + CGLTF_FREE(ptr); +} + +static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count) +{ + if (SIZE_MAX / element_size < count) + { + return NULL; + } + void* result = options->memory.alloc_func(options->memory.user_data, element_size * count); + if (!result) + { + return NULL; + } + memset(result, 0, element_size * count); + return result; +} + +static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data) +{ + (void)file_options; + void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc_func ? memory_options->alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; + + FILE* file = fopen(path, "rb"); + if (!file) + { + return cgltf_result_file_not_found; + } + + cgltf_size file_size = size ? *size : 0; + + if (file_size == 0) + { + fseek(file, 0, SEEK_END); + +#ifdef _MSC_VER + __int64 length = _ftelli64(file); +#else + long length = ftell(file); +#endif + + if (length < 0) + { + fclose(file); + return cgltf_result_io_error; + } + + fseek(file, 0, SEEK_SET); + file_size = (cgltf_size)length; + } + + char* file_data = (char*)memory_alloc(memory_options->user_data, file_size); + if (!file_data) + { + fclose(file); + return cgltf_result_out_of_memory; + } + + cgltf_size read_size = fread(file_data, 1, file_size, file); + + fclose(file); + + if (read_size != file_size) + { + memory_free(memory_options->user_data, file_data); + return cgltf_result_io_error; + } + + if (size) + { + *size = file_size; + } + if (data) + { + *data = file_data; + } + + return cgltf_result_success; +} + +static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) +{ + (void)file_options; + void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; + memfree(memory_options->user_data, data); +} + +static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data); + +cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data) +{ + if (size < GlbHeaderSize) + { + return cgltf_result_data_too_short; + } + + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_options fixed_options = *options; + if (fixed_options.memory.alloc_func == NULL) + { + fixed_options.memory.alloc_func = &cgltf_default_alloc; + } + if (fixed_options.memory.free_func == NULL) + { + fixed_options.memory.free_func = &cgltf_default_free; + } + + uint32_t tmp; + // Magic + memcpy(&tmp, data, 4); + if (tmp != GlbMagic) + { + if (fixed_options.type == cgltf_file_type_invalid) + { + fixed_options.type = cgltf_file_type_gltf; + } + else if (fixed_options.type == cgltf_file_type_glb) + { + return cgltf_result_unknown_format; + } + } + + if (fixed_options.type == cgltf_file_type_gltf) + { + cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_gltf; + + return cgltf_result_success; + } + + const uint8_t* ptr = (const uint8_t*)data; + // Version + memcpy(&tmp, ptr + 4, 4); + uint32_t version = tmp; + if (version != GlbVersion) + { + return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format; + } + + // Total length + memcpy(&tmp, ptr + 8, 4); + if (tmp > size) + { + return cgltf_result_data_too_short; + } + + const uint8_t* json_chunk = ptr + GlbHeaderSize; + + if (GlbHeaderSize + GlbChunkHeaderSize > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: length + uint32_t json_length; + memcpy(&json_length, json_chunk, 4); + if (json_length > size - GlbHeaderSize - GlbChunkHeaderSize) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: magic + memcpy(&tmp, json_chunk + 4, 4); + if (tmp != GlbMagicJsonChunk) + { + return cgltf_result_unknown_format; + } + + json_chunk += GlbChunkHeaderSize; + + const void* bin = NULL; + cgltf_size bin_size = 0; + + if (GlbChunkHeaderSize <= size - GlbHeaderSize - GlbChunkHeaderSize - json_length) + { + // We can read another chunk + const uint8_t* bin_chunk = json_chunk + json_length; + + // Bin chunk: length + uint32_t bin_length; + memcpy(&bin_length, bin_chunk, 4); + if (bin_length > size - GlbHeaderSize - GlbChunkHeaderSize - json_length - GlbChunkHeaderSize) + { + return cgltf_result_data_too_short; + } + + // Bin chunk: magic + memcpy(&tmp, bin_chunk + 4, 4); + if (tmp != GlbMagicBinChunk) + { + return cgltf_result_unknown_format; + } + + bin_chunk += GlbChunkHeaderSize; + + bin = bin_chunk; + bin_size = bin_length; + } + + cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_glb; + (*out_data)->bin = bin; + (*out_data)->bin_size = bin_size; + + return cgltf_result_success; +} + +cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; + + void* file_data = NULL; + cgltf_size file_size = 0; + cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data); + if (result != cgltf_result_success) + { + return result; + } + + result = cgltf_parse(options, file_data, file_size, out_data); + + if (result != cgltf_result_success) + { + file_release(&options->memory, &options->file, file_data); + return result; + } + + (*out_data)->file_data = file_data; + + return cgltf_result_success; +} + +static void cgltf_combine_paths(char* path, const char* base, const char* uri) +{ + const char* s0 = strrchr(base, '/'); + const char* s1 = strrchr(base, '\\'); + const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1; + + if (slash) + { + size_t prefix = slash - base + 1; + + strncpy(path, base, prefix); + strcpy(path + prefix, uri); + } + else + { + strcpy(path, uri); + } +} + +static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; + cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + + char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1); + if (!path) + { + return cgltf_result_out_of_memory; + } + + cgltf_combine_paths(path, gltf_path, uri); + + // after combining, the tail of the resulting path is a uri; decode_uri converts it into path + cgltf_decode_uri(path + strlen(path) - strlen(uri)); + + void* file_data = NULL; + cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data); + + memory_free(options->memory.user_data, path); + + *out_data = (result == cgltf_result_success) ? file_data : NULL; + + return result; +} + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; + + unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size); + if (!data) + { + return cgltf_result_out_of_memory; + } + + unsigned int buffer = 0; + unsigned int buffer_bits = 0; + + for (cgltf_size i = 0; i < size; ++i) + { + while (buffer_bits < 8) + { + char ch = *base64++; + + int index = + (unsigned)(ch - 'A') < 26 ? (ch - 'A') : + (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 : + (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 : + ch == '+' ? 62 : + ch == '/' ? 63 : + -1; + + if (index < 0) + { + memory_free(options->memory.user_data, data); + return cgltf_result_io_error; + } + + buffer = (buffer << 6) | index; + buffer_bits += 6; + } + + data[i] = (unsigned char)(buffer >> (buffer_bits - 8)); + buffer_bits -= 8; + } + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_unhex(char ch) +{ + return + (unsigned)(ch - '0') < 10 ? (ch - '0') : + (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 : + (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 : + -1; +} + +cgltf_size cgltf_decode_string(char* string) +{ + char* read = string + strcspn(string, "\\"); + if (*read == 0) + { + return read - string; + } + char* write = string; + char* last = string; + + for (;;) + { + // Copy characters since last escaped sequence + cgltf_size written = read - last; + memmove(write, last, written); + write += written; + + if (*read++ == 0) + { + break; + } + + // jsmn already checked that all escape sequences are valid + switch (*read++) + { + case '\"': *write++ = '\"'; break; + case '/': *write++ = '/'; break; + case '\\': *write++ = '\\'; break; + case 'b': *write++ = '\b'; break; + case 'f': *write++ = '\f'; break; + case 'r': *write++ = '\r'; break; + case 'n': *write++ = '\n'; break; + case 't': *write++ = '\t'; break; + case 'u': + { + // UCS-2 codepoint \uXXXX to UTF-8 + int character = 0; + for (cgltf_size i = 0; i < 4; ++i) + { + character = (character << 4) + cgltf_unhex(*read++); + } + + if (character <= 0x7F) + { + *write++ = character & 0xFF; + } + else if (character <= 0x7FF) + { + *write++ = 0xC0 | ((character >> 6) & 0xFF); + *write++ = 0x80 | (character & 0x3F); + } + else + { + *write++ = 0xE0 | ((character >> 12) & 0xFF); + *write++ = 0x80 | ((character >> 6) & 0x3F); + *write++ = 0x80 | (character & 0x3F); + } + break; + } + default: + break; + } + + last = read; + read += strcspn(read, "\\"); + } + + *write = 0; + return write - string; +} + +cgltf_size cgltf_decode_uri(char* uri) +{ + char* write = uri; + char* i = uri; + + while (*i) + { + if (*i == '%') + { + int ch1 = cgltf_unhex(i[1]); + + if (ch1 >= 0) + { + int ch2 = cgltf_unhex(i[2]); + + if (ch2 >= 0) + { + *write++ = (char)(ch1 * 16 + ch2); + i += 3; + continue; + } + } + } + + *write++ = *i++; + } + + *write = 0; + return write - uri; +} + +cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin) + { + if (data->bin_size < data->buffers[0].size) + { + return cgltf_result_data_too_short; + } + + data->buffers[0].data = (void*)data->bin; + data->buffers[0].data_free_method = cgltf_data_free_method_none; + } + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + if (data->buffers[i].data) + { + continue; + } + + const char* uri = data->buffers[i].uri; + + if (uri == NULL) + { + continue; + } + + if (strncmp(uri, "data:", 5) == 0) + { + const char* comma = strchr(uri, ','); + + if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) + { + cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + else if (strstr(uri, "://") == NULL && gltf_path) + { + cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_file_release; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + + return cgltf_result_success; +} + +static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) +{ + char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; + cgltf_size bound = 0; + + switch (component_type) + { + case cgltf_component_type_r_8u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned char*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_16u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned short*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_32u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned int*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + default: + ; + } + + return bound; +} + +#if CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result; +#else +#define CGLTF_ASSERT_IF(cond, result) if (cond) return result; +#endif + +cgltf_result cgltf_validate(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + cgltf_accessor* accessor = &data->accessors[i]; + + CGLTF_ASSERT_IF(data->accessors[i].component_type == cgltf_component_type_invalid, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->accessors[i].type == cgltf_type_invalid, cgltf_result_invalid_gltf); + + cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); + + if (accessor->buffer_view) + { + cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size; + + CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short); + } + + if (accessor->is_sparse) + { + cgltf_accessor_sparse* sparse = &accessor->sparse; + + cgltf_size indices_component_size = cgltf_component_size(sparse->indices_component_type); + cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; + cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; + + CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size || + sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u && + sparse->indices_component_type != cgltf_component_type_r_16u && + sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + if (sparse->indices_buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count); + + CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short); + } + } + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size; + + CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short); + + if (data->buffer_views[i].has_meshopt_compression) + { + cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression; + + CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + if (data->meshes[i].weights) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->meshes[i].target_names) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf); + } + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].type == cgltf_primitive_type_invalid, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes_count == 0, cgltf_result_invalid_gltf); + + cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; + + CGLTF_ASSERT_IF(first->count == 0, cgltf_result_invalid_gltf); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); + } + } + + cgltf_accessor* indices = data->meshes[i].primitives[j].indices; + + CGLTF_ASSERT_IF(indices && + indices->component_type != cgltf_component_type_r_8u && + indices->component_type != cgltf_component_type_r_16u && + indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(indices && indices->type != cgltf_type_scalar, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(indices && indices->stride != cgltf_component_size(indices->component_type), cgltf_result_invalid_gltf); + + if (indices && indices->buffer_view && indices->buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); + + CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); + } + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + if (data->nodes[i].weights && data->nodes[i].mesh) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->nodes[i].has_mesh_gpu_instancing) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh == NULL, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes_count == 0, cgltf_result_invalid_gltf); + + cgltf_accessor* first = data->nodes[i].mesh_gpu_instancing.attributes[0].data; + + for (cgltf_size k = 0; k < data->nodes[i].mesh_gpu_instancing.attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + cgltf_node* p1 = data->nodes[i].parent; + cgltf_node* p2 = p1 ? p1->parent : NULL; + + while (p1 && p2) + { + CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf); + + p1 = p1->parent; + p2 = p2->parent ? p2->parent->parent : NULL; + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_animation_channel* channel = &data->animations[i].channels[j]; + + if (!channel->target_node) + { + continue; + } + + cgltf_size components = 1; + + if (channel->target_path == cgltf_animation_path_type_weights) + { + CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf); + + components = channel->target_node->mesh->primitives[0].targets_count; + } + + cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; + + CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + CGLTF_ASSERT_IF(!data->variants[i].name, cgltf_result_invalid_gltf); + } + + return cgltf_result_success; +} + +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) +{ + cgltf_size json_size = extras->end_offset - extras->start_offset; + + if (!dest) + { + if (dest_size) + { + *dest_size = json_size + 1; + return cgltf_result_success; + } + return cgltf_result_invalid_options; + } + + if (*dest_size + 1 < json_size) + { + strncpy(dest, data->json + extras->start_offset, *dest_size - 1); + dest[*dest_size - 1] = 0; + } + else + { + strncpy(dest, data->json + extras->start_offset, json_size); + dest[json_size] = 0; + } + + return cgltf_result_success; +} + +static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras) +{ + data->memory.free_func(data->memory.user_data, extras->data); +} + +static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) +{ + for (cgltf_size i = 0; i < extensions_count; ++i) + { + data->memory.free_func(data->memory.user_data, extensions[i].name); + data->memory.free_func(data->memory.user_data, extensions[i].data); + } + data->memory.free_func(data->memory.user_data, extensions); +} + +void cgltf_free(cgltf_data* data) +{ + if (!data) + { + return; + } + + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; + + data->memory.free_func(data->memory.user_data, data->asset.copyright); + data->memory.free_func(data->memory.user_data, data->asset.generator); + data->memory.free_func(data->memory.user_data, data->asset.version); + data->memory.free_func(data->memory.user_data, data->asset.min_version); + + cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); + cgltf_free_extras(data, &data->asset.extras); + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->accessors[i].name); + + cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); + cgltf_free_extras(data, &data->accessors[i].extras); + } + data->memory.free_func(data->memory.user_data, data->accessors); + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->buffer_views[i].name); + data->memory.free_func(data->memory.user_data, data->buffer_views[i].data); + + cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); + cgltf_free_extras(data, &data->buffer_views[i].extras); + } + data->memory.free_func(data->memory.user_data, data->buffer_views); + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->buffers[i].name); + + if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) + { + file_release(&data->memory, &data->file, data->buffers[i].data); + } + else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) + { + data->memory.free_func(data->memory.user_data, data->buffers[i].data); + } + + data->memory.free_func(data->memory.user_data, data->buffers[i].uri); + + cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); + cgltf_free_extras(data, &data->buffers[i].extras); + } + data->memory.free_func(data->memory.user_data, data->buffers); + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].name); + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets); + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings); + + cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); + cgltf_free_extras(data, &data->meshes[i].primitives[j].extras); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives); + data->memory.free_func(data->memory.user_data, data->meshes[i].weights); + + for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].target_names[j]); + } + + cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); + cgltf_free_extras(data, &data->meshes[i].extras); + + data->memory.free_func(data->memory.user_data, data->meshes[i].target_names); + } + + data->memory.free_func(data->memory.user_data, data->meshes); + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->materials[i].name); + + cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); + cgltf_free_extras(data, &data->materials[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->materials); + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->images[i].name); + data->memory.free_func(data->memory.user_data, data->images[i].uri); + data->memory.free_func(data->memory.user_data, data->images[i].mime_type); + + cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); + cgltf_free_extras(data, &data->images[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->images); + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->textures[i].name); + + cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); + cgltf_free_extras(data, &data->textures[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->textures); + + for (cgltf_size i = 0; i < data->samplers_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->samplers[i].name); + + cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); + cgltf_free_extras(data, &data->samplers[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->samplers); + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->skins[i].name); + data->memory.free_func(data->memory.user_data, data->skins[i].joints); + + cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); + cgltf_free_extras(data, &data->skins[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->skins); + + for (cgltf_size i = 0; i < data->cameras_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->cameras[i].name); + + if (data->cameras[i].type == cgltf_camera_type_perspective) + { + cgltf_free_extras(data, &data->cameras[i].data.perspective.extras); + } + else if (data->cameras[i].type == cgltf_camera_type_orthographic) + { + cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras); + } + + cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); + cgltf_free_extras(data, &data->cameras[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->cameras); + + for (cgltf_size i = 0; i < data->lights_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->lights[i].name); + + cgltf_free_extras(data, &data->lights[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->lights); + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->nodes[i].name); + data->memory.free_func(data->memory.user_data, data->nodes[i].children); + data->memory.free_func(data->memory.user_data, data->nodes[i].weights); + + if (data->nodes[i].has_mesh_gpu_instancing) + { + for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j) + { + data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name); + } + + data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes); + } + + cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); + cgltf_free_extras(data, &data->nodes[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->nodes); + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->scenes[i].name); + data->memory.free_func(data->memory.user_data, data->scenes[i].nodes); + + cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); + cgltf_free_extras(data, &data->scenes[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->scenes); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->animations[i].name); + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); + cgltf_free_extras(data, &data->animations[i].samplers[j].extras); + } + data->memory.free_func(data->memory.user_data, data->animations[i].samplers); + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); + cgltf_free_extras(data, &data->animations[i].channels[j].extras); + } + data->memory.free_func(data->memory.user_data, data->animations[i].channels); + + cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); + cgltf_free_extras(data, &data->animations[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->animations); + + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->variants[i].name); + + cgltf_free_extras(data, &data->variants[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->variants); + + cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); + cgltf_free_extras(data, &data->extras); + + for (cgltf_size i = 0; i < data->extensions_used_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->extensions_used[i]); + } + + data->memory.free_func(data->memory.user_data, data->extensions_used); + + for (cgltf_size i = 0; i < data->extensions_required_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->extensions_required[i]); + } + + data->memory.free_func(data->memory.user_data, data->extensions_required); + + file_release(&data->memory, &data->file, data->file_data); + + data->memory.free_func(data->memory.user_data, data); +} + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + + if (node->has_matrix) + { + memcpy(lm, node->matrix, sizeof(float) * 16); + } + else + { + float tx = node->translation[0]; + float ty = node->translation[1]; + float tz = node->translation[2]; + + float qx = node->rotation[0]; + float qy = node->rotation[1]; + float qz = node->rotation[2]; + float qw = node->rotation[3]; + + float sx = node->scale[0]; + float sy = node->scale[1]; + float sz = node->scale[2]; + + lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx; + lm[1] = (2 * qx*qy + 2 * qz*qw) * sx; + lm[2] = (2 * qx*qz - 2 * qy*qw) * sx; + lm[3] = 0.f; + + lm[4] = (2 * qx*qy - 2 * qz*qw) * sy; + lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy; + lm[6] = (2 * qy*qz + 2 * qx*qw) * sy; + lm[7] = 0.f; + + lm[8] = (2 * qx*qz + 2 * qy*qw) * sz; + lm[9] = (2 * qy*qz - 2 * qx*qw) * sz; + lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz; + lm[11] = 0.f; + + lm[12] = tx; + lm[13] = ty; + lm[14] = tz; + lm[15] = 1.f; + } +} + +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + cgltf_node_transform_local(node, lm); + + const cgltf_node* parent = node->parent; + + while (parent) + { + float pm[16]; + cgltf_node_transform_local(parent, pm); + + for (int i = 0; i < 4; ++i) + { + float l0 = lm[i * 4 + 0]; + float l1 = lm[i * 4 + 1]; + float l2 = lm[i * 4 + 2]; + + float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8]; + float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9]; + float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10]; + + lm[i * 4 + 0] = r0; + lm[i * 4 + 1] = r1; + lm[i * 4 + 2] = r2; + } + + lm[12] += pm[12]; + lm[13] += pm[13]; + lm[14] += pm[14]; + + parent = parent->parent; + } +} + +static cgltf_ssize cgltf_component_read_integer(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16: + return *((const int16_t*) in); + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_8: + return *((const int8_t*) in); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) +{ + if (component_type == cgltf_component_type_r_32f) + { + return *((const float*) in); + } + + if (normalized) + { + switch (component_type) + { + // note: glTF spec doesn't currently define normalized conversions for 32-bit integers + case cgltf_component_type_r_16: + return *((const int16_t*) in) / (cgltf_float)32767; + case cgltf_component_type_r_16u: + return *((const uint16_t*) in) / (cgltf_float)65535; + case cgltf_component_type_r_8: + return *((const int8_t*) in) / (cgltf_float)127; + case cgltf_component_type_r_8u: + return *((const uint8_t*) in) / (cgltf_float)255; + default: + return 0; + } + } + + return (cgltf_float)cgltf_component_read_integer(in, component_type); +} + +static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) { + return 0; + } + + // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. + + cgltf_size component_size = cgltf_component_size(component_type); + + if (type == cgltf_type_mat2 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 5, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 2, component_type, normalized); + out[3] = cgltf_component_read_float(element + 4, component_type, normalized); + out[4] = cgltf_component_read_float(element + 5, component_type, normalized); + out[5] = cgltf_component_read_float(element + 6, component_type, normalized); + out[6] = cgltf_component_read_float(element + 8, component_type, normalized); + out[7] = cgltf_component_read_float(element + 9, component_type, normalized); + out[8] = cgltf_component_read_float(element + 10, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 2) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 2, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 8, component_type, normalized); + out[4] = cgltf_component_read_float(element + 10, component_type, normalized); + out[5] = cgltf_component_read_float(element + 12, component_type, normalized); + out[6] = cgltf_component_read_float(element + 16, component_type, normalized); + out[7] = cgltf_component_read_float(element + 18, component_type, normalized); + out[8] = cgltf_component_read_float(element + 20, component_type, normalized); + return 1; + } + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); + } + return 1; +} + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) +{ + if (view->data) + return (const uint8_t*)view->data; + + if (!view->buffer->data) + return NULL; + + const uint8_t* result = (const uint8_t*)view->buffer->data; + result += view->offset; + return result; +} + +const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index) +{ + for (cgltf_size i = 0; i < prim->attributes_count; ++i) + { + const cgltf_attribute* attr = &prim->attributes[i]; + if (attr->type == type && attr->index == index) + return attr->data; + } + + return NULL; +} + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof(cgltf_float)); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); +} + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count) +{ + cgltf_size floats_per_element = cgltf_num_components(accessor->type); + cgltf_size available_floats = accessor->count * floats_per_element; + if (out == NULL) + { + return available_floats; + } + + float_count = available_floats < float_count ? available_floats : float_count; + cgltf_size element_count = float_count / floats_per_element; + + // First pass: convert each element in the base accessor. + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_count * floats_per_element * sizeof(cgltf_float)); + } + else + { + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset; + + if (accessor->component_type == cgltf_component_type_r_32f && accessor->stride == floats_per_element * sizeof(cgltf_float)) + { + memcpy(out, element, element_count * floats_per_element * sizeof(cgltf_float)); + } + else + { + cgltf_float* dest = out; + + for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element, element += accessor->stride) + { + if (!cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, dest, floats_per_element)) + { + return 0; + } + } + } + } + + // Second pass: write out each element in the sparse accessor. + if (accessor->is_sparse) + { + const cgltf_accessor_sparse* sparse = &accessor->sparse; + + const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); + const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view); + + if (index_data == NULL || reader_head == NULL) + { + return 0; + } + + index_data += sparse->indices_byte_offset; + reader_head += sparse->values_byte_offset; + + cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); + for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride, reader_head += accessor->stride) + { + size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type); + float* writer_head = out + writer_index * floats_per_element; + + if (!cgltf_element_read_float(reader_head, accessor->type, accessor->component_type, accessor->normalized, writer_head, floats_per_element)) + { + return 0; + } + } + } + + return element_count * floats_per_element; +} + +static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_8: + return *((const int8_t*) in); + + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + + case cgltf_component_type_r_16: + return *((const int16_t*) in); + + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + + default: + return 0; + } +} + +static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) + { + return 0; + } + + // Reading integer matrices is not a valid use case + if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4) + { + return 0; + } + + cgltf_size component_size = cgltf_component_size(component_type); + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_uint(element + component_size * i, component_type); + } + return 1; +} + +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof( cgltf_uint )); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); +} + +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) +{ + if (accessor->is_sparse) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + if (accessor->buffer_view == NULL) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + element += accessor->offset + accessor->stride * index; + return cgltf_component_read_index(element, accessor->component_type); +} + +cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object) +{ + assert(object && (cgltf_size)(object - data->meshes) < data->meshes_count); + return (cgltf_size)(object - data->meshes); +} + +cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object) +{ + assert(object && (cgltf_size)(object - data->materials) < data->materials_count); + return (cgltf_size)(object - data->materials); +} + +cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object) +{ + assert(object && (cgltf_size)(object - data->accessors) < data->accessors_count); + return (cgltf_size)(object - data->accessors); +} + +cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object) +{ + assert(object && (cgltf_size)(object - data->buffer_views) < data->buffer_views_count); + return (cgltf_size)(object - data->buffer_views); +} + +cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object) +{ + assert(object && (cgltf_size)(object - data->buffers) < data->buffers_count); + return (cgltf_size)(object - data->buffers); +} + +cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object) +{ + assert(object && (cgltf_size)(object - data->images) < data->images_count); + return (cgltf_size)(object - data->images); +} + +cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object) +{ + assert(object && (cgltf_size)(object - data->textures) < data->textures_count); + return (cgltf_size)(object - data->textures); +} + +cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object) +{ + assert(object && (cgltf_size)(object - data->samplers) < data->samplers_count); + return (cgltf_size)(object - data->samplers); +} + +cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object) +{ + assert(object && (cgltf_size)(object - data->skins) < data->skins_count); + return (cgltf_size)(object - data->skins); +} + +cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object) +{ + assert(object && (cgltf_size)(object - data->cameras) < data->cameras_count); + return (cgltf_size)(object - data->cameras); +} + +cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object) +{ + assert(object && (cgltf_size)(object - data->lights) < data->lights_count); + return (cgltf_size)(object - data->lights); +} + +cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object) +{ + assert(object && (cgltf_size)(object - data->nodes) < data->nodes_count); + return (cgltf_size)(object - data->nodes); +} + +cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object) +{ + assert(object && (cgltf_size)(object - data->scenes) < data->scenes_count); + return (cgltf_size)(object - data->scenes); +} + +cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object) +{ + assert(object && (cgltf_size)(object - data->animations) < data->animations_count); + return (cgltf_size)(object - data->animations); +} + +cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object) +{ + assert(object && (cgltf_size)(object - animation->samplers) < animation->samplers_count); + return (cgltf_size)(object - animation->samplers); +} + +cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object) +{ + assert(object && (cgltf_size)(object - animation->channels) < animation->channels_count); + return (cgltf_size)(object - animation->channels); +} + +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count) +{ + if (out == NULL) + { + return accessor->count; + } + + index_count = accessor->count < index_count ? accessor->count : index_count; + cgltf_size index_component_size = cgltf_component_size(accessor->component_type); + + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + return 0; + } + if (index_component_size > out_component_size) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset; + + if (index_component_size == out_component_size && accessor->stride == out_component_size) + { + memcpy(out, element, index_count * index_component_size); + return index_count; + } + + // The component size of the output array is larger than the component size of the index data, so index data will be padded. + switch (out_component_size) + { + case 2: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) + { + ((uint16_t*)out)[index] = (uint16_t)cgltf_component_read_index(element, accessor->component_type); + } + break; + case 4: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) + { + ((uint32_t*)out)[index] = (uint32_t)cgltf_component_read_index(element, accessor->component_type); + } + break; + default: + break; + } + + return index_count; +} + +#define CGLTF_ERROR_JSON -1 +#define CGLTF_ERROR_NOMEM -2 +#define CGLTF_ERROR_LEGACY -3 + +#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_TOKTYPE_RET(tok_, type_, ret_) if ((tok_).type != (type_)) { return ret_; } +#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ + +#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) +#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; } +#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; + +static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); + size_t const str_len = strlen(str); + size_t const name_length = (size_t)(tok->end - tok->start); + return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; +} + +static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return CGLTF_ATOI(tmp); +} + +static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE_RET(*tok, JSMN_PRIMITIVE, 0); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + long long res = CGLTF_ATOLL(tmp); + return res < 0 ? 0 : (cgltf_size)res; +} + +static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return (cgltf_float)CGLTF_ATOF(tmp); +} + +static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int size = (int)(tok->end - tok->start); + return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; +} + +static int cgltf_skip_json(jsmntok_t const* tokens, int i) +{ + int end = i + 1; + + while (i < end) + { + switch (tokens[i].type) + { + case JSMN_OBJECT: + end += tokens[i].size * 2; + break; + + case JSMN_ARRAY: + end += tokens[i].size; + break; + + case JSMN_PRIMITIVE: + case JSMN_STRING: + break; + + default: + return -1; + } + + i++; + } + + return i; +} + +static void cgltf_fill_float_array(float* out_array, int size, float value) +{ + for (int j = 0; j < size; ++j) + { + out_array[j] = value; + } +} + +static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + if (tokens[i].size != size) + { + return CGLTF_ERROR_JSON; + } + ++i; + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_array[j] = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + return i; +} + +static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + if (*out_string) + { + return CGLTF_ERROR_JSON; + } + int size = (int)(tokens[i].end - tokens[i].start); + char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(result, (const char*)json_chunk + tokens[i].start, size); + result[size] = 0; + *out_string = result; + return i + 1; +} + +static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size) +{ + (void)json_chunk; + if (tokens[i].type != JSMN_ARRAY) + { + return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON; + } + if (*out_array) + { + return CGLTF_ERROR_JSON; + } + int size = tokens[i].size; + void* result = cgltf_calloc(options, element_size, size); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + *out_array = result; + *out_size = size; + return i + 1; +} + +static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < *out_size; ++j) + { + i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); + if (i < 0) + { + return i; + } + } + return i; +} + +static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) +{ + if (*name == '_') + { + *out_type = cgltf_attribute_type_custom; + return; + } + + const char* us = strchr(name, '_'); + size_t len = us ? (size_t)(us - name) : strlen(name); + + if (len == 8 && strncmp(name, "POSITION", 8) == 0) + { + *out_type = cgltf_attribute_type_position; + } + else if (len == 6 && strncmp(name, "NORMAL", 6) == 0) + { + *out_type = cgltf_attribute_type_normal; + } + else if (len == 7 && strncmp(name, "TANGENT", 7) == 0) + { + *out_type = cgltf_attribute_type_tangent; + } + else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0) + { + *out_type = cgltf_attribute_type_texcoord; + } + else if (len == 5 && strncmp(name, "COLOR", 5) == 0) + { + *out_type = cgltf_attribute_type_color; + } + else if (len == 6 && strncmp(name, "JOINTS", 6) == 0) + { + *out_type = cgltf_attribute_type_joints; + } + else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0) + { + *out_type = cgltf_attribute_type_weights; + } + else + { + *out_type = cgltf_attribute_type_invalid; + } + + if (us && *out_type != cgltf_attribute_type_invalid) + { + *out_index = CGLTF_ATOI(us + 1); + if (*out_index < 0) + { + *out_type = cgltf_attribute_type_invalid; + *out_index = 0; + } + } +} + +static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + if (*out_attributes) + { + return CGLTF_ERROR_JSON; + } + + *out_attributes_count = tokens[i].size; + *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count); + ++i; + + if (!*out_attributes) + { + return CGLTF_ERROR_NOMEM; + } + + for (cgltf_size j = 0; j < *out_attributes_count; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name); + if (i < 0) + { + return CGLTF_ERROR_JSON; + } + + cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index); + + (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + + return i; +} + +static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +{ + if (out_extras->data) + { + return CGLTF_ERROR_JSON; + } + + /* fill deprecated fields for now, this will be removed in the future */ + out_extras->start_offset = tokens[i].start; + out_extras->end_offset = tokens[i].end; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!out_extras->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extras->data, (const char*)json_chunk + start, size); + out_extras->data[size] = '\0'; + + i = cgltf_skip_json(tokens, i); + return i; +} + +static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT); + if (out_extension->name) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size name_length = tokens[i].end - tokens[i].start; + out_extension->name = (char*)options->memory.alloc_func(options->memory.user_data, name_length + 1); + if (!out_extension->name) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length); + out_extension->name[name_length] = 0; + i++; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extension->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!out_extension->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->data, (const char*)json_chunk + start, size); + out_extension->data[size] = '\0'; + + i = cgltf_skip_json(tokens, i); + + return i; +} + +static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions) +{ + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(*out_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + *out_extensions_count = 0; + *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!*out_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int j = 0; j < extensions_size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + cgltf_size extension_index = (*out_extensions_count)++; + cgltf_extension* extension = &((*out_extensions)[extension_index]); + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension); + + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0) + { + ++i; + out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh_gpu_instancing(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh_gpu_instancing* out_mesh_gpu_instancing) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_mesh_gpu_instancing->attributes, &out_mesh_gpu_instancing->attributes_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int obj_size = tokens[i].size; + ++i; + + int material = -1; + int variants_tok = -1; + int extras_tok = -1; + + for (int k = 0; k < obj_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0) + { + ++i; + material = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + variants_tok = i+1; + CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY); + + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + extras_tok = i + 1; + i = cgltf_skip_json(tokens, extras_tok); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (material < 0 || variants_tok < 0) + { + return CGLTF_ERROR_JSON; + } + + if (out_mappings) + { + for (int k = 0; k < tokens[variants_tok].size; ++k) + { + int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk); + if (variant < 0) + return variant; + + out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); + out_mappings[*offset].variant = variant; + + if (extras_tok >= 0) + { + int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras); + if (e < 0) + return e; + } + + (*offset)++; + } + } + else + { + (*offset) += tokens[variants_tok].size; + } + } + + return i; +} + +static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0) + { + if (out_prim->mappings) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size mappings_offset = 0; + int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset); + if (k < 0) + { + return k; + } + + out_prim->mappings_count = mappings_offset; + out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count); + + mappings_offset = 0; + i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static cgltf_primitive_type cgltf_json_to_primitive_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 0: + return cgltf_primitive_type_points; + case 1: + return cgltf_primitive_type_lines; + case 2: + return cgltf_primitive_type_line_loop; + case 3: + return cgltf_primitive_type_line_strip; + case 4: + return cgltf_primitive_type_triangles; + case 5: + return cgltf_primitive_type_triangle_strip; + case 6: + return cgltf_primitive_type_triangle_fan; + default: + return cgltf_primitive_type_invalid; + } +} + +static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_prim->type = cgltf_primitive_type_triangles; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + out_prim->type = cgltf_json_to_primitive_type(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) + { + ++i; + out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_prim->targets_count; ++k) + { + i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_prim->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_prim->extensions_count = 0; + out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_prim->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0) + { + out_prim->has_draco_mesh_compression = 1; + i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count); + if (i < 0) + { + return i; + } + + for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index) + { + i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + ++i; + + out_mesh->extras.start_offset = tokens[i].start; + out_mesh->extras.end_offset = tokens[i].end; + + if (tokens[i].type == JSMN_OBJECT) + { + int extras_size = tokens[i].size; + ++i; + + for (int k = 0; k < extras_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i); + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->meshes_count; ++j) + { + i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 5120: + return cgltf_component_type_r_8; + case 5121: + return cgltf_component_type_r_8u; + case 5122: + return cgltf_component_type_r_16; + case 5123: + return cgltf_component_type_r_16u; + case 5125: + return cgltf_component_type_r_32u; + case 5126: + return cgltf_component_type_r_32f; + default: + return cgltf_component_type_invalid; + } +} + +static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_sparse->count = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int indices_size = tokens[i].size; + ++i; + + for (int k = 0; k < indices_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int values_size = tokens[i].size; + ++i; + + for (int k = 0; k < values_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_accessor->offset = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0) + { + ++i; + out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_accessor->count = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) + { + out_accessor->type = cgltf_type_scalar; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) + { + out_accessor->type = cgltf_type_vec2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) + { + out_accessor->type = cgltf_type_vec3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) + { + out_accessor->type = cgltf_type_vec4; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) + { + out_accessor->type = cgltf_type_mat2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) + { + out_accessor->type = cgltf_type_mat3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) + { + out_accessor->type = cgltf_type_mat4; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0) + { + ++i; + out_accessor->has_min = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int min_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0) + { + ++i; + out_accessor->has_max = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int max_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) + { + out_accessor->is_sparse = 1; + i = cgltf_parse_json_accessor_sparse(tokens, i + 1, json_chunk, &out_accessor->sparse); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0) + { + ++i; + out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_transform->has_texcoord = 1; + out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view) +{ + (void)options; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_texture_view->scale = 1.0f; + cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) + { + ++i; + out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int extensions_size = tokens[i].size; + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) + { + out_texture_view->has_transform = 1; + i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) + { + ++i; + out_pbr->metallic_factor = + cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + { + ++i; + out_pbr->roughness_factor = + cgltf_json_to_float(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->base_color_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) + { + ++i; + out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_ior->ior = 1.5f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0) + { + ++i; + out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_specular->specular_factor = 1.0f; + cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f); + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + ++i; + out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0) + { + ++i; + out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0) + { + ++i; + out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0) + { + ++i; + out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0) + { + ++i; + out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default + out_emissive_strength->emissive_strength = 1.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0) + { + ++i; + out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_iridescence* out_iridescence) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default + out_iridescence->iridescence_ior = 1.3f; + out_iridescence->iridescence_thickness_min = 100.f; + out_iridescence->iridescence_thickness_max = 400.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceFactor") == 0) + { + ++i; + out_iridescence->iridescence_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceIor") == 0) + { + ++i; + out_iridescence->iridescence_ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMinimum") == 0) + { + ++i; + out_iridescence->iridescence_thickness_min = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMaximum") == 0) + { + ++i; + out_iridescence->iridescence_thickness_max = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_thickness_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_diffuse_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_diffuse_transmission* out_diff_transmission) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Defaults + cgltf_fill_float_array(out_diff_transmission->diffuse_transmission_color_factor, 3, 1.0f); + out_diff_transmission->diffuse_transmission_factor = 0.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionFactor") == 0) + { + ++i; + out_diff_transmission->diffuse_transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_diff_transmission->diffuse_transmission_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_color_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyStrength") == 0) + { + ++i; + out_anisotropy->anisotropy_strength = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyRotation") == 0) + { + ++i; + out_anisotropy->anisotropy_rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_anisotropy->anisotropy_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_dispersion(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_dispersion* out_dispersion) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "dispersion") == 0) + { + ++i; + out_dispersion->dispersion = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_sampler->wrap_s = cgltf_wrap_mode_repeat; + out_sampler->wrap_t = cgltf_wrap_mode_repeat; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) + { + ++i; + out_sampler->mag_filter + = (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) + { + ++i; + out_sampler->min_filter + = (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) + { + ++i; + out_sampler->wrap_s + = (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + { + ++i; + out_sampler->wrap_t + = (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) + { + ++i; + out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if (out_texture->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_texture->extensions_count = 0; + + if (!out_texture->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0) + { + out_texture->has_basisu = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0) + { + out_texture->has_webp = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f); + out_material->pbr_metallic_roughness.metallic_factor = 1.0f; + out_material->pbr_metallic_roughness.roughness_factor = 1.0f; + + cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f); + cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f); + out_material->pbr_specular_glossiness.glossiness_factor = 1.0f; + + cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f); + out_material->volume.attenuation_distance = FLT_MAX; + + out_material->alpha_cutoff = 0.5f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) + { + out_material->has_pbr_metallic_roughness = 1; + i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->normal_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->occlusion_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->emissive_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_opaque; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_mask; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_blend; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0) + { + ++i; + out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) + { + ++i; + out_material->double_sided = + cgltf_json_to_bool(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_material->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_material->extensions_count= 0; + + if (!out_material->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0) + { + out_material->has_pbr_specular_glossiness = 1; + i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0) + { + out_material->unlit = 1; + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0) + { + out_material->has_clearcoat = 1; + i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0) + { + out_material->has_ior = 1; + i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0) + { + out_material->has_specular = 1; + i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0) + { + out_material->has_transmission = 1; + i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0) + { + out_material->has_volume = 1; + i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0) + { + out_material->has_sheen = 1; + i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0) + { + out_material->has_emissive_strength = 1; + i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_iridescence") == 0) + { + out_material->has_iridescence = 1; + i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_diffuse_transmission") == 0) + { + out_material->has_diffuse_transmission = 1; + i = cgltf_parse_json_diffuse_transmission(options, tokens, i + 1, json_chunk, &out_material->diffuse_transmission); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0) + { + out_material->has_anisotropy = 1; + i = cgltf_parse_json_anisotropy(options, tokens, i + 1, json_chunk, &out_material->anisotropy); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_dispersion") == 0) + { + out_material->has_dispersion = 1; + i = cgltf_parse_json_dispersion(tokens, i + 1, json_chunk, &out_material->dispersion); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->accessors_count; ++j) + { + i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->materials_count; ++j) + { + i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->images_count; ++j) + { + i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->textures_count; ++j) + { + i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->samplers_count; ++j) + { + i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_meshopt_compression->count = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices; + } + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; + } + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_buffer_view->offset = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer_view->size = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_buffer_view->stride = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 34962: + type = cgltf_buffer_view_type_vertices; + break; + case 34963: + type = cgltf_buffer_view_type_indices; + break; + default: + type = cgltf_buffer_view_type_invalid; + break; + } + out_buffer_view->type = (cgltf_buffer_view_type)type; + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_buffer_view->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_buffer_view->extensions_count = 0; + out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_buffer_view->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0) + { + out_buffer_view->has_meshopt_compression = 1; + i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j) + { + i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer->size = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffers_count; ++j) + { + i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_skin->joints_count; ++k) + { + out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->skins_count; ++j) + { + i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + if (out_camera->type != cgltf_camera_type_invalid) + { + return CGLTF_ERROR_JSON; + } + + out_camera->type = cgltf_camera_type_perspective; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0) + { + ++i; + out_camera->data.perspective.has_aspect_ratio = 1; + out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0) + { + ++i; + out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.perspective.has_zfar = 1; + out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + if (out_camera->type != cgltf_camera_type_invalid) + { + return CGLTF_ERROR_JSON; + } + + out_camera->type = cgltf_camera_type_orthographic; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0) + { + ++i; + out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0) + { + ++i; + out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->cameras_count; ++j) + { + i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_light->color[0] = 1.f; + out_light->color[1] = 1.f; + out_light->color[2] = 1.f; + out_light->intensity = 1.f; + + out_light->spot_inner_cone_angle = 0.f; + out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0) + { + ++i; + out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0) + { + out_light->type = cgltf_light_type_directional; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0) + { + out_light->type = cgltf_light_type_point; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0) + { + out_light->type = cgltf_light_type_spot; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0) + { + ++i; + out_light->range = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0) + { + ++i; + out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0) + { + ++i; + out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->lights_count; ++j) + { + i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_node->rotation[3] = 1.0f; + out_node->scale[0] = 1.0f; + out_node->scale[1] = 1.0f; + out_node->scale[2] = 1.0f; + out_node->matrix[0] = 1.0f; + out_node->matrix[5] = 1.0f; + out_node->matrix[10] = 1.0f; + out_node->matrix[15] = 1.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_node->children_count; ++k) + { + out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_node->has_translation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_node->has_rotation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_node->has_scale = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0) + { + out_node->has_matrix = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_node->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_node->extensions_count= 0; + out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_node->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_mesh_gpu_instancing") == 0) + { + out_node->has_mesh_gpu_instancing = 1; + i = cgltf_parse_json_mesh_gpu_instancing(options, tokens, i + 1, json_chunk, &out_node->mesh_gpu_instancing); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->nodes_count; ++j) + { + i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) + { + out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->scenes_count; ++j) + { + i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0) + { + ++i; + out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0) + { + ++i; + out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_linear; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_step; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_cubic_spline; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0) + { + ++i; + out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int target_size = tokens[i].size; + ++i; + + for (int k = 0; k < target_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0) + { + ++i; + out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_translation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_rotation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_channel->target_path = cgltf_animation_path_type_scale; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0) + { + out_channel->target_path = cgltf_animation_path_type_weights; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->samplers_count; ++k) + { + i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->channels_count; ++k) + { + i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->animations_count; ++j) + { + i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->variants_count; ++j) + { + i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2) + { + return CGLTF_ERROR_LEGACY; + } + + return i; +} + +cgltf_size cgltf_num_components(cgltf_type type) { + switch (type) + { + case cgltf_type_vec2: + return 2; + case cgltf_type_vec3: + return 3; + case cgltf_type_vec4: + return 4; + case cgltf_type_mat2: + return 4; + case cgltf_type_mat3: + return 9; + case cgltf_type_mat4: + return 16; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + return 1; + } +} + +cgltf_size cgltf_component_size(cgltf_component_type component_type) { + switch (component_type) + { + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + return 1; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + return 2; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + return 4; + case cgltf_component_type_invalid: + default: + return 0; + } +} + +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_size component_size = cgltf_component_size(component_type); + if (type == cgltf_type_mat2 && component_size == 1) + { + return 8 * component_size; + } + else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) + { + return 12 * component_size; + } + return component_size * cgltf_num_components(type); +} + +static int cgltf_fixup_pointers(cgltf_data* out_data); + +static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0) + { + i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0) + { + i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0) + { + i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0) + { + i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0) + { + i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0) + { + i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0) + { + i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0) + { + i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0) + { + i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0) + { + i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0) + { + i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0) + { + ++i; + out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0) + { + i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_data->data_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_data->data_extensions_count = 0; + out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_data->data_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0) + { + i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) +{ + jsmn_parser parser = { 0, 0, 0 }; + + if (options->json_token_count == 0) + { + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); + + if (token_count <= 0) + { + return cgltf_result_invalid_json; + } + + options->json_token_count = token_count; + } + + jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc_func(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); + + if (!tokens) + { + return cgltf_result_out_of_memory; + } + + jsmn_init(&parser); + + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); + + if (token_count <= 0) + { + options->memory.free_func(options->memory.user_data, tokens); + return cgltf_result_invalid_json; + } + + // this makes sure that we always have an UNDEFINED token at the end of the stream + // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data + tokens[token_count].type = JSMN_UNDEFINED; + + cgltf_data* data = (cgltf_data*)options->memory.alloc_func(options->memory.user_data, sizeof(cgltf_data)); + + if (!data) + { + options->memory.free_func(options->memory.user_data, tokens); + return cgltf_result_out_of_memory; + } + + memset(data, 0, sizeof(cgltf_data)); + data->memory = options->memory; + data->file = options->file; + + int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data); + + options->memory.free_func(options->memory.user_data, tokens); + + if (i < 0) + { + cgltf_free(data); + + switch (i) + { + case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory; + case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf; + default: return cgltf_result_invalid_gltf; + } + } + + if (cgltf_fixup_pointers(data) < 0) + { + cgltf_free(data); + return cgltf_result_invalid_gltf; + } + + data->json = (const char*)json_chunk; + data->json_size = size; + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_fixup_pointers(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count); + } + } + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count); + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count); + } + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count); + } + } + } + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count); + + if (data->accessors[i].is_sparse) + { + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count); + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count); + } + + if (data->accessors[i].buffer_view) + { + data->accessors[i].stride = data->accessors[i].buffer_view->stride; + } + + if (data->accessors[i].stride == 0) + { + data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type); + } + } + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); + } + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count); + } + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_color_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count); + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count); + + if (data->buffer_views[i].has_meshopt_compression) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count); + } + } + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count); + } + + CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count); + CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count); + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count); + + if (data->nodes[i].children[j]->parent) + { + return CGLTF_ERROR_JSON; + } + + data->nodes[i].children[j]->parent = &data->nodes[i]; + } + + CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count); + CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count); + CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count); + CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count); + + if (data->nodes[i].has_mesh_gpu_instancing) + { + for (cgltf_size m = 0; m < data->nodes[i].mesh_gpu_instancing.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].mesh_gpu_instancing.attributes[m].data, data->accessors, data->accessors_count); + } + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count); + + if (data->scenes[i].nodes[j]->parent) + { + return CGLTF_ERROR_JSON; + } + } + } + + CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count); + } + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count); + CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count); + } + } + + return 0; +} + +/* + * -- jsmn.c start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + * + * Copyright (c) 2010 Serge A. Zaitsev + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + ptrdiff_t start, ptrdiff_t end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + ptrdiff_t start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + ptrdiff_t start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, size_t num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +static void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +/* + * -- jsmn.c end -- + */ + +#endif /* #ifdef CGLTF_IMPLEMENTATION */ + +/* cgltf is distributed under MIT license: + * + * Copyright (c) 2018-2021 Johannes Kuhlmann + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */