mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Add pma, wraps and filter management.
This commit is contained in:
parent
235a96ca5c
commit
75f30e297d
@ -65,7 +65,7 @@ export class AssetLoader {
|
|||||||
await Promise.all(textureAtlas.pages.map(async page => {
|
await Promise.all(textureAtlas.pages.map(async page => {
|
||||||
const texture = await this.loadSpineTextureEditor(page.name, page.pma, instance);
|
const texture = await this.loadSpineTextureEditor(page.name, page.pma, instance);
|
||||||
if (texture) {
|
if (texture) {
|
||||||
const spineTexture = new C3TextureEditor(texture, renderer);
|
const spineTexture = new C3TextureEditor(texture, renderer, page);
|
||||||
page.setTexture(spineTexture);
|
page.setTexture(spineTexture);
|
||||||
}
|
}
|
||||||
return texture;
|
return texture;
|
||||||
@ -116,7 +116,7 @@ export class AssetLoader {
|
|||||||
await Promise.all(textureAtlas.pages.map(async page => {
|
await Promise.all(textureAtlas.pages.map(async page => {
|
||||||
const texture = await this.loadSpineTextureRuntime(page.name, page.pma, instance);
|
const texture = await this.loadSpineTextureRuntime(page.name, page.pma, instance);
|
||||||
if (texture) {
|
if (texture) {
|
||||||
const spineTexture = new C3Texture(texture, renderer);
|
const spineTexture = new C3Texture(texture, renderer, page);
|
||||||
page.setTexture(spineTexture);
|
page.setTexture(spineTexture);
|
||||||
}
|
}
|
||||||
return texture;
|
return texture;
|
||||||
@ -136,6 +136,7 @@ export class AssetLoader {
|
|||||||
|
|
||||||
static async createImageBitmapFromBlob (blob: Blob, pma: boolean): Promise<ImageBitmap | null> {
|
static async createImageBitmapFromBlob (blob: Blob, pma: boolean): Promise<ImageBitmap | null> {
|
||||||
try {
|
try {
|
||||||
|
// pma parameters seems to do not matter here. It matters in C3 Texture creation
|
||||||
return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" });
|
return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to create ImageBitmap from blob:", e);
|
console.error("Failed to create ImageBitmap from blob:", e);
|
||||||
|
|||||||
@ -27,25 +27,34 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { BlendMode, Texture, type TextureFilter, type TextureWrap } from "@esotericsoftware/spine-core";
|
import { BlendMode, Texture, type TextureAtlasPage, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
export class C3TextureEditor extends Texture {
|
export class C3TextureEditor extends Texture {
|
||||||
texture: SDK.Gfx.IWebGLTexture;
|
texture: SDK.Gfx.IWebGLTexture;
|
||||||
renderer: SDK.Gfx.IWebGLRenderer;
|
renderer: SDK.Gfx.IWebGLRenderer;
|
||||||
|
|
||||||
constructor (image: HTMLImageElement | ImageBitmap, renderer: SDK.Gfx.IWebGLRenderer) {
|
constructor (image: HTMLImageElement | ImageBitmap, renderer: SDK.Gfx.IWebGLRenderer, page: TextureAtlasPage) {
|
||||||
super(image);
|
super(image);
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.texture = renderer.CreateDynamicTexture(image.width, image.height);
|
const options: TextureCreateOptions = {
|
||||||
this.renderer.UpdateTexture(image, this.texture);
|
wrapX: toC3TextureWrap(page.uWrap),
|
||||||
|
wrapY: toC3TextureWrap(page.vWrap),
|
||||||
|
sampling: toC3Filter(page.minFilter),
|
||||||
|
mipMap: toC3MipMap(page.minFilter),
|
||||||
|
}
|
||||||
|
this.texture = renderer.CreateDynamicTexture(image.width, image.height, options);
|
||||||
|
this.renderer.UpdateTexture(image, this.texture, { premultiplyAlpha: !page.pma });
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter) {
|
setFilters () {
|
||||||
|
// cannot change filter after texture creation
|
||||||
}
|
}
|
||||||
|
|
||||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap) {
|
setWraps () {
|
||||||
|
// cannot change wraps after texture creation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
this.renderer.DeleteTexture(this.texture);
|
this.renderer.DeleteTexture(this.texture);
|
||||||
}
|
}
|
||||||
@ -55,17 +64,26 @@ export class C3Texture extends Texture {
|
|||||||
texture: ITexture;
|
texture: ITexture;
|
||||||
renderer: IRenderer;
|
renderer: IRenderer;
|
||||||
|
|
||||||
constructor (image: HTMLImageElement | ImageBitmap, renderer: IRenderer) {
|
constructor (image: HTMLImageElement | ImageBitmap, renderer: IRenderer, page: TextureAtlasPage) {
|
||||||
super(image);
|
super(image);
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.texture = renderer.createDynamicTexture(image.width, image.height);
|
const options: TextureCreateOptions = {
|
||||||
this.renderer.updateTexture(image, this.texture);
|
wrapX: toC3TextureWrap(page.uWrap),
|
||||||
|
wrapY: toC3TextureWrap(page.vWrap),
|
||||||
|
sampling: toC3Filter(page.minFilter),
|
||||||
|
mipMap: toC3MipMap(page.minFilter),
|
||||||
|
}
|
||||||
|
this.texture = renderer.createDynamicTexture(image.width, image.height, options);
|
||||||
|
this.renderer.updateTexture(image, this.texture, { premultiplyAlpha: !page.pma });
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter) {
|
|
||||||
|
setFilters () {
|
||||||
|
// cannot change filter after texture creation
|
||||||
}
|
}
|
||||||
|
|
||||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap) {
|
setWraps () {
|
||||||
|
// cannot change wraps after texture creation
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
@ -73,6 +91,49 @@ export class C3Texture extends Texture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toC3TextureWrap (wrap: TextureWrap): TextureWrapMode {
|
||||||
|
if (wrap === TextureWrap.ClampToEdge) return "clamp-to-edge";
|
||||||
|
else if (wrap === TextureWrap.MirroredRepeat) return "mirror-repeat";
|
||||||
|
else if (wrap === TextureWrap.Repeat) return "repeat";
|
||||||
|
else throw new Error(`Unknown texture wrap: ${wrap}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toC3MipMap (filter: TextureFilter): boolean {
|
||||||
|
switch (filter) {
|
||||||
|
case TextureFilter.MipMap:
|
||||||
|
case TextureFilter.MipMapLinearNearest:
|
||||||
|
case TextureFilter.MipMapNearestLinear:
|
||||||
|
case TextureFilter.MipMapNearestNearest:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case TextureFilter.Linear:
|
||||||
|
case TextureFilter.Nearest:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown texture filter: ${filter}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toC3Filter (filter: TextureFilter): TextureSamplingMode {
|
||||||
|
switch (filter) {
|
||||||
|
case TextureFilter.Nearest:
|
||||||
|
case TextureFilter.MipMapNearestNearest:
|
||||||
|
return "nearest";
|
||||||
|
|
||||||
|
case TextureFilter.Linear:
|
||||||
|
case TextureFilter.MipMapLinearNearest:
|
||||||
|
case TextureFilter.MipMapNearestLinear:
|
||||||
|
return "bilinear";
|
||||||
|
|
||||||
|
case TextureFilter.MipMap:
|
||||||
|
case TextureFilter.MipMapLinearLinear:
|
||||||
|
return "trilinear";
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown texture filter: ${filter}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const BlendingModeSpineToC3: Record<BlendMode, BlendModeParameter> = {
|
export const BlendingModeSpineToC3: Record<BlendMode, BlendModeParameter> = {
|
||||||
[BlendMode.Normal]: "normal",
|
[BlendMode.Normal]: "normal",
|
||||||
[BlendMode.Additive]: "additive",
|
[BlendMode.Additive]: "additive",
|
||||||
|
|||||||
@ -71,8 +71,10 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// workaround to request a redraw: https://github.com/Scirra/Construct-feature-requests/issues/615
|
||||||
this.x++;
|
this.x++;
|
||||||
this.x--;
|
this.x--;
|
||||||
|
|
||||||
this.update(this.dt);
|
this.update(this.dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,8 +226,12 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
const offsetY = this.y + this.propOffsetY;
|
const offsetY = this.y + this.propOffsetY;
|
||||||
const offsetAngle = this.angle + this.propOffsetAngle;
|
const offsetAngle = this.angle + this.propOffsetAngle;
|
||||||
|
|
||||||
const cos = Math.cos(offsetAngle);
|
let cos = 0;
|
||||||
const sin = Math.sin(offsetAngle);
|
let sin = 0;
|
||||||
|
if (offsetAngle) {
|
||||||
|
cos = Math.cos(offsetAngle);
|
||||||
|
sin = Math.sin(offsetAngle);
|
||||||
|
}
|
||||||
while (command) {
|
while (command) {
|
||||||
const { numVertices, positions, uvs, colors, indices, numIndices, blendMode } = command;
|
const { numVertices, positions, uvs, colors, indices, numIndices, blendMode } = command;
|
||||||
|
|
||||||
@ -236,10 +242,17 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
const dstIndex = i * 3;
|
const dstIndex = i * 3;
|
||||||
const x = positions[srcIndex];
|
const x = positions[srcIndex];
|
||||||
const y = positions[srcIndex + 1];
|
const y = positions[srcIndex + 1];
|
||||||
|
|
||||||
|
if (offsetAngle) {
|
||||||
vertices[dstIndex] = x * cos - y * sin + offsetX;
|
vertices[dstIndex] = x * cos - y * sin + offsetX;
|
||||||
vertices[dstIndex + 1] = x * sin + y * cos + offsetY;
|
vertices[dstIndex + 1] = x * sin + y * cos + offsetY;
|
||||||
|
} else {
|
||||||
|
vertices[dstIndex] = x + offsetX;
|
||||||
|
vertices[dstIndex + 1] = y + offsetY;
|
||||||
|
}
|
||||||
vertices[dstIndex + 2] = 0;
|
vertices[dstIndex + 2] = 0;
|
||||||
|
|
||||||
|
|
||||||
// there's something wrong with the hand after adding the colors on spineboy portal animation
|
// there's something wrong with the hand after adding the colors on spineboy portal animation
|
||||||
const color = colors[i];
|
const color = colors[i];
|
||||||
const colorDst = i * 4;
|
const colorDst = i * 4;
|
||||||
@ -254,6 +267,7 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
renderer.drawMesh(
|
renderer.drawMesh(
|
||||||
vertices.subarray(0, numVertices * 3),
|
vertices.subarray(0, numVertices * 3),
|
||||||
uvs.subarray(0, numVertices * 2),
|
uvs.subarray(0, numVertices * 2),
|
||||||
|
// workaround for this bug: https://github.com/Scirra/Construct-bugs/issues/8746
|
||||||
this.padUint16ArrayForWebGPU(indices.subarray(0, numIndices)),
|
this.padUint16ArrayForWebGPU(indices.subarray(0, numIndices)),
|
||||||
c3colors.subarray(0, numVertices * 4),
|
c3colors.subarray(0, numVertices * 4),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -136,10 +136,10 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
iRenderer.SetTextureFillMode();
|
iRenderer.SetTextureFillMode();
|
||||||
iRenderer.SetTexture(command.texture.texture);
|
iRenderer.SetTexture(command.texture.texture);
|
||||||
|
|
||||||
const padded = this.padUint16ArrayForWebGPU(indices.subarray(0, numIndices));
|
|
||||||
iRenderer.DrawMesh(
|
iRenderer.DrawMesh(
|
||||||
vertices.subarray(0, numVertices * 3),
|
vertices.subarray(0, numVertices * 3),
|
||||||
uvs.subarray(0, numVertices * 2),
|
uvs.subarray(0, numVertices * 2),
|
||||||
|
// workaround for this bug: https://github.com/Scirra/Construct-bugs/issues/8746
|
||||||
this.padUint16ArrayForWebGPU(indices.subarray(0, numIndices)),
|
this.padUint16ArrayForWebGPU(indices.subarray(0, numIndices)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user