新增3.4版本支持
This commit is contained in:
1 parent
4c5033d2f9
commit
9635428f78
21 files changed
+5425
-51
No files matched your search
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
|
||||
// Preserve name + '.' + ext exactly; only split the compound suffix correctly.
|
||||
// Return through Eagle's thumbnail callback so Eagle persists its own metadata.
|
||||
function normalizeItem(src, item) {
|
||||
const filename = path.basename(src);
|
||||
const match = filename.match(/^(.*)\.(atlas\.txt|skel\.bytes)$/i);
|
||||
if (!match || !item || !item.ext) return false;
|
||||
if (item.name + '.' + item.ext !== filename) return false;
|
||||
if (item.name === match[1] && item.ext === match[2]) return false;
|
||||
item.name = match[1]; item.ext = match[2];
|
||||
delete item.text;
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = { normalizeItem };
|
||||
+17
-1
@@ -11,4 +11,20 @@ function dataUrl() {
|
||||
return cachedDataUrl;
|
||||
}
|
||||
|
||||
module.exports = { path: badgePath, dataUrl };
|
||||
function drawVersion(ctx, width, height, version) {
|
||||
if (!/^\d+\.\d+(?:\.\d+)?$/.test(String(version || ''))) return;
|
||||
const fontSize = Math.max(12, Math.round(Math.min(width, height) * 0.055));
|
||||
const margin = Math.max(10, Math.round(Math.min(width, height) * 0.03));
|
||||
ctx.save();
|
||||
try {
|
||||
ctx.font = '600 ' + fontSize + 'px sans-serif';
|
||||
ctx.textAlign = 'left'; ctx.textBaseline = 'middle';
|
||||
const boxHeight = fontSize + 12;
|
||||
const y = height - margin - boxHeight;
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.65)';
|
||||
ctx.fillRect(margin, y, ctx.measureText(version).width + 16, boxHeight);
|
||||
ctx.fillStyle = '#ffffff'; ctx.fillText(version, margin + 8, y + boxHeight / 2);
|
||||
} finally { ctx.restore(); }
|
||||
}
|
||||
|
||||
module.exports = { path: badgePath, dataUrl, drawVersion };
|
||||
@@ -152,6 +152,27 @@ function createLegacyRenderer(spine, context) {
|
||||
return base;
|
||||
}
|
||||
|
||||
// 3.4 predates clipping attachments and computes packed vertices on attachments.
|
||||
function create34Renderer(spine, context) {
|
||||
const base = new (rendererConstructor(spine))(context);
|
||||
base.triangleRendering = true;
|
||||
base.premultipliedAlpha = false;
|
||||
base.draw = skeleton => {
|
||||
try {
|
||||
for (const slot of skeleton.drawOrder) {
|
||||
const attachment = slot.getAttachment();
|
||||
if (!(attachment instanceof spine.RegionAttachment) && !(attachment instanceof spine.MeshAttachment)) continue;
|
||||
const vertices = attachment.updateWorldVertices(slot, false);
|
||||
const image = imageForCanvas(imageFromAttachment(attachment), context, base.premultipliedAlpha);
|
||||
if (!image) continue;
|
||||
context.globalAlpha = vertices[5];
|
||||
drawInterleaved(base, image, vertices, attachment instanceof spine.MeshAttachment ? attachment.triangles : QUAD_TRIANGLES);
|
||||
}
|
||||
} finally { context.globalAlpha = 1; }
|
||||
};
|
||||
return base;
|
||||
}
|
||||
|
||||
function create43Renderer(spine, context) {
|
||||
const Renderer = rendererConstructor(spine);
|
||||
if (!Renderer || !spine.SkeletonRendererCore) throw new Error('Spine 4.3 Canvas 渲染器不完整');
|
||||
@@ -190,9 +211,12 @@ function create43Renderer(spine, context) {
|
||||
}
|
||||
|
||||
function createRenderer(spine, context, runtimeDir) {
|
||||
return runtimeDir === '4.3'
|
||||
const renderer = runtimeDir === '3.4' ? create34Renderer(spine, context) : runtimeDir === '4.3'
|
||||
? create43Renderer(spine, context)
|
||||
: createLegacyRenderer(spine, context);
|
||||
// Explicitly disable debug drawing in every supported runtime.
|
||||
renderer.debugRendering = false;
|
||||
return require('./spine-webgl-rasterizer.js').attach(renderer, context);
|
||||
}
|
||||
|
||||
module.exports = { createRenderer };
|
||||
+16
-10
@@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
// Adapted from Esoteric Software's 3.6/3.7 spine-libgdx SkeletonBinary.java.
|
||||
// Adapted from Esoteric Software's 3.4/3.6/3.7 spine-libgdx SkeletonBinary.java.
|
||||
// Copyright (c) 2013-2019, Esoteric Software. See
|
||||
// LICENSES/Spine-Legacy-Binary-License.txt for the original license notices.
|
||||
// 3.4 source and license: LICENSES/Spine-3.4-License.txt.
|
||||
// Decode the versioned wire format to an in-memory SkeletonJson object. The
|
||||
// matching runtime builds attachments/timelines and applies loader.scale once.
|
||||
|
||||
@@ -60,13 +61,14 @@ class Input {
|
||||
function decode(bytes, runtimeDir) {
|
||||
const r = new Input(bytes);
|
||||
const skeleton = { hash: r.string(), spine: r.string() };
|
||||
if (!/^3\.[67]\./.test(skeleton.spine || '')) r.fail('仅支持 Spine 3.6 / 3.7');
|
||||
if (!/^3\.[467]\./.test(skeleton.spine || '')) r.fail('仅支持 Spine 3.4 / 3.6 / 3.7');
|
||||
const version = skeleton.spine.slice(0, 3);
|
||||
if (runtimeDir && version !== runtimeDir) r.fail('骨骼版本与运行时不一致');
|
||||
const v37 = version === '3.7';
|
||||
const v34 = version === '3.4';
|
||||
skeleton.width = r.float(); skeleton.height = r.float();
|
||||
const nonessential = r.bool();
|
||||
if (nonessential) { skeleton.fps = r.float(); skeleton.images = r.string(); if (v37) skeleton.audio = r.string(); }
|
||||
if (nonessential) { if (!v34) skeleton.fps = r.float(); skeleton.images = r.string(); if (v37) skeleton.audio = r.string(); }
|
||||
const root = { skeleton, bones: [], slots: [], ik: [], transform: [], path: [], skins: dict(), events: dict(), animations: dict() };
|
||||
const boneName = () => r.ref(root.bones).name;
|
||||
const slotName = () => r.ref(root.slots).name;
|
||||
@@ -75,24 +77,26 @@ function decode(bytes, runtimeDir) {
|
||||
for (let i = 0, n = r.count(); i < n; i++) {
|
||||
const b = { name: r.string() }; if (i) b.parent = boneName();
|
||||
fields(b, 'rotation x y scaleX scaleY shearX shearY length');
|
||||
b.transform = r.ref(['normal', 'onlyTranslation', 'noRotationOrReflection', 'noScale', 'noScaleOrReflection']);
|
||||
if (v34) { b.inheritRotation = r.bool(); b.inheritScale = r.bool(); }
|
||||
else b.transform = r.ref(['normal', 'onlyTranslation', 'noRotationOrReflection', 'noScale', 'noScaleOrReflection']);
|
||||
if (nonessential) b.color = r.color(); root.bones.push(b);
|
||||
}
|
||||
for (let i = 0, n = r.count(); i < n; i++) {
|
||||
const s = { name: r.string(), bone: boneName(), color: r.color() };
|
||||
const dark = r.color(); if (dark !== 'ffffffff') s.dark = dark.slice(2);
|
||||
if (!v34) { const dark = r.color(); if (dark !== 'ffffffff') s.dark = dark.slice(2); }
|
||||
s.attachment = r.string(); s.blend = r.ref(['normal', 'additive', 'multiply', 'screen']); root.slots.push(s);
|
||||
}
|
||||
for (let i = 0, n = r.count(); i < n; i++) {
|
||||
const c = { name: r.string(), order: r.varint(), bones: boneList(), target: boneName(), mix: r.float(), bendPositive: r.byte() === 1 };
|
||||
const c = { name: r.string(), order: v34 ? 0 : r.varint(), bones: boneList(), target: boneName(), mix: r.float(), bendPositive: r.byte() === 1 };
|
||||
if (v37) { c.compress = r.bool(); c.stretch = r.bool(); c.uniform = r.bool(); } root.ik.push(c);
|
||||
}
|
||||
for (let i = 0, n = r.count(); i < n; i++) {
|
||||
const c = { name: r.string(), order: r.varint(), bones: boneList(), target: boneName(), local: r.bool(), relative: r.bool() };
|
||||
const c = { name: r.string(), order: v34 ? 0 : r.varint(), bones: boneList(), target: boneName() };
|
||||
if (!v34) { c.local = r.bool(); c.relative = r.bool(); }
|
||||
fields(c, 'rotation x y scaleX scaleY shearY rotateMix translateMix scaleMix shearMix'); root.transform.push(c);
|
||||
}
|
||||
for (let i = 0, n = r.count(); i < n; i++) {
|
||||
const c = { name: r.string(), order: r.varint(), bones: boneList(), target: slotName(),
|
||||
const c = { name: r.string(), order: v34 ? 0 : r.varint(), bones: boneList(), target: slotName(),
|
||||
positionMode: r.ref(['fixed', 'percent']), spacingMode: r.ref(['length', 'fixed', 'percent']), rotateMode: r.ref(['tangent', 'chain', 'chainScale']) };
|
||||
fields(c, 'rotation position spacing rotateMix translateMix'); root.path.push(c);
|
||||
}
|
||||
@@ -108,6 +112,7 @@ function decode(bytes, runtimeDir) {
|
||||
function attachment(key) {
|
||||
const a = { name: r.string() || key };
|
||||
const type = r.byte();
|
||||
if (v34 && type > 4) r.fail('3.4 不支持此附件类型:' + type);
|
||||
a.type = ['region', 'boundingbox', 'mesh', 'linkedmesh', 'path', 'point', 'clipping'][type];
|
||||
if (!a.type) r.fail('未知附件类型:' + type);
|
||||
if (type === 0) {
|
||||
@@ -167,7 +172,7 @@ function decode(bytes, runtimeDir) {
|
||||
const type = r.byte();
|
||||
if (type === 0) timelines.attachment = frames(f => { f.name = r.string(); }, false);
|
||||
else if (type === 1) timelines.color = frames(f => { f.color = r.color(); });
|
||||
else if (type === 2) timelines.twoColor = frames(f => { f.light = r.color(); f.dark = r.color().slice(2); });
|
||||
else if (type === 2 && !v34) timelines.twoColor = frames(f => { f.light = r.color(); f.dark = r.color().slice(2); });
|
||||
else r.fail('未知插槽时间线');
|
||||
}
|
||||
}
|
||||
@@ -228,7 +233,8 @@ function decode(bytes, runtimeDir) {
|
||||
}
|
||||
|
||||
function install(spine, runtimeDir) {
|
||||
if (runtimeDir !== '3.6' && runtimeDir !== '3.7') return spine;
|
||||
if (!['3.4', '3.6', '3.7'].includes(runtimeDir)) return spine;
|
||||
if (runtimeDir === '3.4') spine.AtlasAttachmentLoader = spine.TextureAtlasAttachmentLoader;
|
||||
spine.SkeletonBinary = class SkeletonBinary {
|
||||
constructor(attachmentLoader) { this.attachmentLoader = attachmentLoader; this.scale = 1; }
|
||||
readSkeletonData(bytes) {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
// Eagle's item API has no arbitrary custom fields. Keep plugin metadata in
|
||||
// userData, keyed by library + item ID, without editing Eagle metadata.json.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
function identity(sourcePath, itemId) {
|
||||
const directory = path.dirname(path.resolve(sourcePath));
|
||||
const match = path.basename(directory).match(/^(.+)\.info$/i);
|
||||
return match
|
||||
? path.dirname(directory) + ':' + (itemId || match[1])
|
||||
: path.resolve(sourcePath);
|
||||
}
|
||||
|
||||
function createStore(userDataPath, sourcePath, itemId) {
|
||||
const key = crypto.createHash('sha256').update(identity(sourcePath, itemId)).digest('hex');
|
||||
const directory = path.join(userDataPath, 'SpineViewer', 'preview-settings');
|
||||
const file = path.join(directory, key + '.json');
|
||||
return {
|
||||
file,
|
||||
read() {
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
return typeof data.premultipliedAlpha === 'boolean' ? data.premultipliedAlpha : undefined;
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') return undefined;
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
// This method is called only by the user's checkbox change event.
|
||||
write(premultipliedAlpha) {
|
||||
if (typeof premultipliedAlpha !== 'boolean') throw new TypeError('预乘设置必须为布尔值');
|
||||
fs.mkdirSync(directory, { recursive: true });
|
||||
const temp = file + '.' + crypto.randomBytes(8).toString('hex') + '.tmp';
|
||||
try {
|
||||
fs.writeFileSync(temp, JSON.stringify({ premultipliedAlpha }, null, 2) + '\n', { flag: 'wx' });
|
||||
fs.renameSync(temp, file);
|
||||
} finally {
|
||||
try { fs.unlinkSync(temp); } catch (e) { if (e.code !== 'ENOENT') throw e; }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveDataPath(app) {
|
||||
try {
|
||||
const value = app && (app.userDataPath || (typeof app.getPath === 'function' && await app.getPath('userData')));
|
||||
if (typeof value === 'string' && value) return value;
|
||||
} catch (e) { /* Some Eagle format-view windows reject app.getPath IPC. */ }
|
||||
const home = require('os').homedir();
|
||||
if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Eagle');
|
||||
if (process.platform === 'win32') return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Eagle');
|
||||
return path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'Eagle');
|
||||
}
|
||||
|
||||
module.exports = { createStore, resolveDataPath };
|
||||
+3
-1
@@ -191,7 +191,8 @@ async function renderThumbnail({ skeletonPath, atlasPath, texturePaths, version,
|
||||
ctx.translate(-cx, -cy);
|
||||
|
||||
let renderer = null;
|
||||
if (spine.canvas && spine.canvas.SkeletonRenderer) renderer = new spine.canvas.SkeletonRenderer(ctx);
|
||||
if (runtimeDir === '3.4') renderer = require('./spine-canvas-clipping.js').createRenderer(spine, ctx, runtimeDir);
|
||||
else if (spine.canvas && spine.canvas.SkeletonRenderer) renderer = new spine.canvas.SkeletonRenderer(ctx);
|
||||
else if (spine.SkeletonRenderer) renderer = new spine.SkeletonRenderer(ctx);
|
||||
if (renderer) {
|
||||
// 使用三角渲染,正确处理 mesh 附件(如角色身体的网格)
|
||||
@@ -202,6 +203,7 @@ async function renderThumbnail({ skeletonPath, atlasPath, texturePaths, version,
|
||||
}
|
||||
|
||||
await drawSpineBadge(ctx, cw, ch);
|
||||
spineBadge.drawVersion(ctx, cw, ch, skeletonData.version || version);
|
||||
const buf = canvas.toBuffer('image/png');
|
||||
return buf;
|
||||
}
|
||||
|
||||
+4
-1
@@ -104,6 +104,7 @@ function chooseRuntimeDir(version, isBinary) {
|
||||
const major = parseInt(version.split('.')[0], 10);
|
||||
if (major === 3) {
|
||||
const minor = parseFloat(version);
|
||||
if (minor === 3.4) return '3.4';
|
||||
if (minor <= 3.6) return '3.6';
|
||||
if (minor <= 3.7 + 0.0001) return '3.7';
|
||||
return '3.8';
|
||||
@@ -360,7 +361,9 @@ function collectSpineFiles(roots, validFolders) {
|
||||
*/
|
||||
function collectSpineFilesCached(filePath, roots) {
|
||||
const libRoot = findLibraryRoot(filePath);
|
||||
const key = libRoot || path.dirname(path.dirname(filePath));
|
||||
// Outside an Eagle library, sibling asset folders are independent search
|
||||
// scopes. Sharing their parent as a cache key can return another skeleton.
|
||||
const key = libRoot || JSON.stringify([...roots].sort());
|
||||
const now = Date.now();
|
||||
const cached = fileIndexCache.get(key);
|
||||
if (cached && now - cached.time < 1500) return cached.files;
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
'use strict';
|
||||
|
||||
// Rasterize adjacent mesh triangles together, with GPU edge ownership rules.
|
||||
// Canvas clip()+drawImage() anti-aliases each triangle separately and exposes
|
||||
// the background along shared edges, which looks like a wireframe overlay.
|
||||
function createRasterizer(context) {
|
||||
const document = context.canvas && context.canvas.ownerDocument;
|
||||
if (!document || typeof context.getTransform !== 'function') return null;
|
||||
const canvas = document.createElement('canvas');
|
||||
const gl = canvas.getContext('webgl', {
|
||||
alpha: true, premultipliedAlpha: true, antialias: true,
|
||||
preserveDrawingBuffer: true, depth: false, stencil: false
|
||||
});
|
||||
if (!gl) return null;
|
||||
function shader(type, source) {
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source); gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(shader));
|
||||
return shader;
|
||||
}
|
||||
const vertex = shader(gl.VERTEX_SHADER, `
|
||||
attribute vec2 position;
|
||||
attribute vec2 uv;
|
||||
uniform vec2 resolution;
|
||||
varying vec2 textureUV;
|
||||
void main() {
|
||||
vec2 p = position / resolution * 2.0 - 1.0;
|
||||
gl_Position = vec4(p.x, -p.y, 0.0, 1.0);
|
||||
textureUV = uv;
|
||||
}`);
|
||||
const fragment = shader(gl.FRAGMENT_SHADER, `
|
||||
precision mediump float;
|
||||
uniform sampler2D image;
|
||||
uniform float opacity;
|
||||
varying vec2 textureUV;
|
||||
void main() {
|
||||
vec4 color = texture2D(image, textureUV);
|
||||
float alpha = color.a * opacity;
|
||||
gl_FragColor = vec4(color.rgb * opacity, alpha);
|
||||
}`);
|
||||
const program = gl.createProgram(); gl.attachShader(program, vertex); gl.attachShader(program, fragment); gl.linkProgram(program);
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw new Error(gl.getProgramInfoLog(program));
|
||||
gl.deleteShader(vertex); gl.deleteShader(fragment);
|
||||
const buffer = gl.createBuffer();
|
||||
const position = gl.getAttribLocation(program, 'position'), uv = gl.getAttribLocation(program, 'uv');
|
||||
const resolution = gl.getUniformLocation(program, 'resolution'), opacity = gl.getUniformLocation(program, 'opacity');
|
||||
const textures = new Map();
|
||||
let coordinates = [], currentImage = null, currentAlpha = 1, transform;
|
||||
let disposed = false, lost = false;
|
||||
canvas.addEventListener('webglcontextlost', event => { event.preventDefault(); lost = true; });
|
||||
function texture(image) {
|
||||
if (textures.has(image)) return textures.get(image);
|
||||
const t = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, t);
|
||||
// Premultiply before interpolation so translucent edges do not darken.
|
||||
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
textures.set(image, t); return t;
|
||||
}
|
||||
function flush() {
|
||||
if (!coordinates.length) return;
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture(currentImage));
|
||||
gl.uniform1f(opacity, currentAlpha);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(coordinates), gl.STREAM_DRAW);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, coordinates.length / 4);
|
||||
coordinates.length = 0;
|
||||
}
|
||||
return {
|
||||
begin() {
|
||||
if (disposed || lost || gl.isContextLost()) return false;
|
||||
const target = context.canvas;
|
||||
if (canvas.width !== target.width) canvas.width = target.width;
|
||||
if (canvas.height !== target.height) canvas.height = target.height;
|
||||
transform = context.getTransform(); coordinates.length = 0; currentImage = null;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.useProgram(program); gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||
gl.enableVertexAttribArray(position); gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 16, 0);
|
||||
gl.enableVertexAttribArray(uv); gl.vertexAttribPointer(uv, 2, gl.FLOAT, false, 16, 8);
|
||||
gl.uniform2f(resolution, canvas.width, canvas.height);
|
||||
gl.activeTexture(gl.TEXTURE0); gl.uniform1i(gl.getUniformLocation(program, 'image'), 0);
|
||||
gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
return true;
|
||||
},
|
||||
triangle(image, x0, y0, u0, v0, x1, y1, u1, v1, x2, y2, u2, v2) {
|
||||
const alpha = context.globalAlpha;
|
||||
if (image !== currentImage || alpha !== currentAlpha) { flush(); currentImage = image; currentAlpha = alpha; }
|
||||
const t = transform;
|
||||
coordinates.push(
|
||||
t.a * x0 + t.c * y0 + t.e, t.b * x0 + t.d * y0 + t.f, u0, v0,
|
||||
t.a * x1 + t.c * y1 + t.e, t.b * x1 + t.d * y1 + t.f, u1, v1,
|
||||
t.a * x2 + t.c * y2 + t.e, t.b * x2 + t.d * y2 + t.f, u2, v2);
|
||||
},
|
||||
end() {
|
||||
flush();
|
||||
context.save();
|
||||
try { context.setTransform(1, 0, 0, 1, 0, 0); context.globalAlpha = 1; context.drawImage(canvas, 0, 0); }
|
||||
finally { context.restore(); }
|
||||
},
|
||||
dispose() {
|
||||
if (disposed) return; disposed = true;
|
||||
for (const value of textures.values()) gl.deleteTexture(value);
|
||||
textures.clear(); gl.deleteBuffer(buffer); gl.deleteProgram(program);
|
||||
const extension = gl.getExtension('WEBGL_lose_context'); if (extension) extension.loseContext();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function attach(renderer, context) {
|
||||
let rasterizer;
|
||||
try { rasterizer = createRasterizer(context); } catch (e) { return renderer; }
|
||||
if (!rasterizer) return renderer;
|
||||
const draw = renderer.draw.bind(renderer), triangle = renderer.drawTriangle.bind(renderer);
|
||||
renderer.draw = skeleton => {
|
||||
if (!rasterizer.begin()) return draw(skeleton);
|
||||
renderer.drawTriangle = rasterizer.triangle;
|
||||
try { draw(skeleton); rasterizer.end(); }
|
||||
finally { renderer.drawTriangle = triangle; }
|
||||
};
|
||||
renderer.dispose = rasterizer.dispose;
|
||||
renderer.rasterization = 'webgl';
|
||||
return renderer;
|
||||
}
|
||||
|
||||
module.exports = { createRasterizer, attach };
|
||||
Reference in new issue
Block a user