mirror of
https://github.com/billsonnn/nitro-renderer.git
synced 2025-01-18 22:36:27 +01:00
Start PaletteMapFilter
This commit is contained in:
parent
9cbdd937d2
commit
9a403d72db
@ -1,5 +1,5 @@
|
|||||||
import { AvatarAction, AvatarDirectionAngle, AvatarScaleType, AvatarSetType, IActiveActionData, IAnimationLayerData, IAvatarDataContainer, IAvatarEffectListener, IAvatarFigureContainer, IAvatarImage, IPartColor, ISpriteDataContainer } from '@nitrots/api';
|
import { AvatarAction, AvatarDirectionAngle, AvatarScaleType, AvatarSetType, IActiveActionData, IAnimationLayerData, IAvatarDataContainer, IAvatarEffectListener, IAvatarFigureContainer, IAvatarImage, IPartColor, ISpriteDataContainer } from '@nitrots/api';
|
||||||
import { GetRenderer, GetTexturePool, GetTickerTime, TextureUtils } from '@nitrots/utils';
|
import { GetRenderer, GetTexturePool, GetTickerTime, PaletteMapFilter, TextureUtils } from '@nitrots/utils';
|
||||||
import { ColorMatrixFilter, Container, RenderTexture, Sprite, Texture } from 'pixi.js';
|
import { ColorMatrixFilter, Container, RenderTexture, Sprite, Texture } from 'pixi.js';
|
||||||
import { AvatarFigureContainer } from './AvatarFigureContainer';
|
import { AvatarFigureContainer } from './AvatarFigureContainer';
|
||||||
import { AvatarStructure } from './AvatarStructure';
|
import { AvatarStructure } from './AvatarStructure';
|
||||||
@ -230,18 +230,29 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener
|
|||||||
partCount--;
|
partCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container.filters = [];
|
||||||
|
|
||||||
if(this._avatarSpriteData)
|
if(this._avatarSpriteData)
|
||||||
{
|
{
|
||||||
if(!Array.isArray(container.filters)) container.filters = [];
|
if(this._avatarSpriteData.colorTransform)
|
||||||
|
{
|
||||||
if(this._avatarSpriteData.colorTransform) container.filters.push(this._avatarSpriteData.colorTransform);
|
if(container.filters === undefined || container.filters === null) container.filters = [ this._avatarSpriteData.colorTransform ];
|
||||||
|
else if(Array.isArray(container.filters)) container.filters = [ ...container.filters, this._avatarSpriteData.colorTransform ];
|
||||||
|
else container.filters = [ container.filters, this._avatarSpriteData.colorTransform ];
|
||||||
|
}
|
||||||
|
|
||||||
if(this._avatarSpriteData.paletteIsGrayscale)
|
if(this._avatarSpriteData.paletteIsGrayscale)
|
||||||
{
|
{
|
||||||
this.convertToGrayscale(container);
|
this.convertToGrayscale(container);
|
||||||
|
|
||||||
// TODO: rewrite the palette map filter
|
const paletteMapFilter = new PaletteMapFilter({
|
||||||
//container.filters.push(new PaletteMapFilter(this._avatarSpriteData.reds, PaletteMapFilter.CHANNEL_RED));
|
palette: this._avatarSpriteData.reds,
|
||||||
|
channel: PaletteMapFilter.CHANNEL_RED
|
||||||
|
});
|
||||||
|
|
||||||
|
if(container.filters === undefined || container.filters === null) container.filters = [ paletteMapFilter ];
|
||||||
|
else if(Array.isArray(container.filters)) container.filters = [ ...container.filters, paletteMapFilter ];
|
||||||
|
else container.filters = [ container.filters, paletteMapFilter ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,18 +732,18 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colorFilter = new ColorMatrixFilter();
|
const filter = new ColorMatrixFilter();
|
||||||
|
|
||||||
colorFilter.matrix = [
|
filter.matrix = [
|
||||||
redWeight, greenWeight, blueWeight, 0, 0, // Red channel
|
redWeight, greenWeight, blueWeight, 0, 0, // Red channel
|
||||||
redWeight, greenWeight, blueWeight, 0, 0, // Green channel
|
redWeight, greenWeight, blueWeight, 0, 0, // Green channel
|
||||||
redWeight, greenWeight, blueWeight, 0, 0, // Blue channel
|
redWeight, greenWeight, blueWeight, 0, 0, // Blue channel
|
||||||
0, 0, 0, 1, 0 // Alpha channel
|
0, 0, 0, 1, 0 // Alpha channel
|
||||||
];
|
];
|
||||||
|
|
||||||
if(!Array.isArray(container.filters)) container.filters = [];
|
if(container.filters === undefined || container.filters === null) container.filters = [ filter ];
|
||||||
|
else if(Array.isArray(container.filters)) container.filters = [ ...container.filters, filter ];
|
||||||
container.filters.push(colorFilter);
|
else container.filters = [ container.filters, filter ];
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
147
packages/utils/src/filters/PaletteMapFilter.ts
Normal file
147
packages/utils/src/filters/PaletteMapFilter.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { BufferImageSource, Filter, FilterSystem, GlProgram, RenderSurface, Texture } from 'pixi.js';
|
||||||
|
import { TextureUtils } from '../TextureUtils';
|
||||||
|
|
||||||
|
export interface PaletteMapFilterOptions
|
||||||
|
{
|
||||||
|
palette: number[];
|
||||||
|
channel: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaletteMapFilter extends Filter
|
||||||
|
{
|
||||||
|
public static readonly CHANNEL_RED = 0;
|
||||||
|
public static readonly CHANNEL_GREEN = 1;
|
||||||
|
public static readonly CHANNEL_BLUE = 2;
|
||||||
|
public static readonly CHANNEL_ALPHA = 3;
|
||||||
|
|
||||||
|
public static readonly DEFAULT_OPTIONS: PaletteMapFilterOptions = {
|
||||||
|
palette: [],
|
||||||
|
channel: PaletteMapFilter.CHANNEL_RED,
|
||||||
|
};
|
||||||
|
|
||||||
|
public uniforms: {
|
||||||
|
uPalette: Float32Array,
|
||||||
|
uChannel: Float32Array
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(options: PaletteMapFilterOptions)
|
||||||
|
{
|
||||||
|
options = { ...PaletteMapFilter.DEFAULT_OPTIONS, ...options };
|
||||||
|
|
||||||
|
const glProgram = GlProgram.from({
|
||||||
|
vertex: `in vec2 aPosition;
|
||||||
|
out vec2 vTextureCoord;
|
||||||
|
|
||||||
|
uniform vec4 uInputSize;
|
||||||
|
uniform vec4 uOutputFrame;
|
||||||
|
uniform vec4 uOutputTexture;
|
||||||
|
|
||||||
|
vec4 filterVertexPosition( void )
|
||||||
|
{
|
||||||
|
vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
|
||||||
|
|
||||||
|
position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
|
||||||
|
position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
|
||||||
|
|
||||||
|
return vec4(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 filterTextureCoord( void )
|
||||||
|
{
|
||||||
|
return aPosition * (uOutputFrame.zw * uInputSize.zw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
gl_Position = filterVertexPosition();
|
||||||
|
vTextureCoord = filterTextureCoord();
|
||||||
|
}`,
|
||||||
|
fragment: `
|
||||||
|
in vec2 vTextureCoord;
|
||||||
|
out vec4 finalColor;
|
||||||
|
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
uniform sampler2D uLutTexture;
|
||||||
|
uniform int channel;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
vec4 currentColor = texture(uTexture, vTextureCoord);
|
||||||
|
vec4 adjusted = currentColor;
|
||||||
|
|
||||||
|
if(currentColor.a > 0.0)
|
||||||
|
{
|
||||||
|
if(channel == 0)
|
||||||
|
{
|
||||||
|
adjusted = texture2D(uLutTexture, vec2((currentColor.r * 255.0 + 0.5) / 256.0, 0.5));
|
||||||
|
} else if(channel == 1) {
|
||||||
|
adjusted = texture2D(uLutTexture, vec2((currentColor.g * 255.0 + 0.5) / 256.0, 0.5));
|
||||||
|
} else if(channel == 2) {
|
||||||
|
adjusted = texture2D(uLutTexture, vec2((currentColor.b * 255.0 + 0.5) / 256.0, 0.5));
|
||||||
|
} else if(channel == 3) {
|
||||||
|
adjusted = texture2D(uLutTexture, vec2((currentColor.a * 255.0 + 0.5) / 256.0, 0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalColor = vec4(adjusted.r, adjusted.g, adjusted.b, currentColor.a);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
name: 'palette-map-filter',
|
||||||
|
});
|
||||||
|
|
||||||
|
const lookUpTable = PaletteMapFilter.getLookUpTable(options.palette);
|
||||||
|
|
||||||
|
const lutTexture = new Texture({
|
||||||
|
source: new BufferImageSource({
|
||||||
|
resource: Uint8Array.from(lookUpTable),
|
||||||
|
width: lookUpTable.length / 4,
|
||||||
|
height: 1
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
console.log(await TextureUtils.generateImageUrl(lutTexture));
|
||||||
|
})();
|
||||||
|
|
||||||
|
super({
|
||||||
|
gpuProgram: null,
|
||||||
|
glProgram,
|
||||||
|
resources: {
|
||||||
|
paletteMapUniforms: {
|
||||||
|
uChannel: { value: options.channel, type: 'int' }
|
||||||
|
},
|
||||||
|
uLutTexture: lutTexture.source
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.uniforms = this.resources.paletteMapUniforms.uniforms;
|
||||||
|
|
||||||
|
Object.assign(this, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public apply(
|
||||||
|
filterManager: FilterSystem,
|
||||||
|
input: Texture,
|
||||||
|
output: RenderSurface,
|
||||||
|
clearMode: boolean,
|
||||||
|
): void
|
||||||
|
{
|
||||||
|
|
||||||
|
filterManager.applyFilter(this, input, output, clearMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getLookUpTable(data: number[]): number[]
|
||||||
|
{
|
||||||
|
const lookUpTable = [];
|
||||||
|
|
||||||
|
for(let i = 0; i < data.length; i++)
|
||||||
|
{
|
||||||
|
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_RED] = ((data[i] >> 16) & 0xFF);
|
||||||
|
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_GREEN] = ((data[i] >> 8) & 0xFF);
|
||||||
|
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_BLUE] = (data[i] & 0xFF);
|
||||||
|
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_ALPHA] = ((data[i] >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookUpTable;
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,7 @@ export class WiredFilter extends Filter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
name: 'plane-mask-filter',
|
name: 'wired-filter',
|
||||||
});
|
});
|
||||||
|
|
||||||
super({
|
super({
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
|
export * from './PaletteMapFilter';
|
||||||
export * from './PlaneMaskFilter';
|
export * from './PlaneMaskFilter';
|
||||||
export * from './WiredFilter';
|
export * from './WiredFilter';
|
||||||
|
Loading…
Reference in New Issue
Block a user